changeset 4:65a9f99302cd

Incorporate changes from Jens Zurheide <jens.zurheide@gmx.de> to read tags from the source file and add them to the one being written. Appears to work fine, however it should be optional. (ie work without tagpy just not write tags)
author darius@inchoate.localdomain
date Mon, 12 Nov 2007 15:01:23 +1030
parents de86a9e19151
children f11c5ed0178e
files amakode.py
diffstat 1 files changed, 92 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/amakode.py	Mon Nov 12 14:52:41 2007 +1030
+++ b/amakode.py	Mon Nov 12 15:01:23 2007 +1030
@@ -2,13 +2,25 @@
 
 ############################################################################
 # Transcoder for Amarok
-# (c) 2007 Daniel O'Connor <darius@dons.net.au>
+# - Add support for tagging (jens.zurheide@gmx.de)
+# - Fixed typo in lame encoder (tcuya from kde-apps.org)
+# - Made setting maxjobs easier, although Amarok doesn't appear to issue
+#   multiple requests :(
 #
 # Depends on: Python 2.2
+#             tagpy (optional)
+#
+# The only user servicable parts are the encode/decode (line 103) and the
+# number of concurrent jobs to run (line 225)
+#
+# The optional module tagpy (http://news.tiker.net/software/tagpy) is used 
+# for tag information processing. This allows for writing tags into the 
+# transcoded files.
 #
 ############################################################################
 #
 # Copyright (C) 2007 Daniel O'Connor. All rights reserved.
+# Copyright (C) 2007 Jens Zurheide. All rights reserved.
 #
 # Redistribution and use in source and binary forms, with or without
 # modification, are permitted provided that the following conditions
@@ -33,6 +45,8 @@
 #
 ############################################################################
 
+__version__ = "1.3"
+
 import ConfigParser
 import os
 import sys
@@ -46,8 +60,35 @@
 import urllib
 import urlparse
 import re
+import tagpy
 
-class QueueMgr:
+class tagpywrap(dict):
+    textfields = ['album', 'artist', 'title', 'comment', 'genre']
+    numfields = ['year', 'track']
+    allfields = textfields + numfields
+    
+    def __init__(self, url):
+        f = urllib.urlopen(url)
+
+        self.tagInfo = tagpy.FileRef(f.fp.name).tag()
+        f.close()
+    
+        self['album'] = self.tagInfo.album.strip()
+        self['artist'] = self.tagInfo.artist.strip()
+        self['title'] = self.tagInfo.title.strip()
+        self['comment'] = self.tagInfo.comment.strip()
+        self['year'] = self.tagInfo.year
+        self['genre'] = self.tagInfo.genre.strip()
+        self['track'] = self.tagInfo.track
+        for i in self.textfields:
+            if (self[i] == ""):
+                del self[i]
+
+        for i in self.numfields:
+            if (self[i] == 0):
+                del self[i]
+
+class QueueMgr(object):
     queuedjobs = []
     activejobs = []
     
@@ -86,7 +127,7 @@
         """ Returns true if both queues are empty """
         return(len(self.queuedjobs) == 0 and len(self.activejobs) == 0)
                
-class TranscodeJob:
+class TranscodeJob(object):
     # Programs used to decode (to a wav stream)
     decode = {}
     decode["mp3"] = ["mpg123", "-w", "-", "-"]
@@ -100,9 +141,20 @@
     encode = {}
     encode["mp3"] = ["lame", "--abr", "128", "-", "-"]
     encode["ogg"] = ["oggenc", "-q", "2", "-"]
-    encode["mp4"] = ["faac", "-o", "/dev/stdout", "-"]
+    encode["mp4"] = ["faac", "-wo", "/dev/stdout", "-"]
     encode["m4a"] = encode["mp4"]
-    encode["flac"] = ["flac", "-c", "-"]
+
+    # XXX: can't encode flac - it's wav parser chokes on mpg123's output, it does work
+    # OK if passed through sox but we can't do that. If you really want flac modify
+    # the code & send me a diff or write a wrapper shell script :)
+    #encode["flac"] = ["flac", "-c", "-"]
+
+    # Options for output programs to store ID3 tag information
+    tagopt = {}
+    tagopt["mp3"] = { "album" : "--tl", "artist" : "--ta", "title" : "--tt", "track" : "--tn" }
+    tagopt["ogg"] = { "album" : "-l", "artist" :  "-a", "title" : "-a", "track" : "-N" }
+    tagopt["mp4"] = { "album" : "--album", "artist" : "--artist", "title" : "--title", "track" : "--track" }
+    #tagopt["flac"] = { "album" : "-Talbum=%s", "artist" : "-Tartist=%s", "title" : "-Ttitle=%s", "track" : "-Ttracknumber=%s" }
 
     def __init__(self, _inurl, _tofmt):
         self.errormsg = None
@@ -133,8 +185,39 @@
             self.outurl = urlparse.urlunsplit(["file", None, self.outfname, None, None])
             log.debug("Outputting to " + self.outfname + " " + self.outurl + ")")
             log.debug("Errors to " + self.errfname)
-            self.decoder = subprocess.Popen(self.decode[self.inext], stdin=self.inputfile, stdout=subprocess.PIPE, stderr=self.errfd)
-            self.encoder = subprocess.Popen(self.encode[self.tofmt], stdin=self.decoder.stdout, stdout=self.outfd, stderr=self.errfd)
+            
+            # assemble command line for encoder
+            encoder = []
+            encoder += self.encode[self.tofmt]
+            
+            try:
+                if (self.tofmt in self.tagopt):
+                    taginfo = tagpywrap(self.inurl)
+                    for f in taginfo.allfields:
+                        if (f in taginfo and f in self.tagopt[self.tofmt]):
+                            inf = taginfo[f]
+                            opt = self.tagopt[self.tofmt][f]
+                            log.debug("  %s = %s %s" % (f, opt, inf))
+                            # If we have a substitution, make it. If
+                            # not append the info as a separate
+                            # arg. Note that the tag options are
+                            # passed in as the second option because a
+                            # lot of programs don't parse options
+                            # after their file list.
+                            if ('%s' in opt):
+                                opt = opt.replace('%s', inf)
+                                encoder.insert(1, opt)
+                            else:
+                                encoder.insert(1, opt)
+                                encoder.insert(2, inf)
+            finally:
+                pass
+
+            log.debug("decoder -> " + str(self.decode[self.inext]))
+            log.debug("encoder -> " + str(encoder))
+            self.decoder = subprocess.Popen(self.decode[self.inext], stdin=self.inputfile, stdout=subprocess.PIPE, stderr=self.errfh)
+            self.encoder = subprocess.Popen(encoder, stdin=self.decoder.stdout, stdout=self.outfd, stderr=self.errfh)
+            log.debug("Processes connected")
         except Exception, e:
             log.debug("Failed to start - " + str(e))
             self.errormsg = str(e)
@@ -167,7 +250,7 @@
 ############################################################################
 # amaKode
 ############################################################################
-class amaKode:
+class amaKode(object):
     """ The main application"""
 
     def __init__(self, args):
@@ -290,6 +373,7 @@
     signal.signal(signal.SIGHUP, onStop)
     signal.signal(signal.SIGTERM, onStop)
     if 1:
+        # Run normal application
         app = amaKode(sys.argv)
     else:
         # Quick test case