--- a/mp3togo/__init__.py
+++ b/mp3togo/__init__.py
@@ -21,5 +21,5 @@
# __all__ = ('converter', 'main', 'options', 'setup')
-version = '0.5.2'
+version = '0.5.3'
--- a/mp3togo/cluster.py
+++ b/mp3togo/cluster.py
@@ -209,8 +209,8 @@ def slave(opts):
class Boss:
"""Manage one slave"""
- def __init__(self, host, files, failures, opts, r_schema="ssh %h "):
- """Manage a remote host.
+ def __init__(self, host, files, failures, opts, r_schema=""):
+ """Manage a remote host. use r_schema='ssh %h ' for remote hosts
Files is a FileList of files to process the list should be
shared with the cooperating bosses"""
self.host = host
@@ -222,7 +222,7 @@ class Boss:
if self.pid == 0:
args = r_schema.replace('%h', host)
#args += " mp3togo --cluster-slave True"
- args += " python /home/sim/temp/2go/cluster/run-mp3togo --cluster-slave True"
+ args += " test-mp3togo --cluster-slave True"
args = args.split()
os.execvp(args[0], args)
else:
@@ -340,7 +340,9 @@ class Boss:
def slavedriver(playlist, opts, cooked=None):
- """Signature to run from main.py"""
+ """A simple text mode cluster controller.
+
+ Function signature for running from main.py"""
if opts['verbosity']:
def p(s):
@@ -366,7 +368,7 @@ def slavedriver(playlist, opts, cooked=N
ring = [('localhost', master.poll(), master)]
for slave in slaves:
p(" " + slave)
- theman = Boss(slave, playlist, fails, opts)
+ theman = Boss(slave, playlist, fails, opts, r_schema="ssh %h ")
tr = (slave, theman.poll(), theman)
ring.append(tr)
--- a/mp3togo/conf.py
+++ b/mp3togo/conf.py
@@ -25,6 +25,8 @@ import sys, os
import mp3togo
import mp3togo.options as options
+from mp3togo.helpers import helpers
+
class Fail(options.Fail):
pass
@@ -142,6 +144,20 @@ class Options(options.Options):
self.bin['normalize-audio'] = False
self.bin['dcop'] = False
+ # What input types are supported on this system?
+ self.types = []
+ for helper in helpers.values():
+ if helper['action'] == 'decode':
+ bin = helper['cmd'].split()[0]
+ for path in map(lambda x: os.path.join(x, bin),
+ os.environ['PATH'].split(':')):
+ if os.path.exists(path):
+ if isinstance(helper['type'], (tuple, list)):
+ self.types.extend(helper['type'])
+ else:
+ self.types.append(helper['type'])
+ break
+
self.version = mp3togo.version
self._help_preamble = """
Synopsis:
@@ -224,33 +240,37 @@ Description:
name = name.replace('&', '+')
name = name.replace(';', '.')
name = name.replace('#', '-')
+ name = name.replace('|', '-')
if name[0] == '.':
name = '_' + name
return name
-def absfile(name):
- name = name.strip()
- name = os.path.expanduser(name)
- if name:
- name = os.path.abspath(name)
- return name
+ def getfiletype(self, filename):
+ """Return the format of an audio file"""
+ # Could use some more magic here
+ ft = os.path.splitext(filename)[1][1:]
+ #if ft not in ('mp3', 'ogg', 'flac', 'wav', 'm4a'):
+ if ft not in self.types:
+ raise ErrorUnknownFileType
+ return ft
+
+ def checkfile(self, name):
+ """Verify the existence of an audio file"""
+ name = name.replace('\n', '')
+ if not os.path.exists(name):
+ raise ErrorNoFile
+ name = absfile(name)
+ self.getfiletype(name) # Throws exception if unknown
+ return name
+
+ def est_decoded_size(self, filename):
+ """Estimate size of decoded wav file."""
+ tp = self.getfiletype(filename)
+ helper = find_helper(tp, 'decode')
+ return os.stat(filename).st_size * helpers[helper]['factor']
+
+
-def getfiletype(filename):
- """Return the format of an audio file"""
- # Could use some more magic here
- ft = os.path.splitext(filename)[1][1:]
- if ft not in ('mp3', 'ogg', 'flac', 'wav', 'm4a'):
- raise ErrorUnknownFileType
- return ft
-
-def checkfile(name):
- """Verify the existence of an audio file"""
- name = name.replace('\n', '')
- if not os.path.exists(name):
- raise ErrorNoFile
- name = absfile(name)
- getfiletype(name) # Throws exception if unknown
- return name
def try_print(msg):
"""Try to print a message to a nonblocking stdout"""
@@ -261,3 +281,47 @@ def try_print(msg):
except:
time.sleep(0.01 ** (1.0 / i))
+def absfile(name):
+ name = name.strip()
+ name = os.path.expanduser(name)
+ if name:
+ name = os.path.abspath(name)
+ return name
+
+def find_helper(type, action):
+ """Return the name of the best helper that performs 'action' on 'type'
+
+ Raises KeyError if type or action is unknown."""
+ for helper in helpers.keys():
+ if helpers[helper]['action'] == action and \
+ type in helpers[helper]['type']:
+ return helper
+ raise KeyError
+
+def make_args(helper, input, output, args=''):
+ """Substitute for command line args"""
+ esc = {'z': '%', 'i': "###input###", 'o': "###output###", 'a': args}
+ out = ""
+ fmt = helpers[helper]['cmd']
+ fmt = fmt.replace('%%', '%z')
+ fmt = fmt.split('%')
+ while fmt:
+ out += fmt[0]
+ if len(fmt) <= 1:
+ break
+ fmt = fmt[1:]
+ code = fmt[0] and fmt[0][0]
+ if code in esc.keys():
+ fmt[0] = esc[code] + fmt[0][1:]
+ else:
+ raise ErrorBadFormat
+ out = out.split()
+ # This foolishness is to prevent splitting
+ # filenames with whitespace in them.
+ if "###input###" in out:
+ out[out.index("###input###")] = input
+ if "###output###" in out:
+ out[out.index("###output###")] = output
+ return out
+
+
--- a/mp3togo/filelist.py
+++ b/mp3togo/filelist.py
@@ -63,7 +63,7 @@ class FileList:
self._addplaylist(name)
continue
else:
- name = setup.checkfile(name)
+ name = self._opts.checkfile(name)
except:
continue
@@ -92,14 +92,14 @@ class FileList:
for line in pl:
if line.startswith('/'):
try:
- name = setup.checkfile(line)
+ name = self._opts.checkfile(line)
except:
continue
self._list.append(name)
elif line.startswith("File") and line.find('=') > -1:
name = line.strip().split('=', 1)[1]
try:
- name = setup.checkfile(name)
+ name = self._opts.checkfile(name)
except:
continue
self._list.append(name)
@@ -164,6 +164,8 @@ class FileList:
self._poplock.release()
def __iter__(self):
+ """To use the iterator access, you must promise
+ to not modify the list."""
if not self._lock.locked():
raise setup.ErrorUnlocked
--- a/mp3togo/helpers.py
+++ b/mp3togo/helpers.py
@@ -30,7 +30,7 @@
import os
import shutil
-import mp3togo.conf as conf
+#import mp3togo.conf as conf
class frames:
@@ -132,13 +132,13 @@ def parse_flac_dec(buf):
except IndexError:
return 0
-### wav file "encoder/decoder"
-def recode_wav(input, output, args=None):
- """Factory returns function to copy wav file
-
- Returned function takes no args and copies
- input to output. Throws OSError on error."""
- return lambda: shutil.copyfile(input, output)
+#### wav file "encoder/decoder"
+#def recode_wav(input, output, args=None):
+# """Factory returns function to copy wav file
+#
+# Returned function takes no args and copies
+# input to output. Throws OSError on error."""
+# return lambda: shutil.copyfile(input, output)
def parse_m4a_dec(buf):
n = buf.find('% decoding')
@@ -168,77 +168,49 @@ helpers = {
'oggenc' : {'parser': parse_oggenc,
'cmd': 'oggenc %a -o %o %i',
'type': 'ogg',
+ 'factor': 16.5,
'action': 'encode'},
'lame' : {'parser': parse_lame,
'cmd': 'lame --nohist %a %i %o',
'type': 'mp3',
+ 'factor': 16.5,
'action': 'encode'},
- 'flac_enc':{'parser': parse_flac_enc,
- 'cmd': 'flac ???????',
- 'type': 'flac',
- 'action': 'encode'},
+# 'flac_enc':{'parser': parse_flac_enc,
+# 'cmd': 'flac ???????',
+# 'type': 'flac',
+# 'action': 'encode'},
'wav_enc': {'parser': None,
- 'cmd': recode_wav,
+ #'cmd': recode_wav,
+ 'cmd': 'cp %i %c',
'type': 'wav',
+ 'factor': 1,
'action': 'encode'},
'ogg123' : {'parser': parse_ogg123,
'cmd': 'ogg123 -d wav -f %o %i',
'type': 'ogg',
+ 'factor': 16.5,
'action': 'decode'},
'mpg321' : {'parser': parse_mpg321,
'cmd': 'mpg321 -v -w %o %i',
'type': 'mp3',
+ 'factor': 16.5,
'action': 'decode'},
'flac_dec':{'parser': parse_flac_dec,
'cmd': 'flac --decode -F -o %o %i',
'type': 'flac',
+ 'factor': 2.8,
+ 'action': 'decode'},
+ 'm4a_dec': {'parser': parse_m4a_dec,
+ 'cmd': 'faad -o %o %i',
+ 'type': 'm4a',
+ 'factor': 16.5,
'action': 'decode'},
- 'm4a_dec':{'parser': parse_m4a_dec,
- 'cmd': 'faad -o %o %i',
- 'type': 'm4a',
- 'action': 'decode'},
'wav_dec': {'parser': None,
- 'cmd': recode_wav,
+ #'cmd': recode_wav,
+ 'cmd': 'cp %i %o',
'type': 'wav',
+ 'factor': 1,
'action': 'decode'},
}
-def find_helper(type, action):
- """Return the name of the helper that performs action on type
-
- Raises KeyError if type or action is unknown."""
- return dict([(helpers[x]['type'], x)
- for x in helpers.keys()
- if helpers[x]['action'] == action]
- )[type]
-
-def make_args(helper, input, output, args=''):
- """Substitute for command line args"""
- esc = {'z': '%', 'i': "###input###", 'o': "###output###", 'a': args}
- out = ""
- fmt = helpers[helper]['cmd']
- fmt = fmt.replace('%%', '%z')
- fmt = fmt.split('%')
- while fmt:
- out += fmt[0]
- if len(fmt) <= 1:
- break
- fmt = fmt[1:]
- code = fmt[0] and fmt[0][0]
- if code in esc.keys():
- fmt[0] = esc[code] + fmt[0][1:]
- else:
- raise conf.ErrorBadFormat
- out = out.split()
- if "###input###" in out:
- out[out.index("###input###")] = input
- if "###output###" in out:
- out[out.index("###output###")] = output
- return out
-
-def est_decoded_size(filename):
- # This could be much better
- tp = conf.getfiletype(filename)
- factors = {'mp3': 16.5, 'ogg': 16.5, 'flac': 2.8, 'wav': 1, 'm4a': 16.5}
- return os.stat(filename).st_size * factors[tp]
--- a/mp3togo/tags.py
+++ b/mp3togo/tags.py
@@ -60,11 +60,12 @@ class Tags(UserDict.DictMixin):
format(fmt) returns a formatted string of metadata
"""
- def __init__(self, filename):
+ def __init__(self, filename, opts):
if not os.path.exists(filename):
raise setup.ErrorNoFile
+ self._opts = opts
self._file = filename
- self._type = setup.getfiletype(filename)
+ self._type = opts.getfiletype(filename)
self._tags = {}
self._readok = threading.Event()
self._readok.clear()
@@ -141,7 +142,7 @@ class Tags(UserDict.DictMixin):
return d
try:
- fmt = setup.getfiletype(filename)
+ fmt = self._opts.getfiletype(filename)
except setup.ErrorUnknownFileType:
self._lock.release()
raise
--- a/mp3togo/track.py
+++ b/mp3togo/track.py
@@ -22,13 +22,13 @@ import os
import sys
import threading
-import mp3togo.conf as setup
+import mp3togo.conf as conf
import mp3togo.tags as tags
import mp3togo.task as task
import mp3togo.cache as cache
-import mp3togo.helpers
+from mp3togo.helpers import helpers
-helpers = mp3togo.helpers.helpers
+#helpers = mp3togo.helpers.helpers
READY = "ready"
@@ -82,11 +82,11 @@ class Track:
# Read the tags
# Do this early so that 'treestructure' can access them
- self.tags = tags.Tags(filename)
+ self.tags = tags.Tags(filename, opts)
self.tags.read()
# Names
- filetype = setup.getfiletype(filename)
+ filetype = opts.getfiletype(filename)
dir, base = os.path.split(filename)
base = os.path.splitext(base)[0]
base = base + opts['brwarning']
@@ -96,6 +96,7 @@ class Track:
# See tags.Tags.format.__doc__
fmt = opts['treestructure']
dest = self.tags.format(fmt)
+ dest = opts.cleanfilename(dest)
self._outdir = os.path.dirname(dest)
self._outdir = os.path.join(opts['playerdir'], self._outdir)
@@ -154,8 +155,8 @@ class Track:
# Decode
if not self._hash:
- prog = mp3togo.helpers.find_helper(filetype, 'decode')
- tmpreq = mp3togo.helpers.est_decoded_size(self._filename)
+ prog = conf.find_helper(filetype, 'decode')
+ tmpreq = opts.est_decoded_size(self._filename)
jobname = "Decoding %s" % filetype
if callable(helpers[prog]['cmd']):
# SimpleTask
@@ -164,7 +165,7 @@ class Track:
tmpsize=tmpreq, name=jobname)
else:
# Task
- args = mp3togo.helpers.make_args(prog, self._filename,
+ args = conf.make_args(prog, self._filename,
self._wavname)
job = task.Task(self, args, helpers[prog]['parser'],
undo_decode, tmpsize=tmpreq, name=jobname)
@@ -196,7 +197,7 @@ class Track:
outsize=outreq, name=jobname)
else:
# Task
- args = mp3togo.helpers.make_args(encoder, self._wavname,
+ args = conf.make_args(encoder, self._wavname,
self._outname, opts['encopts'])
job = task.Task(self, args, filter,
undo_encode, outsize=outreq, name=jobname)