home *** CD-ROM | disk | FTP | other *** search
/ PC Welt 2006 November (DVD) / PCWELT_11_2006.ISO / casper / filesystem.squashfs / usr / lib / python2.4 / site-packages / serpentine / converting.py < prev    next >
Encoding:
Python Source  |  2006-08-23  |  12.0 KB  |  394 lines

  1. # Copyright(C) 2004 Tiago Cogumbreiro <cogumbreiro@users.sf.net>
  2. #
  3. # This library is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU Library General Public
  5. # License as published by the Free Software Foundation; either
  6. # version 2 of the License, or(at your option) any later version.
  7. #
  8. # This library is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11. # Library General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU Library General Public
  14. # License along with this library; if not, write to the
  15. # Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  16. # Boston, MA 02111-1307, USA.
  17. #
  18. # Authors: Tiago Cogumbreiro <cogumbreiro@users.sf.net>
  19.  
  20. """"
  21. This module is used to convert audio data to local WAV files. These files
  22. are cached in the local filesystem and can be used by the recording module.
  23. """
  24. import tempfile
  25. import os
  26. import gst
  27.  
  28. from types import StringType
  29.  
  30. # Local imports
  31. import urlutil    
  32. import operations
  33. import audio
  34.  
  35.  
  36. class GetMusic(operations.MeasurableOperation, operations.OperationListener):
  37.     """"
  38.     A Measurable Operation that finishes when the filename is available in the 
  39.     music pool.
  40.     """
  41.     def __init__(self, pool, music):
  42.         operations.MeasurableOperation.__init__(self)
  43.         self.__music = music
  44.         self.__pool = pool
  45.         self.__oper = None
  46.     
  47.     def getProgress(self):
  48.         if self.__oper is not None:
  49.             return self.__oper.progress
  50.         
  51.         return 0.0
  52.             
  53.     progress = property(getProgress)
  54.     
  55.     running = property(lambda self: self.__oper and self.__oper.running)
  56.     
  57.     can_stop = property(lambda self: self.__oper and self.__oper.can_stop)
  58.     
  59.     music = property(lambda self: self.__music)
  60.     
  61.     pool = property(lambda self: self.__pool)
  62.     
  63.     def start(self):
  64.         if self.__pool.is_available(self.__music):
  65.             self._send_finished_event(operations.SUCCESSFUL)
  66.             self.__oper = None
  67.         else:
  68.             try:
  69.                 self.__oper = self.__pool.fetch_music(self.__music)
  70.                 self.__oper.listeners.append(self)
  71.                 self.__oper.start()
  72.             except Exception, e:
  73.                 import traceback
  74.                 traceback.print_exc()
  75.                 self._send_finished_event(operations.ERROR, error=e)
  76.  
  77.     def on_finished(self, event):
  78.         self._send_finished_event(event.id, event.error)
  79.         self.__oper = None
  80.     
  81.     def stop(self):
  82.         assert self.__oper
  83.         self.__oper.stop()
  84.  
  85. class MusicPool:
  86.     """
  87.     A music pool is basically a place where you put your music to convert it
  88.     to local WAV files which will then be used to create audio CDs.
  89.     """
  90.     def __init__(self):
  91.         raise NotImplementedError
  92.         
  93.     def fetch_music(self, music):
  94.         """
  95.         Returns a operations.MeasurableOperation that correspondes the user
  96.         can use to monitor it's progress.
  97.         """
  98.         raise NotImplementedError
  99.     
  100.     def is_available(self, music):
  101.         """
  102.         Returns if a certain music is on cache.
  103.         """
  104.         raise NotImplementedError
  105.     
  106.     def get_filename(self, music):
  107.         """
  108.         Returns the filename associated with a certain music in cache. The user
  109.         must be sure that the file is in cache.
  110.         """
  111.         raise NotImplementedError
  112.     
  113.     def clear(self):
  114.         """
  115.         Clears the pool of it's elements.
  116.         """
  117.         raise NotImplementedError
  118.  
  119. ################################################################################
  120. # GStreamer implementation
  121. #
  122.  
  123. class GstCacheEntry:
  124.     def __init__(self, filename, is_temp):
  125.         self.is_temp = is_temp
  126.         self.filename = filename
  127.  
  128. class GstSourceToWavListener(operations.OperationListener):
  129.     def __init__(self, parent, filename, music):
  130.         self.__filename = filename
  131.         self.__music = music
  132.         self.__parent = parent
  133.     
  134.     def on_finished(self, event):
  135.         if event.id == operations.SUCCESSFUL:
  136.             self.__parent.cache[self.__parent.unique_music_id(self.__music)] = GstCacheEntry(self.__filename, True)
  137.         else:
  138.             os.unlink(self.__filename)
  139.         
  140. class GstMusicPool(MusicPool):
  141.     def __init__(self):
  142.         self.__cache = {}
  143.         self.__temp_dir = None
  144.     ############################################################################
  145.     # Properties
  146.     
  147.     # read-only
  148.     cache = property(lambda self: self.__cache)
  149.     
  150.     # temporaryDir
  151.     def setTemporaryDir(self, temp_dir):
  152.         assert temp_dir == None or isinstance(temp_dir, StringType),\
  153.                "Directory must be a string. Or None for default."
  154.         self.__temp_dir = temp_dir
  155.     
  156.     def getTemporaryDir(self):
  157.         return self.__temp_dir
  158.     
  159.     temporaryDir = property(getTemporaryDir, setTemporaryDir)
  160.     ############################################################################
  161.     
  162.     def is_available(self, music):
  163.         return self.cache.has_key(self.unique_music_id(music))
  164.     
  165.     def get_filename(self, music):
  166.         assert self.is_available(music)
  167.         return self.cache[self.unique_music_id(music)].filename
  168.         
  169.     def get_source(self, music):
  170.         raise NotImplementedError
  171.     
  172.     def fetch_music(self, music):
  173.         """
  174.         Can throw a OSError exception in case of the provided temporary dir being
  175.         invalid.
  176.         """
  177.         assert not self.is_available(music)
  178.         source = self.get_source()
  179.         
  180.         handle, filename = tempfile.mkstemp(suffix = ".wav", dir = self.temporaryDir)
  181.         os.close(handle)
  182.         
  183.         our_listener = GstSourceToWavListener(self, filename, music)
  184.         oper = audio.convert_to_wav(source, music, filename)
  185.         
  186.         oper.listeners.append(our_listener)
  187.         return oper
  188.  
  189.     def clear(self):
  190.         for key in self.cache:
  191.             entry = self.cache[key]
  192.             if entry.is_temp:
  193.                 os.unlink(entry.filename)
  194.         self.__cache = {}
  195.         
  196.     def __del__(self):
  197.         self.clear()
  198.     
  199.     def unique_music_id(self, music):
  200.         pass
  201.  
  202. class GvfsMusicPool(GstMusicPool):
  203.     use_gnomevfs = True
  204.     
  205.     def unique_music_id(self, uri):
  206.         """
  207.         Provides a way of uniquely identifying URI's, in case of user sends:
  208.         file:///foo%20bar
  209.         file:///foo bar
  210.         /foo bar
  211.         Returns always a URL, even if the string was a file path.
  212.         """
  213.         return urlutil.normalize(uri)
  214.     
  215.     def is_wav(self, filename):
  216.         is_pcm = audio.is_wav_pcm(self.get_source(), filename)
  217.         return operations.syncOperation(is_pcm).id == operations.SUCCESSFUL
  218.     
  219.     def is_local(self, filename):
  220.         return urlutil.is_local(filename)
  221.     
  222.     def is_available(self, music):
  223.         on_cache = GstMusicPool.is_available(self, music)
  224.         
  225.         unique_id = self.unique_music_id(music)
  226.  
  227.         if not on_cache and self.is_local(music) and self.is_wav(unique_id):
  228.             # convert to native filename
  229.             filename = urlutil.get_path(unique_id)
  230.             self.cache[unique_id] = GstCacheEntry(filename, False)
  231.             on_cache = True
  232.  
  233.         return on_cache
  234.     
  235.     def get_source(self):
  236.         if self.use_gnomevfs:
  237.             return audio.GVFS_SRC
  238.             
  239.         else:
  240.             return audio.FILE_SRC
  241.  
  242. class FetchMusicListPriv(operations.OperationListener):
  243.     def __init__(self, parent, music_list):
  244.         self.music_list = music_list
  245.         self.parent = parent
  246.     
  247.     def get_music_from_uri(self, uri):
  248.         for m in self.music_list:
  249.             if m["location"] == uri:
  250.                 return m
  251.         return None
  252.     
  253.     def on_finished(self, evt):
  254.             
  255.         if isinstance(evt.source, operations.OperationsQueue):
  256.             self.parent._propagate(evt)
  257.             return
  258.             
  259.         assert isinstance(evt.source, GetMusic)
  260.         if evt.id != operations.SUCCESSFUL:
  261.             return
  262.             
  263.         uri = evt.source.music
  264.         pool = evt.source.pool
  265.         filename = pool.get_filename(uri)
  266.         m = self.get_music_from_uri(uri)
  267.  
  268.         if m:
  269.             m["cache_location"] = filename
  270.         else:
  271.             assert False, "uri '%s' was not found in music list." %(uri)
  272.         
  273.     def before_operation_starts(self, event, operation):
  274.         e = operations.Event(self.parent)
  275.         m = self.get_music_from_uri(operation.music)
  276.         assert m
  277.         
  278.         for l in self.parent.listeners:
  279.             if isinstance(l, FetchMusicListListener):
  280.                 l.before_music_fetched(e, m)
  281.         
  282. class FetchMusicListListener(operations.OperationListener):
  283.     def before_music_fetched(self, event, music):
  284.         pass
  285.     
  286.  
  287. class FetchMusicList(operations.MeasurableOperation):
  288.     """
  289.     Fetches a music list which contains the field 'uri' and replaces it by a
  290.     local filename located inside the pool. When the filename is fetched it
  291.     updates the 'filename' field inside each music entry of the music list.
  292.     """
  293.     
  294.     def __init__(self, music_list, pool):
  295.         operations.MeasurableOperation.__init__(self)
  296.         self.__music_list = music_list
  297.         self.__queue = operations.OperationsQueue()
  298.         self.__pool = pool
  299.         self.__listener = FetchMusicListPriv(self, music_list)
  300.         self.__queue.listeners.append(self.__listener)
  301.     
  302.     def getProgress(self):
  303.         return self.__queue.progress
  304.         
  305.     progress = property(getProgress)
  306.     running = property(lambda self: self.__queue.running)
  307.     
  308.     def start(self):
  309.         for m in self.__music_list:
  310.             get = GetMusic(self.__pool, m["location"])
  311.             get.listeners.append(self.__listener)
  312.             self.__queue.append(get)
  313.  
  314.         self.__queue.start()
  315.     
  316.     can_stop = property(lambda self: self.__queue.can_stop)
  317.     
  318.     def stop(self):
  319.         self.__queue.stop()
  320.  
  321.  
  322.  
  323. if __name__ == '__main__':
  324.     import sys
  325.     import gtk
  326.     import gobject
  327.     import gtkutil
  328.     from os import path
  329.     
  330.     def quit():
  331.         #gtk.main_quit()
  332.         return False
  333.     
  334.     def pp(oper):
  335.         p = oper.get_progress()
  336.         print p
  337.         if p == 1:
  338.             gtk.main_quit()
  339.         return True
  340.         
  341.     class MyListener(FetchMusicListListener):
  342.         def __init__(self, prog, oper):
  343.             FetchMusicListListener.__init__(self)
  344.             self.music = None
  345.             self.prog = prog
  346.             self.oper = oper
  347.             
  348.         def before_music_fetched(self, evt, music):
  349.             print music
  350.             prog_txt = "Converting "
  351.             prog_txt += path.basename(urlutil.get_path(music['location']))
  352.             
  353.             self.prog.sub_progress_text = prog_txt
  354.             self.prog.progress_fraction = self.oper.progress
  355.             
  356.             if self.music:
  357.                 print "Fetched", self.music['cache_location']
  358.                 
  359.             print "Fetching", music['location']
  360.             self.music = music
  361.             
  362.         def on_finished(self, e):
  363.             if self.music:
  364.                 print "Fetched", self.music
  365.             print self.oper.progress
  366.             #self.prog.set_progress_fraction(self.oper.progress)
  367.             gtk.main_quit()
  368.         
  369.         def tick(self):
  370.             self.prog.progress_fraction = self.oper.progress
  371.             return True
  372.     
  373. #    win = gtk.Window(gtk.WINDOW_TOPLEVEL)
  374.     prog = gtkutil.HigProgress()
  375.     prog.primary_text = "Recording Audio Disc"
  376.     prog.secondary_text = "Selected files are to be written to a CD or DVD "   \
  377.                           "disc. This operation may take a long time, "        \
  378.                           "depending on data size and write speed."
  379.     prog.show()
  380. #    win.add(prog)
  381. #    win.set_border_width(6)
  382. #    win.show()
  383.     pool = GvfsMusicPool()
  384.     #f = path.abspath(sys.argv[1])
  385.     music_list = [{'location': path.abspath(sys.argv[1])}]
  386.     fetcher = FetchMusicList(music_list, pool)
  387.     l = MyListener(prog, fetcher)
  388.     fetcher.listeners.append(l)
  389.     gobject.timeout_add(200, l.tick)
  390.     fetcher.start()
  391.     
  392.     gtk.main()
  393.     pool.clear()
  394.