1#!/usr/bin/env python
2
3############################################################################
4# Python wrapper script for running the Amarok LiveCD remastering scripts
5# from within Amarok.  Based on the Python-Qt template script for Amarok
6# (c) 2005 Mark Kretschmann <kretschmann@kde.org>
7#
8# (c) 2005 Leo Franchi <lfranchi@gmail.com>
9#
10# Depends on: Python 2.2, PyQt
11############################################################################
12#
13# This program is free software; you can redistribute it and/or modify
14# it under the terms of the GNU General Public License as published by
15# the Free Software Foundation; either version 2 of the License, or
16# (at your option) any later version.
17#
18############################################################################
19
20import ConfigParser
21import os
22import sys
23import threading
24import signal
25from time import sleep
26
27try:
28    from qt import *
29except:
30    os.popen( "kdialog --sorry 'PyQt (Qt bindings for Python) is required for this script.'" )
31    raise
32
33
34# Replace with real name
35debug_prefix = "LiveCD Remastering"
36
37
38class ConfigDialog ( QDialog ):
39    """ Configuration widget """
40
41    def __init__( self ):
42        QDialog.__init__( self )
43        self.setWFlags( Qt.WDestructiveClose )
44        self.setCaption("Amarok Live! Configuration")
45
46        self.lay = QGridLayout( self, 3, 2)
47
48        self.lay.addColSpacing( 0, 300 )
49
50        self.isopath = QLineEdit( self )
51        self.isopath.setText( "Path to Amarok Live! iso" )
52        self.tmppath = QLineEdit( self )
53        self.tmppath.setText( "Temporary directory used, 2.5gb free needed" )
54
55        self.lay.addWidget( self.isopath, 0, 0 )
56        self.lay.addWidget( self.tmppath, 1, 0 )
57
58        self.isobutton = QPushButton( self )
59        self.isobutton.setText("Browse..." )
60        self.tmpbutton = QPushButton( self )
61        self.tmpbutton.setText("Browse..." )
62
63        self.cancel = QPushButton( self )
64        self.cancel.setText( "Cancel" )
65        self.ok = QPushButton( self )
66        self.ok.setText( "Ok" )
67
68        self.lay.addWidget( self.isobutton, 0, 1 )
69        self.lay.addWidget( self.tmpbutton, 1, 1 )
70        self.lay.addWidget( self.cancel, 2, 1 )
71        self.lay.addWidget( self.ok, 2, 0)
72
73        self.connect( self.isobutton, SIGNAL( "clicked()" ), self.browseISO )
74        self.connect( self.tmpbutton, SIGNAL( "clicked()" ), self.browsePath )
75
76        self.connect( self.ok, SIGNAL( "clicked()" ), self.save )
77        self.connect( self.ok, SIGNAL( "clicked()" ), self.unpack )
78#        self.connect( self.ok, SIGNAL( "clicked()" ), self.destroy )
79        self.connect( self.cancel, SIGNAL( "clicked()" ), self, SLOT("reject()") )
80
81        self.adjustSize()
82
83        path = None
84        try:
85            config = ConfigParser.ConfigParser()
86            config.read( "remasterrc" )
87            path = config.get( "General", "path" )
88            iso = config.get( "General", "iso")
89
90            if not path == "": self.tmppath.setText(path)
91            if not iso == "": self.isopath.setText(iso)
92        except:
93            pass
94
95
96
97    def save( self ):
98        """ Saves configuration to file """
99        self.file = file( "remasterrc", 'w' )
100        self.config = ConfigParser.ConfigParser()
101        self.config.add_section( "General" )
102        self.config.set( "General", "path", self.tmppath.text() )
103        self.config.set( "General", "iso", self.isopath.text() )
104        self.config.write( self.file )
105        self.file.close()
106
107        self.accept()
108
109    def clear():
110
111        self.file = file( "remasterrc", 'w' )
112        self.config = ConfigParser.ConfigParser()
113        self.config.add_section( "General" )
114        self.config.set( "General", "path", ""  )
115        self.config.set( "General", "iso", "" )
116        self.config.write( self.file )
117        self.file.close()
118
119    def browseISO( self ):
120
121        path = QFileDialog.getOpenFileName( "/home",
122                                                 "CD Images (*.iso)",
123                                                 self,
124                                                 "iso choose dialogr",
125                                                 "Choose ISO to remaster")
126        self.isopath.setText( path )
127
128    def browsePath( self ):
129
130        tmp = QFileDialog.getExistingDirectory( "/home",
131                                                self,
132                                                "get tmp dir",
133                                                "Choose working directory",
134                                                1)
135        self.tmppath.setText( tmp )
136
137
138    def unpack( self ):
139
140        # now the fun part, we run part 1
141        fd = os.popen("kde-config --prefix", "r")
142        kdedir = fd.readline()
143        kdedir = kdedir.strip()
144        scriptdir = kdedir + "/share/apps/amarok/scripts/amarok_live"
145        fd.close()
146
147        path, iso = self.readConfig()
148        os.system("kdesu -t sh %s/amarok.live.remaster.part1.sh %s %s" % (scriptdir, path, iso))
149        #os.wait()
150        print "got path: %s" % path
151
152
153
154
155    def readConfig( self ) :
156        path = ""
157        iso = ""
158        try:
159            config = ConfigParser.ConfigParser()
160            config.read("remasterrc")
161            path = config.get("General", "path")
162            iso = config.get("General", "iso")
163        except:
164            pass
165        return (path, iso)
166
167
168
169class Notification( QCustomEvent ):
170    __super_init = QCustomEvent.__init__
171    def __init__( self, str ):
172
173
174        self.__super_init(QCustomEvent.User + 1)
175        self.string = str
176
177class Remasterer( QApplication ):
178    """ The main application, also sets up the Qt event loop """
179
180    def __init__( self, args ):
181        QApplication.__init__( self, args )
182        debug( "Started." )
183
184        # Start separate thread for reading data from stdin
185        self.stdinReader = threading.Thread( target = self.readStdin )
186        self.stdinReader.start()
187
188        self.readSettings()
189
190
191        # ugly hack, thanks mp8 anyway
192        os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Add playlist to livecd\"")
193        os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Add selected to livecd\"")
194        os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Create Remastered CD\"")
195        os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Clear Music on livecd\"")
196
197        os.system("dcop amarok script addCustomMenuItem \"Amarok live\" \"Add playlist to livecd\"")
198        os.system("dcop amarok script addCustomMenuItem \"Amarok live\" \"Add selected to livecd\"")
199        os.system("dcop amarok script addCustomMenuItem \"Amarok live\" \"Create Remastered CD\"")
200        os.system("dcop amarok script addCustomMenuItem \"Amarok live\" \"Clear Music on livecd\"")
201
202
203    def readSettings( self ):
204        """ Reads settings from configuration file """
205
206        try:
207            path = config.get( "General", "path" )
208
209        except:
210            debug( "No config file found, using defaults." )
211
212
213############################################################################
214# Stdin-Reader Thread
215############################################################################
216
217    def readStdin( self ):
218        """ Reads incoming notifications from stdin """
219
220        while True:
221            # Read data from stdin. Will block until data arrives.
222            line = sys.stdin.readline()
223
224            if line:
225                qApp.postEvent( self, Notification(line) )
226            else:
227                break
228
229
230############################################################################
231# Notification Handling
232############################################################################
233
234    def customEvent( self, notification ):
235        """ Handles notifications """
236
237        string = QString(notification.string)
238        debug( "Received notification: " + str( string ) )
239
240        if string.contains( "configure" ):
241            self.configure()
242        if string.contains( "stop"):
243            self.stop()
244
245        elif string.contains( "customMenuClicked" ):
246            if "selected" in string:
247                self.copyTrack( string )
248            elif "playlist" in string:
249                self.copyPlaylist()
250            elif "Create" in string:
251                self.createCD()
252            elif "Clear" in string:
253                self.clearCD()
254
255
256# Notification callbacks. Implement these functions to react to specific notification
257# events from Amarok:
258
259    def configure( self ):
260        debug( "configuration" )
261
262        self.dia = ConfigDialog()
263        self.dia.show()
264        #self.connect( self.dia, SIGNAL( "destroyed()" ), self.readSettings )
265
266    def clearCD( self ):
267
268        self.dia = ConfigDialog()
269        path, iso = self.dia.readConfig()
270
271        os.system("rm -rf %s/amarok.live/music/* %s/amarok.live/playlist/* %s/amarok.live/home/amarok/.kde/share/apps/amarok/current.xml" % (path, path, path))
272
273    def onSignal( self, signum, stackframe ):
274        stop()
275
276    def stop( self ):
277
278        fd = open("/tmp/amarok.stop", "w")
279        fd.write( "stopping")
280        fd.close()
281
282        os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Add playlist to livecd\"")
283        os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Add selected to livecd\"")
284        os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Create Remastered CD\"")
285        os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Clear Music on livecd\"")
286
287
288    def copyPlaylist( self ):
289
290        self.dia = ConfigDialog()
291        path, iso = self.dia.readConfig()
292        if path == "":
293            os.system("dcop amarok playlist popupMessage 'Please run configure first.'")
294            return
295
296        tmpfileloc = os.tmpnam()
297        os.system("dcop amarok playlist saveM3u '%s' false" % tmpfileloc)
298        tmpfile = open(tmpfileloc)
299
300        import urllib
301
302        files = ""
303        m3u = ""
304        for line in tmpfile.readlines():
305            if line[0] != "#":
306
307                line = line.strip()
308
309                # get filename
310                name = line.split("/")[-1]
311
312               #make url
313                url = "file://" + urllib.quote(line)
314
315               #make path on livecd
316                livecdpath = "/music/" + name
317
318                files += url + " "
319                m3u += livecdpath + "\n"
320
321        tmpfile.close()
322
323        files = files.strip()
324
325        os.system("kfmclient copy %s file://%s/amarok.live/music/" % (files, path))
326
327        import random
328        suffix = random.randint(0,10000)
329#        os.system("mkdir %s/amarok.live/home/amarok/.kde/share/apps/amarok/playlists/" % path)
330        m3uOut = open("/tmp/amarok.live.%s.m3u" % suffix, 'w')
331
332        m3u = m3u.strip()
333        m3uOut.write(m3u)
334
335        m3uOut.close()
336
337        os.system("mv /tmp/amarok.live.%s.m3u %s/amarok.live/playlist/" % (suffix,path))
338        os.system("rm /tmp/amarok.live.%s.m3u" % suffix)
339
340
341        os.remove(tmpfileloc)
342
343    def copyTrack( self, menuEvent ):
344
345        event = str( menuEvent )
346        debug( event )
347        self.dia = ConfigDialog()
348
349        path,iso = self.dia.readConfig()
350        if path == "":
351            os.system("kdialog --sorry 'You have not specified where the Amarok live iso is. Please click configure and do so first.'")
352        else:
353            # get the list of files. yes, its ugly. it works though.
354            #files =  event.split(":")[-1][2:-1].split()[2:]
355            #trying out a new one
356         #files = event.split(":")[-1][3:-2].replace("\"Amarok live!\" \"add to livecd\" ", "").split("\" \"")
357            #and another
358
359            files = event.replace("customMenuClicked: Amarok live Add selected to livecd", "").split()
360
361            allfiles = ""
362            for file in files:
363                allfiles += file + " "
364            allfiles = allfiles.strip()
365            os.system("kfmclient copy %s file://%s/amarok.live/music/" % (allfiles, path))
366
367    def createCD( self ):
368
369        self.dia = ConfigDialog()
370        path,iso = self.dia.readConfig()
371        if path == "":
372            os.system("kdialog --sorry 'You have not configured Amarok live! Please run configure.")
373
374        fd = os.popen("kde-config --prefix", "r")
375        kdedir = fd.readline()
376        kdedir = kdedir.strip()
377        scriptdir = kdedir + "/share/apps/amarok/scripts/amarok_live"
378        fd.close()
379
380        os.system("kdesu sh %s/amarok.live.remaster.part2.sh %s" % (scriptdir, path))
381
382        fd = open("/tmp/amarok.script", 'r')
383        y = fd.readline()
384        y = y.strip()
385        if y == "end": # user said no more, clear path
386            self.dia.clear()
387        fd.close()
388
389
390############################################################################
391
392def onSignal( signum, stackframe ):
393    fd = open("/tmp/amarok.stop", "w")
394    fd.write( "stopping")
395    fd.close()
396
397    print 'STOPPING'
398
399    os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Add playlist to livecd\"")
400    os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Add selected to livecd\"")
401    os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Create Remastered CD\"")
402    os.system("dcop amarok script removeCustomMenuItem \"Amarok live\" \"Clear Music on livecd\"")
403
404
405def debug( message ):
406    """ Prints debug message to stdout """
407
408    print debug_prefix + " " + message
409
410def main():
411    app = Remasterer( sys.argv )
412
413    # not sure if it works or not...  playing it safe
414    dia = ConfigDialog()
415
416    app.exec_loop()
417
418if __name__ == "__main__":
419
420    mainapp = threading.Thread(target=main)
421    mainapp.start()
422    signal.signal(15, onSignal)
423    print signal.getsignal(15)
424    while 1: sleep(120)
425
426    #main( sys.argv )
427
428