Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Refactor code to support multiple engines registered at the same time…

…, add

metaprogramming code from _why to utility.rb
  • Loading branch information...
commit 813dbadddc5cfef125c2bbb5145ab362d0506a38 1 parent c6543cc
@paulcbetts authored
View
2  AUTHORS
@@ -1,3 +1,5 @@
Paul Betts <paul.betts@gmail.com>
+
Jonathan Dahl - daemonize.rb
Chris Wanstrath - command.rb
+_why - Metaprogramming code from utility.cs
View
9 lib/controllers.rb
@@ -85,7 +85,14 @@ class MainController < Ramaze::Controller
engine :Erubis
def index
- @items = Yikes.instance.engine.state.get_finished_items
+ @items = []
+ Yikes.instance.active_engines.each do |e|
+ @items = @items + e.state.get_finished_items
+ end
+ logger.debug @items.to_yaml
+
+ # FIXME: Figure out when we have internet why this fails
+ #@items.sort! {|x| x.finished_at}
end
end
View
96 lib/engine.rb
@@ -38,14 +38,12 @@
$logging_level ||= Logger::ERROR
-class EngineManager
-end
-
class Engine
include ApplicationState
- def initialize(transcoder = FFMpegTranscoder)
+ def initialize(transcoder = FFMpegTranscoder, library = nil)
@transcoder = transcoder.new
+ new_state library if library
end
def enqueue_files_to_encode(library, target)
@@ -71,16 +69,6 @@ def do_encode(library, target)
logger.info "Finished"
end
- def poll_directory_and_encode(library, target, rate)
- logger.info "We're daemonized!"
-
- until $do_quit
- do_encode(library,target)
- Kernel.sleep(rate || 60*30)
- end
- end
-
-
# Main function for converting a video file and writing it to a folder
def convert_file(item, state)
if create_path_and_convert(item)
@@ -125,6 +113,86 @@ def should_encode?(item)
end
end
+module EngineManager
+ def add_engine(library, target, transcoder = FFMpegTranscoder)
+ initialize_if_needed
+
+ e = nil
+ @engines_lock.synchronize do
+ e = (@engines[engine_key(library, target)] ||= Engine.new(transcoder, library))
+ end
+ e
+ end
+
+ def active_engines
+ initialize_if_needed
+
+ ret = nil
+ @engines_lock.synchronize do
+ ret = @engines.values
+ end
+ ret
+ end
+
+ def remove_engine(library, target)
+ initialize_if_needed
+
+ @engines_lock.synchronize do
+ @engines.delete engine_key(library, target)
+ end
+ end
+
+ def load_state(path)
+ initialize_if_needed
+
+ begin
+ # We hold a different State class for every library path
+ @engines = YAML::load(File.read(path))
+ rescue
+ @engines = {}
+ end
+ end
+
+ def save_state(path)
+ initialize_if_needed
+
+ @engines_lock.synchronize do
+ File.open(path, 'w') {|f| f.write(YAML::dump(@engines)) }
+ end
+ end
+
+ def poll_directory_and_encode(rate)
+ until $do_quit
+ # FIXME: This is unsynchronized, but if we use the lock, we'll block everyone
+ # else forever
+ @engines.each_pair do |key,engine|
+ break if $do_quit
+ library, target = *key
+
+ logger.info "Starting encode for '#{library}' => '#{target}'"
+ engine.do_encode(library,target)
+ Kernel.sleep(rate || 60*30)
+ end
+
+ while @engines.length == 0
+ break if $do_quit
+ logger.info _("Nothing to do! Taking a nap...")
+ Kernel.sleep(rate || 60*30)
+ end
+ end
+ end
+
+private
+ def initialize_if_needed
+ @engines ||= {}
+ @engines_lock ||= Mutex.new
+ end
+
+ def engine_key(library, target)
+ [library, target]
+ end
+end
+
module ExternalTranscoder
def transcode(input, output)
before_transcode
View
24 lib/main.rb
@@ -49,15 +49,18 @@
AllowedFiletypes = ['.avi', '.mov', '.mp4', '.wmv']
+Thread.abort_on_exception = true
+
class Yikes < Logger::Application
include Singleton
- attr_accessor :engine
+ include EngineManager
def initialize
super(self.class.to_s)
self.level = $logging_level
end
+
#
# Main Methods
#
@@ -137,16 +140,16 @@ def run(args)
# Reset our logging level because option parsing changed it
@log.level = $logging_level
- @engine = Engine.new
- @engine.load_state(File.join(Platform.settings_dir, 'state.yaml'), results[:library])
- @engine.state.target = results[:target]
-
# Actually do stuff
unless results[:background]
- # Just a single run
- @engine.do_encode(results[:library], results[:target])
+ engine = Engine.new(results[:library])
+ engine.do_encode results[:library], results[:target]
else
puts _("Yikes started in the background. Go to http://#{Platform.hostname}.local:4000 !")
+ load_state File.join(Platform.settings_dir, 'state.yaml')
+
+ add_engine results[:library], results[:target]
+
if should_daemonize?
return unless daemonize
@log = Logger.new('/tmp/yikes.log')
@@ -155,20 +158,21 @@ def run(args)
start_web_service_async
begin
- @engine.poll_directory_and_encode(results[:library], results[:target], results[:rate])
+ logger.info "We're daemonized!"
+ poll_directory_and_encode results[:rate]
rescue Exception => e
logger.fatal e.message
logger.fatal e.backtrace.join("\n")
end
+
+ save_state File.join(Platform.settings_dir, 'state.yaml')
end
- @engine.save_state(File.join(Platform.settings_dir, 'state.yaml'))
logger.debug 'Exiting application'
end
-
#
# Auxillary methods
#
View
27 lib/state.rb
@@ -41,29 +41,6 @@
module ApplicationState
attr_accessor :state
- def load_state(path, library)
- # If they give us a bum path, we can't get the real path
- real_lib = nil
- begin
- real_lib = Pathname.new(library).realpath.to_s
- rescue
- real_lib = library
- end
-
- begin
- # We hold a different State class for every library path
- @full_state = YAML::load(File.read(path))
- @state = @full_state[real_lib] || (@full_state[real_lib] = State.new(library))
- rescue
- @full_state = { real_lib => State.new(library) }
- @state = @full_state[real_lib]
- end
- end
-
- def save_state(path)
- File.open(path, 'w') {|f| f.write(YAML::dump(@full_state)) }
- end
-
class State
attr_accessor :encoded_queue
attr_accessor :to_encode_queue
@@ -134,6 +111,10 @@ def get_finished_items
ret
end
end
+
+ def new_state(library)
+ @state = State.new(library)
+ end
end
class EncodingItem
View
25 lib/utility.rb
@@ -66,3 +66,28 @@ def dump_stacks
raise Exception, "Stack Dump"
end
end
+
+# Metaid == a few simple metaclass helper
+# (See http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html.)
+class Object
+ # The hidden singleton lurks behind everyone
+ def metaclass; class << self; self; end; end
+ def meta_eval &blk; metaclass.instance_eval(&blk); end
+
+ # Adds methods to a metaclass
+ def meta_def name, &blk
+ meta_eval { define_method name, &blk }
+ end
+end
+
+class Module
+ # Defines an instance method within a module
+ def module_def name, &blk
+ module_eval { define_method name, &blk }
+ end
+end
+
+class Class
+ alias class_def module_def
+end
+
View
84 spec/engine_spec.rb
@@ -13,34 +13,6 @@
EOF
-describe EngineManager do
- it "should be able to add engines given a source and target" do
- em = EngineManager.new
- source = "/path/to/source.avi"
- target = "/the/target/source.mp4"
- em.add(source, target)
- end
-
- it "should be able to remove engines" do
- em = EngineManager.new
- source = "/path/to/source.avi"
- target = "/the/target/source.mp4"
- em.add(source, target)
- em.remove(source, target)
- end
-
- it "should be able to enumerate through engines" do
- em = EngineManager.new
- source = "/path/to/source.avi"
- target = "/the/target/source.mp4"
- em.add(source, target)
-
- i = 0
- em.each {|x| i=i+1}
- i.should = 1
- end
-end
-
describe Engine do
it "Should convert a file and mark it succeeded or failed" do
source = "/path/to/source.avi"
@@ -67,6 +39,62 @@
end
+class MockEngineManager
+ include EngineManager
+end
+
+describe EngineManager do
+ it "should be able to add engines given a source and target" do
+ em = MockEngineManager.new
+ source = "/path/to/source.avi"
+ target = "/the/target/source.mp4"
+ em.add_engine(source, target)
+ em.active_engines.length.should == 1
+ end
+
+ it "should be able to remove engines" do
+ em = MockEngineManager.new
+ source = "/path/to/source.avi"
+ target = "/the/target/source.mp4"
+
+ em.add_engine(source, target)
+ em.active_engines.length.should == 1
+
+ em.remove_engine(source, target)
+ em.active_engines.length.should == 0
+ end
+
+ it "should save and load state properly" do
+ em = MockEngineManager.new
+ s = em.load_state("/doesnt/___exist")
+ items = [1,2,3,4,5].collect {|x| EncodingItem.new(Library, "bob/foo_#{x}.avi", Target) }
+
+ source = "/path/to/source.avi"
+ target = "/the/target/source.mp4"
+ s = em.add_engine(source, target).state
+
+ s.add_to_queue(items)
+ two_items = s.dequeue_items(2)
+ s.encode_failed! two_items[0]
+ s.encode_succeeded! two_items[1]
+
+ p = Pathname.new(File.join(TestDir, "state.yaml"))
+ p.delete if p.exist?
+ em.save_state(p.to_s)
+
+ em = MockEngineManager.new
+ em.load_state(p.to_s)
+ em.active_engines.length.should == 1
+ s = em.active_engines[0].state
+
+ s.items_count.should == 3
+ s.get_finished_items.length.should == 2
+ s.get_finished_items[1].source_path.should == "/path/to/library/bob/foo_2.avi"
+
+ p.delete
+ end
+end
+
describe FFMpegTranscoder do
before do
@fft = FFMpegTranscoder.new
View
4 spec/main_spec.rb
@@ -40,6 +40,10 @@ def hook_stdio(in_str = "")
output.index("Usage:").should == 0
end
+ it "should include the engine manager" do
+ Yikes.instance.respond_to?(:load_state).should == true
+ end
+
it "should display help options" do
i,o,e = hook_stdio do
Yikes.instance.run(%w{--help})
View
23 spec/state_spec.rb
@@ -27,29 +27,6 @@ class MockState
s.get_finished_items.length.should == 1
s.get_finished_items[0].source_path.should == "/path/to/library/bob/foo_2.avi"
end
-
- it "should save and load state properly" do
- ms = MockState.new
- s = ms.load_state("/doesnt/___exist", Library)
- items = [1,2,3,4,5].collect {|x| EncodingItem.new(Library, "bob/foo_#{x}.avi", Target) }
-
- s.add_to_queue(items)
- two_items = s.dequeue_items(2)
- s.encode_failed! two_items[0]
- s.encode_succeeded! two_items[1]
-
- p = Pathname.new(File.join(TestDir, "state.yaml"))
- p.delete if p.exist?
- ms.save_state(p.to_s)
-
- ms = MockState.new
- s = ms.load_state(p.to_s, Library)
- s.items_count.should == 3
- s.get_finished_items.length.should == 2
- s.get_finished_items[1].source_path.should == "/path/to/library/bob/foo_2.avi"
-
- p.delete
- end
end
describe EncodingItem do
Please sign in to comment.
Something went wrong with that request. Please try again.