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