# -*- coding: iso-8859-15 -*-
### jack_helpers: helper applications for
### jack - extract audio from a CD and encode it using 3rd party software
### Copyright (C) 1999-2004  Arne Zellentin <zarne@users.sf.net>

### This program is free software; you can redistribute it and/or modify
### it under the terms of the GNU General Public License as published by
### the Free Software Foundation; either version 2 of the License, or
### (at your option) any later version.

### This program is distributed in the hope that it will be useful,
### but WITHOUT ANY WARRANTY; without even the implied warranty of
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
### GNU General Public License for more details.

### You should have received a copy of the GNU General Public License
### along with this program; if not, write to the Free Software
### Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import string
import re

import jack_plugins

from jack_globals import *


helpers = {
    'builtin': {
        'type': "dummy",
        'status_blocksize': 160
        },

    'oggenc': { # based on a patch kindly provided by Bryan Larsen.
        'type': "encoder",
        'target': "ogg",
        'can_tag': 1,
        'vbr-cmd': "oggenc -o %o -t %t -a %a -N %n -l %l -G %g -d %y -q %q %i",
        'cmd':     "oggenc -o %o -t %t -a %a -N %n -l %l -G %g -d %y -b %r %i",
        'tags': {
            'ogg': {
                'track': "-t %s",
                'artist': "-a %s",
                'number': "-N %s",
                'album': "-l %s",
                'genre': "-G %s",
                'date': "-d %s",
            },
        },
        'status_blocksize': 64,
        'bitrate_factor': 1,
        'status_start': "%",
        'percent_fkt': r"""
s = string.split(i['buf'], '\r')
if len(s) >= 2:
    s = s[-2]
if len(s) == 1:
    s = s[0]
y0 = string.find(s, "[")
y1 = string.find(s, "%]")
if y0 != -1 and y1 != -1:
    try:
        percent = float(s[y0 + 1:y1])
    except ValueError:
        percent = 0
else:
    percent = 0
""",
    },

    'mp3enc': {
        'type': "encoder",
        'target': "mp3",
        'cmd': "mp3enc -v -qual 9 -br %r -if %i -of %o",
        'otf-cmd': "mp3enc -v -qual 9 -br %r -sti -be -of %o",
        'status_blocksize': 99,
        'bitrate_factor': 1000,
        'status_start': "%",
        'percent_fkt': r"""
s = string.split(i['buf'], '\r')
if len(s) >= 4:
    s = s[-2]
    if string.find(s, "%") >= 0:
        y = string.split(s, " ", 3)
        percent = float(y[0]) / (i['track'][LEN] * CDDA_BLOCKSIZE / 2) * 100.0
    else:
        percent = 0
""",
    },

    'l3enc': {
        'type': "encoder",
        'target': "mp3",
        'cmd': "l3enc -hq -br %r %i %o",
        'status_blocksize': 99,
        'bitrate_factor': 1000,
        'status_start': "%",
        'percent_fkt': r"""
s = string.split(i['buf'], '\r')
if len(s) >= 2: s = s[-2]
if len(s) == 1: s = s[0]
if string.find(s, "%") >= 0:
    y = string.split(s, " / ")
    y0 = string.split(y[0])[-1]
    y1 = string.split(y[1])[0]
    percent=float(y0) / float(y1) * 100.0
else:
    percent = 0
""",
    },

    'lame': {
        'type': "encoder",
        'target': "mp3",
        'inverse-quality': 1,
        'cmd': "lame --preset cbr %r --strictly-enforce-ISO %i %o",
        'vbr-cmd': "lame --preset standard --vbr-new --nohist --strictly-enforce-ISO %i %o",
        'otf-cmd': "lame --preset cbr %r --strictly-enforce-ISO - %o",
        'vbr-otf-cmd': "lame --preset standard --vbr-new --nohist --strictly-enforce-ISO - %o",
        'status_blocksize': 160,
        'bitrate_factor': 1,
        'status_start': "%",
        'percent_fkt': r"""
s = string.split(i['buf'], '\r')
if len(s) >= 2: s=s[-2]
if len(s) == 1: s=s[0]
if string.find(s, "%") >= 0:       # status reporting starts here
    y = string.split(s, "/")
    y1 = string.split(y[1], "(")[0]
    percent = float(y[0]) / float(y1) * 100.0
elif string.find(s, "Frame:") >= 0:    # older versions, like 3.13
    y = string.split(s, "/")
    y0 = string.split(y[0], "[")[-1]
    y1 = string.split(y[1], "]")[0]
    percent = float(y0) / float(y1) * 100.0
else:
    percent = 0
""",
    },

    'gogo': { # Thanks to José Antonio Pérez Sánchez for the vbr and otf presets
        'type': "encoder",
        'target': "mp3",
        'inverse-quality': 1,
        'cmd': "gogo %i %o -b %r",
        'vbr-cmd': "gogo %i %o -v %q",
        'otf-cmd': "gogo stdin %o -b %r",
        'vbr-otf-cmd': "gogo stdin %o -v 4",
        'status_blocksize': 160,
        'bitrate_factor': 1,
        'status_start': "%",
        'percent_fkt': r"""
s = string.split(i['buf'], '\r')
if len(s) >= 2: s=s[-2]
if len(s) == 1: s=s[0]
if string.find(s, "%") >= 0: # status reporting starts here
    s = replace(s, "\000", " ")
    y = string.split(s, "/")
    y0 = string.split(y[0], "{")[-1]
    y1 = string.split(y[1], "}")[0]
    percent = float(y0) / float(y1) * 100.0
else:
    percent = 0
""",
    },

    'bladeenc': {
        'type': "encoder",
        'target': "mp3",
        'cmd': "bladeenc %i %o -br %r",
        'status_blocksize': 180,
        'bitrate_factor': 1000,
        'status_start': "%",
        'percent_fkt': r"""
s = string.split(i['buf'], '\r')
if len(s) >= 2: s=s[-2]
if string.find(s, "Status:") != -1:
    y = string.split(s[8:])
    percent = float(string.split(y[0], '%')[0])
else:
    percent = 0
""",
    },

# xing  definitions kindly provided by Sebastian Weber
    'xing': {
        'type': "encoder",
        'target': "mp3",
        'cmd': "xingmp3enc -B %r %i %o",
        'vbr-cmd': "xingmp3enc -V 100 %i %o",
        'otf-cmd': "xingmp3enc -b %r -- %o",
        'vbr-otf-cmd': "xingmp3enc -V 100 -- %o",
        'status_blocksize': 160,
        'bitrate_factor': 1,
        'status_start': "%",
        'percent_fkt': r"""
s = string.split(i['buf'], '\r')
if len(s) >= 2: s = s[-2]
if string.find(s, "ETA:") != -1:
    y = string.strip(string.split(s, '%')[0])
    if len(y) == 0:
        percent = 0
    else:
        percent = float(y)
else:
    percent = 0
""",
    },

    'flac': {
        'type': "encoder",
        'target': "flac",
        'vbr-cmd': "flac -o %o %i",     
        'vbr-otf-cmd': "flac -fr -fb -fc 2 -fp 16 -fs 44100 -o %o", 
        'status_blocksize': 160,
        'status_start': "%", 
        'percent_fkt': r"""
s = string.split(i['buf'], '\r')    
if len (s) >= 2: s = s[-2]
if len (s) == 1: s = s[0]       
y0 = string.rfind(s, ": ")             
y1 = string.find (s, "%", y0)
if y0 != -1 and y1 != -1: 
    try:
        percent = float(s[y0 + 1:y1])
    except ValueError:
        percent = 0
else:                   
    percent = 0         
""",
    },

    'mppenc': {
        'type': "encoder",
        'target': "mpc",
        'can_tag': 0,
        'vbr-cmd': "mppenc --standard %i %o",
        #'vbr-otf-cmd': "mppenc --standard - %o", # doesn't work, needs WAVE
        'status_blocksize': 160,
        'status_start': "-.-",
        'percent_fkt': r"""
s = string.split(i['buf'], '\r')
if len(s) >= 3:
    s = s[-3]
    s = string.split(string.strip(s))
    if len(s) >= 3 and s[2] == "kbps" and s[0] != "-.-":
        percent = float(s[0])
    else:
        percent = 0
""",
    },

    'cdparanoia': {
        'filters': [[r'\n', r'\r'], [r'(\r)+', r'\r'], [r'(Done\.\r)+', r'Done.\r']],
        'type': "ripper",
        'cmd': "cdparanoia --abort-on-skip -d %d %n %o",
        'otf-cmd': "cdparanoia --abort-on-skip -e -d %d %n -R -",
        'status_blocksize': 500,
        'status_start': "%",
        'status_fkt': r"""
# (== PROGRESS == [                              | 013124 00 ] == :^D * ==)
# (== PROGRESS == [                       >      .| 011923 00 ] == :-) . ==)
tmp = string.split(i['buf'], "\r")
if len(tmp) >= 2:
    tmp = tmp[-2] + " "
    new_status = tmp[17:48] + tmp[49:69] # 68->69 because of newer version
else:
    new_status = "Cannot parse status"
""",
        'otf-status_fkt': r"""
buf = i['buf']
tmp = string.split(buf, "\n")
new_status = ""
if len(tmp) >= 2:
    tmp = string.split(tmp[-2], " @ ")
    if tmp[0] == "##: -2 [wrote]":
        percent = (float(tmp[1]) - (i['track'][START] * CDDA_BLOCKSIZE / 2.0)) / (i['track'][LEN] * CDDA_BLOCKSIZE / 2.0) * 100.0
        new_status = "[otf - reading, %2i%%]" % percent
""",
        'final_status_fkt': r"""
last_status="0123456789012345 [ -- error decoding status --  ]" # fallback
if 0 and cf['_debug']: # disabled for now
    import jack_version
    tmpf=open("%s.debug.%02d.txt" % (jack_version.prog_name, exited_proc['track'][NUM]), "w")
    tmpf.write(exited_proc['buf'])
    del tmpf
tmps = string.split(exited_proc['buf'], '\r')
tmps.reverse()
for tmp in tmps:
    if string.find(tmp, "PROGRESS") != -1:
        last_status = tmp
        break
final_status = ("%sx" % jack_functions.pprint_speed(speed)) + last_status[16:48] + "]"
""",
        'otf-final_status_fkt': r"""
final_status = "[otf - done]"
""",
        #'toc': 1,  # we can't generate correct freedb IDs with cdparanoia.
        'toc_cmd': "cdparanoia -d %d -Q 2>&1",
        'toc_fkt': r"""
while l:
    l = string.rstrip(l)
    if l and l[0:5] == "TOTAL":
        start = 0
    if l and l == '=' * (len(l)):
        start = 1
    elif l and start:
        l = string.split(l, '.', 1)
        num = int(l[0])
        l = string.split(l[1])
        erg.append([num, int(l[0]), int(l[2]), l[4] == 'OK', l[5] == 'yes', int(l[6]), 1, cf['_bitrate'], cf['_name'] % num])
    l = p.readline()
""",
    },

    'cdda2wav': {
        'type': "ripper",
        'cmd': "cdda2wav --no-infofile -H -v 1 -D %d -O wav -t %n %o",
        'status_blocksize': 200,
        'status_start': "percent_done:",
        'status_fkt': r"""
tmp = string.split(i['buf'], "\r")
if len(tmp) >= 2:
    if string.find(tmp[-2], '%') != -1:
        new_status = "ripping: " + string.strip(tmp[-2])
    else:
        new_status = "waiting..."
else:
    new_status = "Cannot parse status"
""",
        'final_status_fkt': r"""
final_status = ("%s" % jack_functions.pprint_speed(speed)) + "x [ DAE done with cdda2wav       ]"
""",
        'toc': 1,
        'toc_cmd': "cdda2wav --no-infofile -D %d -J -v toc --gui 2>&1",
        'toc_fkt': r"""
while 1:
    l = p.readline()
    if not l:
        break
    if l[0] == "T" and l[1] in string.digits and l[2] in string.digits and l[3] == ":":
        num, start, length, type, pre, copy, ch, dummy = string.split(l)[:8]
        if type == "audio":
            num = int(num[1:3])
            start = int(start)
            length = string.replace(length,".", ":")
            length = timestrtoblocks(length)
            pre = pre == "pre-emphasized"
            copy = copy != "copydenied"
            ch = [ "none", "mono", "stereo", "three", "quad" ].index(ch)
            erg.append([num, length, start, copy, pre, ch, 1, cf['_bitrate'], cf['_name'] % (num + 1)])
""",
        'toc_cmd_old': "cdda2wav --no-infofile -D %D -J -v 35 2>&1",
        'toc_fkt_old': r"""
new_c2w = 0
new_toc1 = 0
new_toc2 = 0
new_lengths = []
new_starts = []
while 1:
    l = p.readline()
    if not l:
        break
    l = string.strip(l)
    # new cdda2wav
    if starts_with(l, "Table of Contents: total tracks"):
        new_toc1 = 1
        continue

    if starts_with(l, "Table of Contents: starting sectors"):
        new_toc2 = 1
        new_toc1 = 0
        new_c2w = 1
        continue

    if new_toc2 and l and l[0] in string.digits:
        l = string.split(l, "(")[1:]
        for i in l:
            x = string.split(i, ")")[0]
            x = string.strip(x)
            try:
                new_starts.append(int(x))
            except:
                pass
        continue

    if new_toc1 and l and l[0] in string.digits:
        l = string.split(l, "(")[1:]
        for i in l:
            if string.find(i, ":") >= 0:
                x = string.split(i, ")")[0]
                x = replace(x, ".", ":")
                new_lengths.append(timestrtoblocks(x))
        continue

    # old cdda2wav
    if l and l[0:11] == "Album title":
        start = 1
    elif l and start:
        l = string.split(l)
        if l[0] == "Leadout:":
            start = 0
        else:
            num = int(l[0][1:3])
            if l[6] == "stereo":
                channels = 2
            elif l[6] == "mono":
                channels = 1
            else:
                channels = 0
            t_start = int(l[1])
            msf = string.split(l[2], ":")
            sf = string.split(msf[1], ".")
            t_length = int(sf[1]) + int(sf[0]) * 75 + int(msf[0]) * 60 * 75
            erg.append([num, t_length, t_start, l[5] == "copyallowed", l[4] != "linear", channels, 1, cf['_bitrate'], cf['_name'] % num])
if new_c2w and len(new_lengths) == len(new_starts) - 1:
    for i in range(min(len(new_lengths), len(new_starts))): # this provokes an error if the lists are of different length
        erg.append([i + 1, new_lengths[i], new_starts[i], 0, 0, 2, 1, cf['_bitrate'], cf['_name'] % (i + 1)])
""",
    },

    'dagrab': {
        'type': "ripper",
        'cmd': "dagrab -d %d -f %o %n",
        'status_blocksize': 100,
        'status_start': "total:",
        'status_fkt': r"""
tmp = string.split(i['buf'], "\r")
if len(tmp) >= 2:
    if string.find(tmp[-2], 'total:') != -1:
        new_status = string.strip(tmp[-2])
    else:
        new_status = "waiting..."
else:
    new_status = "Cannot parse status"
""",
        'final_status_fkt': r"""
final_status = ("%s" % jack_functions.pprint_speed(speed)) + "x [ DAE done with dagrab         ]"
""",
        'toc': 1,
        'toc_cmd': "dagrab -d %d -i 2>&1",
        'toc_fkt': r"""
while l:
    l = string.strip(l)
    if l and l[0:5] == "track":
        start = 1
    elif l and start:
        l = string.split(l)
        if l[3] == "leadout":
            start = 0
        else:
            num = int(l[0])
            channels = 2
            copy = 0
            pre = 0
            t_start = int(l[1]) - 150
            t_length = int(l[2])
            erg.append([num, t_length, t_start, copy, pre, channels, 1, cf['_bitrate'], cf['_name'] % num])
    l = p.readline()
""",
    },

    'tosha': {
        'type': "ripper",
        'cmd': "tosha -d %d -f wav -t %n -o %o",
        'status_blocksize': 100,
        'status_start': "total:",
        'status_fkt': r"""
x = string.split(i['buf'], '\r')[-2]
if string.find(x, 'total:') != -1:
    new_status = string.strip(string.split(i['buf'], '\r')[-2])
else:
    new_status = "waiting..."
""",
        'final_status_fkt': r"""
final_status = ("%s" % jack_functions.pprint_speed(speed)) + "x [ DAE done with tosha          ]"
""",
        'toc': 1,
        'toc_cmd': "tosha -d %d -iq 2>&1",
        'toc_fkt': r"""
while l:
    l = string.rstrip(l)
    if l:
        l = string.split(l)
        num = int(l[0])
        erg.append([num, 1 + int(l[3]) - int(l[2]), int(l[2]), 0, 0, 2, 1, cf['_bitrate'], cf['_name'] % num])
    l = p.readline()
""",
    },

    'CDDB.py': {
        'type': "toc-reader",
        'toc': 1,
        'toc_fkt': r"""
import cdrom
if not os.path.exists(cf['_cd_device']):
    error("Device %s does not exist!" % cf['_cd_device'])
if not os.access(cf['_cd_device'], os.R_OK):
    error("You don't have permission to access device %s!" % cf['_cd_device'])
try:
    device = cdrom.open(cf['_cd_device'])
    (first, last) = cdrom.toc_header(device)
except cdrom.error, m:
    error("Access of CD device %s resulted in error: %s" % (cf['_cd_device'], m[1]))

toc = []
for i in range(first, last + 1):
    (min, sec, frame) = cdrom.toc_entry(device, i)
    toc.append(min * 60 * 75 + sec * 75 + frame)
(min, sec, frame) = cdrom.leadout(device)
device.close()
toc.append(min * 60 * 75 + sec * 75 + frame)
for i in range(first, last + 1):
    erg.append([i, toc[i - first + 1] - toc[i - first], toc[i - first] - toc[0], 0, 0, 2, 1, cf['_bitrate'], cf['_name'] % i])
""",
    }
}

helpers['lame-user'] = helpers['lame'].copy()
helpers['lame-user'].update({'cmd': "lame --preset cbr %r --strictly-enforce-ISO %i %o",
        'vbr-cmd': "lame -V %q --vbr-new --nohist --strictly-enforce-ISO %i %o",
        'otf-cmd': "lame --preset cbr %r --strictly-enforce-ISO - %o",
        'vbr-otf-cmd': "lame -V %q --vbr-new --nohist --strictly-enforce-ISO - %o", })

def init():
    # import plugin
    jack_plugins.import_helpers()

    # compile exec strings
    for h in helpers.keys():
        for i in helpers[h].keys():
            if i[-4:] == "_fkt":
                helpers[h][i] = compile(helpers[h][i], '<string>', 'exec')

    # compile filters
    for h in helpers.keys():
        if helpers[h].has_key('filters'):
            newf = []
            for i in helpers[h]['filters']:
                newf.append([re.compile(i[0]), i[1]])
            helpers[h]['filters'] = newf
