<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,3 +1,5 @@
 Paul Betts &lt;paul.betts@gmail.com&gt;
+
 Jonathan Dahl - daemonize.rb
 Chris Wanstrath - command.rb
+_why - Metaprogramming code from utility.cs</diff>
      <filename>AUTHORS</filename>
    </modified>
    <modified>
      <diff>@@ -85,7 +85,14 @@ class MainController &lt; 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
 </diff>
      <filename>lib/controllers.rb</filename>
    </modified>
    <modified>
      <diff>@@ -38,14 +38,12 @@ include GetText
 
 $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 @@ class Engine
 		logger.info &quot;Finished&quot;
 	end
 
-	def poll_directory_and_encode(library, target, rate)
-		logger.info &quot;We're daemonized!&quot;
-
-		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 @@ class Engine
 	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 &quot;Starting encode for '#{library}' =&gt; '#{target}'&quot;
+				engine.do_encode(library,target)
+				Kernel.sleep(rate || 60*30)
+			end
+
+			while @engines.length == 0 
+				break if $do_quit
+				logger.info _(&quot;Nothing to do! Taking a nap...&quot;)
+				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</diff>
      <filename>lib/engine.rb</filename>
    </modified>
    <modified>
      <diff>@@ -49,15 +49,18 @@ $logging_level = ($DEBUG ? Logger::DEBUG : Logger::ERROR)
 
 AllowedFiletypes = ['.avi', '.mov', '.mp4', '.wmv']
 
+Thread.abort_on_exception = true
+
 class Yikes &lt; 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 @@ class Yikes &lt; Logger::Application
 		# 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 _(&quot;Yikes started in the background. Go to http://#{Platform.hostname}.local:4000 !&quot;)
+			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 @@ class Yikes &lt; Logger::Application
 			start_web_service_async
 
 			begin 
-				@engine.poll_directory_and_encode(results[:library], results[:target], results[:rate])
+				logger.info &quot;We're daemonized!&quot;
+				poll_directory_and_encode results[:rate]
 			rescue Exception =&gt; e
 				logger.fatal e.message
 				logger.fatal e.backtrace.join(&quot;\n&quot;)
 			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
 	#</diff>
      <filename>lib/main.rb</filename>
    </modified>
    <modified>
      <diff>@@ -41,29 +41,6 @@ ItemsInFinishedList = 30
 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 =&gt; 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 @@ module ApplicationState
 			ret
 		end
 	end
+
+	def new_state(library)
+		@state = State.new(library)
+	end
 end
 
 class EncodingItem</diff>
      <filename>lib/state.rb</filename>
    </modified>
    <modified>
      <diff>@@ -66,3 +66,28 @@ def dump_stacks
 		raise Exception, &quot;Stack Dump&quot;
 	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 &lt;&lt; self; self; end; end
+    def meta_eval &amp;blk; metaclass.instance_eval(&amp;blk); end
+
+    # Adds methods to a metaclass
+    def meta_def name, &amp;blk
+        meta_eval { define_method name, &amp;blk }
+    end
+end
+
+class Module
+    # Defines an instance method within a module
+    def module_def name, &amp;blk
+        module_eval { define_method name, &amp;blk }
+    end
+end
+
+class Class
+    alias class_def module_def
+end
+</diff>
      <filename>lib/utility.rb</filename>
    </modified>
    <modified>
      <diff>@@ -13,34 +13,6 @@ target: /the/target
 
 EOF
 
-describe EngineManager do
-	it &quot;should be able to add engines given a source and target&quot; do
-		em = EngineManager.new
-		source = &quot;/path/to/source.avi&quot;
-		target = &quot;/the/target/source.mp4&quot;
-		em.add(source, target)
-	end
-
-	it &quot;should be able to remove engines&quot; do
-		em = EngineManager.new
-		source = &quot;/path/to/source.avi&quot;
-		target = &quot;/the/target/source.mp4&quot;
-		em.add(source, target)
-		em.remove(source, target)
-	end
-
-	it &quot;should be able to enumerate through engines&quot; do
-		em = EngineManager.new
-		source = &quot;/path/to/source.avi&quot;
-		target = &quot;/the/target/source.mp4&quot;
-		em.add(source, target)
-		
-		i = 0
-		em.each {|x| i=i+1}
-		i.should = 1
-	end
-end
-
 describe Engine do
 	it &quot;Should convert a file and mark it succeeded or failed&quot; do
 		source = &quot;/path/to/source.avi&quot;
@@ -67,6 +39,62 @@ describe Engine do
 
 end
 
+class MockEngineManager
+	include EngineManager
+end
+
+describe EngineManager do
+	it &quot;should be able to add engines given a source and target&quot; do
+		em = MockEngineManager.new
+		source = &quot;/path/to/source.avi&quot;
+		target = &quot;/the/target/source.mp4&quot;
+		em.add_engine(source, target)
+		em.active_engines.length.should == 1
+	end
+
+	it &quot;should be able to remove engines&quot; do
+		em = MockEngineManager.new
+		source = &quot;/path/to/source.avi&quot;
+		target = &quot;/the/target/source.mp4&quot;
+
+		em.add_engine(source, target)
+		em.active_engines.length.should == 1
+
+		em.remove_engine(source, target)
+		em.active_engines.length.should == 0
+	end
+
+	it &quot;should save and load state properly&quot; do
+		em = MockEngineManager.new
+		s = em.load_state(&quot;/doesnt/___exist&quot;)
+		items = [1,2,3,4,5].collect {|x| EncodingItem.new(Library, &quot;bob/foo_#{x}.avi&quot;, Target) }
+
+		source = &quot;/path/to/source.avi&quot;
+		target = &quot;/the/target/source.mp4&quot;
+		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, &quot;state.yaml&quot;))
+		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 == &quot;/path/to/library/bob/foo_2.avi&quot;
+		
+		p.delete
+	end
+end
+
 describe FFMpegTranscoder do
 	before do
 		@fft = FFMpegTranscoder.new</diff>
      <filename>spec/engine_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -40,6 +40,10 @@ describe Yikes do
 		output.index(&quot;Usage:&quot;).should == 0
 	end
 
+	it &quot;should include the engine manager&quot; do
+		Yikes.instance.respond_to?(:load_state).should == true
+	end
+
 	it &quot;should display help options&quot; do
 		i,o,e = hook_stdio do
 			Yikes.instance.run(%w{--help})</diff>
      <filename>spec/main_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -27,29 +27,6 @@ describe ApplicationState::State do
 		s.get_finished_items.length.should == 1
 		s.get_finished_items[0].source_path.should == &quot;/path/to/library/bob/foo_2.avi&quot;
 	end
-
-	it &quot;should save and load state properly&quot; do
-		ms = MockState.new
-		s = ms.load_state(&quot;/doesnt/___exist&quot;, Library)
-		items = [1,2,3,4,5].collect {|x| EncodingItem.new(Library, &quot;bob/foo_#{x}.avi&quot;, 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, &quot;state.yaml&quot;))
-		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 == &quot;/path/to/library/bob/foo_2.avi&quot;
-		
-		p.delete
-	end
 end
 
 describe EncodingItem do</diff>
      <filename>spec/state_spec.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>c6543cc43fa258e1c138a2e091d2824e9760c7b0</id>
    </parent>
  </parents>
  <author>
    <name>Paul Betts</name>
    <email>paul@paulbetts.org</email>
  </author>
  <url>http://github.com/xpaulbettsx/yikes/commit/813dbadddc5cfef125c2bbb5145ab362d0506a38</url>
  <id>813dbadddc5cfef125c2bbb5145ab362d0506a38</id>
  <committed-date>2008-06-27T19:58:18-07:00</committed-date>
  <authored-date>2008-06-27T19:56:41-07:00</authored-date>
  <message>Refactor code to support multiple engines registered at the same time, add
metaprogramming code from _why to utility.rb</message>
  <tree>d0595d8f5f16fe4bc30f0fceafc77ff0b541d1f7</tree>
  <committer>
    <name>Paul Betts</name>
    <email>paul@paulbetts.org</email>
  </committer>
</commit>
