From 1c96a883e6089188fb147a9981030f1f3a7d67bb Mon Sep 17 00:00:00 2001 From: khayber Date: Sun, 23 Jan 2005 16:34:33 +0000 Subject: [PATCH] First commit --- .DirIcon | Bin 0 -> 4293 bytes AppInfo.xml | 11 + AppRun | 16 ++ CDLow.py | 384 +++++++++++++++++++++++++++ CDROM.py | 393 +++++++++++++++++++++++++++ Help/COPYING | 340 ++++++++++++++++++++++++ Help/Changes | 0 Help/README | 36 +++ Help/TODO | 4 + Options.xml | 36 +++ PyCDDB.py | 99 +++++++ cd_logic.py | 137 ++++++++++ findrox.py | 92 +++++++ pixmaps/media-eject.png | Bin 0 -> 579 bytes pixmaps/media-ffwd.png | Bin 0 -> 480 bytes pixmaps/media-next.png | Bin 0 -> 510 bytes pixmaps/media-pause.png | Bin 0 -> 367 bytes pixmaps/media-play.png | Bin 0 -> 382 bytes pixmaps/media-prev.png | Bin 0 -> 572 bytes pixmaps/media-record.png | Bin 0 -> 925 bytes pixmaps/media-repeat.png | Bin 0 -> 368 bytes pixmaps/media-rewind.png | Bin 0 -> 460 bytes pixmaps/media-shuffle.png | Bin 0 -> 516 bytes pixmaps/media-stop.png | Bin 0 -> 340 bytes pixmaps/media-track.png | Bin 0 -> 201 bytes pixmaps/volume-max.png | Bin 0 -> 316 bytes pixmaps/volume-medium.png | Bin 0 -> 330 bytes pixmaps/volume-min.png | Bin 0 -> 311 bytes pixmaps/volume-mute.png | Bin 0 -> 381 bytes pixmaps/volume-zero.png | Bin 0 -> 300 bytes ripper.py | 663 ++++++++++++++++++++++++++++++++++++++++++++++ 31 files changed, 2211 insertions(+) create mode 100644 .DirIcon create mode 100644 AppInfo.xml create mode 100755 AppRun create mode 100644 CDLow.py create mode 100644 CDROM.py create mode 100644 Help/COPYING create mode 100644 Help/Changes create mode 100644 Help/README create mode 100644 Help/TODO create mode 100644 Options.xml create mode 100644 PyCDDB.py create mode 100644 cd_logic.py create mode 100644 findrox.py create mode 100644 pixmaps/media-eject.png create mode 100644 pixmaps/media-ffwd.png create mode 100644 pixmaps/media-next.png create mode 100644 pixmaps/media-pause.png create mode 100644 pixmaps/media-play.png create mode 100644 pixmaps/media-prev.png create mode 100644 pixmaps/media-record.png create mode 100644 pixmaps/media-repeat.png create mode 100644 pixmaps/media-rewind.png create mode 100644 pixmaps/media-shuffle.png create mode 100644 pixmaps/media-stop.png create mode 100644 pixmaps/media-track.png create mode 100644 pixmaps/volume-max.png create mode 100644 pixmaps/volume-medium.png create mode 100644 pixmaps/volume-min.png create mode 100644 pixmaps/volume-mute.png create mode 100644 pixmaps/volume-zero.png create mode 100644 ripper.py diff --git a/.DirIcon b/.DirIcon new file mode 100644 index 0000000000000000000000000000000000000000..da8cae2df6a920ff06f94e99ed8dcb8beb10c95c GIT binary patch literal 4293 zcwPa;5IXOPP)fG%=-=?0P73;W;*5&y9Q6$-o<2Y2S1@iecv$K;F@);b*U@CRe zern(T*+VZ?pQ=`Q0vMy_T6!Jix?AIN070*pN49MhKX?1>JL4-?ZWH2(Zi2xmK5qoY zrQtXZl}euZ`4s8taZKC5?+xPhN3d;+LkFKUzw^XPuO^c`<~SS%3e;l7xDrHi*U~Qo zEkHZ)k;doGO_z9JAbmwo5f{C83j%w$ZsyO|tX`4udVGSe=crZ+n5K?po7lFEAk==( zwk<5jLKYQdmxiW#NyIzYyZ0;dBM;xTy{n5yfaQRnTJS6NG2e0aldkckxajrHHLLl{ zXd)gpEQ@S*j$(0|Vkw8NSFtP;923WJux$%NFXK2iF1H)6*N^52;8wl({4qZ9$*;+O z_oa_-)igc}v{K7pDz$9(`=sy7id?@7K%}XOFKpe`xhxp;kV}r%d*(LYj^NRKl;HY)8qtDq_%P!Tw3q|sE#L}o&CmN-zfz`Sh;xsmU$G; ze*R-laN~{qYv0))J@VM&f3g@rSh|$X8*b=nipSfrZ41)~QLUD#RLiJp2Ddgrz`xYl zwCSVRwqmE#(-~c_>fvxa6pgL*$g=88PM)kO?Ks#@O>x_y)AjIRumoM)Q_j)m=zjXaNAvrtJ?^lBW|F{_M8<8lQc2ICEL$ zF9N7;ygq-2%cJ=(&SK34Y#hhI=Zg>sF2yuW>y=kt82;%`_YYR92 z$F@vN+rY96484qDlrW7VhFQXPY`Z>QBLiJE3wbtef9UkR5B=9SlesG@f6)S^3i;gp zXr-!`y^Tf6wrwn{imq3got+dkEkH-dPWk4W?^yr+@BfRQ%gv^@ZQH%t=L^MkJ;%(< znBesU5hM}QG%m(~V;Puc(W#crQhla{YboE54?Xn7`>yqj3~>HDQ+)#`=l`I0-s?CH zx?aUFbSjlRg?xrWVU%;pSGaF)7okvlWY3;^Zv|{o6cs@*#KFNAux(3FH7|l7;J6$t z)4;~TFmz_pImRZO8EW;A!*#4oWxz?LIMaXpRC0WLWQoV)qf{!OSF)5U(`2)0rY0{i zJC_yQ+LxVMZ+(DRY^5wo3UjmP=CKlM_?q@4`KNt6);fj3@%*7@!4DiZLqx&?Gf_v&Yswl!O!K{_?=xZybBW0lvl}ms$u=s#f{_fdko<+qV{Xi;^e=13}uCG!ctLQMEXV z;zp8WL{Y?X6jW71b@?fm=P)g^ZV3@V5SW-8;X6-!gK%U!n>KAC;P(@bgo(wXcr-7k z1_!BBD(Je-^z=0M-+w=aLV;pD%?-D$W@U2^x~-GW&ah%-H&bWE9$ePZ`tl!t=E2`A z1ZWggjRmEu>r7{J_6;pfLZqW(iO{;FnMkymK(L))Fp8>rQ4}|VAk(`;-ipWTL6QU%MaJcF5sk#??cGdQcNfWIlF7+Q4jw#6DwRS^ z!0Shj5ueWED~p^cCmA_9f{N`&5Dz@}{GtBK0W=^SV5U?eIXh=3VsV<5FYlD2;bjCv z34Go#s=EdN;5ZhBX;7_}u`CnI(&_JemLLA`-;kACxaF4Jczr(Hs)|cd(KI)*BvC3B zDHkg=H#f6m$4wmXJ5Dm0B%jX{TM=gEmgP*!6;77U6AMHsr7ECXc)i+F&;9D9D*`N( znM#HJWU}n8l*e0|nlvpM>lA{)1TMFRBq;!xrcSk5CZEqRdiGVGfBqQ`9!j%%&An{d zvW0LsjMwW$)7-e~v#%%$E?LFz_o2E~qVX7a+<6D#aG3V?cKS{oM={-e>0@8w_KkP) z%J8qrjpYfd9^7vCzQq9Yg4d0RuL5~sq*CFVzj~p3=;*QiyEbk(xMAzobuH`GZuEvC z%Uz-<3h9~C_KAT$`_1E{%EY7#m(s%CPwXWYi{tb8&@`~ z0W1lH=xS}INt6ViZR0PO9q&w<_U7j9oMbkXE zTq+~OLqsD1y4J48>+@q-76Jkj=f;?sI>UvjJc1}Nol0@X9e42TFQ3I5R%z>Pp|$@2 zmc0X)tX#p<-@c45gwUyVPI(|htr1B9Q^1A#>sg>G2m&1)9kjJAB^Hb0_Xn^ni_?Ss z96jBPD8=z91Wp-|s*47rh9xq1KL@yf5kGA4i z7Lp{Pscvlh?N!C)02&ZBwYINHtqsgB0GY1e=YWc$C5_M@5olN~JQt_@8I- zd3|_2KAw8|X+Hbe2N*klp3&qP{J{X@vnr#f53^-`gnS`~APBfzE-J+;{;-!bD`Ob? zC5JFgOJvbPb|rv?5GI$F|5Du|S!$cO1;B8--B`ATOLZX%0=ixyJ(p&DY@BRno_$X} zO<&&$M$U{dc*%I|VLCjs|KRcGUwrB4 zV&$&7wQ&WQ#^nd1z{a+=whg9Xuztfj5FnqgRkE@qae8QorEM)pl1v~FVBNZP2tXpy zLOh<}+~^qnCr1g$XIQmnGqNIaHaWq@4Vy5FHX{RPP!$iSPYfN)<%>_&C-L_NSSSG? z25ji=?%r6bR0&5z#9~o&U8h_rQmm9wRgJOplT3_HGMmXDNg`Xe?jRD0B1sa0BXI1E zH@Pr5Lw8$+-mM=-a0L4LPm;^#@T&nNL1K7l_%!en7HtA7PNTn%*nYnsS(bU`7r&%? zSr?XNVB0orOP0{pu?%^qi-13fKMavy(zT&%r|n$rZ9_9*uZ3L348x9UV() zYj3A1k-#4aAc&XZFcOK-+xr1B^LY*(p5ge>7bzDfxa+PvS+Qa{E|;sZ8-Cd~^mM+2#flYpJRSxI2N@n7Mpac5MZqu(48ve(Xo#Mk9_Hre-ZEhe z<=vc=B!Dj97N5`e>E7Pnwc&8M?&D%@Pt>fP=2(`6Y1Ud!MNuw3v+%i6sYIbrAeBmy z&*!l$3t5)YG>u#?H=RzW?*o3l7~@`x3ZRhn2W=rtr4Cp?5Q2qxorUlV7)ky9LrH?XS48 nbv*`sFBD%W@9RI0yzlaVtJg?S@{j8$00000NkvXXu0mjfMM^`` literal 0 HcwPel00001 diff --git a/AppInfo.xml b/AppInfo.xml new file mode 100644 index 0000000..9eac5fa --- /dev/null +++ b/AppInfo.xml @@ -0,0 +1,11 @@ + + + MP3 CD Ripper + + MP3 CD Ripper + Ken Hayber (portions rds@rdsarts.com) + 0.0.1 + GNU General Public License + http://khayber.dyndns.org/rox/ripper + + diff --git a/AppRun b/AppRun new file mode 100755 index 0000000..00ea285 --- /dev/null +++ b/AppRun @@ -0,0 +1,16 @@ +#!/usr/bin/env python +import findrox; findrox.version(1, 9, 8) +import rox +from rox import g + +import ripper + +try: + g.threads_init() + + theapp = ripper.Ripper() + theapp.show() + + rox.mainloop() +except: + rox.report_exception() diff --git a/CDLow.py b/CDLow.py new file mode 100644 index 0000000..d123a1a --- /dev/null +++ b/CDLow.py @@ -0,0 +1,384 @@ +""" + CDLow - Low level CDROM interface + Written to replace CDAudio, CDDB and PyGAME.CD and + add functionality (skipping) + Originally from the Gryphon CD player (http://gryphon.sourceforge.net/) + + Copyright 2001 (c) Christian Storgaard. + Changes made by Ron Kuslak , 2003-2004 + + 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 +""" + +from fcntl import ioctl +from CDROM import * +import os, struct + +debug = 0 + +trayopen = 0 + +NOSTATUS = 0 +PAUSED = 1 +STOPPED = 2 +NODISC = 3 +PLAYING = 4 +ERROR = 5 + +tochdr_fmt = "BB" +tocentry_fmt = "BBBix" +addr_fmt = "BBB"+"x"*(struct.calcsize('i')-3) +region_fmt = "BBBBBB" +subchnl_fmt = "BBxBBiBBB" + +class cd: + path = "/dev/cdrom" + start = end = None + open = cd = 0 + last = (0,0,0) + skip = 3 # In seconds + data = 0 # Display Data-tracks + +""" #### Internal functions #### + #### -- start -- ####""" +def cd_open(): + if cd.cd > 0: + cd.open = 1 + return 0 + else: + try: + cd.cd = os.open(cd.path, os.O_RDONLY|os.O_NONBLOCK) + trayopen = 0 + return 0 + except: + cd.status = NODISC + trayopen = 1 + return 1 + +def cd_close(): + if cd.cd < 1: + cd.open = 0 + return 0 + os.close(cd.cd) + cd.cd = 0 + cd.open = 0 + return 0 + +"""exit function""" +import atexit +atexit.register(cd_close) + +""" #### Internal functions #### + #### -- end -- ####""" + +def stop(): +# cd_open() + """ We don't care if there's a CD in or not, we're just stopping ;)""" + if get_status() == PAUSED: + try: + ioctl(cd.cd, CDROMRESUME) + except: + pass +# cd_open() + try: + ioctl(cd.cd, CDROMSTOP) + except: + pass +# cd_close() + +def pause(): + status = get_status() +# cd_open() + if status == PLAYING: + ioctl(cd.cd, CDROMPAUSE) + elif status == PAUSED: + ioctl(cd.cd, CDROMRESUME) +# cd_close() + +def forward(val=None): + if val!=None: skip(0,val,1) + else: skip(0) + +def rewind(val=None): + if val!=None: skip(1,val,1) + skip(1) + +def goto(min, sec, frm=0): + """See if we have the last track to play in cache, else set to last on disc""" + if cd.last == (0,0,0): + if cd.end == None: get_header() + cd.last = get_track_time(cd.end) + try: play_msf(min, sec, frm, cd.last[0], cd.last[1], cd.last[2]) + except: pass + +def skip(direction, amount=None, frames=0): + if amount == None: amount = cd.skip + if not frames: amount = amount*CD_FRAMES + curmin, cursec, curfrm = get_current_timing()['abs'] + start = msf2frm(curmin,cursec,curfrm) + """Apply according to which direction we are skipping""" + if direction == 1:start=start-amount + else:start=start+amount + startmin,startsec,startfrm=frm2msf(start) + """See if we have the last track to play in cache, else set to last on disc""" + if cd.last == (0,0,0): + if cd.end == None: get_header() + cd.last = get_track_time(cd.end) + try: play_msf(startmin, startsec, startfrm, cd.last[0], cd.last[1], cd.last[2]) + except: pass + +def play(first,last=None): + if last == None:last=first + i=get_track_time(first) + cd.last = get_track_time(last+1) + play_msf(i[0],i[1],i[2],cd.last[0],cd.last[1],cd.last[2]) + +def play_msf(startmin, startsec, startfrm, endmin, endsec, endfrm): +# cd_open() + cd.last=(endmin,endsec,endfrm) + region = struct.pack(region_fmt,startmin,startsec,startfrm,endmin,endsec,endfrm) + ioctl(cd.cd, CDROMPLAYMSF, region) +# cd_close() + +def eject(): + cd_open() + try: + ioctl(cd.cd, CDROMSTOP) + # Always unlock door before eject, in case something else locked it + ioctl(cd.cd, CDROM_LOCKDOOR, 0) + except: + # ioctl(cd.cd, CDROMRESET) + ioctl(cd.cd, CDROMSTOP) + # Always unlock door before eject, in case something else locked it + ioctl(cd.cd, CDROM_LOCKDOOR, 0) + try: + ioctl(cd.cd, CDROMEJECT_SW, 0) + ioctl(cd.cd, CDROMEJECT) + except: + ioctl(cd.cd, CDROMEJECT_SW, 0) + ioctl(cd.cd, CDROMEJECT) +# cd_close() + +def close(): +# cd_open() + try: + ioctl(cd.cd, CDROMCLOSETRAY) + except: + pass +# cd_close() + +def do_eject(): + if debug: print "Activated:\tCDLow.do_eject" + cd_close() + cd_open() + global trayopen + if debug: print "\tStatus:",trayopen + ioctl(cd.cd, CDROMEJECT_SW, 0) + if not trayopen: + try: + stop() + if debug: print "\tStopped" + except: + pass + eject() + if debug: print "\tEjected Tray" + cd.cd = 0 + cd.status = NODISC + trayopen = 1 + else: + close() + if debug: print "\tClosed Tray" + trayopen = 0 + cd_close() + if debug: print "\tClosed CD" + cd_open() + if debug: print "\tOpened CD" + return trayopen + +def get_volume(): +# cd_open() + try: + return struct.unpack("BBBB",ioctl(cd.cd, CDROMVOLREAD, struct.pack("BBBB",0,0,0,0))) + + except: + return (0,0,0,0) +# cd_close() + +def set_volume(frontleft,frontright=None,backleft=0,backright=0): +# cd_open() + if frontright == None: frontright = frontleft + try: + return struct.unpack("BBBB",ioctl(cd.cd, CDROMVOLCTRL, struct.pack("BBBB",frontleft,frontright,backleft,backright))) + + except: + return 1 +# cd_close() + +def get_status(time=0, all=0): +# cd_open() + result = 0 + i = 1 + while result == 0 and i < 5: + try: # If this fails then it's because it's happening too fast, just retry + info = ioctl(cd.cd, CDROMSUBCHNL, struct.pack(subchnl_fmt, CDROM_MSF,0,0,0,0,0,0,0)) + drvstatus = get_drive_status() + result = 1 + except: + i = i +1 +# cd_close() + format, status, track, something, absaddr, relmin, relsec, relfrm = struct.unpack(subchnl_fmt, info) + absmin, abssec, absfrm = struct.unpack(addr_fmt, struct.pack("i", absaddr)) + zero = (1, 0,0,0, 0,0,0) + if status == CDROM_AUDIO_PLAY : cd.status = PLAYING + elif status == CDROM_AUDIO_PAUSED : cd.status = PAUSED + elif status == CDROM_AUDIO_COMPLETED : + cd.status = STOPPED + track,relmin,relsec,relfrm,absmin,abssec,absfrm = zero + elif status == CDROM_AUDIO_ERROR : + cd.status = ERROR + track,relmin,relsec,relfrm,absmin,abssec,absfrm = zero + elif status == CDROM_AUDIO_INVALID : + cd.status = ERROR + track,relmin,relsec,relfrm,absmin,abssec,absfrm = zero + elif status == CDROM_AUDIO_NO_STATUS : + cd.status = NOSTATUS + track,relmin,relsec,relfrm,absmin,abssec,absfrm = zero + if drvstatus == CDS_NO_DISC : + cd.status = NODISC + track,relmin,relsec,relfrm,absmin,abssec,absfrm = zero + if all: return {'status': drvstatus, 'format': format, 'status': status, 'track': track, 'something': something, 'absaddr': absaddr, 'relmin': relmin, 'relsec': relsec, 'relfrm': relfrm} + elif time: return {'cur_t': track,'rel':(relmin,relsec,relfrm),'abs':(absmin,abssec,absfrm)} + else: return cd.status + +def get_disc_type(): +# cd_open() + ret = ioctl(cd.cd, CDROM_DRIVE_STATUS) +# cd_close() + return ret + +def get_drive_status(): +# cd_open() + ret = 999 + ret = ioctl(cd.cd, CDROM_DRIVE_STATUS) +# cd_close() + return ret + +def get_header(): +# cd_open() + tochdr = struct.pack(tochdr_fmt, 0, 0) + tochdr = ioctl(cd.cd, CDROMREADTOCHDR, tochdr) + cd.start, cd.end = struct.unpack(tochdr_fmt, tochdr) +# cd_close() + +def get_track_time(trk): + if cd.start == None: + get_header() + if trk < 1: trk = 1 + elif trk > cd.end: trk = CDROM_LEADOUT +# cd_open() + toc = struct.pack(tocentry_fmt, trk, 0, CDROM_MSF, 0) + toc = ioctl(cd.cd, CDROMREADTOCENTRY, toc) + + track, adrctrl, format, addr = struct.unpack(tocentry_fmt, toc) + m, s, f = struct.unpack(addr_fmt, struct.pack("i", addr)) + + adr = adrctrl & 0xf + ctrl = (adrctrl & 0xf0) >> 4 + + start = (m * CD_SECS + s) * CD_FRAMES + f # 60 and 75 + data = 0 + if ctrl & CDROM_DATA_TRACK: + data = 1 +# cd_close() + return m,s,f,start,data + +def get_track_length(first,last=None): + if last==None: last=first+1 + off1min, off1sec, off1frm, off1, data1 = get_track_time(first) + off2min, off2sec, off2frm, off2, data2 = get_track_time(last) + + lenmin,lensec,lenfrm=frm2msf(off2-off1) + + return off1min,off1sec,off1frm,lenmin,lensec,lenfrm,off1,data1 + +def get_tracks_id(): + get_header() + offset = [] + data = [] + length = [] + tracklist = range(cd.start,cd.end+1) + tracklist.append(CDROM_LEADOUT) + for i in tracklist: + m,s,f,lenm,lens,lenf,start,tdata = get_track_length(i) + offset.append((m, s, f)) + data.append(tdata) + if i <= cd.end: # We don't want length of CDROM_LEADOUT ;) + length.append((lenm, lens, lenf))#0)) # 0 is CDAudio's way + tracks = {'start_t':cd.start, 'end_t':cd.end, 'offset': offset, 'length': length, 'id': disc_id(offset, cd.start, cd.end), 'data': data} + tracks['cddb'] = get_cddb_id(tracks['id'][2:-1], tracks['id'][-1]) + return tracks + +def get_current_timing(): # Wrapper for get_status(1) + return get_status(1) + +def disc_id(offset, first, last): + track_frames = [] + checksum = long(0) + for i in range(first-1, last): + (min, sec, frame) = offset[i] + checksum = checksum + id_sum(min*CD_SECS + sec) + track_frames.append(min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame) + (min, sec, frame) = offset[last] + track_frames.append(min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame) + + total_time = (track_frames[-1] / CD_FRAMES) - (track_frames[0] / CD_FRAMES) + + discid = (int((checksum % 0xff) << 24) | total_time << 8 | last) + + return [discid,last]+track_frames[:-1]+[track_frames[-1]/CD_FRAMES] + +def id_sum(s): + sum = 0 + while s > 0: + sum = sum + (s % 10) + s = s / 10 + return sum + +def frm2msf(frm): + min = (frm / CD_FRAMES) / CD_SECS + sec = (frm / CD_FRAMES) - (min * CD_SECS) + frm = frm - ((min * CD_SECS) * CD_FRAMES + sec * CD_FRAMES) + return min,sec,frm + +def msf2frm(min,sec,frm): + return min*CD_SECS*CD_FRAMES+sec*CD_FRAMES+frm + +def get_cddb_id(tracks, disclen): + checksum = long(0) + for i in tracks: + sum = 0 + mfs = (i / CD_FRAMES) + while mfs > 0: + sum = sum + (mfs % 10) + mfs = mfs / 10 + checksum = checksum + sum + tot_time = disclen - tracks[0] / CD_FRAMES + return "%08x" % ((checksum % 0xff) << 24 | tot_time << 8 | len(tracks)) + + +""" #### Startup code #### + #### ####""" + +cd_open() diff --git a/CDROM.py b/CDROM.py new file mode 100644 index 0000000..e670a62 --- /dev/null +++ b/CDROM.py @@ -0,0 +1,393 @@ +""" + CDROM.py + Constants for CDLow.py + Copyright 2003 Christian Storgaard + Changes made by Ron Kuslak , 12/15/2003, + and are limited to the formating of this message. + + 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 struct +def sizeof(type): + return struct.calcsize(type) + +CDC_CD_R=0x2000 +CDC_CD_RW=0x4000 +CDC_CLOSE_TRAY=0x1 +CDC_DRIVE_STATUS=0x800 +CDC_DVD=0x8000 +CDC_DVD_R=0x10000 +CDC_DVD_RAM=0x20000 +CDC_GENERIC_PACKET=0x1000 +CDC_IOCTLS=0x400 +CDC_LOCK=0x4 +CDC_MCN=0x40 +CDC_MEDIA_CHANGED=0x80 +CDC_MULTI_SESSION=0x20 +CDC_OPEN_TRAY=0x2 +CDC_PLAY_AUDIO=0x100 +CDC_RESET=0x200 +CDC_SELECT_DISC=0x10 +CDC_SELECT_SPEED=0x8 +CDO_AUTO_CLOSE=0x1 +CDO_AUTO_EJECT=0x2 +CDO_CHECK_TYPE=0x10 +CDO_LOCK=0x8 +CDO_USE_FFLAGS=0x4 +CDROMAUDIOBUFSIZ=0x5382 +CDROMCLOSETRAY=0x5319 +CDROMEJECT=0x5309 +CDROMEJECT_SW=0x530f +CDROMGETSPINDOWN=0x531d +CDROMMULTISESSION=0x5310 +CDROMPAUSE=0x5301 +CDROMPLAYBLK=0x5317 +CDROMPLAYMSF=0x5303 +CDROMPLAYTRKIND=0x5304 +CDROMREADALL=0x5318 +CDROMREADAUDIO=0x530e +CDROMREADCOOKED=0x5315 +CDROMREADMODE1=0x530d +CDROMREADMODE2=0x530c +CDROMREADRAW=0x5314 +CDROMREADTOCENTRY=0x5306 +CDROMREADTOCHDR=0x5305 +CDROMRESET=0x5312 +CDROMRESUME=0x5302 +CDROMSEEK=0x5316 +CDROMSETSPINDOWN=0x531e +CDROMSTART=0x5308 +CDROMSTOP=0x5307 +CDROMSUBCHNL=0x530b +CDROMVOLCTRL=0x530a +CDROMVOLREAD=0x5313 +CDROM_AUDIO_COMPLETED=0x13 +CDROM_AUDIO_ERROR=0x14 +CDROM_AUDIO_INVALID=0x00 +CDROM_AUDIO_NO_STATUS=0x15 +CDROM_AUDIO_PAUSED=0x12 +CDROM_AUDIO_PLAY=0x11 +CDROM_CHANGER_NSLOTS=0x5328 +CDROM_CLEAR_OPTIONS=0x5321 +CDROM_DATA_TRACK=0x04 +CDROM_DEBUG=0x5330 +CDROM_DISC_STATUS=0x5327 +CDROM_DRIVE_STATUS=0x5326 +CDROM_GET_CAPABILITY=0x5331 +CDROM_GET_MCN=0x5311 +CDROM_GET_UPC=CDROM_GET_MCN +CDROM_LAST_WRITTEN=0x5395 +CDROM_LBA=0x01 +CDROM_LEADOUT=0xAA +CDROM_LOCKDOOR=0x5329 +CDROM_MEDIA_CHANGED=0x5325 +CDROM_MSF=0x02 +CDROM_NEXT_WRITABLE=0x5394 +CDROM_PACKET_SIZE=12 +CDROM_SELECT_DISC=0x5323 +CDROM_SELECT_SPEED=0x5322 +CDROM_SEND_PACKET=0x5393 +CDROM_SET_OPTIONS=0x5320 +CDSL_CURRENT=( (int ) ( ~ 0 >> 1 ) ) +CDSL_NONE=( (int ) ( ~ 0 >> 1 ) - 1 ) +CDS_AUDIO=100 +CDS_DATA_1=101 +CDS_DATA_2=102 +CDS_DISC_OK=4 +CDS_DRIVE_NOT_READY=3 +CDS_MIXED=105 +CDS_NO_DISC=1 +CDS_NO_INFO=0 +CDS_TRAY_OPEN=2 +CDS_XA_2_1=103 +CDS_XA_2_2=104 +CD_CHUNK_SIZE=24 +CD_ECC_SIZE=276 +CD_EDC_SIZE=4 +CD_FRAMES=75 +CD_FRAMESIZE=2048 +CD_FRAMESIZE_RAW=2352 +CD_SYNC_SIZE=12 +CD_HEAD_SIZE=4 +CD_FRAMESIZE_RAW0=( CD_FRAMESIZE_RAW - CD_SYNC_SIZE - CD_HEAD_SIZE ) +CD_FRAMESIZE_RAW1=( CD_FRAMESIZE_RAW - CD_SYNC_SIZE ) +CD_FRAMESIZE_RAWER=2646 +CD_FRAMESIZE_SUB=96 +CD_MINS=74 +CD_MSF_OFFSET=150 +CD_NUM_OF_CHUNKS=98 +CD_PART_MAX=64 +CD_PART_MASK=( CD_PART_MAX - 1 ) +CD_SECS=60 +CD_SUBHEAD_SIZE=8 +CD_XA_HEAD=( CD_HEAD_SIZE + CD_SUBHEAD_SIZE ) +CD_XA_SYNC_HEAD=( CD_SYNC_SIZE + CD_XA_HEAD ) +CD_XA_TAIL=( CD_EDC_SIZE + CD_ECC_SIZE ) +CD_ZERO_SIZE=8 +CGC_DATA_NONE=3 +CGC_DATA_READ=2 +CGC_DATA_UNKNOWN=0 +CGC_DATA_WRITE=1 +DVD_AUTH=0x5392 +DVD_AUTH_ESTABLISHED=5 +DVD_AUTH_FAILURE=6 +DVD_CGMS_RESTRICTED=3 +DVD_CGMS_SINGLE=2 +DVD_CGMS_UNRESTRICTED=0 +DVD_CPM_COPYRIGHTED=1 +DVD_CPM_NO_COPYRIGHT=0 +DVD_CP_SEC_EXIST=1 +DVD_CP_SEC_NONE=0 +DVD_HOST_SEND_CHALLENGE=1 +DVD_HOST_SEND_KEY2=4 +DVD_HOST_SEND_RPC_STATE=11 +DVD_INVALIDATE_AGID=9 +DVD_LAYERS=4 +DVD_LU_SEND_AGID=0 +DVD_LU_SEND_ASF=8 +DVD_LU_SEND_CHALLENGE=3 +DVD_LU_SEND_KEY1=2 +DVD_LU_SEND_RPC_STATE=10 +DVD_LU_SEND_TITLE_KEY=7 +DVD_READ_STRUCT=0x5390 +DVD_STRUCT_BCA=0x03 +DVD_STRUCT_COPYRIGHT=0x01 +DVD_STRUCT_DISCKEY=0x02 +DVD_STRUCT_MANUFACT=0x04 +DVD_STRUCT_PHYSICAL=0x00 +DVD_WRITE_STRUCT=0x5391 +#'EOPNOTSUPP' undefined in 'EDRIVE_CANT_DO_THIS' +GPCMD_BLANK=0xa1 +GPCMD_CLOSE_TRACK=0x5b +GPCMD_FLUSH_CACHE=0x35 +GPCMD_FORMAT_UNIT=0x04 +GPCMD_GET_CONFIGURATION=0x46 +GPCMD_GET_EVENT_STATUS_NOTIFICATION=0x4a +GPCMD_GET_MEDIA_STATUS=0xda +GPCMD_GET_PERFORMANCE=0xac +GPCMD_INQUIRY=0x12 +GPCMD_LOAD_UNLOAD=0xa6 +GPCMD_MECHANISM_STATUS=0xbd +GPCMD_MODE_SELECT_10=0x55 +GPCMD_MODE_SENSE_10=0x5a +GPCMD_PAUSE_RESUME=0x4b +GPCMD_PLAYAUDIO_TI=0x48 +GPCMD_PLAY_AUDIO_10=0x45 +GPCMD_PLAY_AUDIO_MSF=0x47 +GPCMD_PLAY_AUDIO_TI=0x48 +GPCMD_PLAY_CD=0xbc +GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL=0x1e +GPCMD_READ_10=0x28 +GPCMD_READ_12=0xa8 +GPCMD_READ_CD=0xbe +GPCMD_READ_CDVD_CAPACITY=0x25 +GPCMD_READ_CD_MSF=0xb9 +GPCMD_READ_DISC_INFO=0x51 +GPCMD_READ_DVD_STRUCTURE=0xad +GPCMD_READ_FORMAT_CAPACITIES=0x23 +GPCMD_READ_HEADER=0x44 +GPCMD_READ_SUBCHANNEL=0x42 +GPCMD_READ_TOC_PMA_ATIP=0x43 +GPCMD_READ_TRACK_RZONE_INFO=0x52 +GPCMD_REPAIR_RZONE_TRACK=0x58 +GPCMD_REPORT_KEY=0xa4 +GPCMD_REQUEST_SENSE=0x03 +GPCMD_RESERVE_RZONE_TRACK=0x53 +GPCMD_SCAN=0xba +GPCMD_SEEK=0x2b +GPCMD_SEND_DVD_STRUCTURE=0xad +GPCMD_SEND_EVENT=0xa2 +GPCMD_SEND_KEY=0xa3 +GPCMD_SEND_OPC=0x54 +GPCMD_SET_READ_AHEAD=0xa7 +GPCMD_SET_SPEED=0xbb +GPCMD_SET_STREAMING=0xb6 +GPCMD_START_STOP_UNIT=0x1b +GPCMD_STOP_PLAY_SCAN=0x4e +GPCMD_TEST_UNIT_READY=0x00 +GPCMD_VERIFY_10=0x2f +GPCMD_WRITE_10=0x2a +GPCMD_WRITE_AND_VERIFY_10=0x2e +GPMODE_ALL_PAGES=0x3f +GPMODE_AUDIO_CTL_PAGE=0x0e +GPMODE_CAPABILITIES_PAGE=0x2a +GPMODE_CDROM_PAGE=0x0d +GPMODE_FAULT_FAIL_PAGE=0x1c +GPMODE_POWER_PAGE=0x1a +GPMODE_R_W_ERROR_PAGE=0x01 +GPMODE_TO_PROTECT_PAGE=0x1d +GPMODE_WRITE_PARMS_PAGE=0x05 +_I386_BYTEORDER_H=None +_I386_TYPES_H=None +_LINUX_BYTEORDER_GENERIC_H=None +_LINUX_BYTEORDER_LITTLE_ENDIAN_H=None +_LINUX_BYTEORDER_SWAB_H=None +_LINUX_CDROM_H=None +__ELF__=1 +__LITTLE_ENDIAN=1234 +__LITTLE_ENDIAN_BITFIELD=None +#'__u16' undefined in '___constant_swab16' +#'__u32' undefined in '___constant_swab32' +#'__u64' undefined in '___constant_swab64' +#'__u16' undefined in '___swab16' +#'__u32' undefined in '___swab32' +#'__u64' undefined in '___swab64' +#'__u16' undefined in '__arch__swab16' +#'__u16' undefined in '__arch__swab16' +#'__arch__swab16p': failed dependancy:'__arch__swab16' +#'do' undefined in '__arch__swab16s' +#'__u32' undefined in '__arch__swab32' +#'__u32' undefined in '__arch__swab32' +#'__arch__swab32p': failed dependancy:'__arch__swab32' +#'do' undefined in '__arch__swab32s' +#'__u64' undefined in '__arch__swab64' +#'__u64' undefined in '__arch__swab64' +#'__arch__swab64p': failed dependancy:'__arch__swab64' +#'do' undefined in '__arch__swab64s' +#'__fswab16' undefined in '__swab16' +#'__be16_to_cpu': failed dependancy:'__swab16' +#'__swab16p' undefined in '__be16_to_cpup' +#'__swab16s' undefined in '__be16_to_cpus' +#'__fswab32' undefined in '__swab32' +#'__be32_to_cpu': failed dependancy:'__swab32' +#'__swab32p' undefined in '__be32_to_cpup' +#'__swab32s' undefined in '__be32_to_cpus' +#'__fswab64' undefined in '__swab64' +#'__be64_to_cpu': failed dependancy:'__swab64' +#'__swab64p' undefined in '__be64_to_cpup' +#'__swab64s' undefined in '__be64_to_cpus' +#'__u16' undefined in '___constant_swab16' +#'__constant_be16_to_cpu': failed dependancy:'___constant_swab16' +#'__u32' undefined in '___constant_swab32' +#'__constant_be32_to_cpu': failed dependancy:'___constant_swab32' +#'__u64' undefined in '___constant_swab64' +#'__constant_be64_to_cpu': failed dependancy:'___constant_swab64' +#'__u16' undefined in '___constant_swab16' +#'__constant_cpu_to_be16': failed dependancy:'___constant_swab16' +#'__u32' undefined in '___constant_swab32' +#'__constant_cpu_to_be32': failed dependancy:'___constant_swab32' +#'__u64' undefined in '___constant_swab64' +#'__constant_cpu_to_be64': failed dependancy:'___constant_swab64' +def __constant_cpu_to_le16(x): + return ( (__u16 ) (x ) ) +def __constant_cpu_to_le32(x): + return ( (__u32 ) (x ) ) +def __constant_cpu_to_le64(x): + return ( (__u64 ) (x ) ) +#'__u32' undefined in '___constant_swab32' +#'__constant_htonl': failed dependancy:'___constant_swab32' +#'__u16' undefined in '___constant_swab16' +#'__constant_htons': failed dependancy:'___constant_swab16' +def __constant_le16_to_cpu(x): + return ( (__u16 ) (x ) ) +def __constant_le32_to_cpu(x): + return ( (__u32 ) (x ) ) +def __constant_le64_to_cpu(x): + return ( (__u64 ) (x ) ) +#'__u32' undefined in '___constant_swab32' +#'__constant_ntohl': failed dependancy:'___constant_swab32' +#'__u16' undefined in '___constant_swab16' +#'__constant_ntohs': failed dependancy:'___constant_swab16' +#'__fswab16' undefined in '__swab16' +#'__cpu_to_be16': failed dependancy:'__swab16' +#'__swab16p' undefined in '__cpu_to_be16p' +#'__swab16s' undefined in '__cpu_to_be16s' +#'__fswab32' undefined in '__swab32' +#'__cpu_to_be32': failed dependancy:'__swab32' +#'__swab32p' undefined in '__cpu_to_be32p' +#'__swab32s' undefined in '__cpu_to_be32s' +#'__fswab64' undefined in '__swab64' +#'__cpu_to_be64': failed dependancy:'__swab64' +#'__swab64p' undefined in '__cpu_to_be64p' +#'__swab64s' undefined in '__cpu_to_be64s' +def __cpu_to_le16(x): + return ( (__u16 ) (x ) ) +#Traceback (most recent call last): +# File "./h2py.py", line 203, in output_py +# exec(_outs,env) +# File "", line 1 +# def __cpu_to_le16p(x): return ( * (__u16* ) (x ) ) +# ^ +# SyntaxError: invalid syntax +#skipping '__cpu_to_le16p' +#'do' undefined in '__cpu_to_le16s' +def __cpu_to_le32(x): + return ( (__u32 ) (x ) ) +#Traceback (most recent call last): +# File "./h2py.py", line 203, in output_py +# exec(_outs,env) +# File "", line 1 +# def __cpu_to_le32p(x): return ( * (__u32* ) (x ) ) +# ^ +# SyntaxError: invalid syntax +#skipping '__cpu_to_le32p' +#'do' undefined in '__cpu_to_le32s' +def __cpu_to_le64(x): + return ( (__u64 ) (x ) ) +#Traceback (most recent call last): +# File "./h2py.py", line 203, in output_py +# exec(_outs,env) +# File "", line 1 +# def __cpu_to_le64p(x): return ( * (__u64* ) (x ) ) +# ^ +# SyntaxError: invalid syntax +#skipping '__cpu_to_le64p' +#'do' undefined in '__cpu_to_le64s' +__i386=1 +__i386__=1 +def __le16_to_cpu(x): + return ( (__u16 ) (x ) ) +#Traceback (most recent call last): +# File "./h2py.py", line 203, in output_py +# exec(_outs,env) +# File "", line 1 +# def __le16_to_cpup(x): return ( * (__u16* ) (x ) ) +# ^ +# SyntaxError: invalid syntax +#skipping '__le16_to_cpup' +#'do' undefined in '__le16_to_cpus' +def __le32_to_cpu(x): + return ( (__u32 ) (x ) ) +#Traceback (most recent call last): +# File "./h2py.py", line 203, in output_py +# exec(_outs,env) +# File "", line 1 +# def __le32_to_cpup(x): return ( * (__u32* ) (x ) ) +# ^ +# SyntaxError: invalid syntax +#skipping '__le32_to_cpup' +#'do' undefined in '__le32_to_cpus' +def __le64_to_cpu(x): + return ( (__u64 ) (x ) ) +#Traceback (most recent call last): +# File "./h2py.py", line 203, in output_py +# exec(_outs,env) +# File "", line 1 +# def __le64_to_cpup(x): return ( * (__u64* ) (x ) ) +# ^ +# SyntaxError: invalid syntax +#skipping '__le64_to_cpup' +#'do' undefined in '__le64_to_cpus' +__linux=1 +__linux__=1 +#'__fswab16' undefined in '__swab16' +#'__fswab32' undefined in '__swab32' +#'__fswab64' undefined in '__swab64' +__unix=1 +__unix__=1 +i386=1 +linux=1 +unix=1 diff --git a/Help/COPYING b/Help/COPYING new file mode 100644 index 0000000..60549be --- /dev/null +++ b/Help/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Help/Changes b/Help/Changes new file mode 100644 index 0000000..e69de29 diff --git a/Help/README b/Help/README new file mode 100644 index 0000000..fd01e18 --- /dev/null +++ b/Help/README @@ -0,0 +1,36 @@ + Ripper + A MP3 ripper/encoder + by Ken Hayber + +Brief +~~~~~ +GUI front-end to cdda2wav and lame to rip and encode CD music to mp3 +The latest version of Ripper is at: + + http://khayber.dyndns.org/rox/ripper + + +Conditions +~~~~~~~~~~ +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 + +Please report any bugs to me, + + +Instructions +~~~~~~~~~~~~ +Extract the tgz file and run the application. Select the Preferences button and configure +things for your system. It is best if you are familiar with making cdda2wav and lame +work from the command line. Email the author for help :) diff --git a/Help/TODO b/Help/TODO new file mode 100644 index 0000000..b227113 --- /dev/null +++ b/Help/TODO @@ -0,0 +1,4 @@ +TODO items for Ripper + +* Add Ogg support +* Support cdparanoia and other ripper/encoders. \ No newline at end of file diff --git a/Options.xml b/Options.xml new file mode 100644 index 0000000..835e1e2 --- /dev/null +++ b/Options.xml @@ -0,0 +1,36 @@ + + +
+ + The (optional) default location of your music. + + + The full url to reach freedb or equivalent. By default this is 'http://freedb.freedb.org/~cddb/cddb.cgi', but there are mirrors. Leave blank to disable CDDB queries. + +
+ +
+ + The application used for ripping from CD. + + + Device name of cdrom. For example: /dev/cdrom. + + + SCSI-style device name. For example: ATAPI:0,1,0. + + + Options for the encoder. For example: quality, speed, etc. + +
+ +
+ + The application used for encoding to MP3. + + + Options for the encoder. For example: vbr, id3 tags, min bitrate. + +
+ +
diff --git a/PyCDDB.py b/PyCDDB.py new file mode 100644 index 0000000..31a1140 --- /dev/null +++ b/PyCDDB.py @@ -0,0 +1,99 @@ +""" Python CDDB-Access +Access a freedb service from Python. +""" + +import urllib +import os +import getpass +import string +import re +import socket + +__version__ = "0.1.0" + +class PyCDDB: + def __init__(self, cddb_server = 'http://freedb.freedb.org/~cddb/cddb.cgi', + app = "PyCDDB", version = __version__): + self.cddb_server = cddb_server + self.user = getpass.getuser() + self.host = socket.gethostname() + self.app = app + self.version = version + self.protocol = 5 + self.code = 0 + + def status(self): + return self.code + + def message(self): + if self.code == 0: + return "" + elif self.code == 200: + return "Found exact match" + elif self.code == 202: + return "No match found" + elif self.code == 210: + return "Ok" + elif self.code == 211: + return "Found inexact matches" + elif self.code == 401: + return "Specified CDDB entry not found" + elif self.code == 402: + return "Server error" + elif self.code == 403: + return "Database entry is corrupt" + elif self.code == 409: + return "No handshake" + + def query(self, discid): + if discid != "": + result = [] + self.track_offset = map(string.atoi, string.split(discid)[2:-1]) + self.disc_length = string.atoi(string.split(discid)[-1:][0]) * 75 + query = urllib.quote_plus(string.rstrip(discid)) + url = "%s?cmd=cddb+query+%s&hello=%s+%s+%s+%s&proto=%d" % \ + (self.cddb_server, query, self.user, self.host, self.app, + self.version, self.protocol) + response = urllib.urlopen(url) + header = response.readline() + if re.match("[0-9]+.*", header): + self.code = string.atoi(string.split(header, ' ', 1)[0]) + if self.code == 200: # Exact match + info = string.split(header, ' ', 3) + result.append( { 'category': info[1], 'disc_id': info[2], 'title': info[3] } ) + elif self.code == 210 or self.code == 211: # Multiple exact mattches or inexact match + line = string.rstrip(response.readline()) + while line != ".": + info = string.split(line, ' ', 2) + result.append( { 'category': info[0], 'disc_id': info[1], 'title': info[2] } ) + line = string.rstrip(response.readline()) + + return result + + def read(self, query_item): + result = {} + url = "%s?cmd=cddb+read+%s+%s&hello=%s+%s+%s+%s&proto=%d" % \ + (self.cddb_server, query_item['category'], query_item['disc_id'], + self.user, self.host, self.app, self.version, self.protocol) + response = urllib.urlopen(url) + header = response.readline() + self.code = string.atoi(string.split(header, ' ', 1)[0]) + if self.code == 210: + re_indexed_key = re.compile("([^=0123456789]+)([0-9]+)=(.*)") + re_key = re.compile("([^=]+)=(.*)") + for line in response.readlines(): + line = string.rstrip(line) + m = re_indexed_key.match(line) + if m: + (key, index, data) = m.groups() + if result.has_key(key): + result[key].append(data) + else: + result[key] = [] + result[key].append(data) + else: + m = re_key.match(line) + if m: + (key, data) = m.groups() + result[key] = data + return result diff --git a/cd_logic.py b/cd_logic.py new file mode 100644 index 0000000..a761cf3 --- /dev/null +++ b/cd_logic.py @@ -0,0 +1,137 @@ +""" + cd_logic.py + Uses the CD library to control the CD and return it's status. + + Copyright 2003 Ron Kuslak + All rights reserved. + + 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. + + 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 CDLow as cd + +updated = False + +def set_dev(dev='/dev/cdrom'): + """ Changes the CD device. Makes change. Dances poorly. """ + cd.cd_close() + cd.cd.path = str(dev) + cd.cd_open() + updated = False + return + +def get_disc_id(): + temp = cd.get_tracks_id() + id = temp['id'] + return int(id[0]) + +def get_cddb_id(): + temp = cd.get_tracks_id() + id = temp['id'] + return id + +def get_track_time(): + min, sec, temp = cd.get_status(1)['rel'] + return ((min * 60) + sec) + +def get_track_time_total(track=None): + if not track: track = current_track() + t1, t2, t3, min, sec, t4, t5, t6 = cd.get_track_length(track) + return ((min * 60) + sec) + +def get_dev(): + return str(cd.cd.path) + +def check_dev(): + # print cd.get_drive_status() + try: + return cd.get_drive_status() + except IOError, err: + print "IOError. Passing 0. Error was " + str(err) + return -1 + +def total_tracks(): + #try: + return cd.get_tracks_id()['end_t'] + # return int(cd_info['end_t']) + #except : + # return 0 + #excect: # We should not reach here! + # print 'ERROR IN cd.py - total_tracks(). Please report this error.' + # return + +def current_track(): + # try: + temp = cd.get_status(1) + return int(temp['cur_t']) + # except: + return -1 +def update(): + try: + status = cd.get_status() + except: + status = 0 + + if status == 0: + updated = True + +def get_status(): + """ Returns CD's playing state. Yes, this is just a wrapper around + get_status. DEAL. ;)""" + try: + status = cd.get_status() + except: + # print "Error in play_pause" + # # TODO: Do something. :P + return 0 + return status + +def play(track = 1): + cd.play(track, total_tracks()) + +def stop(): + cd.stop() + update() + +def eject(): + cd.eject() + update() + +def next(): + """ Advances CD one track, or puts it at track 1 if at last. """ + + next = current_track() + 1 + last_t = total_tracks() + if next < last_t: + play(next) + else: + play(1) + update() + return 0 + +def prev(): + prev = current_track() - 1 + + if prev == 0: + prev = (total_tracks() - 1) + + play(prev) + update() + return 0 + +def pause(): + cd.pause() + update() + +def jump(seconds): + cd.skip(0, seconds) diff --git a/findrox.py b/findrox.py new file mode 100644 index 0000000..ea4442c --- /dev/null +++ b/findrox.py @@ -0,0 +1,92 @@ +# Most of the common code needed by ROX applications is in ROX-Lib2. +# Except this code, which is needed to find ROX-Lib2 in the first place! + +# Just make sure you import findrox before importing anything inside +# ROX-Lib2... + +import os, sys +from os.path import exists +import string + +def version(major, minor, micro): + """Find ROX-Lib2, with a version >= (major, minor, micro), and + add it to sys.path. If version is missing or too old, either + prompt the user, or (if possible) upgrade it automatically.""" + + if not os.getenv('ROXLIB_DISABLE_ZEROINSTALL') and os.path.exists('/uri/0install/rox.sourceforge.net'): + # We're using ZeroInstall. Good :-) + zpath = '/uri/0install/rox.sourceforge.net/lib/ROX-Lib2/' \ + 'latest' + if os.path.exists(zpath): + vs = os.readlink(zpath).split('-')[-1] + v = map(int, vs.split('.')) + if v[0] < major or v[1] < minor or v[2] < micro: + if os.system('0refresh rox.sourceforge.net'): + report_error('Using ROX-Lib in Zero Install, but cached version (%s) is too old (need %d.%d.%d) and updating failed (is zero-install running?)' % (vs, major, minor, micro)) + sys.path.append(zpath + '/python') + return + print >>sys.stderr, "Using Zero Install, but failed to " \ + "fetch", zpath, "-- trying non-0install system." + + try: + path = os.environ['LIBDIRPATH'] + paths = string.split(path, ':') + except KeyError: + paths = [os.environ['HOME'] + '/lib', + '/usr/local/lib', '/usr/lib' ] + + for p in paths: + p = os.path.join(p, 'ROX-Lib2') + if exists(p): + # TODO: check version is new enough + sys.path.append(os.path.join(p, 'python')) + import rox + if major == 1 and minor == 9 and micro < 10: + return # Can't check version + if not hasattr(rox, 'roxlib_version'): + break + if (major, minor, micro) <= rox.roxlib_version: + return # OK + report_error("This program needs ROX-Lib2 (version %d.%d.%d) " % \ + (major, minor, micro) + "to run.\n" + \ + "I tried all of these places:\n\n" + \ + string.join(paths, '\n') + '\n\n' + \ + "ROX-Lib2 is available from:\n" + \ + "http://rox.sourceforge.net") + +def report_error(err): + "Write 'error' to stderr and, if possible, display a dialog box too." + try: + sys.stderr.write('*** ' + err + '\n') + except: + pass + try: + import pygtk; pygtk.require('2.0') + import gtk; g = gtk + except: + import gtk + win = gtk.GtkDialog() + message = gtk.GtkLabel(err + + '\n\nAlso, pygtk2 needs to be present') + win.set_title('Missing ROX-Lib2') + win.set_position(gtk.WIN_POS_CENTER) + message.set_padding(20, 20) + win.vbox.pack_start(message) + + ok = gtk.GtkButton("OK") + ok.set_flags(gtk.CAN_DEFAULT) + win.action_area.pack_start(ok) + ok.connect('clicked', gtk.mainquit) + ok.grab_default() + + win.connect('destroy', gtk.mainquit) + win.show_all() + gtk.mainloop() + else: + box = g.MessageDialog(None, g.MESSAGE_ERROR, 0, + g.BUTTONS_OK, err) + box.set_title('Missing ROX-Lib2') + box.set_position(g.WIN_POS_CENTER) + box.set_default_response(g.RESPONSE_OK) + box.run() + sys.exit(1) diff --git a/pixmaps/media-eject.png b/pixmaps/media-eject.png new file mode 100644 index 0000000000000000000000000000000000000000..ea3de85af7bbb6afee5af24e288d8a3b1da9bad7 GIT binary patch literal 579 zcwPZR0=)f+P)+lb zZxNA!s;KfVNB0-};tJNY50-`v=7=yJIV+3mq zU-tKpOcD`!S_H`n@P4n?+wF8ZWLZX@=LA7O6h$+8>3JSu7^14rMPz4Y6z3hPx;1eE zRb2y?fdq(4##ctras2?^iO8q%7G)!NF!lvI6;RN#5=* z3}Eond3QaK;w$MNN@jAz**-hQRj+Y+1e=7HmEeWqxQ=bl`M_L{-V#?^qLG z8}+$vO|(jXR+4_ve5Q}zr<(iK_blz1m7jX3AG3Dg{BdeUVd5_-@1FEVK7q6=5t7M1 z>m2h8$6?cT>-B=Aml%4jyrA2d;ld)=zcZ?L_qVeRo!pb>I+)DM zI3k?3DsDRmIG?;IU_pe=#$Edw4cV4nvAb|8F(j`kXk*L! z-#)C4Sqr(7eFaz;7nx2yW%8N3`|qLo+ov*4`E15}DmA-U`tcs$PtO{zC=~j#5!B>Ej!C>%x z>^ZwQFTP|oKv8RX&}`yd!t#q1LI^}r^b+`X=LU&Oh~t=t-si(3UUwyiNV*fw=2FmkwFAK|mivR!s07*qoM6N<$f|%yh A1poj5 literal 0 HcwPel00001 diff --git a/pixmaps/media-pause.png b/pixmaps/media-pause.png new file mode 100644 index 0000000000000000000000000000000000000000..1e03ef15642d43fc3e4075074cfda00916576cf3 GIT binary patch literal 367 zcwXxa@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmSN`?>!lvI6;RN#5=* z46hhoFg#|x8g?5f!dc)ES21sUt(hAGf@b6=JgmT^v2W0 zF{I+w+o^_mOojrk^UIHISQ9QC#>L*@Fw;^}PtQ5*ynUMY2k{3lcYJf;ebe{bI4>>!+)ky(X%Vj>BBjeh2Ki;L(*41^wsnm}`&9)6)i&ljwJ-$8jfhrrH zgUgFueDD9RnE6Px>i7ENdZq`@ov~P(C}F2x_~7x!&v)1FV|?$Gww0lXGal$~22WQ% Jmvv4FO#pT5kh1^) literal 0 HcwPel00001 diff --git a/pixmaps/media-play.png b/pixmaps/media-play.png new file mode 100644 index 0000000000000000000000000000000000000000..d252e2de892c70a41dfb35a83ae7ceb94460a700 GIT binary patch literal 382 zcwXxa@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmSN`?>!lvI6;RN#5=* z3}Eond3QaK;wC))?>~S2 znK@xj@ws5}&x~Dd2eKk31s#3(V#XX}*FceC##JGL$tp{ad`K|ae24W)X>Zqs*K0TL z-JGpezCZKs+g+z(9hSc-D5`p35S2LqyH5W*gReRT-G@yGywpq0i5*! literal 0 HcwPel00001 diff --git a/pixmaps/media-prev.png b/pixmaps/media-prev.png new file mode 100644 index 0000000000000000000000000000000000000000..2271a614f7c50a85990e25b60b26f0b08ec02a02 GIT binary patch literal 572 zcwPZK0>k}@P)b>AP8xmGhgs7-ePemOdOCJMT zsuYHTgj$Kv1!hF07UsSHuh9W{Vyj@u!V6GD8!?5(hao`(#g1bph91ew@jd@{{_CS- zxQv4EGjcb81>hPm2h@PG0-d#y*7sSUF`(m*s%n9_cDvoPSzX-&w9|R6`}=Q-0Bd7S zoMavF@+i(Zq9`i5mgA>Swx@QR3kbuIS{P0hUw?3)l{+g_0hNqg51?uc&beIi$B(v% z<7Jj^FO>!aza3a-GsSmzp0T)ii)M3~U_K~)MLmGQU_iBE#`D|5&fC=M4H}JwnI95>N4#TbJW0J0ohWI0HZq;qg^NEFpM{d!8p7(@gL5Ltlh z06}mC&;Rqf)oQ&{)s4;e7K1dU-|u6s1(AOn(>ZJLyuUYzh`d+Tjfb0U&d+<8Y85v* z2!-EZxTHJ}-<#}i1X&f4oh12ywXQT^SfSwgerAz~?5gSy5qbIf(@AN-7l2fUQ<4`V z@=8^|iOAcsEBtPmZK|rTM`u3H3aII198D`w)pb?9dHH+&KjRnvCZM!o0{1BZ0000< KMNUMnLSTZ{>-aqY literal 0 HcwPel00001 diff --git a/pixmaps/media-record.png b/pixmaps/media-record.png new file mode 100644 index 0000000000000000000000000000000000000000..a95830d23feff594419729f269ac619385cfa8a7 GIT binary patch literal 925 zcwPaW17iG%P)tfx-KLBKaLBNS@KF|T0K=(o5Iba%i8TbUK0zstJ z$oBmGW-tys1iYWLt+Ovac9gkPoFi6%1ivfB*YzGNm0d>0CO)rJDyRMlAO{=^3#e0q-5lBu>6P zk!JR#H^@CPhm%NP`963)vA9EOe2VPEBaHuanQtrA^ivCquWfB@eXt*3?>lFJV@Zp} z*GE%Ko;XQrB!`-H)Tp6G4b!NhW*wR}(nlX=^28}#85!i#>dJVjR9f8+i0?ZO0xz7+ zA0{c0&gBVrZ(su^s;2-6LP2Omb~H~S6wi$vVQp<~QBu*etgr3_aDd~pgK=VE0NpM@ z@8+%G{pcVG0iJ{m#6t0S(&pm&I>0h;M*u)B?Ri)hbbk364GdtTzDIwl0YU@qP6t9m zveoA5ZXIC$pc452L0||hsBK>%9L^E5!1^1*7-0}bP`kF%zpq;la_S&J2iV!Qo%|uM zM`PzYrr9Q$On`)?e*J|A1>K&vuw6WbpieZV=KkqAmEr3z$DM4{-G$Ko|J1d}Hs-Y&roz2pqCP9Q-H)5dxlpu0+c~ z$A-^-tMldZGQj#la{#b1JevQa)bZK#4fRMHo}uN3w0uLuH`M)*hHuyl3?KJGUUglH z#Uj8*_q0uCW@f74){mnLahrq@j)W7sXj>|2?n%tXKvh{;-!+-yK&Q16a?CF3bVA!>R?CRyvA#!Tp z`@pKQx3udgFao}SxuiSb%^364=7tk{%hpelB(IWYk|Lk@eNhy0u@>E|O{Y^ydUqKN zD+iw)eQVJwgNFJqqBczp`?UtWUB<{5V`|FBb-lf=L$hhn_Y8oGgEj^$hxnPt;Jb~% z5@4}dY>JP77a(X59{?w9f7>KYQ)O9pW2b6Z3Dmi*MZg7c1zZESfNXwdct&uX=lNDC z4uU{sSxVAe(%Ab6k{+rl4a4wHa%=dIbh*>0NqUUq*c-rq(W&^JPWb@|iSO!DMquCo O0000!lvI6;RN#5=* z3}Eond3QaK;w+O~Oeun}iSU(g`HS%bYknuD$6!Lu85*=B=fdRf6kmAiGz@Bh8E<$#Ie%&R3=^MA`-*61#Y{jR($P@Hi> zig{b0IR7_k$EN|t!Udnpa+77BZZ%4hPzgD@_pa=XGR~t{m6W9JGlnv}eZ1INgiCwF z8lg)wO_U_=yI+x*yNd7f8Rhx$pFUf;?L1hzxw5VGj7`pMrgmqEHpY*)U(YznyJX6Q zpn2P(#7{i+`Evi4hXh-Tz=4R^<3I8OR)#oyn)GC~z@vZfzOl`Ft~fiE<%s{y!Z`tp zYuFc0e$e>j*|jp+=7Z`zDMBl+$oxxkau!lq&k%5muX^wF&VG;MFL*534?JJKE6G0Tlf1^>jZG^xD4s$UGo+#J{35bC^NFmq=>8?s) zH>*c?{}Y&@!C>$v%jxES5ZnoZVCeh)({j1|c|0D6+a|aVloGbSe*~yjt7$YEec2`XF(A+L z1f*KMUY8`fCij3>{eJ(uG7u9)$h#68B!W&ed}F_(ZQAoN4#X|{ktA=EPtI~SU)$fc zytW*0WsJE8(Cv0B)9LiMSS)@p#!OuVPTruDC30jh7mLMUU5gtQf-6faQZ3T$S+m(Z zUoMxuZExU;eOsLE2EPG2C4F1_B&Xq5LU3G3Uc@A6;zh;e*#(l`CVnv25B~u6B$^&>nBXq}0000!lvI6;RN#5=* z46hhoFg#|x8g?5f!dc)ES21sUt(hAQ)Rnq^o9>8bkWnr zF{I+w+bNEGO$Gw4@@GV*Dsptn9C&f9*Zp#@+|kQBABrjMNL;!=BD1H{KJ$`K{qytl zmoeAZNISacaGtrnms!t&$3%Dk&d1sTOwSiCx@~qcdi7CfqqhPajNg9CR?b_zN?+Sb zrf+Zh-tA|5zGk_W?ydX6=6`zi>sdCZ-NaWgss*;JN=U1dQR#d*M|s;1<;Dx&?O)^u z2I_bSDbM@6fSF<0WlpW8mV+;tL>symt!hzP%+11}i>tV=4{an!PC{xWt~$(697)4fnNXs literal 0 HcwPel00001 diff --git a/pixmaps/media-track.png b/pixmaps/media-track.png new file mode 100644 index 0000000000000000000000000000000000000000..4cb2023b43bbdd87d98902e1c73b7a7dc22db26f GIT binary patch literal 201 zcwXxa@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_2o-U3d z6}Og7-pzYJfyX6&uAz|=+k%9pQ#80Yd|A5C;aH86-y(-?^J{AkOm8^BzcQ*&+(Gjm z)5$mX%~dB&Gq@y5-!kz%FyWM2!N{U8`*ONLo$&bv6;0dBXCFU3Dt*ZDATmd;@03f~ wpM^(ni?qv&{mqWuvg3=nO?U8Fop-NUHN-;Z9WT6G1#}66r>mdKI;Vst0K#QV-2eap literal 0 HcwPel00001 diff --git a/pixmaps/volume-max.png b/pixmaps/volume-max.png new file mode 100644 index 0000000000000000000000000000000000000000..0f5718ae52d073605e92bf03ef7c4b3eb9777aa5 GIT binary patch literal 316 zcwXxa@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G_SAk65n@1Y}5kiEpy*OmPempq@QxXk1FQ$V4Uo-U3d6?3j$wB|bGAkgqoe(PnE zOs8ELl6#yFoH|?~&%bbL#0`yG8Xc)ZuF_KzCbS%p?wPuQ)6h<9ZdPR4zMC^!>$!iL zMKZh=cHt7b@AhQF{S^-XR2ga)um_whopQ9M=k?o=4J$?Z8q5qBb}?*qzm{U8;+wzY z-n7q}0ga1uZ~xfGFTiLf!2FeGL)1FXCho)v)vm{z1XnZcS|{x}DJ4zV!RNAoZ}tl7 z4O2ezWH8DeFI;xsisNRyM(iVPmHKqi1xrs{Kl*-wy-@i>uE4M$=Ch#iVeoYIb6Mw< G&;$T1ZFNxq literal 0 HcwPel00001 diff --git a/pixmaps/volume-medium.png b/pixmaps/volume-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..9262e680aeb267f44026a2b1733a332dcfdc1a1d GIT binary patch literal 330 zcwXxa@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G_SAk65n@1Y}5kiEpy*OmPempq@AQey23EuheKPZ!6KiaB>D+VV3x3be_)XG({6 zI6ayoZXc`4C8a1R6gW?SOMa(-qsJHXS+`F|#P2n~`R2`=J?HCq{!G{Vt|HyA#JGXM zH1@mb1gRs2HjZm-%pXeohn-&O7a718QP0Gouq$F5_Fe}FIy5U7=KpDDv* zwIaKIvvv1k=EZtRSA08Mgzc4_dLA4)w{y!`)|FPDC!LO6KfQeYdWX#u#Fz3hs2IP0 zx!GxT--_(*9%*;I--XxZhcPttwd*n}+*I4Qv1^-D(3$R^JdP3{XKoBhxhj~UJI}oJ zwVyys^_oAt^LKJ67VO%q&|;v~us=q*Pk6~o=JPtnUS^JaXUZ&h-*)$*Tu;Pb*;6iJ z2l$2U|0(OAS`{hA@Mh8HdX_mY`S1AKLYMj;lxFNt;he;_u@&ea22WQ%mvv4FO#t7- BbIJe! literal 0 HcwPel00001 diff --git a/pixmaps/volume-mute.png b/pixmaps/volume-mute.png new file mode 100644 index 0000000000000000000000000000000000000000..77d1e3ea6f57ef4ad26d4f5ce4d5e781043d16fe GIT binary patch literal 381 zcwXxa@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G_SAk65n@1Y}5kiEpy*OmPempq>~n@g(gE(QihE>9Q7kcv4+CvD6+6d=-OpC#w8 ztHVjLL}zYIn_ndt`~3yg>GDd+?Ci}PZgY<4i5~IoPFQ$BXi8F7>$`b;_h!p2KAFJI zFEi;*_5S~JKHooJ>9jzxpkC_@cZb8{gwLc#}R;6r-wd37=DbHSw$wsf>nRS5LvUSGRL3!_E7ym5#{NTX0xnGr+vV4CqtFifxSn;a+oLBss{!8z- Y-L*vUyZdD|U_dc=y85}Sb4q9e0O*dCn*aa+ literal 0 HcwPel00001 diff --git a/pixmaps/volume-zero.png b/pixmaps/volume-zero.png new file mode 100644 index 0000000000000000000000000000000000000000..83f36cd60c3180948570647a1dddc0fc7b654b6f GIT binary patch literal 300 zcwXxa@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b z3=G_SAk65n@1Y}5kiEpy*OmPempq@QzRAPmAwZ#>o-U3d6?2jkB;p>teO_O1=D>ju z@?Of_2Y(tIXgRBp7`NzxpS7@j@GhGn^uL1h@?zJqEg{=Y~k!zQXwy q%oBJW%XUrKVzbFcvrpy?1H&u(gvvwp({2Gh!QkoY=d#Wzp$PzQ>UskJ literal 0 HcwPel00001 diff --git a/ripper.py b/ripper.py new file mode 100644 index 0000000..40191c9 --- /dev/null +++ b/ripper.py @@ -0,0 +1,663 @@ +""" + ripper.py + GUI front-end to cdda2wav and lame. + + Copyright 2004 Kenneth Hayber + All rights reserved. + + 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. + + 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 rox +from rox import g, i18n, app_options +import os, sys, signal, re, string, socket, time, popen2, threading, Queue +from threading import * +from rox.options import Option + +socket.setdefaulttimeout(30) #CDDB doesn't do this and will wait forever. +import PyCDDB, cd_logic + +_ = rox.i18n.translation(os.path.join(rox.app_dir, 'Messages')) + +#Who am I and how did I get here? +APP_NAME = 'Ripper' #I could call it Mr. Giles, but that would be gay. +APP_PATH = os.path.split(os.path.abspath(sys.argv[0]))[0] + + +#Options.xml processing +rox.setup_app_options(APP_NAME) + +#assume that everyone puts their music in ~/Music +LIBRARY = Option('library', os.path.expanduser('~')+'/MyMusic') + +RIPPER = Option('ripper', 'cdda2wav') +RIPPER_DEV = Option('ripper_dev', '/dev/cdrom') +RIPPER_LUN = Option('ripper_lun', 'ATAPI:0,1,0') +RIPPER_OPTS = Option('ripper_opts', '-x -H') + +ENCODER = Option('encoder', 'lame') +ENCODER_OPTS = Option('encoder_opts', '--vbr-new -b160 --nohist --add-id3v2') + + +#CDDB Server and Options +CDDB_SERVER = Option('cddb_server', 'http://freedb.freedb.org/~cddb/cddb.cgi') + +#ENCODER options +#RIPPER options + +rox.app_options.notify() + + +#Column indicies +COL_ENABLE = 0 +COL_TRACK = 1 +COL_TIME = 2 +COL_STATUS = 3 + + +# My gentoo python doesn't have universal line ending support compiled in +# and these guys (cdda2wav and lame) use CR line endings to pretty-up their output. +def myreadline(file): + '''Return a line of input using \r or \n as terminators''' + line = '' + while '\n' not in line and '\r' not in line: + char = file.read(1) + if char == '': return line + line += char + return line + + +def which(filename): + '''Return the full path of an executable if found on the path''' + if (filename == None) or (filename == ''): + return None + + env_path = os.getenv('PATH').split(':') + for p in env_path: + if os.access(p+'/'+filename, os.X_OK): + return p+'/'+filename + return None + + +def strip_illegal(instr): + '''remove illegal (filename) characters from string''' + str = instr + str = str.strip() + str = string.translate(str, string.maketrans(r'/+{}*.?', r'--()___')) + return str + + +class Ripper(rox.Window): + '''Rip and Encode a CD''' + def __init__(self): + rox.Window.__init__(self) + + self.set_title(APP_NAME) + self.set_border_width(1) + self.set_default_size(450, 500) + self.set_position(g.WIN_POS_MOUSE) + + #capture wm delete event + self.connect("delete_event", self.delete_event) + + # Update things when options change + rox.app_options.add_notify(self.get_options) + + + #song list + ####################################### + swin = g.ScrolledWindow() + self.scroll_window = swin + + swin.set_border_width(3) + swin.set_policy(g.POLICY_AUTOMATIC, g.POLICY_AUTOMATIC) + swin.set_shadow_type(g.SHADOW_IN) + + self.store = g.ListStore(int, str, str, str) + view = g.TreeView(self.store) + self.view = view + swin.add(view) + view.set_rules_hint(True) + +# self.view.add_events(g.gdk.BUTTON_PRESS_MASK) +# self.view.connect('button-press-event', self.button_press) + + cell = g.CellRendererToggle() + cell.connect('toggled', self.toggle_check) + column = g.TreeViewColumn('', cell, active=COL_ENABLE) + view.append_column(column) + column.set_resizable(False) + column.set_reorderable(False) + + cell = g.CellRendererText() + column = g.TreeViewColumn(_('Track'), cell, text = COL_TRACK) + view.append_column(column) + column.set_resizable(True) + column.set_reorderable(False) + + cell = g.CellRendererText() + column = g.TreeViewColumn(_('Time'), cell, text = COL_TIME) + view.append_column(column) + column.set_resizable(True) + column.set_reorderable(False) + + cell = g.CellRendererText() + column = g.TreeViewColumn(_('Status'), cell, text = COL_STATUS) + view.append_column(column) + column.set_resizable(True) + column.set_reorderable(False) + + view.connect('row-activated', self.activate) + self.selection = view.get_selection() + self.handler = self.selection.connect('changed', self.set_selection) + + + self.toolbar = g.Toolbar() + self.toolbar.set_style(g.TOOLBAR_ICONS) + self.toolbar.insert_stock(g.STOCK_PREFERENCES, + _('Settings'), None, self.show_options, None, 0) + self.stop_btn = self.toolbar.insert_stock(g.STOCK_STOP, + _('Stop'), None, self.stop, None, 0) + self.rip_btn = self.toolbar.insert_stock(g.STOCK_EXECUTE, + _('Rip & Encode'), None, self.rip_n_encode, None, 0) + self.refresh_btn = self.toolbar.insert_stock(g.STOCK_REFRESH, + _('Reload CD'), None, self.do_get_tracks, None, 0) + + + self.table = g.Table(1, 5, g.FALSE) + x_pad = 2 + y_pad = 1 + + self.artist_entry = g.Entry(max=255) + self.artist_entry.connect('changed', self.stuff_changed) + self.table.attach(g.Label(str=_('Artist')), 0, 1, 2, 3, 0, 0, 4, y_pad) + self.table.attach(self.artist_entry, 1, 2, 2, 3, g.EXPAND|g.FILL, 0, x_pad, y_pad) + + self.album_entry = g.Entry(max=255) + self.album_entry.connect('changed', self.stuff_changed) + self.table.attach(g.Label(str=_('Album')), 0, 1, 3, 4, 0, 0, 4, y_pad) + self.table.attach(self.album_entry, 1, 2, 3, 4, g.EXPAND|g.FILL, 0, x_pad, y_pad) + + self.genre_entry = g.Entry(max=255) + self.genre_entry.connect('changed', self.stuff_changed) + self.table.attach(g.Label(str=_('Genre')), 0, 1, 4, 5, 0, 0, 4, y_pad) + self.table.attach(self.genre_entry, 1, 2, 4, 5, g.EXPAND|g.FILL, 0, x_pad, y_pad) + + self.year_entry = g.Entry(max=4) + self.year_entry.connect('changed', self.stuff_changed) + self.table.attach(g.Label(str=_('Year')), 0, 1, 5, 6, 0, 0, 4, y_pad) + self.table.attach(self.year_entry, 1, 2, 5, 6, g.EXPAND|g.FILL, 0, x_pad, y_pad) + + + # Create layout, pack and show widgets + self.vbox = g.VBox() + self.add(self.vbox) + self.vbox.pack_start(self.toolbar, False, True, 0) + self.vbox.pack_start(self.table, False, True, 0) + self.vbox.pack_start(self.scroll_window, True, True, 0) + self.vbox.show_all() + + self.cddb_thd = None + self.ripper_thd = None + self.encoder_thd = None + self.is_ripping = False + self.is_encoding = False + self.please_stop = False + + cd_logic.set_dev(RIPPER_DEV.value) + self.cd_status = cd_logic.check_dev() + + self.disc_id = None + self.do_get_tracks() + + # Set up thread for GUI updates + g.timeout_add(1000, self.update_gui) + + + def update_gui(self): + '''Update button status based on current state''' + if self.is_ripping or self.is_encoding: + self.stop_btn.set_sensitive(True) + self.rip_btn.set_sensitive(False) + self.refresh_btn.set_sensitive(False) + + if not self.is_ripping and not self.is_encoding: + self.stop_btn.set_sensitive(False) + self.rip_btn.set_sensitive(True) + self.refresh_btn.set_sensitive(True) + + #get tracks if cd changed + try: + cd_status = cd_logic.check_dev() + if self.cd_status != cd_status: + self.cd_status = cd_status +# cd_logic.get_disc_id() +# if self.disc_id <> disc_id: +# print self.disc_id, disc_id + self.do_get_tracks() + except: pass + + return True + + + def stuff_changed(self, button=None): + '''Get new text from edit boxes and save it''' + self.genre = self.genre_entry.get_text() + self.artist = self.artist_entry.get_text() + self.album = self.album_entry.get_text() + self.year = self.year_entry.get_text() + + + def runit(self, it): + '''Run a function in a thread''' + thd_it = Thread(name='mythread', target=it) + thd_it.setDaemon(True) + thd_it.start() + return thd_it + + + def stop(self, it): + '''Stop current rip/encode process''' + self.please_stop = True + + + def do_get_tracks(self, button=None): + '''Get the track info (cddb and cd) in a thread''' + if self.is_ripping: + return + self.cddb_thd = self.runit(self.get_tracks) + + + def get_tracks(self): + '''Get the track info (cddb and cd)''' + stuff = self.get_cddb() + if stuff == False: + #print "no disc in tray?" + g.threads_enter() + self.store.clear() + self.artist_entry.set_text('') + self.album_entry.set_text('') + self.genre_entry.set_text('') + self.year_entry.set_text('') + g.threads_leave() + return + else: + (count, artist, album, genre, year, tracklist) = stuff + #print count, artist, album, genre, year, tracklist + + self.artist = artist + self.count = count + self.album = album + self.genre = genre + self.year = year + self.tracklist = tracklist + + g.threads_enter() + + if artist: self.artist_entry.set_text(artist) + if album: self.album_entry.set_text(album) + if genre: self.genre_entry.set_text(genre) + if year: self.year_entry.set_text(year) + + for track in tracklist: + #print song + iter = self.store.append(None) + self.store.set(iter, COL_TRACK, track[0]) + self.store.set(iter, COL_TIME, track[1]) + self.store.set(iter, COL_ENABLE, True) + + g.threads_leave() + + + def get_cddb(self): + '''Query cddb for track and cd info''' + g.threads_enter() + dlg = g.MessageDialog(buttons=g.BUTTONS_CANCEL, message_format="Getting Track Info.") + dlg.set_position(g.WIN_POS_NONE) + (a, b) = dlg.get_size() + (x, y) = self.get_position() + (dx, dy) = self.get_size() + dlg.move(x+dx/2-a/2, y+dy/2-b/2) + dlg.show() + g.threads_leave() + + count = artist = genre = album = year = None + tracklist = [] + tracktime = [] + + try: + disc_id = cd_logic.get_disc_id() + self.disc_id = disc_id + #print disc_id + + g.threads_enter() + dlg.set_title('Got Disc ID') + g.threads_leave() + + count = cd_logic.total_tracks() + cddb_id = cd_logic.get_cddb_id() + + #PyCDDB wants a string delimited by spaces, go figure. + cddb_id_string = '' + for n in cddb_id: + cddb_id_string += str(n)+' ' + #print cddb_id, cddb_id_string + + for i in range(count): + tracktime = cd_logic.get_track_time_total(i) + track_time = time.strftime('%M:%S', time.gmtime(tracktime)) + tracklist.append((_('Track')+`i`,track_time)) + + try: + db = PyCDDB.PyCDDB(CDDB_SERVER.value) + query_info = db.query(cddb_id_string) + #print query_info + + g.threads_enter() + dlg.set_title('Got Disc Info') + g.threads_leave() + + #make sure we didn't get an error, then query CDDB + if len(query_info) > 0: + index = 0 #but we might want to choose one of the others? + read_info = db.read(query_info[index]) + #print read_info + g.threads_enter() + dlg.set_title('Got Track Info') + g.threads_leave() + + try: + (artist, album) = query_info[index]['title'].split('/') + artist = artist.strip() + album = album.strip() + genre = query_info[index]['category'] + if genre in ['misc', 'data']: + genre = 'Other' + + print query_info['year'] + print read_info['EXTD'] + print read_info['YEARD'] + + #x = re.match(r'.*YEAR: (.+).*',read_info['EXTD']) + #if x: + # print x.group(1) + # year = x.group(1) + except: + pass + + if len(read_info['TTITLE']) > 0: + for i in range(count): + try: + track_name = read_info['TTITLE'][i] + track_time = tracklist[i][1] + #print i, track_name, track_time + tracklist[i] = (track_name, track_time) + except: + pass + except: + pass + + except: + g.threads_enter() + dlg.destroy() + g.threads_leave() + return False + + g.threads_enter() + dlg.destroy() + g.threads_leave() + return count, artist, album, genre, year, tracklist + + + def get_cdda2wav(self, tracknum, track): + '''Run cdda2wav to rip a track from the CD''' + cdda2wav_cmd = RIPPER.value + cdda2wav_dev = RIPPER_DEV.value + cdda2wav_lun = RIPPER_LUN.value + cdda2wav_args = '-D%s -A%s -t %d "%s"' % ( + cdda2wav_lun, cdda2wav_dev, tracknum+1, strip_illegal(track)) + cdda2wav_opts = RIPPER_OPTS.value + #print cdda2wav_opts, cdda2wav_args + + thing = popen2.Popen4(cdda2wav_cmd+' '+cdda2wav_opts+' '+cdda2wav_args ) + outfile = thing.fromchild + + while True: + line = myreadline(outfile) + if line: + x = re.match('([\s0-9]+)%', line) + if x: + percent = int(x.group(1)) + self.status_update(tracknum, 'rip', percent) + else: + break + if self.please_stop: + break + + if self.please_stop: + os.kill(thing.pid, signal.SIGKILL) + + code = thing.wait() + self.status_update(tracknum, 'rip', 100) + #print code + return code + + + def get_lame(self, tracknum, track, artist, genre, album, year): + '''Run lame to encode a wav file to mp3''' + try: + int_year = int(year) + except: + int_year = 1 + + lame_cmd = ENCODER.value + lame_opts = ENCODER_OPTS.value + lame_tags = '--ta "%s" --tt "%s" --tl "%s" --tg "%s" --tn %d --ty %d' % ( + artist, track, album, genre, tracknum+1, int_year) + lame_args = '"%s" "%s"' % (strip_illegal(track)+'.wav', strip_illegal(track)+'.mp3') + + #print lame_opts, lame_tags, lame_args + + thing = popen2.Popen4(lame_cmd+' '+lame_opts+' '+lame_tags+' '+lame_args ) + outfile = thing.fromchild + + while True: + line = myreadline(outfile) + if line: + #print line + #for some reason getting this right for lame was a royal pain. + x = re.match(r"^[\s]+([0-9]+)/([0-9]+)", line) + if x: + percent = int(100 * (float(x.group(1)) / float(x.group(2)))) + self.status_update(tracknum, 'enc', percent) + else: + break + if self.please_stop: + break + + if self.please_stop: + os.kill(thing.pid, signal.SIGKILL) + + code = thing.wait() + self.status_update(tracknum, 'enc', 100) + #print code + return code + + + def rip_n_encode(self, button=None): + '''Process all selected tracks (rip and encode)''' + try: os.chdir(os.path.expanduser('~')) + except: pass + try: os.mkdir(LIBRARY.value) + except: pass + try: os.chdir(LIBRARY.value) + except: pass + + if self.count and self.artist and self.album: + try: os.mkdir(self.artist) + except: pass + + try: os.mkdir(self.artist+'/'+self.album) + except: pass + + try: os.chdir(self.artist+'/'+self.album) + except: pass + + self.please_stop = False + + #the queue to feed tracks from ripper to encoder + self.wavqueue = Queue.Queue(1000) + + self.ripper_thd = self.runit(self.ripit) + self.encoder_thd = self.runit(self.encodeit) + + + def ripit(self): + '''Thread to rip all selected tracks''' + self.is_ripping = True + for i in range(self.count): + if self.please_stop: + break; + + if self.store[i][COL_ENABLE]: + track = self.store[i][COL_TRACK] + #print i, track + status = self.get_cdda2wav(i, track) + if status <> 0: + print 'cdda2wav died %d' % status + self.status_update(i, 'rip_error', 0) + else: + #push this track on the queue for the encoder + self.wavqueue.put((track, i)) + + #push None object to tell encoder we're done + if self.wavqueue: + self.wavqueue.put((None, None)) + + self.is_ripping = False + + + def encodeit(self): + '''Thread to encode all tracks from the wavqueue''' + self.is_encoding = True + while True: + if self.please_stop: + break + (track, tracknum) = self.wavqueue.get(True) + if track == None: + break + status = self.get_lame(tracknum, track, self.artist, self.genre, self.album, self.year) + if status <> 0: + print 'lame died %d' % status + self.status_update(tracknum, 'enc_error', 0) + try: os.unlink(strip_illegal(track)+".wav") + except: pass + try: os.unlink(strip_illegal(track)+".inf") + except: pass + + self.is_encoding = False + del self.wavqueue + + + def status_update(self, row, state, percent): + '''Callback from rip/encode threads to update display''' + g.threads_enter() + + iter = self.store.get_iter((row,)) + if not iter: return + + if state == 'rip': + if percent < 100: + self.store.set_value(iter, COL_STATUS, _('Ripping')+': %d%%' % percent) + else: + self.store.set_value(iter, COL_STATUS, _('Ripping')+': '+_('done')) + + if state == 'enc': + if percent < 100: + self.store.set_value(iter, COL_STATUS, _('Encoding')+': %d%%' % percent) + else: + self.store.set_value(iter, COL_STATUS, _('Encoding')+': '+_('done')) + + if state == 'rip_error': + self.store.set_value(iter, COL_STATUS, _('Ripping')+': '+_('error')) + + if state == 'enc_error': + self.store.set_value(iter, COL_STATUS, _('Encoding')+': '+_('error')) + + g.threads_leave() + + + def activate(self, view, path, column): + '''Edit a track name''' + model, iter = self.view.get_selection().get_selected() + if iter: + track = model.get_value(iter, COL_TRACK) + dlg = g.Dialog(APP_NAME) + dlg.set_position(g.WIN_POS_NONE) + dlg.set_default_size(350, 100) + (a, b) = dlg.get_size() + (x, y) = self.get_position() + (dx, dy) = self.get_size() + dlg.move(x+dx/2-a/2, y+dy/2-b/2) + dlg.show() + + entry = g.Entry() + entry.set_text(track) + dlg.set_position(g.WIN_POS_MOUSE) + entry.show() + entry.set_activates_default(True) + dlg.vbox.pack_start(entry) + + dlg.add_button(g.STOCK_OK, g.RESPONSE_OK) + dlg.add_button(g.STOCK_CANCEL, g.RESPONSE_CANCEL) + dlg.set_default_response(g.RESPONSE_OK) + response = dlg.run() + + if response == g.RESPONSE_OK: + track = entry.get_text() + #print track + model.set_value(iter, COL_TRACK, track) + dlg.destroy() + + + def toggle_check(self, cell, rownum): + '''Toggle state for each song''' + row = self.store[rownum] + row[COL_ENABLE] = not row[COL_ENABLE] + self.store.row_changed(rownum, row.iter) + + + def set_selection(self, thing): + '''Get current selection''' + #model, iter = self.view.get_selection().get_selected() + #if iter: + # track = model.get_value(iter, COL_TRACK) + + def show_options(self, button=None): + '''Show Options dialog''' + rox.edit_options() + + def get_options(self): + '''Get changed Options''' + pass + + def delete_event(self, ev, e1): + '''Bye-bye''' + self.close() + + def close(self, button = None): + '''We're outta here!''' + self.destroy() + + -- 2.11.4.GIT