clMain.py

00001 #!/usr/bin/env python
00002 # -*- coding: utf-8 -*-
00003 
00004 # ----------------------------------------------------------------------------
00005 # pyjama - python jamendo audioplayer
00006 # Copyright (c) 2008 Daniel Nögel
00007 #
00008 # This program is free software: you can redistribute it and/or modify
00009 # it under the terms of the GNU General Public License as published by
00010 # the Free Software Foundation, either version 3 of the License, or
00011 # (at your option) any later version.
00012 #
00013 # This program is distributed in the hope that it will be useful,
00014 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00015 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016 # GNU General Public License for more details.
00017 # You should have received a copy of the GNU General Public License
00018 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
00019 # ----------------------------------------------------------------------------
00020 
00021 # ----------------------------------------------------------------------------
00022 # This is pyjama's main class. It holds allmost every other class, which
00023 # is needed. If you are writing plugins, this main class will be passed
00024 # to you plugin's __init__ function!
00025 # ----------------------------------------------------------------------------
00026 
00027 ## @package clMain
00028 # Pyjama's main class 'main' will be given
00029 # to any derived class.
00030 # (Almost) any class is referenced here.
00031 
00032 # math
00033 from math import floor
00034 # Time formatting
00035 from time import strftime, gmtime, time, sleep
00036 
00037 # Laden von Dateien
00038 import urllib
00039 import hashlib
00040 
00041 # Threading
00042 from threading import Thread
00043 
00044 import traceback 
00045 import os
00046 import sys
00047 
00048 #try:
00049 #    import clXMLRPC
00050 #except:
00051 #    print("Error import clXMLRPC")
00052 #try:
00053 #    import socket
00054 #    socket.setdefaulttimeout(3) # Timeout auf 3 sec. setzen
00055 #except:
00056 #    print ("Error importing module socket")
00057 
00058 try:
00059     import clXMLRPC
00060     XMLRPC_AVAILABLE = True
00061 except:
00062     XMLRPC_AVAILABLE = False
00063     print ("Error import clXMLRPC")
00064 
00065 try:
00066     import gnome.ui
00067     GNOME_UI_AVAILABLE = True
00068 except:
00069     GNOME_UI_AVAILABLE = False
00070 
00071 #RegEx
00072 import re
00073 
00074 import functions
00075 import clSettings
00076 import clJamendo
00077 if os.name == "posix" or "mac" or "OVERRIDE-PLAYER" in sys.argv:
00078     import clGstreamer010 as PLAYER
00079 else:
00080     import clPlayerForWin as PLAYER
00081 
00082 #import clDB
00083 import dbthreaded
00084 
00085 
00086 import clThreadedDownload #clPlayer
00087 from clWidgets import *
00088 import clEntry#, clProgressWindow
00089 import clPlugin, clEvent
00090 import notification
00091 import clBrowserInterface
00092 import download_db
00093 import clLayouts
00094 import clPreferences
00095 from extended_modules import clPlaylists
00096 from extended_modules import clBookmarks
00097 
00098 
00099 try:
00100     import download_db as update
00101 except:
00102     print ("Error importing update module")
00103 
00104 ## Pyjama's main class which is passed to most modules as pyjama
00105 class main():
00106     ## The Constructor
00107     # @param self Object Pointer
00108     # @param parent The class calling pyjama.
00109     # In this case this is clWindow. Since 'main'
00110     # is thought to be the main class, parent is later
00111     # treated as if it was a child of 'main'
00112     # @param options  A OptionParser object
00113     def __init__(self, parent, options):
00114         ## Holds a bool indicating if pyjama is running verbose
00115         self.verbose = options.verbose
00116         ## Holds a bool indicating if pyjama is running in debugging mode
00117         self.debug = options.debug
00118         ## Holds a bool indicating if pyjama is running in debugging extreme mode
00119         self.debug_extreme = options.debug_extreme
00120 #        self.print_tracebacks = options.print_tracebacks
00121         
00122         ## Hold pyjama's current version
00123         self.version = functions.VERSION
00124         if GNOME_UI_AVAILABLE:
00125             gnome.init("Pyjama", self.version)
00126         print "PYJAMA FOUND IN %s" % functions.install_dir()
00127 
00128 
00129         ## In some cases to many windows pop up on pyjama's
00130         # startup.
00131         # 
00132         self.need_attention = False
00133 
00134         ## Pyjama's Event class
00135         self.Events = clEvent.Events()
00136         # Create some events:
00137         self.Events.add_event("pluginloaded")
00138         self.Events.add_event("nowplaying")
00139         self.Events.add_event("alldone")
00140         self.Events.add_event("showing_album_page")
00141         self.Events.add_event("showing_artist_page")
00142         self.Events.add_event("firstrun")
00143         self.Events.add_event("error")
00144         self.Events.add_event("info")
00145         self.Events.add_event("layout_changed")
00146         self.Events.add_event("populate_listmenu")
00147         self.Events.add_event("populate_playlistmenu")
00148         self.Events.add_event("scrolled_window_resized")
00149         self.Events.add_event("playlist_tooltip")
00150 
00151         self.Events.add_event("albuminfo_created")
00152 
00153 
00154 #        self.Events.pluginloaded += self.ev_plugin_loaded
00155 #        self.Events.eventlist['pluginloaded'] +=
00156         self.Events.connect_event("pluginloaded", self.ev_plugin_loaded)
00157 
00158         self.home_fkt = self.go_home_fkt
00159 
00160         # Function called when an error occures:
00161         #self.error_fkt = self.error
00162         self.Events.connect_event("error", self.error)
00163         self.Events.connect_event("info", self.info)
00164 
00165         ## Pyjama's Settings class
00166         # Use for simple general settings
00167         # data will be stored in a pyjama.cfg
00168         self.settings = clSettings.settings(self)
00169 
00170         ## Pyjama's database setting class
00171         # Use this, if you need to store many
00172         # values.
00173         self.settingsdb = dbthreaded.DB_Settings(self)
00174 #        self.settingsdb = clDB.DB_Settings(self)
00175         ## Database class  clDB.DB
00176         self.db = dbthreaded.DB(self)
00177 #        self.db = clDB.DB(self)
00178 
00179         ## Some database-dump tools
00180         self.dump_tools = download_db.dump_tools(self)
00181         ## The clWindow.gtkWIN reference 
00182         self.window = parent
00183         ## The notification TrayIcon class notification.TrayIcon
00184         self.icon = notification.TrayIcon(self)
00185         ## The Audio class clGstreamer010.Player
00186         self.player = PLAYER.Player(self)
00187         ## Pyjama's class for jamendo's get2 api clJamendo.Jamendo
00188         self.jamendo = clJamendo.Jamendo(self)
00189         ## The browser class clBrowserInterfacce.Browser for handling
00190         # different browser calls
00191         self.browser = clBrowserInterface.Browser(self)
00192         ## Pyjama's clLayouts.Layouts is referenced here
00193         # it is most important for anything to show up in the
00194         # main paned field
00195         self.layouts = clLayouts.Layouts(self)
00196         ## Preferences
00197         self.preferences = clPreferences.Preferences(self)
00198 
00199         if XMLRPC_AVAILABLE:
00200             self.xmlrpc = clXMLRPC.XMLRPC(self)
00201         
00202             for item in sys.argv:
00203                 if item.endswith(".m3u"):
00204                     if self.xmlrpc.role == "client":
00205                         try:
00206                             self.xmlrpc.server.test("test123")
00207                         except Exception, inst:
00208                             print ("Could not connect to server: %s" % inst)
00209                             break
00210 
00211         
00212         ## A boold indicating if theming is used or not
00213         self.nocolor = options.theme != None or options.nocolor
00214         ## Switches every 200 ms to rearrange widgets if the
00215         # window size changed
00216         # \todo Improve since this is really ugly!
00217         self.allow_rearrange = True
00218 
00219         ## Pyjama's home directory
00220         self.home = functions.preparedirs()
00221         ## Stores the currently marked item 
00222         self.tvMarkedItem = None
00223 
00224         ## If one sets this attrib to a string
00225         # which is an file's uri, pyjama will
00226         # load that file as a playlist and set
00227         # this attrib to None again
00228         self.playlist_to_load = None
00229         
00230         # Tracks currently shown in draw_albumdetail
00231         # This was replaced through StockBotton- Tags!
00232         #self.tracks = {}
00233         
00234         # Is the Timer running?
00235 #        self.timer = False
00236 
00237         ## Function to call when a database should be downloaded
00238         self.download_database = self.simple_database_downloader
00239 
00240         ## Bool - is the progressbar pulsing?
00241         self.pulsing = False
00242         
00243         ## List storing history_back items
00244         self.historyBack = []
00245         ## List storing history_forward items
00246         self.historyForward = []
00247         ## Dictionary stroing the currently shown page
00248         self.historyCurrent = {}
00249 
00250         #
00251         # Load additional modules
00252         #
00253         # Some modules have been plugins before that were moved
00254         # here in order not to bloat the plugin interace to much.
00255         # I think it is a good decision to not implement extended
00256         # functionality in the main source.
00257         print ("Loading modules")
00258         self.playlists = clPlaylists.main(self)
00259         self.bookmarks = clBookmarks.main(self)
00260 
00261         ## If a playlist was given:
00262         if len(sys.argv)>1:
00263             if os.path.exists(sys.argv[1]):
00264                 if self.check_another_instance_running() is True:
00265                     
00266                     try:
00267                         self.xmlrpc.send_playlist(sys.argv[1])
00268                         sys.exit(0)
00269                     except Exception, inst:
00270                         print ("Error passing the playlist to the running instance - will no play it in this instance: %s" % inst)
00271                         #self.playlists.open_playlist_from_file(sys.argv[1])
00272                         self.playlist_to_load = sys.argv[1]
00273                 else:
00274                     # if no other instance was found, this instance
00275                     # will play the playlist
00276                     #~ self.playlists.open_playlist_from_file(sys.argv[1])
00277                     self.playlist_to_load = sys.argv[1]
00278 
00279         ## clPlugin.Plugins class referenced here.
00280         # Loaded last to have any other class loaded
00281         # for the plugins
00282         self.plugins = clPlugin.Plugins(self)
00283         if self.settings.get_value("PYJAMA", "FIRST_RUN", True) == True:
00284             self.Events.raise_event("firstrun")
00285             self.settings.set_value("PYJAMA", "FIRST_RUN", "False")
00286 
00287 #        self.Events.raise_event("error", None, "test1")
00288 #        self.Events.raise_event("error", None, "test2")
00289 
00290         # decided to let the timer run all the time
00291         self.start_timer()
00292     
00293 
00294 #        self.plugins.loaded['test2'].test()
00295 
00296         if os.name != "posix" and os.name != "mac" and not "OVERRIDE-PLAYER" in sys.argv:
00297             self.Events.raise_event("info", text="You are running pyjama on windows. As far as I tested it, it worked fine except from the player support.\nFor this reason for windows the module <i>clPlayerForWin</i> was loaded for playback. This is only a hack for testing reasons, if you want to try out <i>clGstreamer010</i>, run pyjama with '<b>pyjama OVERRIDE-PLAYER</b>'")
00298 
00299     ## Navigate to pyjama's default page
00300     # shown at startup
00301     # @param self The Object Pointer
00302     def go_home(self):
00303         self.jamendo.last_query_hack()
00304         self.home_fkt()
00305 
00306     ## Pyjama's default home-function showing
00307     # 10 best rated albums of this week
00308     # @param self The Object Pointer
00309     def go_home_fkt(self):
00310         self.layouts.show_layout("top", 10, "ratingweek", 1, "all", who_called = "on_bHome_clicked")
00311 
00312     ## Set a new function to call when go_home() is called
00313     # @param self The Object Pointer
00314     # @param fkt The function to call when go_home() is called
00315     def set_home_fkt(self, fkt):
00316         self.home_fkt = fkt
00317 
00318     
00319 
00320     ## Read a jamendo m3u playlist file and populates the playlist with that
00321     # @param playlist The playlist's uri
00322     # @return List with track ids
00323     def read_playlist(self, playlist):
00324             if self.debug:
00325                 print "Loading Playlist %s" % playlist
00326             fh = file(playlist, "r")
00327             if fh:
00328                 lines = fh.readlines()
00329                 fh.close()
00330             else:
00331                 print ("Error reading %s") % playlist
00332                 return
00333             if True:#dialog.get_filter() == filter1: #M3U
00334                 track_ids = []
00335                 tracks = []
00336                 rg1 = re.compile('.*?\\d+.*?(\\d+)',re.IGNORECASE|re.DOTALL)
00337                 rg2 = re.compile('.*?(\/)(stream)(\/)(\d+)',re.IGNORECASE|re.DOTALL)
00338                 for line in lines:
00339                     if "jamendo.com/get2/stream/track/redirect/?id=" in line:
00340                         m = rg1.search(line.strip())
00341                         if m:
00342                             track_id=m.group(1)
00343                             track_ids.append(int(track_id))
00344                     elif "jamendo.com/stream/" in line:
00345                         m = rg2.search(line.strip())
00346                         if m:
00347                             track_id=m.group(4)
00348                             track_ids.append(int(track_id))
00349                     elif line.startswith("#"):
00350                         pass
00351                     else:
00352                         print ("This is not a Jamendo track or not readable with pyjama: %s") % line.strip()
00353                 return track_ids
00354 
00355     ## Downloads the database from jamendo and 
00356     # prints all messages to console
00357     # @param self Object Pointer
00358     # @param force_jamendo If set to True the database 
00359     # will be downloaded directly from jamendo,
00360     # if set to False, the database will be downloaded
00361     # from a mirror
00362     def simple_database_downloader(self, force_jamendo=False):
00363         dia = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, 0, _("Downloading database dump from Jamendo right now. Please be patient.\n\nFor more information please run Pyjama from console.\n\nThis might take up to a minute"))
00364         dia.show()
00365         self.window.do_events()
00366         dump = update.dump_tools()
00367         dump.delete_db()
00368         dump.create_tables()
00369         ret = dump.create_db(force_jamendo)
00370         if ret == "nofile":
00371             if force_jamendo:
00372                  self.Events.raise_event("error", None, "Error downloading the database dump from jamendo. Please try again later and notify me about this.")
00373             else:
00374                 self.Events.raise_event("error", None, "Error downloading the database dump from the mirror.\nPlease try running 'pyjama --update-jamendo' in order to get the database right from jamendo.")
00375             dia.destroy()
00376             return
00377         dump.finish()
00378         dia.destroy()
00379 
00380         self.db = dbthreaded.DB(self)
00381         self.window.sbStatus.set_text("Counter", _("Artists: %i, Albums: %i, Tracks: %i") % (self.db.artists, self.db.albums, self.db.tracks))
00382 
00383     ## Sets a download function for jamendo's database
00384     # @param self Object Pointer
00385     # @param fkt The function to run
00386     def set_download_database_fkt(self, fkt):
00387         self.download_database = fkt
00388 
00389     ## Show the plugin dialog
00390     # @param self Object Pointer
00391     # @param event Dummy param for callbacks
00392     # @return None
00393     def show_plugins(self, event=None):
00394         clPlugin.ShowPluginsDialog(self)
00395 
00396     ##  Show the preferences dialog
00397     # @param self OP
00398     # @param widget Dummy param for callbacks
00399     # @param name Set this for a module's / plugin's
00400     # name in order to show its page on start
00401     def show_preferences(self, widget=None, name=None):
00402         self.preferences.show_preferences(name)
00403 
00404     ## This function can only tell if another  instance is
00405     # running but not, if NO other instance is running
00406     # @return True if another instance is running, None if undecideable(?)
00407     def check_another_instance_running(self):
00408         if XMLRPC_AVAILABLE:
00409             self.xmlrpc.test()
00410             if self.xmlrpc.role == "client":
00411                 return True
00412             else:
00413                 return None
00414         else:
00415             return None
00416 
00417     ## Quit some running processes
00418     # usually called by window.really_quit()
00419     def quit(self):
00420         self.settingsdb.quit()
00421         self.db.quit()
00422 
00423         # write correct shutdown to config
00424         self.settings.set_value("PYJAMA", "crashed", False)
00425 
00426         # shutdown xmlrpc server
00427         self.xmlrpc.quit()
00428 
00429     ## Reloads the current layout page
00430     # @param self Object Pointer
00431     # @param event Dummy param for callbacks
00432     # @param ignore_cache Bool indicating if reload should be cached or not.
00433     # Set it to True if you want pyjama to reload this query from Jamendo
00434     # @return None
00435     def reload_current_page(self, ev=None, ignore_cache=False):
00436         # save scroll position for scrolledwindow
00437         adjustment = self.window.scrolledwindow.get_vadjustment()
00438         self.window.scrolledwindow.hide()
00439 
00440         if ignore_cache is True:
00441             self.jamendo.set_ignore_cache(True)
00442 
00443         # reloading page
00444         data = self.historyCurrent
00445         self.layouts.show_layout(data['layout'], data['data1'], data['data2'], data['data3'], data['data4'], fromhistory=True, who_called='reload_current_page')
00446 
00447         self.jamendo.set_ignore_cache(False)
00448 
00449         # load scroll position
00450         self.window.scrolledwindow.set_vadjustment(adjustment)
00451         self.window.scrolledwindow.show()
00452 
00453     ## shows an info dialog
00454     def info(self, title="Information", text=""):
00455         dialog = MyDialog(title,self.window, \
00456             gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, \
00457             (gtk.STOCK_OK, gtk.RESPONSE_ACCEPT), gtk.STOCK_DIALOG_INFO, text)
00458         dialog.set_icon_from_file(os.path.join(functions.install_dir(), "images", "pyjama.png"))
00459         dialog.run()
00460         dialog.destroy()
00461 
00462 
00463     ## Called whenever pyjama.Events.raise_event("error", *args) is fired.
00464     # Will print an error and its traceback to console
00465     # @param self Object Pointer
00466     # @param error_inst A short error message
00467     # @param desc Optional description of that error
00468     # @return None
00469     def error(self, error_inst, desc=False):
00470         print "An error occured: %s" % error_inst
00471         tb =  traceback.format_exc()
00472         print "-"*60
00473         print tb
00474         print "-"*60
00475         if not desc: 
00476             desc = error_inst
00477         print desc
00478         self.window.show_error_message(desc, tb)
00479 
00480 
00481     ## Called whenever pyjama.Events.raise_event("pluginloaded", *args) is fired.
00482     # Prints some infos for the loaded plugin to console
00483     # @param self Object Pointer
00484     # @param name The plugin's full name
00485     # @param version The plugin's version
00486     # @param order Plugin's order value (see clPlugin documentation)
00487     # @param mod_name Plugin's file name
00488     # @return None
00489     def ev_plugin_loaded(self, name, version, order, mod_name):# plugin_name, module_name):
00490 #        if self.verbose:
00491         print ("Plugin '%s' V%s (%s) loaded") % (name, version, mod_name)
00492 
00493     ## Setting self.allow_rearrange.
00494     # This function is called by an gobject Timeout and allways return False
00495     # @param self Object Pointer
00496     # @return False
00497     def set_allow_rearrange(self):
00498         self.allow_rearrange=True
00499         return False
00500 
00501 
00502 #    def check_next_possible(self, page=None, rpp=None):
00503 #        if page == None: page = self.cur_page
00504 #        if rpp == None: rpp = self.results_per_page
00505 #        if page >= floor(self.db.albums / rpp) - 2:
00506 #            self.window.sbNextPage.set_sensitive(False)
00507 #        else:
00508 #            self.window.sbNextPage.set_sensitive(True)
00509 
00510 #    def check_prev_possible(self, page=None):
00511 #        if page == None: page = self.cur_page
00512 #        if page == 1:
00513 #            self.window.sbPrevPage.set_sensitive(False)
00514 #        else:
00515 #            self.window.sbPrevPage.set_sensitive(True)
00516             
00517 
00518     ## Add a given list of track objects to playlist
00519     # @param self Object Pointer
00520     # @param tracks List of Tracks
00521     # @param play Optional bool. If this is set True 
00522     # pyjama will start playing the first track of the given list
00523     # @return None
00524     def appendtracks(self, tracks, play = False):
00525         cur = len(self.player.playlist)
00526         for track in tracks:
00527             track.uid = "%f%s" % (time(), track.uid)
00528             self.add2playlist(track)
00529             status = self.player.status
00530         if play:
00531             self.window.on_bStop_clicked(None)
00532             self.setplaylist(cur)
00533             self.window.on_bPlay_clicked(None)        
00534 
00535     ## Removes an item from the playlist
00536     # @param self Object Pointer
00537     # @param item Integer
00538     # @return None
00539     def remove_item_from_playlist(self, item):
00540         if item < self.tvMarkedItem:
00541             self.tvMarkedItem -= 1
00542         elif item == self.tvMarkedItem:
00543             self.tvMarkedItem = None
00544         self.player.remove(item)
00545 
00546     ## Moves an item in the playlist
00547     # @param self Object Pointer
00548     # @param s Source path of item moved
00549     # @param d Destination path of the item moved
00550     # @param before Indicates of the item is dropped after or before the destination
00551     # @return None
00552     def move_playlist_item(self, s, d, before): # sourcepath, destpath
00553         c = self.tvMarkedItem
00554         if d > s:
00555             if before: 
00556                 bf = -1
00557             else:
00558                 bf = 0
00559         else:
00560             if before: 
00561                 bf = 0
00562             else:
00563                 bf = 1
00564         dest = d + bf
00565 
00566 
00567         self.tvMarkedItem = dest
00568         self.player.move_item(s, d, before)
00569 
00570 
00571     ## Adds a single track to the playlist
00572     # @param self Object Pointer
00573     # @param track a Track
00574     # @return None
00575     def add2playlist(self, track):
00576         self.player.add2playlist(track)
00577 
00578         artist_name = track.artist_name
00579         track_name = track.name
00580         track_numalbum = track.numalbum
00581 
00582 #        gtk.gdk.threads_enter()
00583         markup = self.window.markupNormalEntry.replace("__ARTIST__", artist_name).replace("__TITLE__", track_name).replace("__NUM__", str(track_numalbum))
00584         tmpIter = self.window.liststore.append([markup]) #, track.id
00585         path = self.window.liststore.get_path(tmpIter)
00586 #        gtk.gdk.threads_leave()
00587 #        tt = gtk.Tooltip()
00588 #        tt.set_text("")
00589 #        self.window.tvPlaylist.set_tooltip_row(tt, path)
00590 
00591 
00592     ## Set the Cover image and some other informations.
00593     # This methode is called whenever the mouse moves over
00594     # the playlist.
00595     # @param self Object Pointer
00596     # @param path Optional path of the playlist item to set cover for.
00597     # If this value is None, the currently selected item will be shown.
00598     # @return None
00599     def showInfo(self, path=None):
00600         if path == None:
00601             if self.tvMarkedItem != None and self.tvMarkedItem < len(self.player.playlist):
00602                 path = self.tvMarkedItem
00603             else:
00604                 return None
00605 #        if path == None:
00606 #            ret = self.window.tvPlaylistSelection.get_selected()
00607 #            if ret != None:
00608 #                model, retIter = ret
00609 #                if retIter != None:
00610 #                    path = model.get_path(retIter)[0]
00611 #                else:
00612 #                    return None
00613 #            else:
00614 #                return None
00615             
00616         track = self.player.playlist[path]
00617         #Covers = simplejson.loads(track['album_Covers'])
00618 
00619         self.window.lbCoverArtist.set_text(track.artist_name)
00620         self.window.lbCoverAlbum.set_text(track.album_name)
00621 
00622         # set image via thread
00623         thr = Thread(target = self.__get_image_for_showInfo, args = ([track]))
00624         thr.start()
00625 
00626 
00627     def __get_image_for_showInfo(self, track):
00628         img = self.get_album_image(track.album_id)
00629         if not img: 
00630             self.window.imgCover.set_from_stock(gtk.STOCK_MISSING_IMAGE, gtk.ICON_SIZE_DND)
00631         else:
00632             self.window.imgCover.set_from_file(img)
00633 
00634     ## Mark the currently played item and unmark the last played item
00635     # @param self Object Pointer
00636     # @param activate_item Path of the item to mark
00637     # @return None
00638     def setplaylist(self, activate_item):
00639         self.window.pbSong.set_fraction(0)
00640         self.window.pbSong.set_text("")
00641 
00642         model = self.window.tvPlaylist.get_model()
00643         retIter = model.get_iter(activate_item)
00644         self.showInfo(activate_item)
00645 
00646         # Unmark last played Item
00647         if self.tvMarkedItem != None and self.player.last_played != None and self.tvMarkedItem < len(self.player.playlist):
00648             track = self.player.last_played
00649             artist_name = track.artist_name
00650             track.name = track.name
00651             track.numalbum = str(track.numalbum)
00652             markup = self.window.markupNormalEntry.replace("__ARTIST__", artist_name).replace("__TITLE__", track.name).replace("__NUM__", track.numalbum)
00653             self.window.liststore.set(model.get_iter(self.tvMarkedItem), 0, markup)
00654 
00655         # Mark cur. played item
00656         
00657         self.window.tvSelection.select_iter(retIter)
00658         track = self.player.playlist[activate_item] #self.player.cur_playing
00659         artist_name = track.artist_name
00660         track.name = track.name
00661         track.numalbum = str(track.numalbum)
00662         markup = self.window.markupCurPlaying.replace("__ARTIST__", artist_name).replace("__TITLE__", track.name).replace("__NUM__", track.numalbum)
00663         self.window.liststore.set(retIter, 0, markup)
00664         # remember, which item is marked
00665         self.tvMarkedItem = activate_item
00666 
00667     ## Empty function
00668     # @param self Object Pointer
00669     # @return None
00670     def stop_timer(self):
00671         pass
00672 
00673     ## Start the gobject timer.
00674     # This will call timer_event every 200ms
00675     # @param self Object Pointer
00676     # @return None
00677     def start_timer(self):
00678         gobject.timeout_add(200, self.timer_event)
00679 #        self.timer = True
00680 
00681     ## Start pulsing the progressbar
00682     # @param self Object Pointer
00683     # @param text Optional string. Will be set as text for the progressbar.
00684     # Will set "" if no value is given.
00685     # @return None
00686     def start_pulsing(self, text = ""):
00687         gobject.timeout_add(50, self.pulse)
00688         self.window.pbWait.set_fraction(0)        
00689         self.window.pbWait.set_text(text)
00690         self.window.pbWait.show()
00691         
00692 
00693     ## Will pulse the progressbar a step
00694     # @param self Object Pointer
00695     # @return 
00696     # - True if the progressbar is in pulsing mode
00697     # - False if the progressbar is in normal mode
00698     def pulse(self):
00699         self.window.pbWait.pulse()
00700         if self.pulsing:
00701             return True
00702         else:
00703             return False
00704     
00705     ## Stops pulsing
00706     # @param self Object Pointer
00707     # @return None
00708     def stop_pulsing(self):
00709         self.pulsing = False
00710         self.window.pbWait.hide()
00711 
00712     ## This event is called ever 200ms by start_timer().
00713     # This methods interacts with the player class and gets the current
00714     # track position and some other data. It sets the current status and
00715     # pulses the progressbar with pulse().
00716     # @param self Object Pointer
00717     # @return True to keep the gobject timer running.
00718     def timer_event(self):
00719 #        ## check if a playlist file was copied to ~/.pyjama/jamendo-playlist.m3u
00720 #        dest = os.path.join(self.home, "jamendo-playlist.m3u")
00721 #        if os.path.exists(dest):
00722 #            print "found file"
00723             
00724         # Populate the playlist if self.playlist_to_load
00725         # it not None
00726         if self.playlist_to_load is not None:
00727             if os.path.exists(self.playlist_to_load):
00728                 self.playlists.open_playlist_from_file(self.playlist_to_load)
00729             self.playlist_to_load = None
00730 
00731 
00732         #self.window.pbWait.pulse()
00733         self.player.check_status()
00734         status = self.player.status
00735         trans_stat = _(status)
00736         fehler = ["Error", "Stop", "End", None]
00737         self.setStatus( trans_stat )
00738         if status == "paused":
00739             self.window.bPlay.setimage(gtk.STOCK_MEDIA_PLAY)
00740             self.window.bPlay.tag = "play"
00741             if self.window.pbSong.counter > 2:
00742                 if self.window.pbSong.get_text() == _("paused"):
00743                     if self.window.pbSong.ShowRemainingTime:
00744                         txt =  "-%s" % functions.sec2time(self.player.togo*-1)
00745                     else:
00746                         txt = "%s/%s" % (functions.sec2time(self.player.cursec), functions.sec2time(self.player.duration))
00747                 else:
00748                     txt = _("paused")
00749                 if "00:00:00" in txt or txt == "-00:00": txt = "buffering... %i%%" % self.player.buffer
00750                 if self.window.pbSong.get_text != txt:
00751                     self.window.pbSong.set_text(txt)
00752                 self.window.pbSong.counter = 0
00753             self.window.pbSong.counter +=1
00754             return True
00755         elif not status in fehler and self.player.percentage != None:
00756             if self.window.bPlay.tag != "pause":
00757                 self.window.bPlay.setimage(gtk.STOCK_MEDIA_PAUSE)
00758                 self.window.bPlay.tag = "pause"
00759             #print self.player.text
00760             if self.player.duration > 0 and self.player.duration < 59*60:
00761                 if self.window.pbSong.get_fraction != self.player.percentage:
00762                     self.window.pbSong.set_fraction(self.player.percentage)            
00763                 if self.window.pbSong.ShowRemainingTime:
00764                     txt =  "-%s" % functions.sec2time(self.player.togo*-1)
00765                 else:
00766                     txt = "%s/%s" % (functions.sec2time(self.player.cursec), functions.sec2time(self.player.duration))
00767             else:
00768                 self.window.pbSong.pulse()
00769                 txt = "%s" % functions.sec2time(self.player.cursec)
00770             if "00:00:00" in txt or txt == "-00:00": txt = "buffering... %i%%" % self.player.buffer
00771             if self.window.pbSong.get_text != txt:
00772                 self.window.pbSong.set_text(txt)
00773             return True
00774         elif status == "Stop" or status == "End": # and not self.pulsing:
00775             #self.window.bPlay.setimage(gtk.STOCK_MEDIA_PLAY)
00776             self.window.bPlay.tag = "play"
00777             if self.icon.menu.play_button_status != "play":
00778                 self.icon.menu.switch_play_button("play")
00779             if self.window.pbSong.get_fraction() != 0:
00780                 self.window.pbSong.set_fraction(0)
00781             if self.window.pbSong.get_text()!= "":
00782                 self.window.pbSong.set_text("")
00783 #            self.timer = False
00784             return True
00785         else:
00786             self.window.bPlay.tag = "play"
00787             return True
00788 
00789     ## Sets the "Info" column of the statusbar to text
00790     # @param self Object Pointer
00791     # @param text The text to set
00792     # @return None
00793     def setInfo(self, text):
00794         self.window.sbStatus.set_text("VarInfo", text)
00795 
00796     ## Sets the "Player" column of the statusbar to text
00797     # @param self Object Pointer
00798     # @param text The text to set
00799     # @return None
00800     def setStatus(self, text):
00801         self.window.sbStatus.set_text("Player", text)
00802 
00803 
00804 
00805     ## Shows a notification
00806     # @param self Object Pointer
00807     # @param caption The notification's caption as string
00808     # @param text The notification's text as stirng
00809     # @param icon The notification's icon as URI string
00810     # @param size The notification's icon size as int
00811     # @return None
00812     def notification(self, caption, text, icon="pyjama", size = 100):
00813         self.icon.show_notification(caption, text, icon, size)
00814 
00815     ## Iconifies the window to taskbar if its shown and shows the window
00816     # when its hidden.
00817     # @param self Object Pointer
00818     # @param ev1 Dummy param for callbacks
00819     # @return None
00820     def switch_window_state(self, ev1=None):
00821         if self.window.window.get_state() & gtk.gdk.WINDOW_STATE_ICONIFIED:
00822             self.window.deiconify()
00823             self.window.set_skip_taskbar_hint(False)
00824         else:
00825             self.window.set_skip_taskbar_hint(True)
00826             self.window.iconify()
00827 
00828 
00829     ## Downloads and caches an album's image from jamendo
00830     # @param self Object Pointer
00831     # @param album_id The album's id
00832     # @param size Optional size as int - default is 100
00833     # @return
00834     # - None if an error occures
00835     # - The file's URI as string if downloading was succesfull
00836     def get_album_image(self, album_id, size = 100):
00837         download_from = "http://imgjam.com/albums/%s/covers/1.%i.jpg"  % (album_id, size)
00838         name = "http://api.jamendo.com/get2/image/album/redirect/?id=%s&imagesize=%i" % (album_id, size)
00839         md5hash = hashlib.md5(name).hexdigest()
00840         fh = os.path.join(self.home, "images", md5hash)
00841         if not os.path.exists(fh):
00842             try:
00843                 print "download"
00844                 urllib.urlretrieve(download_from, fh)
00845             except IOError:
00846                 print ("Could not load image")
00847                 return None
00848         return fh

Generated on Thu Jun 4 19:08:24 2009 for Pyjama by  doxygen 1.5.8