# HG changeset patch # User darius@inchoate.localdomain # Date 1194841883 -37800 # Node ID 65a9f99302cd9de5ff96cbc542721e14cff2bdbc # Parent de86a9e19151ac9f68b389e0507c496b70133dc3 Incorporate changes from Jens Zurheide 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) diff -r de86a9e19151 -r 65a9f99302cd amakode.py --- 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 +# - 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