summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorAnton Khirnov <wyskas@gmail.com>2008-12-17 20:48:52 +0100
committerAnton Khirnov <wyskas@gmail.com>2008-12-17 20:48:52 +0100
commit4942e2f39fc9dc61745c952f0ddef6804351398f (patch)
tree20b28f22ca8b9290d690aad2c1653a25d51a75ad /plugins
parent6ff27d9b54cb75d041bd18589a1c6b5f606d51c3 (diff)
get rid of tabs
Diffstat (limited to 'plugins')
-rw-r--r--plugins/AlbumCover.py462
-rw-r--r--plugins/Library.py120
-rw-r--r--plugins/Logger.py110
-rw-r--r--plugins/Lyrics.py494
-rw-r--r--plugins/MPD.py34
-rw-r--r--plugins/Notify.py288
-rw-r--r--plugins/PlayControl.py682
-rw-r--r--plugins/Playlist.py136
-rw-r--r--plugins/Scrobbler.py698
-rw-r--r--plugins/Shortcuts.py150
-rw-r--r--plugins/SongStatus.py136
-rw-r--r--plugins/Systray.py202
-rw-r--r--plugins/Tabs.py276
-rw-r--r--plugins/__init__.py112
14 files changed, 1950 insertions, 1950 deletions
diff --git a/plugins/AlbumCover.py b/plugins/AlbumCover.py
index cb67f51..d2952ca 100644
--- a/plugins/AlbumCover.py
+++ b/plugins/AlbumCover.py
@@ -17,207 +17,207 @@ ALBUMCOVER_FETCH0_DEFAULT=1
ALBUMCOVER_FETCH1_DEFAULT=1
class wgAlbumCover(QtGui.QWidget):
- " container for the image"
- img=None
- imgLoaded=False
- p=None # plugin
- acFormat=None
- def __init__(self, p, parent=None):
- QtGui.QWidget.__init__(self,parent)
- self.p=p
- self.img=QtGui.QImage()
- self.setMinimumSize(64,64)
-
- def mousePressEvent(self, event):
- if event.button()==QtCore.Qt.RightButton:
- song=monty.getCurrentSong()
- file=QtGui.QFileDialog.getOpenFileName(self
- , "Select album cover for %s - %s"%(song.getArtist(), song.getAlbum())
- , ""
- , "Images ("+" ".join(map(lambda ext: "*.%s"%(ext), ALBUMCOVER_GFX_EXTS_DEFAULT.split(',')))+")"
- )
- if file:
- cur=self.getLocalACPath(song, probe=True)
- # remove the previous cover
- try:
- if cur:
- self.p.extended("removing %s"%(cur))
- os.remove(cur)
- else:
- cur=self.getLocalACPath(song, probe=False)
- self.p.extended("copy %s to %s"%(file, cur))
- shutil.copy(file, cur)
- except Exception, e:
- self.p.normal("failed to set new cover: %s"%(str(e)))
- else:
- return
- self.refresh()
-
- def getIMG(self):
- if self.imgLoaded:
- return self.img
- return None
+ " container for the image"
+ img=None
+ imgLoaded=False
+ p=None # plugin
+ acFormat=None
+ def __init__(self, p, parent=None):
+ QtGui.QWidget.__init__(self,parent)
+ self.p=p
+ self.img=QtGui.QImage()
+ self.setMinimumSize(64,64)
+
+ def mousePressEvent(self, event):
+ if event.button()==QtCore.Qt.RightButton:
+ song=monty.getCurrentSong()
+ file=QtGui.QFileDialog.getOpenFileName(self
+ , "Select album cover for %s - %s"%(song.getArtist(), song.getAlbum())
+ , ""
+ , "Images ("+" ".join(map(lambda ext: "*.%s"%(ext), ALBUMCOVER_GFX_EXTS_DEFAULT.split(',')))+")"
+ )
+ if file:
+ cur=self.getLocalACPath(song, probe=True)
+ # remove the previous cover
+ try:
+ if cur:
+ self.p.extended("removing %s"%(cur))
+ os.remove(cur)
+ else:
+ cur=self.getLocalACPath(song, probe=False)
+ self.p.extended("copy %s to %s"%(file, cur))
+ shutil.copy(file, cur)
+ except Exception, e:
+ self.p.normal("failed to set new cover: %s"%(str(e)))
+ else:
+ return
+ self.refresh()
+
+ def getIMG(self):
+ if self.imgLoaded:
+ return self.img
+ return None
- def paintEvent(self, event):
- l=min(self.width(),self.height())
- p=QtGui.QPainter(self)
- rect=QtCore.QRectF((self.width()-l)/2,0,l,l)
- p.drawImage(rect,self.img)
+ def paintEvent(self, event):
+ l=min(self.width(),self.height())
+ p=QtGui.QPainter(self)
+ rect=QtCore.QRectF((self.width()-l)/2,0,l,l)
+ p.drawImage(rect,self.img)
- def refresh(self):
- self.p.extended("refreshing cover")
- song=monty.getCurrentSong()
- try:
- song._data['file']
- except:
- self.img.load('')
- self.update()
- return
- start_new_thread(self.fetchCover, (song,))
-
- def fetchCover(self, song):
- """Fetch cover (from internet or local dir)"""
- # set default cover
- self.imgLoaded=False
- self.img.load('gfx/no-cd-cover.png')
- self.acFormat=format.compile(self.p.getSetting('downloadto'))
- for i in [0, 1]:
- src=self.p.getSetting('fetch%i'%(i))
- if src!=AC_NO_FETCH and self.fetchCoverSrc(song, src):
- # ACK!
- self.imgLoaded=True
- break
- self.update()
-
- def getLocalACPath(self, song, probe):
- """Get the local path of an albumcover. If $probe, then try covers*exts for existing file."""
- # fetch gfx extensions
- exts=self.p.getSetting('gfx_exts').split(',')
- exts=map(lambda ext: ext.strip(), exts)
-
- # fetch cover album titles
- covers=self.p.getSetting('files').split(',')
- covers=map(lambda title: title.strip(), covers)
-
- params={'music_dir': mpdSettings.get('music_directory'), 'cover':'%s.%s'%(covers[0], exts[0])}
- if probe:
- self.p.debug("probing ...")
- for cover in covers:
- for ext in exts:
- params['cover']='%s.%s'%(cover, ext)
- path=self.acFormat(format.params(song, params))
- self.p.debug(" path: %s"%(path))
- fInfo=QtCore.QFileInfo(path)
- if fInfo.exists():
- self.p.debug(" OK!")
- return path
- self.p.debug("done probing: no matching albumcover found")
- return None
- else:
- self.p.debug("no probing")
- path=self.acFormat(format.params(song, params))
- self.p.debug(" path: %s"%(path))
- return path
+ def refresh(self):
+ self.p.extended("refreshing cover")
+ song=monty.getCurrentSong()
+ try:
+ song._data['file']
+ except:
+ self.img.load('')
+ self.update()
+ return
+ start_new_thread(self.fetchCover, (song,))
+
+ def fetchCover(self, song):
+ """Fetch cover (from internet or local dir)"""
+ # set default cover
+ self.imgLoaded=False
+ self.img.load('gfx/no-cd-cover.png')
+ self.acFormat=format.compile(self.p.getSetting('downloadto'))
+ for i in [0, 1]:
+ src=self.p.getSetting('fetch%i'%(i))
+ if src!=AC_NO_FETCH and self.fetchCoverSrc(song, src):
+ # ACK!
+ self.imgLoaded=True
+ break
+ self.update()
+
+ def getLocalACPath(self, song, probe):
+ """Get the local path of an albumcover. If $probe, then try covers*exts for existing file."""
+ # fetch gfx extensions
+ exts=self.p.getSetting('gfx_exts').split(',')
+ exts=map(lambda ext: ext.strip(), exts)
+
+ # fetch cover album titles
+ covers=self.p.getSetting('files').split(',')
+ covers=map(lambda title: title.strip(), covers)
+
+ params={'music_dir': mpdSettings.get('music_directory'), 'cover':'%s.%s'%(covers[0], exts[0])}
+ if probe:
+ self.p.debug("probing ...")
+ for cover in covers:
+ for ext in exts:
+ params['cover']='%s.%s'%(cover, ext)
+ path=self.acFormat(format.params(song, params))
+ self.p.debug(" path: %s"%(path))
+ fInfo=QtCore.QFileInfo(path)
+ if fInfo.exists():
+ self.p.debug(" OK!")
+ return path
+ self.p.debug("done probing: no matching albumcover found")
+ return None
+ else:
+ self.p.debug("no probing")
+ path=self.acFormat(format.params(song, params))
+ self.p.debug(" path: %s"%(path))
+ return path
-
- def fetchCoverSrc(self, song, src):
- """Fetch the album cover for $song from $src."""
- if not src in [AC_FETCH_INTERNET, AC_FETCH_LOCAL_DIR]:
- print "wgAlbumCover::fetchCover - invalid source "+str(src)
- return False
-
- if src==AC_FETCH_INTERNET:
- # look on the internetz!
- try:
- if not song.getArtist() or not song.getAlbum():
- return False
- # get the url from amazon WS
- coverURL=AmazonAlbumImage(song.getArtist(), song.getAlbum()).fetch()
- self.p.extended("fetch from Amazon")
- if not coverURL:
- self.p.normal("not found on Amazon")
- return False
- # read the url, i.e. retrieve image data
- img=urllib.urlopen(coverURL)
- # where do we save to?
- file=self.getLocalACPath(song, False)
- # open file, and write the read of img!
- f=open(file,'wb')
- f.write(img.read())
- f.close()
+
+ def fetchCoverSrc(self, song, src):
+ """Fetch the album cover for $song from $src."""
+ if not src in [AC_FETCH_INTERNET, AC_FETCH_LOCAL_DIR]:
+ print "wgAlbumCover::fetchCover - invalid source "+str(src)
+ return False
+
+ if src==AC_FETCH_INTERNET:
+ # look on the internetz!
+ try:
+ if not song.getArtist() or not song.getAlbum():
+ return False
+ # get the url from amazon WS
+ coverURL=AmazonAlbumImage(song.getArtist(), song.getAlbum()).fetch()
+ self.p.extended("fetch from Amazon")
+ if not coverURL:
+ self.p.normal("not found on Amazon")
+ return False
+ # read the url, i.e. retrieve image data
+ img=urllib.urlopen(coverURL)
+ # where do we save to?
+ file=self.getLocalACPath(song, False)
+ # open file, and write the read of img!
+ f=open(file,'wb')
+ f.write(img.read())
+ f.close()
- # load image; should work now!
- self.img.load(file)
- return True
- except:
- self.p.normal("failed to download cover from Amazon")
- print_exc()
- return False
-
- file=self.getLocalACPath(song, True)
- if file:
- try:
- self.img.load(file)
- self.p.extended("cover set!")
- return True
- except:
- self.p.normal("failed to load %s"%(path))
+ # load image; should work now!
+ self.img.load(file)
+ return True
+ except:
+ self.p.normal("failed to download cover from Amazon")
+ print_exc()
+ return False
+
+ file=self.getLocalACPath(song, True)
+ if file:
+ try:
+ self.img.load(file)
+ self.p.extended("cover set!")
+ return True
+ except:
+ self.p.normal("failed to load %s"%(path))
class pluginAlbumCover(Plugin):
- o=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'AlbumCover')
- self.addMontyListener('onSongChange', self.onEvent)
- self.addMontyListener('onReady', self.onEvent)
- self.addMontyListener('onDisconnect', self.onEvent)
- self.addMontyListener('onStateChange', self.onEvent)
-
- def _load(self):
- self.o=wgAlbumCover(self, None)
- self.o.refresh()
- def _unload(self):
- self.o=None
- def getInfo(self):
- return "Display the album cover of the currently playing album."
- def getExtInfo(self):
- return "Displays the album cover of the currently playing album in a widget.\n" \
- "This album cover can be fetched from various locations:\n" \
- " local dir: the directory in which the album is located;\n" \
- " internet: look on amazon for the album and corresponding cover\n" \
- "Settings:\n" \
- " albumcover.fetch$i: what source to fetch from on step $i. If step $i fails, move on to step $i+1;\n" \
- " albumcover.downloadto: where to download album covers from internet to. This string can contain the normal tags of the current playing song, plus $music_dir and $cover.\n" \
- " albumcover.files: comma separated list of filenames (without extension)to be considered an album cover. Extensions jpg, jpeg, png, gif and bmp are used.\n"
+ o=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'AlbumCover')
+ self.addMontyListener('onSongChange', self.onEvent)
+ self.addMontyListener('onReady', self.onEvent)
+ self.addMontyListener('onDisconnect', self.onEvent)
+ self.addMontyListener('onStateChange', self.onEvent)
+
+ def _load(self):
+ self.o=wgAlbumCover(self, None)
+ self.o.refresh()
+ def _unload(self):
+ self.o=None
+ def getInfo(self):
+ return "Display the album cover of the currently playing album."
+ def getExtInfo(self):
+ return "Displays the album cover of the currently playing album in a widget.\n" \
+ "This album cover can be fetched from various locations:\n" \
+ " local dir: the directory in which the album is located;\n" \
+ " internet: look on amazon for the album and corresponding cover\n" \
+ "Settings:\n" \
+ " albumcover.fetch$i: what source to fetch from on step $i. If step $i fails, move on to step $i+1;\n" \
+ " albumcover.downloadto: where to download album covers from internet to. This string can contain the normal tags of the current playing song, plus $music_dir and $cover.\n" \
+ " albumcover.files: comma separated list of filenames (without extension)to be considered an album cover. Extensions jpg, jpeg, png, gif and bmp are used.\n"
- def getWidget(self):
- return self.o
+ def getWidget(self):
+ return self.o
- def _getDockWidget(self):
- return self._createDock(self.o)
+ def _getDockWidget(self):
+ return self._createDock(self.o)
- def onEvent(self, params):
- self.o.refresh()
+ def onEvent(self, params):
+ self.o.refresh()
- def _getSettings(self):
- ret=[]
- nums=['first', 'second']
- actions=[QtGui.QComboBox(), QtGui.QComboBox()]
- for i,action in enumerate(actions):
- setting='fetch%i'%(i)
- action.addItem("On the %s action, Monty rested."%(nums[i]))
- action.addItem("Local dir")
- action.addItem("Internet")
- action.setCurrentIndex(int(self.getSetting(setting, str(i+1))))
- ret.append([setting, 'Action %i'%(i+1), 'What to do on the %s step.'%(nums[i]), action])
+ def _getSettings(self):
+ ret=[]
+ nums=['first', 'second']
+ actions=[QtGui.QComboBox(), QtGui.QComboBox()]
+ for i,action in enumerate(actions):
+ setting='fetch%i'%(i)
+ action.addItem("On the %s action, Monty rested."%(nums[i]))
+ action.addItem("Local dir")
+ action.addItem("Internet")
+ action.setCurrentIndex(int(self.getSetting(setting, str(i+1))))
+ ret.append([setting, 'Action %i'%(i+1), 'What to do on the %s step.'%(nums[i]), action])
- ret.append(['downloadto', 'Local dir', 'Specifies where to save album covers fetched from internet to.\nPossible tags: music_dir, cover, file, artist, title, album', QtGui.QLineEdit(self.getSetting('downloadto'))])
- ret.append(['files', 'Album cover files', 'Comma separated list of titles that are to be considered album covers. E.g. cover,album.', QtGui.QLineEdit(self.getSetting('files'))])
-
- return ret
-
- def afterSaveSettings(self):
- self.o.refresh()
+ ret.append(['downloadto', 'Local dir', 'Specifies where to save album covers fetched from internet to.\nPossible tags: music_dir, cover, file, artist, title, album', QtGui.QLineEdit(self.getSetting('downloadto'))])
+ ret.append(['files', 'Album cover files', 'Comma separated list of titles that are to be considered album covers. E.g. cover,album.', QtGui.QLineEdit(self.getSetting('files'))])
+
+ return ret
+
+ def afterSaveSettings(self):
+ self.o.refresh()
# This is the amazon cover fetcher using their webservice api
@@ -228,48 +228,48 @@ import urllib
AMAZON_AWS_ID = "0K4RZZKHSB5N2XYJWF02"
class AmazonAlbumImage(object):
- awsurl = "http://ecs.amazonaws.com/onca/xml"
- def __init__(self, artist, album):
- self.artist = artist
- self.album = album
-
- def fetch(self):
- url = self._GetResultURL(self._SearchAmazon())
- if not url:
- return None
- img_re = re.compile(r'''registerImage\("original_image", "([^"]+)"''')
- try:
- prod_data = urllib.urlopen(url).read()
- except:
- self.important("timeout opening %s"%(url))
- return None
- m = img_re.search(prod_data)
- if not m:
- return None
- img_url = m.group(1)
- return img_url
+ awsurl = "http://ecs.amazonaws.com/onca/xml"
+ def __init__(self, artist, album):
+ self.artist = artist
+ self.album = album
+
+ def fetch(self):
+ url = self._GetResultURL(self._SearchAmazon())
+ if not url:
+ return None
+ img_re = re.compile(r'''registerImage\("original_image", "([^"]+)"''')
+ try:
+ prod_data = urllib.urlopen(url).read()
+ except:
+ self.important("timeout opening %s"%(url))
+ return None
+ m = img_re.search(prod_data)
+ if not m:
+ return None
+ img_url = m.group(1)
+ return img_url
- def _SearchAmazon(self):
- data = {
- "Service": "AWSECommerceService",
- "Version": "2005-03-23",
- "Operation": "ItemSearch",
- "ContentType": "text/xml",
- "SubscriptionId": AMAZON_AWS_ID,
- "SearchIndex": "Music",
- "ResponseGroup": "Small",
- }
-
- data["Artist"] = self.artist
- data["Keywords"] = self.album
-
- fd = urllib.urlopen("%s?%s" % (self.awsurl, urllib.urlencode(data)))
- return fd.read()
+ def _SearchAmazon(self):
+ data = {
+ "Service": "AWSECommerceService",
+ "Version": "2005-03-23",
+ "Operation": "ItemSearch",
+ "ContentType": "text/xml",
+ "SubscriptionId": AMAZON_AWS_ID,
+ "SearchIndex": "Music",
+ "ResponseGroup": "Small",
+ }
+
+ data["Artist"] = self.artist
+ data["Keywords"] = self.album
+
+ fd = urllib.urlopen("%s?%s" % (self.awsurl, urllib.urlencode(data)))
+ return fd.read()
- def _GetResultURL(self, xmldata):
- if not xmldata:
- return None
- url_re = re.compile(r"<DetailPageURL>([^<]+)</DetailPageURL>")
- m = url_re.search(xmldata)
- return m and m.group(1)
+ def _GetResultURL(self, xmldata):
+ if not xmldata:
+ return None
+ url_re = re.compile(r"<DetailPageURL>([^<]+)</DetailPageURL>")
+ m = url_re.search(xmldata)
+ return m and m.group(1)
diff --git a/plugins/Library.py b/plugins/Library.py
index b1d86e9..bcd4ae7 100644
--- a/plugins/Library.py
+++ b/plugins/Library.py
@@ -6,69 +6,69 @@ from wgPlaylist import Playlist
from wgSongList import clrRowSel
LIBRARY_MODES_DEFAULT='$artist\n'\
- '$artist/$date - $album\n'\
- '$artist - $album\n'\
- '$album ($artist)\n'\
- '$genre\n'\
- '$genre/$artist\n'\
- '$genre/$artist - $album\n'
+ '$artist/$date - $album\n'\
+ '$artist - $album\n'\
+ '$album ($artist)\n'\
+ '$genre\n'\
+ '$genre/$artist\n'\
+ '$genre/$artist - $album\n'
class pluginLibrary(Plugin):
- o=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'Library')
- def _load(self):
- self.o=Playlist(self.winMain, self, ['song'], 'Library'
- , self.onDoubleClick, self.onKeyPress, self.getSetting('modes').split('\n'))
- def _unload(self):
- self.o=None
- def getInfo(self):
- return "List showing all the songs allowing filtering and grouping."
-
- def getList(self):
- return self.o
-
- def _getDockWidget(self):
- return self._createDock(self.o)
-
- def onDoubleClick(self):
- self.addLibrarySelToPlaylist()
+ o=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'Library')
+ def _load(self):
+ self.o=Playlist(self.winMain, self, ['song'], 'Library'
+ , self.onDoubleClick, self.onKeyPress, self.getSetting('modes').split('\n'))
+ def _unload(self):
+ self.o=None
+ def getInfo(self):
+ return "List showing all the songs allowing filtering and grouping."
+
+ def getList(self):
+ return self.o
+
+ def _getDockWidget(self):
+ return self._createDock(self.o)
+
+ def onDoubleClick(self):
+ self.addLibrarySelToPlaylist()
- def onKeyPress(self, event):
- # Add selection, or entire library to playlist using ENTER-key.
- if event.key()==QtCore.Qt.Key_Enter or event.key()==QtCore.Qt.Key_Return:
- self.addLibrarySelToPlaylist()
- return QtGui.QWidget.keyPressEvent(self.o, event)
+ def onKeyPress(self, event):
+ # Add selection, or entire library to playlist using ENTER-key.
+ if event.key()==QtCore.Qt.Key_Enter or event.key()==QtCore.Qt.Key_Return:
+ self.addLibrarySelToPlaylist()
+ return QtGui.QWidget.keyPressEvent(self.o, event)
- def addLibrarySelToPlaylist(self):
- """Add the library selection to the playlist."""
- songs=self.o.selectedSongs()
- self.setStatus('Adding '+str(len(songs))+' songs to library ...')
- doEvents()
+ def addLibrarySelToPlaylist(self):
+ """Add the library selection to the playlist."""
+ songs=self.o.selectedSongs()
+ self.setStatus('Adding '+str(len(songs))+' songs to library ...')
+ doEvents()
- # add filepaths of selected songs to path
- paths=map(lambda song: unicode(song.getFilepath()), songs)
- # add in chunks of 1000
- CHUNK_SIZE=1000
- start=0
- while start<len(paths):
- if start+CHUNK_SIZE<len(paths):
- end=start+CHUNK_SIZE
- else:
- end=len(paths)
- self.setStatus('Adding '+str(len(songs))+' songs to library: %i%%'%(int(100*start/len(paths))))
- doEvents()
- monty.addToPlaylist(paths[start:end])
- start+=CHUNK_SIZE
+ # add filepaths of selected songs to path
+ paths=map(lambda song: unicode(song.getFilepath()), songs)
+ # add in chunks of 1000
+ CHUNK_SIZE=1000
+ start=0
+ while start<len(paths):
+ if start+CHUNK_SIZE<len(paths):
+ end=start+CHUNK_SIZE
+ else:
+ end=len(paths)
+ self.setStatus('Adding '+str(len(songs))+' songs to library: %i%%'%(int(100*start/len(paths))))
+ doEvents()
+ monty.addToPlaylist(paths[start:end])
+ start+=CHUNK_SIZE
- self.setStatus('')
- doEvents()
- self.getWinMain().fillPlaylist()
+ self.setStatus('')
+ doEvents()
+ self.getWinMain().fillPlaylist()
- def _getSettings(self):
- modes=QtGui.QTextEdit()
- modes.insertPlainText(self.getSetting('modes'))
- return [
- ['modes', 'Modes', 'Sets the available modes.', modes],
- ]
- def afterSaveSettings(self):
- self.o.setModes(self.getSetting('modes').split('\n'))
+ def _getSettings(self):
+ modes=QtGui.QTextEdit()
+ modes.insertPlainText(self.getSetting('modes'))
+ return [
+ ['modes', 'Modes', 'Sets the available modes.', modes],
+ ]
+ def afterSaveSettings(self):
+ self.o.setModes(self.getSetting('modes').split('\n'))
diff --git a/plugins/Logger.py b/plugins/Logger.py
index 575fd5b..4e8ce6b 100644
--- a/plugins/Logger.py
+++ b/plugins/Logger.py
@@ -6,62 +6,62 @@ from misc import Button
LOGGER_LEVEL_DEFAULT=log.LOG_NORMAL
class wgLogger(QtGui.QWidget):
- """Shows log information"""
- " list containing the log"
- log=None
- btnClear=None
- p=None
- def __init__(self, p, parent=None):
- QtGui.QWidget.__init__(self, parent)
- self.p=p
-
- self.log=QtGui.QListWidget(self)
- self.btnClear=Button("Clear", self.clear)
- self.cmbLevel=QtGui.QComboBox(self)
-
- self.cmbLevel.addItem("Nothing")
- self.cmbLevel.addItem("Important")
- self.cmbLevel.addItem("Normal")
- self.cmbLevel.addItem("Extended")
- self.cmbLevel.addItem("Debug")
-
- self.cmbLevel.setCurrentIndex(int(self.p.getSetting("level")))
- self.onCmbLevelChanged(self.cmbLevel.currentIndex())
+ """Shows log information"""
+ " list containing the log"
+ log=None
+ btnClear=None
+ p=None
+ def __init__(self, p, parent=None):
+ QtGui.QWidget.__init__(self, parent)
+ self.p=p
+
+ self.log=QtGui.QListWidget(self)
+ self.btnClear=Button("Clear", self.clear)
+ self.cmbLevel=QtGui.QComboBox(self)
+
+ self.cmbLevel.addItem("Nothing")
+ self.cmbLevel.addItem("Important")
+ self.cmbLevel.addItem("Normal")
+ self.cmbLevel.addItem("Extended")
+ self.cmbLevel.addItem("Debug")
+
+ self.cmbLevel.setCurrentIndex(int(self.p.getSetting("level")))
+ self.onCmbLevelChanged(self.cmbLevel.currentIndex())
- layout=QtGui.QVBoxLayout()
- layout2=QtGui.QHBoxLayout()
- self.setLayout(layout)
+ layout=QtGui.QVBoxLayout()
+ layout2=QtGui.QHBoxLayout()
+ self.setLayout(layout)
- layout2.addWidget(self.cmbLevel)
- layout2.addWidget(self.btnClear)
- layout.addLayout(layout2)
- layout.addWidget(self.log)
-
- self.connect(self.cmbLevel, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbLevelChanged)
-
- def onCmbLevelChanged(self, newval):
- log.setLevel(newval)
- log.extended("Switching loglevel to %s"%(str(newval)))
- settings.set("logger.level", newval)
- def writer(self, item, level):
- self.log.addItem(item)
- self.log.setCurrentRow(self.log.count()-1)
- #self.log.scrollToItem(self.log.item(self.log.count()))
- def clear(self):
- self.log.clear()
+ layout2.addWidget(self.cmbLevel)
+ layout2.addWidget(self.btnClear)
+ layout.addLayout(layout2)
+ layout.addWidget(self.log)
+
+ self.connect(self.cmbLevel, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbLevelChanged)
+
+ def onCmbLevelChanged(self, newval):
+ log.setLevel(newval)
+ log.extended("Switching loglevel to %s"%(str(newval)))
+ settings.set("logger.level", newval)
+ def writer(self, item, level):
+ self.log.addItem(item)
+ self.log.setCurrentRow(self.log.count()-1)
+ #self.log.scrollToItem(self.log.item(self.log.count()))
+ def clear(self):
+ self.log.clear()
class pluginLogger(Plugin):
- o=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'Logger')
- def _load(self):
- self.o=wgLogger(self, None)
- log.setWriter(self.o.writer)
- def _unload(self):
- self.o=None
- log.setWriter(log._writer)
- def getInfo(self):
- return "Shows information from the log."
-
- def _getDockWidget(self):
- return self._createDock(self.o)
+ o=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'Logger')
+ def _load(self):
+ self.o=wgLogger(self, None)
+ log.setWriter(self.o.writer)
+ def _unload(self):
+ self.o=None
+ log.setWriter(log._writer)
+ def getInfo(self):
+ return "Shows information from the log."
+
+ def _getDockWidget(self):
+ return self._createDock(self.o)
diff --git a/plugins/Lyrics.py b/plugins/Lyrics.py
index 2e4d31c..840ec06 100644
--- a/plugins/Lyrics.py
+++ b/plugins/Lyrics.py
@@ -13,273 +13,273 @@ from clMonty import monty
from clPlugin import *
class ResetEvent(QtCore.QEvent):
- song=None
- def __init__(self, song=None):
- QtCore.QEvent.__init__(self,QtCore.QEvent.User)
- self.song=song
+ song=None
+ def __init__(self, song=None):
+ QtCore.QEvent.__init__(self,QtCore.QEvent.User)
+ self.song=song
class AddHtmlEvent(QtCore.QEvent):
- html=None
- def __init__(self,html):
- QtCore.QEvent.__init__(self,QtCore.QEvent.User)
- self.html=html
+ html=None
+ def __init__(self,html):
+ QtCore.QEvent.__init__(self,QtCore.QEvent.User)
+ self.html=html
class AddTextEvent(QtCore.QEvent):
- text=None
- def __init__(self,text):
- QtCore.QEvent.__init__(self,QtCore.QEvent.User)
- self.text=text
+ text=None
+ def __init__(self,text):
+ QtCore.QEvent.__init__(self,QtCore.QEvent.User)
+ self.text=text
LYRICS_ENGINE_DEFAULT='http://www.google.com/search?q=lyrics+"$artist"+"$title"'
LYRICS_SITES_DEFAULT=\
- 'absolutelyrics.com <div id="realText">(.*?)</div>\n'\
- 'azlyrics.com <br><br>.*?<br><br>(.*?)<br><br>\n'\
- 'oldielyrics.com song_in_top2.*?<br>(.*?)<script\n'\
- 'lyricstime.com phone-left.gif.*?<p>(.*?)</p>\n'\
- 'lyricsfire.com class="lyric">.*?Song.*?<br>(.*?)</pre>\n'\
- 'lyricsfreak.com <div.*?id="content".*?>(.*?)<blockquote\n'\
- 'artists.letssingit.com <pre>(.*?)</pre>\n'\
- 'gugalyrics.com </h1>(.*?)<a\n'\
- 'lyricsmania.com </strong> :(.*?)&#91;\n'\
- 'leoslyrics.com <font face=.*?size=-1>(.*?)</font>\n'\
- 'bluesforpeace.com <blockquote>(.*?)</blockquote>\n'
+ 'absolutelyrics.com <div id="realText">(.*?)</div>\n'\
+ 'azlyrics.com <br><br>.*?<br><br>(.*?)<br><br>\n'\
+ 'oldielyrics.com song_in_top2.*?<br>(.*?)<script\n'\
+ 'lyricstime.com phone-left.gif.*?<p>(.*?)</p>\n'\
+ 'lyricsfire.com class="lyric">.*?Song.*?<br>(.*?)</pre>\n'\
+ 'lyricsfreak.com <div.*?id="content".*?>(.*?)<blockquote\n'\
+ 'artists.letssingit.com <pre>(.*?)</pre>\n'\
+ 'gugalyrics.com </h1>(.*?)<a\n'\
+ 'lyricsmania.com </strong> :(.*?)&#91;\n'\
+ 'leoslyrics.com <font face=.*?size=-1>(.*?)</font>\n'\
+ 'bluesforpeace.com <blockquote>(.*?)</blockquote>\n'
LYRICS_DOWNLOADTO_DEFAULT='~/.lyrics/$artist/$artist - $title.txt'
LYRICS_AUTOSCROLL_DEFAULT='1'
class wgLyrics(QtGui.QWidget):
- " contains the lyrics"
- txtView=None # text-object
- curLyrics=None # current lyrics
- btnEdit=None
- btnRefetch=None
- btnSave=None
- btnSearch=None
- editMode=False
- lyFormat=None
- p=None # plugin
- def __init__(self, p, parent=None):
- QtGui.QWidget.__init__(self, parent)
- self.p=p
- self.curLyrics=""
- self.btnEdit=Button("Edit lyrics", self.onBtnEditClick)
- self.btnRefetch=Button("Refetch", self.onBtnRefetchClick)
- self.btnSave=Button("Save lyrics", self.onBtnSaveClick)
- self.btnSearch=Button("Search www", self.onBtnSearch)
-
- self.txtView=QtGui.QTextEdit(parent)
- self.txtView.setReadOnly(True)
-
- self.setMode(False)
-
- layout=QtGui.QVBoxLayout()
- hlayout=QtGui.QHBoxLayout()
- layout.addLayout(hlayout)
- hlayout.addWidget(self.btnEdit)
- hlayout.addWidget(self.btnSave)
- hlayout.addWidget(self.btnRefetch)
- hlayout.addWidget(self.btnSearch)
- layout.addWidget(self.txtView)
- self.setLayout(layout)
+ " contains the lyrics"
+ txtView=None # text-object
+ curLyrics=None # current lyrics
+ btnEdit=None
+ btnRefetch=None
+ btnSave=None
+ btnSearch=None
+ editMode=False
+ lyFormat=None
+ p=None # plugin
+ def __init__(self, p, parent=None):
+ QtGui.QWidget.__init__(self, parent)
+ self.p=p
+ self.curLyrics=""
+ self.btnEdit=Button("Edit lyrics", self.onBtnEditClick)
+ self.btnRefetch=Button("Refetch", self.onBtnRefetchClick)
+ self.btnSave=Button("Save lyrics", self.onBtnSaveClick)
+ self.btnSearch=Button("Search www", self.onBtnSearch)
+
+ self.txtView=QtGui.QTextEdit(parent)
+ self.txtView.setReadOnly(True)
+
+ self.setMode(False)
+
+ layout=QtGui.QVBoxLayout()
+ hlayout=QtGui.QHBoxLayout()
+ layout.addLayout(hlayout)
+ hlayout.addWidget(self.btnEdit)
+ hlayout.addWidget(self.btnSave)
+ hlayout.addWidget(self.btnRefetch)
+ hlayout.addWidget(self.btnSearch)
+ layout.addWidget(self.txtView)
+ self.setLayout(layout)
-
- def setMode(self, mode):
- self.editMode=mode
- self.btnSave.setVisible(mode)
- self.btnEdit.setVisible(not mode)
- self.txtView.setReadOnly(not mode)
-
- def onBtnSearch(self):
- SE=self.p.getSetting('engine')
- f=format.compile(SE)
- SE_url=toAscii(f(format.params(monty.getCurrentSong())))
- webbrowser.open(urllib.quote(SE_url, ":/+?="))
-
- def onBtnEditClick(self):
- self.setMode(True)
- self.txtView.setPlainText(self.curLyrics)
-
- def onBtnSaveClick(self):
- self.setMode(False)
- self.curLyrics=self.txtView.toPlainText()
- file=open(self.getLyricsFilePath(monty.getCurrentSong()), 'w')
- file.write(self.curLyrics)
- file.close()
-
- # just to test if everything's still fine
- self.fetchLyrics(monty.getCurrentSong())
-
- def onBtnRefetchClick(self):
- # force refetch
- lyFName=self.getLyricsFilePath(monty.getCurrentSong())
- try:
- os.remove(lyFName)
- except:
- pass
- self.refresh()
-
- def refresh(self):
- # if we're editing while song changes, too bad: we don't save, for the moment!
- if self.editMode:
- self.setMode(False)
-
- self.lyFormat=format.compile(self.p.getSetting('downloadto'))
- song=monty.getCurrentSong()
- try:
- song._data['file']
- except:
- self.resetTxt()
- return
-
- self.resetTxt(song)
- start_new_thread(self.fetchLyrics, (song,))
-
- def customEvent(self, event):
- if isinstance(event,ResetEvent):
- self.resetTxt(event.song)
- elif isinstance(event,AddTextEvent):
- self.txtView.insertPlainText(event.text)
- elif isinstance(event,AddHtmlEvent):
- self.txtView.insertHtml(event.html)
+
+ def setMode(self, mode):
+ self.editMode=mode
+ self.btnSave.setVisible(mode)
+ self.btnEdit.setVisible(not mode)
+ self.txtView.setReadOnly(not mode)
+
+ def onBtnSearch(self):
+ SE=self.p.getSetting('engine')
+ f=format.compile(SE)
+ SE_url=toAscii(f(format.params(monty.getCurrentSong())))
+ webbrowser.open(urllib.quote(SE_url, ":/+?="))
+
+ def onBtnEditClick(self):
+ self.setMode(True)
+ self.txtView.setPlainText(self.curLyrics)
+
+ def onBtnSaveClick(self):
+ self.setMode(False)
+ self.curLyrics=self.txtView.toPlainText()
+ file=open(self.getLyricsFilePath(monty.getCurrentSong()), 'w')
+ file.write(self.curLyrics)
+ file.close()
+
+ # just to test if everything's still fine
+ self.fetchLyrics(monty.getCurrentSong())
+
+ def onBtnRefetchClick(self):
+ # force refetch
+ lyFName=self.getLyricsFilePath(monty.getCurrentSong())
+ try:
+ os.remove(lyFName)
+ except:
+ pass
+ self.refresh()
+
+ def refresh(self):
+ # if we're editing while song changes, too bad: we don't save, for the moment!
+ if self.editMode:
+ self.setMode(False)
+
+ self.lyFormat=format.compile(self.p.getSetting('downloadto'))
+ song=monty.getCurrentSong()
+ try:
+ song._data['file']
+ except:
+ self.resetTxt()
+ return
+
+ self.resetTxt(song)
+ start_new_thread(self.fetchLyrics, (song,))
+
+ def customEvent(self, event):
+ if isinstance(event,ResetEvent):
+ self.resetTxt(event.song)
+ elif isinstance(event,AddTextEvent):
+ self.txtView.insertPlainText(event.text)
+ elif isinstance(event,AddHtmlEvent):
+ self.txtView.insertHtml(event.html)
- def getLyricsFilePath(self, song):
- params={'music_dir': mpdSettings.get('music_directory')}
- path=self.lyFormat(format.params(song, params))
- return toAscii(os.path.expanduser(path))
-
- _mutex=QtCore.QMutex()
- _fetchCnt=0
- def fetchLyrics(self, song):
- # only allow 1 instance to look lyrics!
- self._mutex.lock()
- if self._fetchCnt:
- self._mutex.unlock()
- return
- self._fetchCnt=1
- self._mutex.unlock()
+ def getLyricsFilePath(self, song):
+ params={'music_dir': mpdSettings.get('music_directory')}
+ path=self.lyFormat(format.params(song, params))
+ return toAscii(os.path.expanduser(path))
+
+ _mutex=QtCore.QMutex()
+ _fetchCnt=0
+ def fetchLyrics(self, song):
+ # only allow 1 instance to look lyrics!
+ self._mutex.lock()
+ if self._fetchCnt:
+ self._mutex.unlock()
+ return
+ self._fetchCnt=1
+ self._mutex.unlock()
- QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
-
- lyFName=self.getLyricsFilePath(song)
- self.p.debug("checking %s"%(lyFName))
- self.curLyrics=""
- # does the file exist? if yes, read that one!
- try:
- file=open(lyFName, 'r')
- self.curLyrics=file.read()
- file.close()
- QtCore.QCoreApplication.postEvent(self, AddTextEvent(self.curLyrics))
- self._fetchCnt=0
- self.p.extended("Fetch lyrics from file")
- return
- except Exception, e:
- self.p.debug("fail - %s"%(str(e)))
+ QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
+
+ lyFName=self.getLyricsFilePath(song)
+ self.p.debug("checking %s"%(lyFName))
+ self.curLyrics=""
+ # does the file exist? if yes, read that one!
+ try:
+ file=open(lyFName, 'r')
+ self.curLyrics=file.read()
+ file.close()
+ QtCore.QCoreApplication.postEvent(self, AddTextEvent(self.curLyrics))
+ self._fetchCnt=0
+ self.p.extended("Fetch lyrics from file")
+ return
+ except Exception, e:
+ self.p.debug("fail - %s"%(str(e)))
- # fetch from inet
- QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('<i>Searching lyrics ...</i>'))
- self.p.extended("Fetch lyrics from internet")
-
- lines=self.p.getSetting('sites').split('\n')
- sites={}
- for line in lines:
- if line.strip():
- sites[line[0:line.find('\t')]]=line[line.find('\t'):].strip()
- # construct URL to search!
- SE=self.p.getSetting('engine')
- try:
- ret=fetch(SE, sites, song, {})
- QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
- if ret:
- self.p.extended("Success!")
- self.curLyrics=ret[0]
- # save for later use!
- if lyFName:
- # we can't save if the path isn't correct
- try:
- self.p.extended("Saving to %s"%(lyFName))
- try:
- # fails when dir exists
- os.makedirs(os.path.dirname(os.path.expanduser(lyFName)))
- except Exception, e:
- pass
- file=open(lyFName, 'w')
- file.write(self.curLyrics)
- file.close()
- except Exception, e:
- # probably a wrong path!
- self.p.normal("Failed to write lyrics %s"%(str(e)))
- else:
- self.curLyrics=""
- txt="No lyrics found :'("
- self.p.extended("Lyrics not found!")
- QtCore.QCoreApplication.postEvent(self, AddTextEvent(txt))
-
- QtCore.QCoreApplication.postEvent(self, AddTextEvent(self.curLyrics))
- if ret:
- QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('<br /><br /><a href="%s">%s</a>'%(ret[1],ret[1])))
+ # fetch from inet
+ QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('<i>Searching lyrics ...</i>'))
+ self.p.extended("Fetch lyrics from internet")
+
+ lines=self.p.getSetting('sites').split('\n')
+ sites={}
+ for line in lines:
+ if line.strip():
+ sites[line[0:line.find('\t')]]=line[line.find('\t'):].strip()
+ # construct URL to search!
+ SE=self.p.getSetting('engine')
+ try:
+ ret=fetch(SE, sites, song, {})
+ QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
+ if ret:
+ self.p.extended("Success!")
+ self.curLyrics=ret[0]
+ # save for later use!
+ if lyFName:
+ # we can't save if the path isn't correct
+ try:
+ self.p.extended("Saving to %s"%(lyFName))
+ try:
+ # fails when dir exists
+ os.makedirs(os.path.dirname(os.path.expanduser(lyFName)))
+ except Exception, e:
+ pass
+ file=open(lyFName, 'w')
+ file.write(self.curLyrics)
+ file.close()
+ except Exception, e:
+ # probably a wrong path!
+ self.p.normal("Failed to write lyrics %s"%(str(e)))
+ else:
+ self.curLyrics=""
+ txt="No lyrics found :'("
+ self.p.extended("Lyrics not found!")
+ QtCore.QCoreApplication.postEvent(self, AddTextEvent(txt))
+
+ QtCore.QCoreApplication.postEvent(self, AddTextEvent(self.curLyrics))
+ if ret:
+ QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('<br /><br /><a href="%s">%s</a>'%(ret[1],ret[1])))
- except Exception, e:
- QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
- QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('Woops, error! Possible causes:'\
- '<br />no internet connection?'\
- '<br />site unavailable'\
- '<br />an error in the fetching regular expression'\
- '<br />(exception: %s)'%(str(e))))
- self._fetchCnt=0
+ except Exception, e:
+ QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
+ QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('Woops, error! Possible causes:'\
+ '<br />no internet connection?'\
+ '<br />site unavailable'\
+ '<br />an error in the fetching regular expression'\
+ '<br />(exception: %s)'%(str(e))))
+ self._fetchCnt=0
- def resetTxt(self, song=None):
- self.txtView.clear()
- if song:
- self.txtView.insertHtml('<b>%s</b>\n<br /><u>%s</u><br />'\
- '<br />\n\n'%(song.getTitle(), song.getArtist()))
-
- def autoScroll(self, time):
- t=self.txtView
- max=t.verticalScrollBar().maximum()
- if max<=0:
- return
- t.verticalScrollBar().setValue((max+t.height()/t.currentFont().pointSize())*time/monty.getCurrentSong()._data['time'])
-
+ def resetTxt(self, song=None):
+ self.txtView.clear()
+ if song:
+ self.txtView.insertHtml('<b>%s</b>\n<br /><u>%s</u><br />'\
+ '<br />\n\n'%(song.getTitle(), song.getArtist()))
+
+ def autoScroll(self, time):
+ t=self.txtView
+ max=t.verticalScrollBar().maximum()
+ if max<=0:
+ return
+ t.verticalScrollBar().setValue((max+t.height()/t.currentFont().pointSize())*time/monty.getCurrentSong()._data['time'])
+
class pluginLyrics(Plugin):
- o=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'Lyrics')
- self.addMontyListener('onSongChange', self.refresh)
- self.addMontyListener('onReady', self.refresh)
- self.addMontyListener('onDisconnect', self.onDisconnect)
- self.addMontyListener('onTimeChange', self.onTimeChange)
- def _load(self):
- self.o=wgLyrics(self, None)
- self.o.refresh()
- def _unload(self):
- self.o=None
- def getInfo(self):
- return "Show (and fetch) the lyrics of the currently playing song."
-
- def _getDockWidget(self):
- return self._createDock(self.o)
+ o=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'Lyrics')
+ self.addMontyListener('onSongChange', self.refresh)
+ self.addMontyListener('onReady', self.refresh)
+ self.addMontyListener('onDisconnect', self.onDisconnect)
+ self.addMontyListener('onTimeChange', self.onTimeChange)
+ def _load(self):
+ self.o=wgLyrics(self, None)
+ self.o.refresh()
+ def _unload(self):
+ self.o=None
+ def getInfo(self):
+ return "Show (and fetch) the lyrics of the currently playing song."
+
+ def _getDockWidget(self):
+ return self._createDock(self.o)
- def refresh(self, params):
- self.o.refresh()
- def onDisconnect(self, params):
- self.o.resetTxt()
- def onTimeChange(self, params):
- if self.getSetting("autoScroll")!="0" and self.o.editMode==False:
- # could be done better, but this is just plain simple :)
- self.o.autoScroll(params['newTime'])
+ def refresh(self, params):
+ self.o.refresh()
+ def onDisconnect(self, params):
+ self.o.resetTxt()
+ def onTimeChange(self, params):
+ if self.getSetting("autoScroll")!="0" and self.o.editMode==False:
+ # could be done better, but this is just plain simple :)
+ self.o.autoScroll(params['newTime'])
- def _getSettings(self):
- sites=QtGui.QTextEdit()
- sites.insertPlainText(self.getSetting('sites'))
- autoscroll=QtGui.QCheckBox("Autoscroll")
- autoscroll.setCheckState(QtCore.Qt.Checked if self.getSetting("autoScroll")!=0 else QtCore.Qt.Unchecked)
- return [
- ['engine', 'Search engine', 'The URL that is used to search. $artist, $title and $album are replaced in the URL.', QtGui.QLineEdit(self.getSetting('engine'))],
- ['sites', 'Sites & regexes', 'This field contains all sites, together with the regex needed to fetch the lyrics.\nEvery line must look like this: $domain $regex-start(.*?)$regex-end\n$domain is the domain of the lyrics website, $regex-start is the regex indicating the start of the lyrics, $regex-end indicates the end. E.g. foolyrics.org <lyrics>(.*?)</lyrics>', sites],
- ['downloadto', 'Target', 'Specifies where to save lyrics fetched from internet to.\nPossible tags: music_dir, file, artist, title, album', QtGui.QLineEdit(self.getSetting('downloadto'))],
- ['autoScroll', 'Autoscroll', 'Check this box to scroll the contents automatically as the time advances', autoscroll],
- ]
- def afterSaveSettings(self):
- self.o.refresh()
+ def _getSettings(self):
+ sites=QtGui.QTextEdit()
+ sites.insertPlainText(self.getSetting('sites'))
+ autoscroll=QtGui.QCheckBox("Autoscroll")
+ autoscroll.setCheckState(QtCore.Qt.Checked if self.getSetting("autoScroll")!=0 else QtCore.Qt.Unchecked)
+ return [
+ ['engine', 'Search engine', 'The URL that is used to search. $artist, $title and $album are replaced in the URL.', QtGui.QLineEdit(self.getSetting('engine'))],
+ ['sites', 'Sites & regexes', 'This field contains all sites, together with the regex needed to fetch the lyrics.\nEvery line must look like this: $domain $regex-start(.*?)$regex-end\n$domain is the domain of the lyrics website, $regex-start is the regex indicating the start of the lyrics, $regex-end indicates the end. E.g. foolyrics.org <lyrics>(.*?)</lyrics>', sites],
+ ['downloadto', 'Target', 'Specifies where to save lyrics fetched from internet to.\nPossible tags: music_dir, file, artist, title, album', QtGui.QLineEdit(self.getSetting('downloadto'))],
+ ['autoScroll', 'Autoscroll', 'Check this box to scroll the contents automatically as the time advances', autoscroll],
+ ]
+ def afterSaveSettings(self):
+ self.o.refresh()
diff --git a/plugins/MPD.py b/plugins/MPD.py
index dd41230..c7e8c9c 100644
--- a/plugins/MPD.py
+++ b/plugins/MPD.py
@@ -8,21 +8,21 @@ MPD_HOST_DEFAULT='localhost'
MPD_PORT_DEFAULT='6600'
class pluginMPD(Plugin):
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'MPD')
- def getInfo(self):
- return "Provides an interface to the MPD settings."
-
-
- def _getSettings(self):
- return [
- ['host', 'Host', 'Host where mpd resides.', QtGui.QLineEdit(self.getSetting('host'))],
- ['port', 'Port', 'Port of mpd.', QtGui.QLineEdit(self.getSetting('port'))],
- ['music_directory', 'Music directory', 'Root directory where all music is located.', QtGui.QLineEdit(mpdSettings.get('music_directory')), mpdSettings],
- ['', 'Update database', 'Updates the database.\nUse this if you have changed the music_directory. Updating will save all entries on the MPD tab.', Button('Update db', self.onBtnUpdateDBClick)],
- ]
- def onBtnUpdateDBClick(self):
- self.saveSettings()
- monty.updateDB([mpdSettings.get('music_directory')])
- pass
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'MPD')
+ def getInfo(self):
+ return "Provides an interface to the MPD settings."
+
+
+ def _getSettings(self):
+ return [
+ ['host', 'Host', 'Host where mpd resides.', QtGui.QLineEdit(self.getSetting('host'))],
+ ['port', 'Port', 'Port of mpd.', QtGui.QLineEdit(self.getSetting('port'))],
+ ['music_directory', 'Music directory', 'Root directory where all music is located.', QtGui.QLineEdit(mpdSettings.get('music_directory')), mpdSettings],
+ ['', 'Update database', 'Updates the database.\nUse this if you have changed the music_directory. Updating will save all entries on the MPD tab.', Button('Update db', self.onBtnUpdateDBClick)],
+ ]
+ def onBtnUpdateDBClick(self):
+ self.saveSettings()
+ monty.updateDB([mpdSettings.get('music_directory')])
+ pass
diff --git a/plugins/Notify.py b/plugins/Notify.py
index 14e35af..4c6de63 100644
--- a/plugins/Notify.py
+++ b/plugins/Notify.py
@@ -12,156 +12,156 @@ NOTIFY_SONGFORMAT_DEFAULT='$if($artist,$artist)$if($album, - [$album #$track])\n
NOTIFY_TIMER_DEFAULT=3
class winNotify(QtGui.QWidget):
- _timerID=None
- resizeWindow=True
- winMain=None
- p=None
+ _timerID=None
+ resizeWindow=True
+ winMain=None
+ p=None
- # data used for showing off
- timer=None
- msg=None
- song=None
- xtra_tags=None
+ # data used for showing off
+ timer=None
+ msg=None
+ song=None
+ xtra_tags=None
- def __init__(self, p, winMain, parent=None):
- QtGui.QWidget.__init__(self, parent)
- self.p=p
- self.winMain=winMain
-
- self.setWindowFlags(QtCore.Qt.ToolTip)
- self.setWindowOpacity(0.7)
-
- font=QtGui.QFont()
- font.setPixelSize(30)
- font.setFamily('Comic Sans Ms')
- self.setFont(font)
-
- def mousePressEvent(self, event):
- self.hide()
+ def __init__(self, p, winMain, parent=None):
+ QtGui.QWidget.__init__(self, parent)
+ self.p=p
+ self.winMain=winMain
+
+ self.setWindowFlags(QtCore.Qt.ToolTip)
+ self.setWindowOpacity(0.7)
+
+ font=QtGui.QFont()
+ font.setPixelSize(30)
+ font.setFamily('Comic Sans Ms')
+ self.setFont(font)
+
+ def mousePressEvent(self, event):
+ self.hide()
- def show(self, msg, song=None, xtra_tags={}, time=3):
- if not self.isVisible():
- self.setVisible(True)
- self.resizeWindow=True
- self.msg=format.compile(msg)
- self.song=song
- self.xtra_tags=xtra_tags
- if self._timerID:
- self.killTimer(self._timerID)
- self._timerID=self.startTimer(500)
- try:
- self.timer=int(time)*2
- except:
- self.timer=3
- self.raise_()
- self.timerEvent(None)
-
- def hide(self):
- if self._timerID:
- self.killTimer(self._timerID)
- self._timerID=None
- self.setHidden(True)
+ def show(self, msg, song=None, xtra_tags={}, time=3):
+ if not self.isVisible():
+ self.setVisible(True)
+ self.resizeWindow=True
+ self.msg=format.compile(msg)
+ self.song=song
+ self.xtra_tags=xtra_tags
+ if self._timerID:
+ self.killTimer(self._timerID)
+ self._timerID=self.startTimer(500)
+ try:
+ self.timer=int(time)*2
+ except:
+ self.timer=3
+ self.raise_()
+ self.timerEvent(None)
+
+ def hide(self):
+ if self._timerID:
+ self.killTimer(self._timerID)
+ self._timerID=None
+ self.setHidden(True)
-
- def centerH(self):
- screen = QtGui.QDesktopWidget().screenGeometry()
- size = self.geometry()
- self.move((screen.width()-size.width())/2, 100)
- def paintEvent(self, event):
- p=QtGui.QPainter(self)
- margin=3 # margin in pixels
- spacing=10 # space between album cover and text
-
- # determine the Rect our message must fit in
- txt=self.msg(format.params(self.song, self.xtra_tags))
- rect=p.boundingRect(0,0,1,1, QtCore.Qt.AlignHCenter, txt)
-
- # check if 1/ albumcover plugin is loaded, and 2/ there is an
- # album cover
- width=0
- try:
- cover=plugins.getPlugin('albumcover')
- img=cover.getWidget().getIMG()
- if img:
- width=128
- else:
- spacing=0
- except:
- img=None
- pass
-
- # do we have to resize?
- if self.resizeWindow:
- self.resizeWindow=False
- self.resize(rect.width()+width+margin*3+spacing, max(width,rect.height())+margin*2)
- self.centerH()
-
- # fill up with a nice color :)
- p.fillRect(QtCore.QRect(0,0,self.width(),self.height()), QtGui.QBrush(QtGui.QColor(230,230,255)))
-
- # draw album cover if necessary
- if img:
- rImg=QtCore.QRectF(margin,margin,width,width)
- p.drawImage(rImg, img)
-
- # Pen In Black ...
- p.setPen(QtCore.Qt.black)
- rect=p.boundingRect(width+margin+spacing,margin,
- rect.width(),self.height(), QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter, txt)
- p.drawText(rect, QtCore.Qt.AlignHCenter, txt)
-
- def timerEvent(self, event):
- self.timer-=1
- if self.timer<=0:
- self.hide()
- self.update()
-
+
+ def centerH(self):
+ screen = QtGui.QDesktopWidget().screenGeometry()
+ size = self.geometry()
+ self.move((screen.width()-size.width())/2, 100)
+ def paintEvent(self, event):
+ p=QtGui.QPainter(self)
+ margin=3 # margin in pixels
+ spacing=10 # space between album cover and text
+
+ # determine the Rect our message must fit in
+ txt=self.msg(format.params(self.song, self.xtra_tags))
+ rect=p.boundingRect(0,0,1,1, QtCore.Qt.AlignHCenter, txt)
+
+ # check if 1/ albumcover plugin is loaded, and 2/ there is an
+ # album cover
+ width=0
+ try:
+ cover=plugins.getPlugin('albumcover')
+ img=cover.getWidget().getIMG()
+ if img:
+ width=128
+ else:
+ spacing=0
+ except:
+ img=None
+ pass
+
+ # do we have to resize?
+ if self.resizeWindow:
+ self.resizeWindow=False
+ self.resize(rect.width()+width+margin*3+spacing, max(width,rect.height())+margin*2)
+ self.centerH()
+
+ # fill up with a nice color :)
+ p.fillRect(QtCore.QRect(0,0,self.width(),self.height()), QtGui.QBrush(QtGui.QColor(230,230,255)))
+
+ # draw album cover if necessary
+ if img:
+ rImg=QtCore.QRectF(margin,margin,width,width)
+ p.drawImage(rImg, img)
+
+ # Pen In Black ...
+ p.setPen(QtCore.Qt.black)
+ rect=p.boundingRect(width+margin+spacing,margin,
+ rect.width(),self.height(), QtCore.Qt.AlignHCenter|QtCore.Qt.AlignVCenter, txt)
+ p.drawText(rect, QtCore.Qt.AlignHCenter, txt)
+
+ def timerEvent(self, event):
+ self.timer-=1
+ if self.timer<=0:
+ self.hide()
+ self.update()
+
class pluginNotify(Plugin):
- o=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'Notify')
- self.addMontyListener('onSongChange', self.onSongChange)
- self.addMontyListener('onReady', self.onReady)
- self.addMontyListener('onDisconnect', self.onDisconnect)
- self.addMontyListener('onStateChange', self.onStateChange)
- self.addMontyListener('onVolumeChange', self.onVolumeChange)
-
- def _load(self):
- self.o=winNotify(self, self.winMain)
- def _unload(self):
- self.o=None
- def getInfo(self):
- return "Show interesting events in a popup window."
+ o=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'Notify')
+ self.addMontyListener('onSongChange', self.onSongChange)
+ self.addMontyListener('onReady', self.onReady)
+ self.addMontyListener('onDisconnect', self.onDisconnect)
+ self.addMontyListener('onStateChange', self.onStateChange)
+ self.addMontyListener('onVolumeChange', self.onVolumeChange)
+
+ def _load(self):
+ self.o=winNotify(self, self.winMain)
+ def _unload(self):
+ self.o=None
+ def getInfo(self):
+ return "Show interesting events in a popup window."
- def onSongChange(self, params):
- self.o.show(self.getSetting('songformat').replace("\n", "\\n"), monty.getCurrentSong()
- , time=self.getSetting('timer'))
+ def onSongChange(self, params):
+ self.o.show(self.getSetting('songformat').replace("\n", "\\n"), monty.getCurrentSong()
+ , time=self.getSetting('timer'))
- def onReady(self, params):
- self.o.show('montypc loaded!', monty.getCurrentSong(), time=self.getSetting('timer'))
-
- def onDisconnect(self, params):
- self.o.show('Disconnected!', time=self.getSetting('timer'))
-
- def onStateChange(self, params):
- self.o.show(params['newState'], monty.getCurrentSong(), time=self.getSetting('timer'))
-
- def onVolumeChange(self, params):
- self.o.show('Volume: %i%%'%(params['newVolume']), monty.getCurrentSong(), time=self.getSetting('timer'))
-
- def _getSettings(self):
- txt=QtGui.QTextEdit()
- txt.insertPlainText(self.getSetting('songformat'))
- return [
- ['songformat', 'Song format', 'How to format the current playing song.', txt],
- ['timer', 'Show seconds', 'How many seconds does the notification have to be shown.', QtGui.QLineEdit(str(self.getSetting('timer')))],
- ]
- def afterSaveSettings(self):
- try:
- int(self.getSetting('timer'))
- except:
- self.getSetting('timer')
- self.getSettingWidget('notify.timer').setText(str(NOTIFY_DEFAULT_TIMER))
- self.onSongChange(None)
+ def onReady(self, params):
+ self.o.show('montypc loaded!', monty.getCurrentSong(), time=self.getSetting('timer'))
+
+ def onDisconnect(self, params):
+ self.o.show('Disconnected!', time=self.getSetting('timer'))
+
+ def onStateChange(self, params):
+ self.o.show(params['newState'], monty.getCurrentSong(), time=self.getSetting('timer'))
+
+ def onVolumeChange(self, params):
+ self.o.show('Volume: %i%%'%(params['newVolume']), monty.getCurrentSong(), time=self.getSetting('timer'))
+
+ def _getSettings(self):
+ txt=QtGui.QTextEdit()
+ txt.insertPlainText(self.getSetting('songformat'))
+ return [
+ ['songformat', 'Song format', 'How to format the current playing song.', txt],
+ ['timer', 'Show seconds', 'How many seconds does the notification have to be shown.', QtGui.QLineEdit(str(self.getSetting('timer')))],
+ ]
+ def afterSaveSettings(self):
+ try:
+ int(self.getSetting('timer'))
+ except:
+ self.getSetting('timer')
+ self.getSettingWidget('notify.timer').setText(str(NOTIFY_DEFAULT_TIMER))
+ self.onSongChange(None)
diff --git a/plugins/PlayControl.py b/plugins/PlayControl.py
index 586527c..be7006b 100644
--- a/plugins/PlayControl.py
+++ b/plugins/PlayControl.py
@@ -12,15 +12,15 @@ import plugins
# Note that REPEAT precedes RANDOM. E.g. if repeat
# is ALBUM, and random is SONG, then it'll take a random song
# from the current album ...
-PC_RANDOM_NO=0 # no randomness
-PC_RANDOM_SONG=1 # choose a song random from the playlist
-PC_RANDOM_ALBUM=2 # choose a random album, and play that fully
-PC_RANDOM_QUEUE=3 # choose next song from the queue
+PC_RANDOM_NO=0 # no randomness
+PC_RANDOM_SONG=1 # choose a song random from the playlist
+PC_RANDOM_ALBUM=2 # choose a random album, and play that fully
+PC_RANDOM_QUEUE=3 # choose next song from the queue
-PC_REPEAT_NO=0 # no repeat
-PC_REPEAT_SONG=1 # repeat current song
-PC_REPEAT_ALBUM=2 # repeat current album
-PC_REPEAT_PLAYLIST=3 # repeat playlist
+PC_REPEAT_NO=0 # no repeat
+PC_REPEAT_SONG=1 # repeat current song
+PC_REPEAT_ALBUM=2 # repeat current album
+PC_REPEAT_PLAYLIST=3 # repeat playlist
PLAYCONTROL_SHUFFLE_DEFAULT=PC_RANDOM_ALBUM
PLAYCONTROL_OLDSHUFFLE_DEFAULT=PLAYCONTROL_SHUFFLE_DEFAULT
@@ -29,355 +29,355 @@ PLAYCONTROL_REPEAT_DEFAULT=PC_REPEAT_PLAYLIST
class wgPlayControl(QtGui.QWidget):
- """Displays controls for interacting with playing, like play, volume ..."""
- " control buttons"
- btnPlayPause=None
- btnStop=None
- btnPrevious=None
- btnNext=None
- " button to jump to current song"
- btnJmpCurrent=None
- " slider for current time"
- slrTime=None
- " slider for volume"
- slrVolume=None
- " indicator for volume"
- svgVolume=None
- " all objects in this widget"
- objects=None
+ """Displays controls for interacting with playing, like play, volume ..."""
+ " control buttons"
+ btnPlayPause=None
+ btnStop=None
+ btnPrevious=None
+ btnNext=None
+ " button to jump to current song"
+ btnJmpCurrent=None
+ " slider for current time"
+ slrTime=None
+ " slider for volume"
+ slrVolume=None
+ " indicator for volume"
+ svgVolume=None
+ " all objects in this widget"
+ objects=None
- cmbRepeat=None
- cmbShuffle=None
- p=None
-
- " contains the songs of the album the current song is playing. None, if the album is not set"
- curAlbumSongs=None
-
- " queued songs: int*"
- queuedSongs=[]
- # what mode where we in before the queue started?
- beforeQueuedMode=None
+ cmbRepeat=None
+ cmbShuffle=None
+ p=None
+
+ " contains the songs of the album the current song is playing. None, if the album is not set"
+ curAlbumSongs=None
+
+ " queued songs: int*"
+ queuedSongs=[]
+ # what mode where we in before the queue started?
+ beforeQueuedMode=None
- def __init__(self, p, parent=None):
- QtGui.QWidget.__init__(self, parent)
- self.p=p
+ def __init__(self, p, parent=None):
+ QtGui.QWidget.__init__(self, parent)
+ self.p=p
- class wgSvgSwitcher(QtSvg.QSvgWidget):
- """Widget showing an svg-image, which, when clicked, will (un)hide an element."""
- # the element we wish to hide/show
- scroller=None
- def __init__(self,scroller,parent=None):
- QtSvg.QSvgWidget.__init__(self,parent)
- self.scroller=scroller
- def mousePressEvent(self,event):
- self.scroller.setVisible(not self.scroller.isVisible())
- def wheelEvent(self, event):
- event.accept()
- numDegrees=event.delta() / 8
- numSteps=5*numDegrees/15
- self.scroller.setValue(self.scroller.value()+numSteps)
+ class wgSvgSwitcher(QtSvg.QSvgWidget):
+ """Widget showing an svg-image, which, when clicked, will (un)hide an element."""
+ # the element we wish to hide/show
+ scroller=None
+ def __init__(self,scroller,parent=None):
+ QtSvg.QSvgWidget.__init__(self,parent)
+ self.scroller=scroller
+ def mousePressEvent(self,event):
+ self.scroller.setVisible(not self.scroller.isVisible())
+ def wheelEvent(self, event):
+ event.accept()
+ numDegrees=event.delta() / 8
+ numSteps=5*numDegrees/15
+ self.scroller.setValue(self.scroller.value()+numSteps)
- self.slrTime=QtGui.QSlider(QtCore.Qt.Horizontal, self)
- self.slrTime.setMinimumWidth(100)
- self.slrVolume=QtGui.QSlider(QtCore.Qt.Vertical, self)
- self.slrVolume.setMaximum(100)
- self.slrVolume.setMinimumWidth(100)
- self.slrVolume.setMaximumWidth(350)
- # set to some value that'll never be chosen, that way onChange will be called automatically :)
- self.slrVolume.setValue(3.141595)
- self.slrVolume.setVisible(False)
- self.svgVolume=wgSvgSwitcher(self.slrVolume)
- self.svgVolume.setMaximumSize(64,64)
- self.svgVolume.setMinimumSize(64,64)
- self.btnPlayPause=Button("play", self.onBtnPlayPauseClick, 'gfx/media-playback-start.svg', True)
- self.btnStop=Button("stop", self.onBtnStopClick, 'gfx/media-playback-stop.svg', True)
- self.btnPrevious=Button("prev", self.onBtnPreviousClick, 'gfx/media-skip-backward.svg', True)
- self.btnNext=Button("next", self.onBtnNextClick, 'gfx/media-skip-forward.svg', True)
- self.btnJmpCurrent=Button("Current", self.onBtnJmpCurrentClick)
+ self.slrTime=QtGui.QSlider(QtCore.Qt.Horizontal, self)
+ self.slrTime.setMinimumWidth(100)
+ self.slrVolume=QtGui.QSlider(QtCore.Qt.Vertical, self)
+ self.slrVolume.setMaximum(100)
+ self.slrVolume.setMinimumWidth(100)
+ self.slrVolume.setMaximumWidth(350)
+ # set to some value that'll never be chosen, that way onChange will be called automatically :)
+ self.slrVolume.setValue(3.141595)
+ self.slrVolume.setVisible(False)
+ self.svgVolume=wgSvgSwitcher(self.slrVolume)
+ self.svgVolume.setMaximumSize(64,64)
+ self.svgVolume.setMinimumSize(64,64)
+ self.btnPlayPause=Button("play", self.onBtnPlayPauseClick, 'gfx/media-playback-start.svg', True)
+ self.btnStop=Button("stop", self.onBtnStopClick, 'gfx/media-playback-stop.svg', True)
+ self.btnPrevious=Button("prev", self.onBtnPreviousClick, 'gfx/media-skip-backward.svg', True)
+ self.btnNext=Button("next", self.onBtnNextClick, 'gfx/media-skip-forward.svg', True)
+ self.btnJmpCurrent=Button("Current", self.onBtnJmpCurrentClick)
- self.cmbShuffle=QtGui.QComboBox(self)
- self.cmbShuffle.addItem('Don\'t play dices')
- self.cmbShuffle.addItem('Random song')
- self.cmbShuffle.addItem('Random album')
- self.cmbShuffle.addItem('Queue')
- self.cmbShuffle.setCurrentIndex(int(self.p.getSetting('shuffle')))
-
- self.cmbRepeat=QtGui.QComboBox(self)
- self.cmbRepeat.addItem('No repeat')
- self.cmbRepeat.addItem('Repeat current song')
- self.cmbRepeat.addItem('Repeat album')
- self.cmbRepeat.addItem('Playlist')
- self.cmbRepeat.setCurrentIndex(int(self.p.getSetting('repeat')))
+ self.cmbShuffle=QtGui.QComboBox(self)
+ self.cmbShuffle.addItem('Don\'t play dices')
+ self.cmbShuffle.addItem('Random song')
+ self.cmbShuffle.addItem('Random album')
+ self.cmbShuffle.addItem('Queue')
+ self.cmbShuffle.setCurrentIndex(int(self.p.getSetting('shuffle')))
+
+ self.cmbRepeat=QtGui.QComboBox(self)
+ self.cmbRepeat.addItem('No repeat')
+ self.cmbRepeat.addItem('Repeat current song')
+ self.cmbRepeat.addItem('Repeat album')
+ self.cmbRepeat.addItem('Playlist')
+ self.cmbRepeat.setCurrentIndex(int(self.p.getSetting('repeat')))
- self.objects=[self.slrVolume, self.slrTime,
- self.btnStop, self.btnNext, self.btnPrevious]
+ self.objects=[self.slrVolume, self.slrTime,
+ self.btnStop, self.btnNext, self.btnPrevious]
- layout=QtGui.QHBoxLayout(parent)
- layout2=QtGui.QHBoxLayout(parent)
- layoutWidget=QtGui.QVBoxLayout(parent)
- layoutWidget.addLayout(layout)
- layoutWidget.addLayout(layout2)
- self.setLayout(layoutWidget)
-
- layout.addWidget(self.btnPrevious)
- layout.addWidget(self.btnPlayPause)
- layout.addWidget(self.btnStop)
- layout.addWidget(self.btnNext)
- layout.addWidget(self.slrTime)
- layout.addWidget(self.slrVolume)
- layout.addWidget(self.svgVolume)
+ layout=QtGui.QHBoxLayout(parent)
+ layout2=QtGui.QHBoxLayout(parent)
+ layoutWidget=QtGui.QVBoxLayout(parent)
+ layoutWidget.addLayout(layout)
+ layoutWidget.addLayout(layout2)
+ self.setLayout(layoutWidget)
+
+ layout.addWidget(self.btnPrevious)
+ layout.addWidget(self.btnPlayPause)
+ layout.addWidget(self.btnStop)
+ layout.addWidget(self.btnNext)
+ layout.addWidget(self.slrTime)
+ layout.addWidget(self.slrVolume)
+ layout.addWidget(self.svgVolume)
- layout2.addWidget(self.cmbRepeat)
- layout2.addWidget(self.cmbShuffle)
- layout2.addWidget(self.btnJmpCurrent)
-
- # queue gets loaded in _load of pluginPlayControl
- self.queuedSongs=[]
- self._onQueueUpdate()
+ layout2.addWidget(self.cmbRepeat)
+ layout2.addWidget(self.cmbShuffle)
+ layout2.addWidget(self.btnJmpCurrent)
+
+ # queue gets loaded in _load of pluginPlayControl
+ self.queuedSongs=[]
+ self._onQueueUpdate()
- self.connect(self.slrVolume, QtCore.SIGNAL('valueChanged(int)'),self.onVolumeSliderChange)
- self.connect(self.slrTime, QtCore.SIGNAL('sliderReleased()'),self.onTimeSliderChange)
-
- self.connect(self.cmbRepeat, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbRepeatChanged)
- self.connect(self.cmbShuffle, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbShuffleChanged)
-
- def addSongsToQueue(self, songs):
- self.queuedSongs.extend(songs)
- self._onQueueUpdate()
- if self.cmbShuffle.currentIndex()!=PC_RANDOM_QUEUE:
- self.cmbShuffle.setCurrentIndex(PC_RANDOM_QUEUE)
-
- def _onQueueUpdate(self):
- """This method gets called whenever the queue is updated"""
- self.cmbShuffle.setItemText(PC_RANDOM_QUEUE, "Queue (%i)"%(len(self.queuedSongs)))
-
- def onBtnJmpCurrentClick(self):
- plugins.getPlugin("Playlist").getPlaylist().ensureVisible(monty.getCurrentSong().getID())
- def onStateChange(self, params):
- newState=monty.getStatus()['state']
+ self.connect(self.slrVolume, QtCore.SIGNAL('valueChanged(int)'),self.onVolumeSliderChange)
+ self.connect(self.slrTime, QtCore.SIGNAL('sliderReleased()'),self.onTimeSliderChange)
+
+ self.connect(self.cmbRepeat, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbRepeatChanged)
+ self.connect(self.cmbShuffle, QtCore.SIGNAL('currentIndexChanged(int)'),self.onCmbShuffleChanged)
+
+ def addSongsToQueue(self, songs):
+ self.queuedSongs.extend(songs)
+ self._onQueueUpdate()
+ if self.cmbShuffle.currentIndex()!=PC_RANDOM_QUEUE:
+ self.cmbShuffle.setCurrentIndex(PC_RANDOM_QUEUE)
+
+ def _onQueueUpdate(self):
+ """This method gets called whenever the queue is updated"""
+ self.cmbShuffle.setItemText(PC_RANDOM_QUEUE, "Queue (%i)"%(len(self.queuedSongs)))
+
+ def onBtnJmpCurrentClick(self):
+ plugins.getPlugin("Playlist").getPlaylist().ensureVisible(monty.getCurrentSong().getID())
+ def onStateChange(self, params):
+ newState=monty.getStatus()['state']
- map(lambda o: o.setEnabled(newState!='stop'), self.objects)
+ map(lambda o: o.setEnabled(newState!='stop'), self.objects)
- if newState=='play':
- self.btnPlayPause.changeIcon('gfx/media-playback-pause.svg')
- self.btnPlayPause.setText('pauze')
- elif newState=='pause' or newState=='stop':
- self.btnPlayPause.changeIcon('gfx/media-playback-start.svg')
- self.btnPlayPause.setText('play')
- def onVolumeChange(self, params):
- self.slrVolume.setValue(params['newVolume'])
- def onDisconnect(self, params):
- map(lambda o: o.setEnabled(False), self.objects)
- def onTimeChange(self, params):
- if not self.slrTime.isSliderDown():
- self.slrTime.setValue(params['newTime'])
- def onSongChange(self, params):
- try:
- self.slrTime.setMaximum(monty.getStatus()['length'])
- self.slrTime.setEnabled(True)
- except:
- pass
- # look in another thread for the songs in the current album!
- params=()
- start_new_thread(self.findAlbumSongs, params)
+ if newState=='play':
+ self.btnPlayPause.changeIcon('gfx/media-playback-pause.svg')
+ self.btnPlayPause.setText('pauze')
+ elif newState=='pause' or newState=='stop':
+ self.btnPlayPause.changeIcon('gfx/media-playback-start.svg')
+ self.btnPlayPause.setText('play')
+ def onVolumeChange(self, params):
+ self.slrVolume.setValue(params['newVolume'])
+ def onDisconnect(self, params):
+ map(lambda o: o.setEnabled(False), self.objects)
+ def onTimeChange(self, params):
+ if not self.slrTime.isSliderDown():
+ self.slrTime.setValue(params['newTime'])
+ def onSongChange(self, params):
+ try:
+ self.slrTime.setMaximum(monty.getStatus()['length'])
+ self.slrTime.setEnabled(True)
+ except:
+ pass
+ # look in another thread for the songs in the current album!
+ params=()
+ start_new_thread(self.findAlbumSongs, params)
- def beforeSongChange(self, params):
- nextID=None
- song=monty.getCurrentSong()
- # decide here what next song to play!
- repeat=self.cmbRepeat.currentIndex()
- random=self.cmbShuffle.currentIndex()
- # is the current song the last of the album?
- try:
- eofAlbum=int(song.getTrack())==int(self.curAlbumSongs[-1].getTrack())
- except:
- eofAlbum=False
- if repeat==PC_REPEAT_NO:
- # no repeat, nothing to see here! Pass on!
- pass
- elif repeat==PC_REPEAT_SONG:
- # we must repeat the previous song!
- nextID=params['curSongID']
- elif repeat==PC_REPEAT_ALBUM:
- # check if we are at the last track, if it is, we must start a new!
- if eofAlbum:
- nextID=self.curAlbumSongs[0].getID()
- elif repeat==PC_REPEAT_PLAYLIST:
- # repeating the playlist is handled by monty itself;
- # it is set in onCmbRepeatChanged.
- pass
-
- if repeat!=PC_REPEAT_SONG:
- if random==PC_RANDOM_QUEUE:
- # we must check here for the queue, because, if the queue is empty, we must
- # choose the next one according the method!
- # pick the next song from the queue. Simple as that :)
- if len(self.queuedSongs):
- # pick the front
- nextID=self.queuedSongs.pop(0)
- self._onQueueUpdate()
- else:
- # no songs anymore, so we must restore the old mode!
- # We should never arrive here though ...
- self.cmbShuffle.setCurrentIndex(int(self.p.getSetting('oldshuffle')))
-
- if random==PC_RANDOM_NO:
- # just follow our leader Monty.
- pass
- elif random==PC_RANDOM_SONG:
- # pick a random song! This depends on what repeat-mode we're in.
- if repeat==PC_REPEAT_NO or repeat==PC_REPEAT_PLAYLIST:
- # we don't repeat anything, so we can just let monty pick the
- # next random one!
- pass
- elif repeat==PC_REPEAT_ALBUM and self.curAlbumSongs:
- # pick random song from current album
- nextID=self.curAlbumSongs[randint(0,len(self.curAlbumSongs)-1)].getID()
- elif random==PC_RANDOM_ALBUM:
- # pick a random album! This means, we pick the first song of a random
- # album.
- if eofAlbum and (repeat==PC_REPEAT_PLAYLIST or repeat==PC_REPEAT_NO):
- # all first songs of an album
- albums=filter(lambda s: s.getAlbum() and s.getTrack()==1, monty.listPlaylist())
- nextID=albums[randint(0,len(albums)-1)].getID()
- else:
- # we're not at end of album, so we fetch the next id
- # We must do this, because albums are not necesseraly in the same order
- for i in xrange(len(self.curAlbumSongs)):
- if self.curAlbumSongs[i].getID()==song.getID():
- nextID=self.curAlbumSongs[i+1].getID()
- break
-
- if random==PC_RANDOM_QUEUE:
- # now here reset the mode to the previous, if needed
- if len(self.queuedSongs)==0:
- self.cmbShuffle.setCurrentIndex(int(self.p.getSetting('oldshuffle')))
+ def beforeSongChange(self, params):
+ nextID=None
+ song=monty.getCurrentSong()
+ # decide here what next song to play!
+ repeat=self.cmbRepeat.currentIndex()
+ random=self.cmbShuffle.currentIndex()
+ # is the current song the last of the album?
+ try:
+ eofAlbum=int(song.getTrack())==int(self.curAlbumSongs[-1].getTrack())
+ except:
+ eofAlbum=False
+ if repeat==PC_REPEAT_NO:
+ # no repeat, nothing to see here! Pass on!
+ pass
+ elif repeat==PC_REPEAT_SONG:
+ # we must repeat the previous song!
+ nextID=params['curSongID']
+ elif repeat==PC_REPEAT_ALBUM:
+ # check if we are at the last track, if it is, we must start a new!
+ if eofAlbum:
+ nextID=self.curAlbumSongs[0].getID()
+ elif repeat==PC_REPEAT_PLAYLIST:
+ # repeating the playlist is handled by monty itself;
+ # it is set in onCmbRepeatChanged.
+ pass
+
+ if repeat!=PC_REPEAT_SONG:
+ if random==PC_RANDOM_QUEUE:
+ # we must check here for the queue, because, if the queue is empty, we must
+ # choose the next one according the method!
+ # pick the next song from the queue. Simple as that :)
+ if len(self.queuedSongs):
+ # pick the front
+ nextID=self.queuedSongs.pop(0)
+ self._onQueueUpdate()
+ else:
+ # no songs anymore, so we must restore the old mode!
+ # We should never arrive here though ...
+ self.cmbShuffle.setCurrentIndex(int(self.p.getSetting('oldshuffle')))
+
+ if random==PC_RANDOM_NO:
+ # just follow our leader Monty.
+ pass
+ elif random==PC_RANDOM_SONG:
+ # pick a random song! This depends on what repeat-mode we're in.
+ if repeat==PC_REPEAT_NO or repeat==PC_REPEAT_PLAYLIST:
+ # we don't repeat anything, so we can just let monty pick the
+ # next random one!
+ pass
+ elif repeat==PC_REPEAT_ALBUM and self.curAlbumSongs:
+ # pick random song from current album
+ nextID=self.curAlbumSongs[randint(0,len(self.curAlbumSongs)-1)].getID()
+ elif random==PC_RANDOM_ALBUM:
+ # pick a random album! This means, we pick the first song of a random
+ # album.
+ if eofAlbum and (repeat==PC_REPEAT_PLAYLIST or repeat==PC_REPEAT_NO):
+ # all first songs of an album
+ albums=filter(lambda s: s.getAlbum() and s.getTrack()==1, monty.listPlaylist())
+ nextID=albums[randint(0,len(albums)-1)].getID()
+ else:
+ # we're not at end of album, so we fetch the next id
+ # We must do this, because albums are not necesseraly in the same order
+ for i in xrange(len(self.curAlbumSongs)):
+ if self.curAlbumSongs[i].getID()==song.getID():
+ nextID=self.curAlbumSongs[i+1].getID()
+ break
+
+ if random==PC_RANDOM_QUEUE:
+ # now here reset the mode to the previous, if needed
+ if len(self.queuedSongs)==0:
+ self.cmbShuffle.setCurrentIndex(int(self.p.getSetting('oldshuffle')))
- if nextID!=None:
- monty.play(nextID)
-
- def getCurAlbumSongs(self):
- return self.curAlbumSongs
- def findAlbumSongs(self):
- """This method looks for the songs in the album of current playing song."""
- song=monty.getCurrentSong()
- if self.curAlbumSongs and clSong.isSameAlbum(song, self.curAlbumSongs[0]):
- return
- self.curAlbumSongs=None
- if not song or not song.getAlbum():
- return
- self.curAlbumSongs=filter(lambda s: clSong.isSameAlbum(s, song), monty.listPlaylist())
- self.curAlbumSongs=sorted(self.curAlbumSongs, lambda l,r: numeric_compare(l.getTrack(), r.getTrack()))
+ if nextID!=None:
+ monty.play(nextID)
+
+ def getCurAlbumSongs(self):
+ return self.curAlbumSongs
+ def findAlbumSongs(self):
+ """This method looks for the songs in the album of current playing song."""
+ song=monty.getCurrentSong()
+ if self.curAlbumSongs and clSong.isSameAlbum(song, self.curAlbumSongs[0]):
+ return
+ self.curAlbumSongs=None
+ if not song or not song.getAlbum():
+ return
+ self.curAlbumSongs=filter(lambda s: clSong.isSameAlbum(s, song), monty.listPlaylist())
+ self.curAlbumSongs=sorted(self.curAlbumSongs, lambda l,r: numeric_compare(l.getTrack(), r.getTrack()))
- def onBtnPlayPauseClick(self):
- status=monty.getStatus()
- if status['state']=='play':
- monty.pause()
- self.p.extended("Toggling playback")
- elif status['state']=='stop':
- monty.play(None)
- self.p.extended("Pausing playback")
- else:
- monty.resume()
- def onBtnStopClick(self):
- monty.stop()
- self.p.extended("Stopping playback")
- def onBtnPreviousClick(self):
- monty.previous()
- self.p.extended("Playing previous")
- def onBtnNextClick(self):
- monty.next()
- self.p.extended("Playing next")
- def onTimeSliderChange(self):
- monty.seek(self.slrTime.value())
- def onVolumeSliderChange(self):
- v=self.slrVolume.value()
- monty.setVolume(v)
- if v<=1:
- mode='mute'
- else:
- mode=('0', 'min', 'med', 'max')[int(3*v/100)]
- self.svgVolume.load('gfx/stock_volume-%s.svg'%(mode))
-
- def onCmbRepeatChanged(self, newval):
- self.p.setSetting('repeat', newval)
- if newval==PC_REPEAT_PLAYLIST:
- monty.repeat(1)
- else:
- monty.repeat(0)
- def onCmbShuffleChanged(self, newval):
- if newval==PC_RANDOM_QUEUE:
- # must do some extra's if moving to queued-mode
- if len(self.queuedSongs):
- self.p.setSetting('oldshuffle', self.p.getSetting('shuffle'))
- else:
- self.cmbShuffle.setCurrentIndex(int(self.p.getSetting('shuffle')))
- return
- else:
- # clear the queued songs when switching
- self.queuedSongs=[]
- self._onQueueUpdate()
+ def onBtnPlayPauseClick(self):
+ status=monty.getStatus()
+ if status['state']=='play':
+ monty.pause()
+ self.p.extended("Toggling playback")
+ elif status['state']=='stop':
+ monty.play(None)
+ self.p.extended("Pausing playback")
+ else:
+ monty.resume()
+ def onBtnStopClick(self):
+ monty.stop()
+ self.p.extended("Stopping playback")
+ def onBtnPreviousClick(self):
+ monty.previous()
+ self.p.extended("Playing previous")
+ def onBtnNextClick(self):
+ monty.next()
+ self.p.extended("Playing next")
+ def onTimeSliderChange(self):
+ monty.seek(self.slrTime.value())
+ def onVolumeSliderChange(self):
+ v=self.slrVolume.value()
+ monty.setVolume(v)
+ if v<=1:
+ mode='mute'
+ else:
+ mode=('0', 'min', 'med', 'max')[int(3*v/100)]
+ self.svgVolume.load('gfx/stock_volume-%s.svg'%(mode))
+
+ def onCmbRepeatChanged(self, newval):
+ self.p.setSetting('repeat', newval)
+ if newval==PC_REPEAT_PLAYLIST:
+ monty.repeat(1)
+ else:
+ monty.repeat(0)
+ def onCmbShuffleChanged(self, newval):
+ if newval==PC_RANDOM_QUEUE:
+ # must do some extra's if moving to queued-mode
+ if len(self.queuedSongs):
+ self.p.setSetting('oldshuffle', self.p.getSetting('shuffle'))
+ else:
+ self.cmbShuffle.setCurrentIndex(int(self.p.getSetting('shuffle')))
+ return
+ else:
+ # clear the queued songs when switching
+ self.queuedSongs=[]
+ self._onQueueUpdate()
- self.p.setSetting('shuffle', newval)
- if newval==PC_RANDOM_SONG:
- monty.random(1)
- else:
- monty.random(0)
+ self.p.setSetting('shuffle', newval)
+ if newval==PC_RANDOM_SONG:
+ monty.random(1)
+ else:
+ monty.random(0)
- # save and load the queue
- def saveQueue(self):
- # save the ids as a list of space-separated numbers
- self.p.extended("saving queue")
- self.p.setSetting('queue', str(self.queuedSongs)[1:-1].replace(',', ''))
- def loadQueue(self):
- # just read all the numbers!
- self.p.extended("loading queue")
- self.queuedSongs=[]
- i=0
- ids=self.p.getSetting('queue').split(' ')
- for id in ids:
- try:
- self.queuedSongs.append(int(id))
- except:
- pass
- self._onQueueUpdate()
+ # save and load the queue
+ def saveQueue(self):
+ # save the ids as a list of space-separated numbers
+ self.p.extended("saving queue")
+ self.p.setSetting('queue', str(self.queuedSongs)[1:-1].replace(',', ''))
+ def loadQueue(self):
+ # just read all the numbers!
+ self.p.extended("loading queue")
+ self.queuedSongs=[]
+ i=0
+ ids=self.p.getSetting('queue').split(' ')
+ for id in ids:
+ try:
+ self.queuedSongs.append(int(id))
+ except:
+ pass
+ self._onQueueUpdate()
class pluginPlayControl(Plugin):
- o=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'PlayControl')
- self.addMontyListener('onStateChange', self.onStateChange)
- self.addMontyListener('beforeSongChange', self.beforeSongChange)
- self.addMontyListener('onSongChange', self.onSongChange)
- self.addMontyListener('onVolumeChange', self.onVolumeChange)
- self.addMontyListener('onReady', self.onStateChange)
- self.addMontyListener('onDisconnect', self.onDisconnect)
- self.addMontyListener('onTimeChange', self.onTimeChange)
- def _load(self):
- self.o=wgPlayControl(self, None)
- self.o.loadQueue()
- def _unload(self):
- self.o.saveQueue()
- self.o=None
- def getInfo(self):
- return "Have total control over the playing!"
-
- def addSongsToQueue(self, songs):
- return self.o.addSongsToQueue(songs)
-
- def onStateChange(self, params):
- self.o.onStateChange(params)
- def beforeSongChange(self, params):
- self.o.beforeSongChange(params)
- def onSongChange(self, params):
- self.o.onSongChange(params)
- def onVolumeChange(self, params):
- self.o.onVolumeChange(params)
- def onDisconnect(self, params):
- self.o.onDisconnect(params)
- def onTimeChange(self, params):
- self.o.onTimeChange(params)
-
- def _getDockWidget(self):
- return self._createDock(self.o)
+ o=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'PlayControl')
+ self.addMontyListener('onStateChange', self.onStateChange)
+ self.addMontyListener('beforeSongChange', self.beforeSongChange)
+ self.addMontyListener('onSongChange', self.onSongChange)
+ self.addMontyListener('onVolumeChange', self.onVolumeChange)
+ self.addMontyListener('onReady', self.onStateChange)
+ self.addMontyListener('onDisconnect', self.onDisconnect)
+ self.addMontyListener('onTimeChange', self.onTimeChange)
+ def _load(self):
+ self.o=wgPlayControl(self, None)
+ self.o.loadQueue()
+ def _unload(self):
+ self.o.saveQueue()
+ self.o=None
+ def getInfo(self):
+ return "Have total control over the playing!"
+
+ def addSongsToQueue(self, songs):
+ return self.o.addSongsToQueue(songs)
+
+ def onStateChange(self, params):
+ self.o.onStateChange(params)
+ def beforeSongChange(self, params):
+ self.o.beforeSongChange(params)
+ def onSongChange(self, params):
+ self.o.onSongChange(params)
+ def onVolumeChange(self, params):
+ self.o.onVolumeChange(params)
+ def onDisconnect(self, params):
+ self.o.onDisconnect(params)
+ def onTimeChange(self, params):
+ self.o.onTimeChange(params)
+
+ def _getDockWidget(self):
+ return self._createDock(self.o)
diff --git a/plugins/Playlist.py b/plugins/Playlist.py
index 3cb812b..811441d 100644
--- a/plugins/Playlist.py
+++ b/plugins/Playlist.py
@@ -7,85 +7,85 @@ from wgSongList import clrRowSel
import plugins
PLAYLIST_MODES_DEFAULT='$artist\n'\
- '$artist/$date - $album\n'\
- '$artist - $album\n'\
- '$album ($artist)\n'\
- '$genre\n'\
- '$genre/$artist\n'\
- '$genre/$artist - $album\n'
+ '$artist/$date - $album\n'\
+ '$artist - $album\n'\
+ '$album ($artist)\n'\
+ '$genre\n'\
+ '$genre/$artist\n'\
+ '$genre/$artist - $album\n'
# Dependencies:
# playcontrol
class pluginPlaylist(Plugin):
- o=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'Playlist')
- self.addMontyListener('onSongChange', self.onSongChange)
- def _load(self):
- self.o=Playlist(self.winMain, self, ['artist', 'title', 'track', 'album'], 'Playlist'
- , self.onDoubleClick, self.onKeyPress, self.getSetting('modes').split('\n'))
- def _unload(self):
- self.o=None
- def getPlaylist(self):
- return self.o
- def getInfo(self):
- return "The playlist showing the songs that will be played."
-
- def getList(self):
- return self.o
+ o=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'Playlist')
+ self.addMontyListener('onSongChange', self.onSongChange)
+ def _load(self):
+ self.o=Playlist(self.winMain, self, ['artist', 'title', 'track', 'album'], 'Playlist'
+ , self.onDoubleClick, self.onKeyPress, self.getSetting('modes').split('\n'))
+ def _unload(self):
+ self.o=None
+ def getPlaylist(self):
+ return self.o
+ def getInfo(self):
+ return "The playlist showing the songs that will be played."
+
+ def getList(self):
+ return self.o
- def _getDockWidget(self):
- return self._createDock(self.o)
+ def _getDockWidget(self):
+ return self._createDock(self.o)
- def onDoubleClick(self):
- monty.play(self.o.getSelItemID())
+ def onDoubleClick(self):
+ monty.play(self.o.getSelItemID())
- def onKeyPress(self, event):
- if event.matches(QtGui.QKeySequence.Delete):
- # remove selection from playlist using DELETE-key
- ids=self.o.selectedIds()
- self.setStatus('Deleting '+str(len(ids))+' songs from playlist ...')
- doEvents()
+ def onKeyPress(self, event):
+ if event.matches(QtGui.QKeySequence.Delete):
+ # remove selection from playlist using DELETE-key
+ ids=self.o.selectedIds()
+ self.setStatus('Deleting '+str(len(ids))+' songs from playlist ...')
+ doEvents()
- monty.deleteFromPlaylist(ids)
+ monty.deleteFromPlaylist(ids)
- self.setStatus('')
- doEvents()
+ self.setStatus('')
+ doEvents()
- self.getWinMain().fillPlaylist()
- elif event.key()==QtCore.Qt.Key_Q:
- # queue selected songs
- # Hoho, this one needs the playcontrol plugin!
- plugins.getPlugin('playcontrol').addSongsToQueue(self.o.selectedIds())
- return QtGui.QWidget.keyPressEvent(self.o, event)
-
- def onSongChange(self, params):
- lst=self.o
- lst.colorID(int(params['newSongID']), clrRowSel)
+ self.getWinMain().fillPlaylist()
+ elif event.key()==QtCore.Qt.Key_Q:
+ # queue selected songs
+ # Hoho, this one needs the playcontrol plugin!
+ plugins.getPlugin('playcontrol').addSongsToQueue(self.o.selectedIds())
+ return QtGui.QWidget.keyPressEvent(self.o, event)
+
+ def onSongChange(self, params):
+ lst=self.o
+ lst.colorID(int(params['newSongID']), clrRowSel)
- if params['newSongID']!=-1:
- lst.ensureVisible(params['newSongID'])
+ if params['newSongID']!=-1:
+ lst.ensureVisible(params['newSongID'])
- _rowColorModifier=0
- _rowColorAdder=1
- def timerEvent(self, event):
- curSong=monty.getCurrentSong()
- if curSong:
- lst=self.lstPlaylist
- # color current playing song
- lst.colorID(curSong.getID(),
- QtGui.QColor(140-self._rowColorModifier*5,140-self._rowColorModifier*5,180))
+ _rowColorModifier=0
+ _rowColorAdder=1
+ def timerEvent(self, event):
+ curSong=monty.getCurrentSong()
+ if curSong:
+ lst=self.lstPlaylist
+ # color current playing song
+ lst.colorID(curSong.getID(),
+ QtGui.QColor(140-self._rowColorModifier*5,140-self._rowColorModifier*5,180))
- # make sure color changes nicely over time
- self._rowColorModifier=self._rowColorModifier+self._rowColorAdder
- if abs(self._rowColorModifier)>4:
- self._rowColorAdder=-1*self._rowColorAdder
+ # make sure color changes nicely over time
+ self._rowColorModifier=self._rowColorModifier+self._rowColorAdder
+ if abs(self._rowColorModifier)>4:
+ self._rowColorAdder=-1*self._rowColorAdder
- def _getSettings(self):
- modes=QtGui.QTextEdit()
- modes.insertPlainText(self.getSetting('modes'))
- return [
- ['modes', 'Modes', 'Sets the available modes.', modes],
- ]
- def afterSaveSettings(self):
- self.o.setModes(self.getSetting('modes').split('\n'))
+ def _getSettings(self):
+ modes=QtGui.QTextEdit()
+ modes.insertPlainText(self.getSetting('modes'))
+ return [
+ ['modes', 'Modes', 'Sets the available modes.', modes],
+ ]
+ def afterSaveSettings(self):
+ self.o.setModes(self.getSetting('modes').split('\n'))
diff --git a/plugins/Scrobbler.py b/plugins/Scrobbler.py
index bb2321f..ced3964 100644
--- a/plugins/Scrobbler.py
+++ b/plugins/Scrobbler.py
@@ -9,90 +9,90 @@ SCROBBLER_PASSWORD_DEFAULT=''
# TODO cached failed submissions
class pluginScrobbler(Plugin):
- submitted=False
- time=None
- loggedIn=False
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'Scrobbler')
- self.addMontyListener('onSongChange', self.onSongChange)
- self.addMontyListener('onTimeChange', self.onTimeChange)
-
- def _load(self):
- self._login()
-
- def _login(self):
- self.submitting=False
- self.loggedIn=False
- if self._username():
- self.normal("logging in %s"%(self._username()))
- try:
- login(self._username(), self._password())
- self.loggedIn=True
- except Exception, e:
- self.normal("failed to login: "+str(e))
- else:
- self.debug("no username provided, not logging in")
-
- def _username(self):
- return self.getSetting('username')
- def _password(self):
- return self.getSetting('password')
- def onTimeChange(self, params):
- if self.submitted==False and self.loggedIn:
- song=monty.getCurrentSong()
- if song.getTag('time')>30:
- if int(params['newTime'])>int(song.getTag('time'))/2 \
- or int(params['newTime'])>240:
- if not self.time:
- self.onSongChange(None)
- self.normal("submitting song")
- submit(song.getArtist(), song.getTitle(), self.time, 'P', '', song.getTag('time'), song.getAlbum(), song.getTrack(), False)
- self.debug("flushing ...")
- try:
- flush()
- self.submitted=True
- except Exception, e:
- self.important("failed to submit song1 - %s"%(e))
- self.extended("Logging in ...")
- self._login()
- try:
- flush()
- self.submitted=True
- except Exception, e:
- self.important("failed to submit song2 - %s"%(e))
- if self.submitted:
- self.debug("flushed")
- else:
- self.important("failed to submit the song!")
-
- def onSongChange(self, params):
- if self.loggedIn==False:
- return
- self.time=int(time.mktime(datetime.utcnow().timetuple()))
- self.submitted=False
- song=monty.getCurrentSong()
- # for loop: in case of SessionError, we need to retry once ...
- for i in [0, 1]:
- try:
- self.extended("submitting now playing")
- now_playing(song.getArtist(), song.getTitle(), song.getAlbum(), "", song.getTrack())
- self.debug("submitted")
- break
- except AuthError, e:
- self.important("failed to submit playing song - %s"%(e))
- break
- except SessionError, e:
- self.normal("session error")
- self._login()
- def _getSettings(self):
- return [
- ['username', 'Username', 'Username to submit to last.fm.', QtGui.QLineEdit(self._username())],
- ['password', 'Password', 'Password to user to submit. Note that the password is stored *unencrypted* to file.', QtGui.QLineEdit(self._password())],
- ]
- def afterSaveSettings(self):
- self._login()
- def getInfo(self):
- return "Submits tracks to last.fm"
+ submitted=False
+ time=None
+ loggedIn=False
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'Scrobbler')
+ self.addMontyListener('onSongChange', self.onSongChange)
+ self.addMontyListener('onTimeChange', self.onTimeChange)
+
+ def _load(self):
+ self._login()
+
+ def _login(self):
+ self.submitting=False
+ self.loggedIn=False
+ if self._username():
+ self.normal("logging in %s"%(self._username()))
+ try:
+ login(self._username(), self._password())
+ self.loggedIn=True
+ except Exception, e:
+ self.normal("failed to login: "+str(e))
+ else:
+ self.debug("no username provided, not logging in")
+
+ def _username(self):
+ return self.getSetting('username')
+ def _password(self):
+ return self.getSetting('password')
+ def onTimeChange(self, params):
+ if self.submitted==False and self.loggedIn:
+ song=monty.getCurrentSong()
+ if song.getTag('time')>30:
+ if int(params['newTime'])>int(song.getTag('time'))/2 \
+ or int(params['newTime'])>240:
+ if not self.time:
+ self.onSongChange(None)
+ self.normal("submitting song")
+ submit(song.getArtist(), song.getTitle(), self.time, 'P', '', song.getTag('time'), song.getAlbum(), song.getTrack(), False)
+ self.debug("flushing ...")
+ try:
+ flush()
+ self.submitted=True
+ except Exception, e:
+ self.important("failed to submit song1 - %s"%(e))
+ self.extended("Logging in ...")
+ self._login()
+ try:
+ flush()
+ self.submitted=True
+ except Exception, e:
+ self.important("failed to submit song2 - %s"%(e))
+ if self.submitted:
+ self.debug("flushed")
+ else:
+ self.important("failed to submit the song!")
+
+ def onSongChange(self, params):
+ if self.loggedIn==False:
+ return
+ self.time=int(time.mktime(datetime.utcnow().timetuple()))
+ self.submitted=False
+ song=monty.getCurrentSong()
+ # for loop: in case of SessionError, we need to retry once ...
+ for i in [0, 1]:
+ try:
+ self.extended("submitting now playing")
+ now_playing(song.getArtist(), song.getTitle(), song.getAlbum(), "", song.getTrack())
+ self.debug("submitted")
+ break
+ except AuthError, e:
+ self.important("failed to submit playing song - %s"%(e))
+ break
+ except SessionError, e:
+ self.normal("session error")
+ self._login()
+ def _getSettings(self):
+ return [
+ ['username', 'Username', 'Username to submit to last.fm.', QtGui.QLineEdit(self._username())],
+ ['password', 'Password', 'Password to user to submit. Note that the password is stored *unencrypted* to file.', QtGui.QLineEdit(self._password())],
+ ]
+ def afterSaveSettings(self):
+ self._login()
+ def getInfo(self):
+ return "Submits tracks to last.fm"
@@ -110,293 +110,293 @@ from datetime import datetime, timedelta
from md5 import md5
SESSION_ID = None
-POST_URL = None
-NOW_URL = None
+POST_URL = None
+NOW_URL = None
HARD_FAILS = 0
-LAST_HS = None # Last handshake time
-HS_DELAY = 0 # wait this many seconds until next handshake
+LAST_HS = None # Last handshake time
+HS_DELAY = 0 # wait this many seconds until next handshake
SUBMIT_CACHE = []
-MAX_CACHE = 5 # keep only this many songs in the cache
+MAX_CACHE = 5 # keep only this many songs in the cache
PROTOCOL_VERSION = '1.2'
class BackendError(Exception):
- "Raised if the AS backend does something funny"
- pass
+ "Raised if the AS backend does something funny"
+ pass
class AuthError(Exception):
- "Raised on authencitation errors"
- pass
+ "Raised on authencitation errors"
+ pass
class PostError(Exception):
- "Raised if something goes wrong when posting data to AS"
- pass
+ "Raised if something goes wrong when posting data to AS"
+ pass
class SessionError(Exception):
- "Raised when problems with the session exist"
- pass
+ "Raised when problems with the session exist"
+ pass
class ProtocolError(Exception):
- "Raised on general Protocol errors"
- pass
+ "Raised on general Protocol errors"
+ pass
def login( user, password, client=('tst', '1.0') ):
- """Authencitate with AS (The Handshake)
-
- @param user: The username
- @param password: The password
- @param client: Client information (see http://www.audioscrobbler.net/development/protocol/ for more info)
- @type client: Tuple: (client-id, client-version)"""
- global LAST_HS, SESSION_ID, POST_URL, NOW_URL, HARD_FAILS, HS_DELAY, PROTOCOL_VERSION
-
- if LAST_HS is not None:
- next_allowed_hs = LAST_HS + timedelta(seconds=HS_DELAY)
- if datetime.now() < next_allowed_hs:
- delta = next_allowed_hs - datetime.now()
- raise ProtocolError("""Please wait another %d seconds until next handshake (login) attempt.""" % delta.seconds)
-
- LAST_HS = datetime.now()
-
- tstamp = int(mktime(datetime.now().timetuple()))
- url = "http://post.audioscrobbler.com/"
- pwhash = md5(password).hexdigest()
- token = md5( "%s%d" % (pwhash, int(tstamp))).hexdigest()
- values = {
- 'hs': 'true',
- 'p' : PROTOCOL_VERSION,
- 'c': client[0],
- 'v': client[1],
- 'u': user,
- 't': tstamp,
- 'a': token
- }
- data = urllib.urlencode(values)
- req = urllib2.Request("%s?%s" % (url, data) )
- response = urllib2.urlopen(req)
- result = response.read()
- lines = result.split('\n')
-
-
- if lines[0] == 'BADAUTH':
- raise AuthError('Bad username/password')
-
- elif lines[0] == 'BANNED':
- raise Exception('''This client-version was banned by Audioscrobbler. Please contact the author of this module!''')
-
- elif lines[0] == 'BADTIME':
- raise ValueError('''Your system time is out of sync with Audioscrobbler.Consider using an NTP-client to keep you system time in sync.''')
-
- elif lines[0].startswith('FAILED'):
- handle_hard_error()
- raise BackendError("Authencitation with AS failed. Reason: %s" %
- lines[0])
-
- elif lines[0] == 'OK':
- # wooooooohooooooo. We made it!
- SESSION_ID = lines[1]
- NOW_URL = lines[2]
- POST_URL = lines[3]
- HARD_FAILS = 0
-
- else:
- # some hard error
- handle_hard_error()
+ """Authencitate with AS (The Handshake)
+
+ @param user: The username
+ @param password: The password
+ @param client: Client information (see http://www.audioscrobbler.net/development/protocol/ for more info)
+ @type client: Tuple: (client-id, client-version)"""
+ global LAST_HS, SESSION_ID, POST_URL, NOW_URL, HARD_FAILS, HS_DELAY, PROTOCOL_VERSION
+
+ if LAST_HS is not None:
+ next_allowed_hs = LAST_HS + timedelta(seconds=HS_DELAY)
+ if datetime.now() < next_allowed_hs:
+ delta = next_allowed_hs - datetime.now()
+ raise ProtocolError("""Please wait another %d seconds until next handshake (login) attempt.""" % delta.seconds)
+
+ LAST_HS = datetime.now()
+
+ tstamp = int(mktime(datetime.now().timetuple()))
+ url = "http://post.audioscrobbler.com/"
+ pwhash = md5(password).hexdigest()
+ token = md5( "%s%d" % (pwhash, int(tstamp))).hexdigest()
+ values = {
+ 'hs': 'true',
+ 'p' : PROTOCOL_VERSION,
+ 'c': client[0],
+ 'v': client[1],
+ 'u': user,
+ 't': tstamp,
+ 'a': token
+ }
+ data = urllib.urlencode(values)
+ req = urllib2.Request("%s?%s" % (url, data) )
+ response = urllib2.urlopen(req)
+ result = response.read()
+ lines = result.split('\n')
+
+
+ if lines[0] == 'BADAUTH':
+ raise AuthError('Bad username/password')
+
+ elif lines[0] == 'BANNED':
+ raise Exception('''This client-version was banned by Audioscrobbler. Please contact the author of this module!''')
+
+ elif lines[0] == 'BADTIME':
+ raise ValueError('''Your system time is out of sync with Audioscrobbler.Consider using an NTP-client to keep you system time in sync.''')
+
+ elif lines[0].startswith('FAILED'):
+ handle_hard_error()
+ raise BackendError("Authencitation with AS failed. Reason: %s" %
+ lines[0])
+
+ elif lines[0] == 'OK':
+ # wooooooohooooooo. We made it!
+ SESSION_ID = lines[1]
+ NOW_URL = lines[2]
+ POST_URL = lines[3]
+ HARD_FAILS = 0
+
+ else:
+ # some hard error
+ handle_hard_error()
def handle_hard_error():
- "Handles hard errors."
- global SESSION_ID, HARD_FAILS, HS_DELAY
+ "Handles hard errors."
+ global SESSION_ID, HARD_FAILS, HS_DELAY
- if HS_DELAY == 0:
- HS_DELAY = 60
- elif HS_DELAY < 120*60:
- HS_DELAY *= 2
- if HS_DELAY > 120*60:
- HS_DELAY = 120*60
+ if HS_DELAY == 0:
+ HS_DELAY = 60
+ elif HS_DELAY < 120*60:
+ HS_DELAY *= 2
+ if HS_DELAY > 120*60:
+ HS_DELAY = 120*60
- HARD_FAILS += 1
- if HARD_FAILS == 3:
- SESSION_ID = None
+ HARD_FAILS += 1
+ if HARD_FAILS == 3:
+ SESSION_ID = None
def now_playing( artist, track, album="", length="", trackno="", mbid="" ):
- """Tells audioscrobbler what is currently running in your player. This won't
- affect the user-profile on last.fm. To do submissions, use the "submit"
- method
-
- @param artist: The artist name
- @param track: The track name
- @param album: The album name
- @param length: The song length in seconds
- @param trackno: The track number
- @param mbid: The MusicBrainz Track ID
- @return: True on success, False on failure"""
-
- global SESSION_ID, NOW_URL
-
- if SESSION_ID is None:
- raise AuthError("Please 'login()' first. (No session available)")
-
- if POST_URL is None:
- raise PostError("Unable to post data. Post URL was empty!")
-
- if length != "" and type(length) != type(1):
- raise TypeError("length should be of type int")
-
- if trackno != "" and type(trackno) != type(1):
- raise TypeError("trackno should be of type int")
-
- values = {'s': SESSION_ID,
- 'a': unicode(artist).encode('utf-8'),
- 't': unicode(track).encode('utf-8'),
- 'b': unicode(album).encode('utf-8'),
- 'l': length,
- 'n': trackno,
- 'm': mbid }
-
- data = urllib.urlencode(values)
- req = urllib2.Request(NOW_URL, data)
- response = urllib2.urlopen(req)
- result = response.read()
-
- if result.strip() == "OK":
- return True
- elif result.strip() == "BADSESSION" :
- raise SessionError('Invalid session')
- else:
- return False
+ """Tells audioscrobbler what is currently running in your player. This won't
+ affect the user-profile on last.fm. To do submissions, use the "submit"
+ method
+
+ @param artist: The artist name
+ @param track: The track name
+ @param album: The album name
+ @param length: The song length in seconds
+ @param trackno: The track number
+ @param mbid: The MusicBrainz Track ID
+ @return: True on success, False on failure"""
+
+ global SESSION_ID, NOW_URL
+
+ if SESSION_ID is None:
+ raise AuthError("Please 'login()' first. (No session available)")
+
+ if POST_URL is None:
+ raise PostError("Unable to post data. Post URL was empty!")
+
+ if length != "" and type(length) != type(1):
+ raise TypeError("length should be of type int")
+
+ if trackno != "" and type(trackno) != type(1):
+ raise TypeError("trackno should be of type int")
+
+ values = {'s': SESSION_ID,
+ 'a': unicode(artist).encode('utf-8'),
+ 't': unicode(track).encode('utf-8'),
+ 'b': unicode(album).encode('utf-8'),
+ 'l': length,
+ 'n': trackno,
+ 'm': mbid }
+
+ data = urllib.urlencode(values)
+ req = urllib2.Request(NOW_URL, data)
+ response = urllib2.urlopen(req)
+ result = response.read()
+
+ if result.strip() == "OK":
+ return True
+ elif result.strip() == "BADSESSION" :
+ raise SessionError('Invalid session')
+ else:
+ return False
def submit(artist, track, time, source='P', rating="", length="", album="",
- trackno="", mbid="", autoflush=False):
- """Append a song to the submission cache. Use 'flush()' to send the cache to
- AS. You can also set "autoflush" to True.
-
- From the Audioscrobbler protocol docs:
- ---------------------------------------------------------------------------
-
- The client should monitor the user's interaction with the music playing
- service to whatever extent the service allows. In order to qualify for
- submission all of the following criteria must be met:
-
- 1. The track must be submitted once it has finished playing. Whether it has
- finished playing naturally or has been manually stopped by the user is
- irrelevant.
- 2. The track must have been played for a duration of at least 240 seconds or
- half the track's total length, whichever comes first. Skipping or pausing
- the track is irrelevant as long as the appropriate amount has been played.
- 3. The total playback time for the track must be more than 30 seconds. Do
- not submit tracks shorter than this.
- 4. Unless the client has been specially configured, it should not attempt to
- interpret filename information to obtain metadata instead of tags (ID3,
- etc).
-
- @param artist: Artist name
- @param track: Track name
- @param time: Time the track *started* playing in the UTC timezone (see
- datetime.utcnow()).
-
- Example: int(time.mktime(datetime.utcnow()))
- @param source: Source of the track. One of:
- 'P': Chosen by the user
- 'R': Non-personalised broadcast (e.g. Shoutcast, BBC Radio 1)
- 'E': Personalised recommendation except Last.fm (e.g.
- Pandora, Launchcast)
- 'L': Last.fm (any mode). In this case, the 5-digit Last.fm
- recommendation key must be appended to this source ID to
- prove the validity of the submission (for example,
- "L1b48a").
- 'U': Source unknown
- @param rating: The rating of the song. One of:
- 'L': Love (on any mode if the user has manually loved the
- track)
- 'B': Ban (only if source=L)
- 'S': Skip (only if source=L)
- '': Not applicable
- @param length: The song length in seconds
- @param album: The album name
- @param trackno:The track number
- @param mbid: MusicBrainz Track ID
- @param autoflush: Automatically flush the cache to AS?
- """
-
- global SUBMIT_CACHE, MAX_CACHE
-
- source = source.upper()
- rating = rating.upper()
-
- if source == 'L' and (rating == 'B' or rating == 'S'):
- raise ProtocolError("""You can only use rating 'B' or 'S' on source 'L'.See the docs!""")
-
- if source == 'P' and length == '':
- raise ProtocolError("""Song length must be specified when using 'P' as source!""")
-
- if type(time) != type(1):
- raise ValueError("""The time parameter must be of type int (unix timestamp). Instead it was %s""" % time)
-
- SUBMIT_CACHE.append(
- { 'a': unicode(artist).encode('utf-8'),
- 't': unicode(track).encode('utf-8'),
- 'i': time,
- 'o': source,
- 'r': rating,
- 'l': length,
- 'b': unicode(album).encode('utf-8'),
- 'n': trackno,
- 'm': mbid
- }
- )
-
- if autoflush or len(SUBMIT_CACHE) >= MAX_CACHE:
- flush()
+ trackno="", mbid="", autoflush=False):
+ """Append a song to the submission cache. Use 'flush()' to send the cache to
+ AS. You can also set "autoflush" to True.
+
+ From the Audioscrobbler protocol docs:
+ ---------------------------------------------------------------------------
+
+ The client should monitor the user's interaction with the music playing
+ service to whatever extent the service allows. In order to qualify for
+ submission all of the following criteria must be met:
+
+ 1. The track must be submitted once it has finished playing. Whether it has
+ finished playing naturally or has been manually stopped by the user is
+ irrelevant.
+ 2. The track must have been played for a duration of at least 240 seconds or
+ half the track's total length, whichever comes first. Skipping or pausing
+ the track is irrelevant as long as the appropriate amount has been played.
+ 3. The total playback time for the track must be more than 30 seconds. Do
+ not submit tracks shorter than this.
+ 4. Unless the client has been specially configured, it should not attempt to
+ interpret filename information to obtain metadata instead of tags (ID3,
+ etc).
+
+ @param artist: Artist name
+ @param track: Track name
+ @param time: Time the track *started* playing in the UTC timezone (see
+ datetime.utcnow()).
+
+ Example: int(time.mktime(datetime.utcnow()))
+ @param source: Source of the track. One of:
+ 'P': Chosen by the user
+ 'R': Non-personalised broadcast (e.g. Shoutcast, BBC Radio 1)
+ 'E': Personalised recommendation except Last.fm (e.g.
+ Pandora, Launchcast)
+ 'L': Last.fm (any mode). In this case, the 5-digit Last.fm
+ recommendation key must be appended to this source ID to
+ prove the validity of the submission (for example,
+ "L1b48a").
+ 'U': Source unknown
+ @param rating: The rating of the song. One of:
+ 'L': Love (on any mode if the user has manually loved the
+ track)
+ 'B': Ban (only if source=L)
+ 'S': Skip (only if source=L)
+ '': Not applicable
+ @param length: The song length in seconds
+ @param album: The album name
+ @param trackno:The track number
+ @param mbid: MusicBrainz Track ID
+ @param autoflush: Automatically flush the cache to AS?
+ """
+
+ global SUBMIT_CACHE, MAX_CACHE
+
+ source = source.upper()
+ rating = rating.upper()
+
+ if source == 'L' and (rating == 'B' or rating == 'S'):
+ raise ProtocolError("""You can only use rating 'B' or 'S' on source 'L'.See the docs!""")
+
+ if source == 'P' and length == '':
+ raise ProtocolError("""Song length must be specified when using 'P' as source!""")
+
+ if type(time) != type(1):
+ raise ValueError("""The time parameter must be of type int (unix timestamp). Instead it was %s""" % time)
+
+ SUBMIT_CACHE.append(
+ { 'a': unicode(artist).encode('utf-8'),
+ 't': unicode(track).encode('utf-8'),
+ 'i': time,
+ 'o': source,
+ 'r': rating,
+ 'l': length,
+ 'b': unicode(album).encode('utf-8'),
+ 'n': trackno,
+ 'm': mbid
+ }
+ )
+
+ if autoflush or len(SUBMIT_CACHE) >= MAX_CACHE:
+ flush()
def flush():
- "Sends the cached songs to AS."
- global SUBMIT_CACHE
-
- values = {}
-
- for i, item in enumerate(SUBMIT_CACHE):
- for key in item:
- values[key + "[%d]" % i] = item[key]
-
- values['s'] = SESSION_ID
-
- data = urllib.urlencode(values)
- req = urllib2.Request(POST_URL, data)
- response = urllib2.urlopen(req)
- result = response.read()
- lines = result.split('\n')
-
- if lines[0] == "OK":
- SUBMIT_CACHE = []
- return True
- elif lines[0] == "BADSESSION" :
- raise SessionError('Invalid session')
- elif lines[0].startswith('FAILED'):
- handle_hard_error()
- raise BackendError("Authencitation with AS failed. Reason: %s" %
- lines[0])
- else:
- # some hard error
- handle_hard_error()
- return False
+ "Sends the cached songs to AS."
+ global SUBMIT_CACHE
+
+ values = {}
+
+ for i, item in enumerate(SUBMIT_CACHE):
+ for key in item:
+ values[key + "[%d]" % i] = item[key]
+
+ values['s'] = SESSION_ID
+
+ data = urllib.urlencode(values)
+ req = urllib2.Request(POST_URL, data)
+ response = urllib2.urlopen(req)
+ result = response.read()
+ lines = result.split('\n')
+
+ if lines[0] == "OK":
+ SUBMIT_CACHE = []
+ return True
+ elif lines[0] == "BADSESSION" :
+ raise SessionError('Invalid session')
+ elif lines[0].startswith('FAILED'):
+ handle_hard_error()
+ raise BackendError("Authencitation with AS failed. Reason: %s" %
+ lines[0])
+ else:
+ # some hard error
+ handle_hard_error()
+ return False
if __name__ == "__main__":
- login( 'user', 'password' )
- submit(
- 'De/Vision',
- 'Scars',
- 1192374052,
- source='P',
- length=3*60+44
- )
- submit(
- 'Spineshank',
- 'Beginning of the End',
- 1192374052+(5*60),
- source='P',
- length=3*60+32
- )
- submit(
- 'Dry Cell',
- 'Body Crumbles',
- 1192374052+(10*60),
- source='P',
- length=3*60+3
- )
- print flush()
+ login( 'user', 'password' )
+ submit(
+ 'De/Vision',
+ 'Scars',
+ 1192374052,
+ source='P',
+ length=3*60+44
+ )
+ submit(
+ 'Spineshank',
+ 'Beginning of the End',
+ 1192374052+(5*60),
+ source='P',
+ length=3*60+32
+ )
+ submit(
+ 'Dry Cell',
+ 'Body Crumbles',
+ 1192374052+(10*60),
+ source='P',
+ length=3*60+3
+ )
+ print flush()
diff --git a/plugins/Shortcuts.py b/plugins/Shortcuts.py
index baa462f..e76078f 100644
--- a/plugins/Shortcuts.py
+++ b/plugins/Shortcuts.py
@@ -6,81 +6,81 @@ from misc import *
import format
class pluginShortcuts(Plugin):
- keys=None
- col=None
- actionPrefix=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'Shortcuts')
+ keys=None
+ col=None
+ actionPrefix=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'Shortcuts')
- def _load(self):
- winMain=self.getWinMain()
- # Note people wanting to implement global shortcuts in KDE4 (and sniffing through
- # this code): one needs to have a KApplication running, else shortcuts will fail
- self.col=kdeui.KActionCollection(None)
- self.col.addAssociatedWidget(winMain)
- self.keys=[
- ['toggleplay', QtCore.Qt.META+QtCore.Qt.Key_Home, self.togglePlay],
- ['volumeup', QtCore.Qt.META+QtCore.Qt.Key_PageDown, lambda b: monty.setVolume(monty.getVolume()-5)],
- ['volumedown', QtCore.Qt.META+QtCore.Qt.Key_PageUp, lambda b: monty.setVolume(monty.getVolume()+5)],
- ['playnext', QtCore.Qt.META+QtCore.Qt.Key_Right, monty.next],
- ['playprevious', QtCore.Qt.META+QtCore.Qt.Key_Left, monty.previous],
- ['showosd', QtCore.Qt.META+QtCore.Qt.Key_O, self.showOSD],
- ['togglewin', QtCore.Qt.META+QtCore.Qt.Key_P, self.toggleWinMain],
- ]
- # Note: don't use any non-alphanumerics in the prefix, as else it won't work
- self.actionPrefix="shortcuts"
-
- for entry in self.keys:
- name=entry[0]
- key=entry[1]
- callback=entry[2]
-
- self.debug("%s - %s"%(name, QtGui.QKeySequence(key).toString()))
-
- action=kdeui.KAction(winMain) # winMain needed
- action.setText(name)
- action.setObjectName(name)
- action.setGlobalShortcut(kdeui.KShortcut(key))
- QtCore.QObject.connect(action, QtCore.SIGNAL('triggered(bool)'), callback)
- self.col.addAction("%s%s"%(self.actionPrefix, action.objectName()), action)
- def _unload(self):
- actions=self.col.actions()
- for action in actions:
- try:
- if action.objectName()[0:len(self.actionPrefix)]==self.actionPrefix:
- self.debug("removing %s"%(action.objectName()))
- self.col.removeAction(action)
- except Exception, e:
- self.important(str(e))
-
- def getInfo(self):
- return "Shortcuts for mpd."
+ def _load(self):
+ winMain=self.getWinMain()
+ # Note people wanting to implement global shortcuts in KDE4 (and sniffing through
+ # this code): one needs to have a KApplication running, else shortcuts will fail
+ self.col=kdeui.KActionCollection(None)
+ self.col.addAssociatedWidget(winMain)
+ self.keys=[
+ ['toggleplay', QtCore.Qt.META+QtCore.Qt.Key_Home, self.togglePlay],
+ ['volumeup', QtCore.Qt.META+QtCore.Qt.Key_PageDown, lambda b: monty.setVolume(monty.getVolume()-5)],
+ ['volumedown', QtCore.Qt.META+QtCore.Qt.Key_PageUp, lambda b: monty.setVolume(monty.getVolume()+5)],
+ ['playnext', QtCore.Qt.META+QtCore.Qt.Key_Right, monty.next],
+ ['playprevious', QtCore.Qt.META+QtCore.Qt.Key_Left, monty.previous],
+ ['showosd', QtCore.Qt.META+QtCore.Qt.Key_O, self.showOSD],
+ ['togglewin', QtCore.Qt.META+QtCore.Qt.Key_P, self.toggleWinMain],
+ ]
+ # Note: don't use any non-alphanumerics in the prefix, as else it won't work
+ self.actionPrefix="shortcuts"
+
+ for entry in self.keys:
+ name=entry[0]
+ key=entry[1]
+ callback=entry[2]
+
+ self.debug("%s - %s"%(name, QtGui.QKeySequence(key).toString()))
+
+ action=kdeui.KAction(winMain) # winMain needed
+ action.setText(name)
+ action.setObjectName(name)
+ action.setGlobalShortcut(kdeui.KShortcut(key))
+ QtCore.QObject.connect(action, QtCore.SIGNAL('triggered(bool)'), callback)
+ self.col.addAction("%s%s"%(self.actionPrefix, action.objectName()), action)
+ def _unload(self):
+ actions=self.col.actions()
+ for action in actions:
+ try:
+ if action.objectName()[0:len(self.actionPrefix)]==self.actionPrefix:
+ self.debug("removing %s"%(action.objectName()))
+ self.col.removeAction(action)
+ except Exception, e:
+ self.important(str(e))
+
+ def getInfo(self):
+ return "Shortcuts for mpd."
- def showOSD(self, b):
- plugins.getPlugin('notify').onSongChange(None)
+ def showOSD(self, b):
+ plugins.getPlugin('notify').onSongChange(None)
- def togglePlay(self, btns=None, mods=None):
- if monty.isPlaying():
- monty.pause()
- else:
- monty.resume()
-
- def toggleWinMain(self, b):
- w=self.getWinMain()
- if w.isVisible():
- w.setVisible(False)
- else:
- w.setVisible(True)
-
- def _getSettings(self):
- txt=QtGui.QTextEdit()
- txt.setReadOnly(True)
- txt.insertPlainText("Keybindings (read-only)\n")
- for entry in self.keys:
- name=entry[0]
- key=entry[1]
-
- txt.insertPlainText("%s\t%s\n"%(name, QtGui.QKeySequence(key).toString()))
- return [
- ['', 'Keybindings', 'Current keybindings', txt],
- ]
+ def togglePlay(self, btns=None, mods=None):
+ if monty.isPlaying():
+ monty.pause()
+ else:
+ monty.resume()
+
+ def toggleWinMain(self, b):
+ w=self.getWinMain()
+ if w.isVisible():
+ w.setVisible(False)
+ else:
+ w.setVisible(True)
+
+ def _getSettings(self):
+ txt=QtGui.QTextEdit()
+ txt.setReadOnly(True)
+ txt.insertPlainText("Keybindings (read-only)\n")
+ for entry in self.keys:
+ name=entry[0]
+ key=entry[1]
+
+ txt.insertPlainText("%s\t%s\n"%(name, QtGui.QKeySequence(key).toString()))
+ return [
+ ['', 'Keybindings', 'Current keybindings', txt],
+ ]
diff --git a/plugins/SongStatus.py b/plugins/SongStatus.py
index 052e99b..71aa6e4 100644
--- a/plugins/SongStatus.py
+++ b/plugins/SongStatus.py
@@ -12,79 +12,79 @@ SONGSTATUS_FORMAT_DEFAULT='<font size="4">now $state</font>'\
'$if($length,<br /><font size="4">$time/$length</font>)'
class wgSongStatus(QtGui.QWidget):
- """Displays the status of the current song, if playing."""
- " label containing the info"
- lblInfo=None
- format=None
- p=None
- def __init__(self, p, parent=None):
- QtGui.QWidget.__init__(self, parent)
- self.p=p
+ """Displays the status of the current song, if playing."""
+ " label containing the info"
+ lblInfo=None
+ format=None
+ p=None
+ def __init__(self, p, parent=None):
+ QtGui.QWidget.__init__(self, parent)
+ self.p=p
- self.lblInfo=QtGui.QLabel()
- self.setMinimumWidth(400)
-
- layout=QtGui.QHBoxLayout()
- self.setLayout(layout)
+ self.lblInfo=QtGui.QLabel()
+ self.setMinimumWidth(400)
+
+ layout=QtGui.QHBoxLayout()
+ self.setLayout(layout)
- layout.addWidget(self.lblInfo)
- self.updateFormat()
+ layout.addWidget(self.lblInfo)
+ self.updateFormat()
- def update(self, params):
- status=monty.getStatus()
- song=monty.getCurrentSong()
+ def update(self, params):
+ status=monty.getStatus()
+ song=monty.getCurrentSong()
- values={'state':''}
- try:
- values['state']={'play':'playing', 'stop':'stopped', 'pause':'paused'}[status['state']]
- if 'time' in status:
- values['length']=sec2min(status['length'])
- values['time']=sec2min(status['time'])
- except:
- pass
-
- if song:
- self.lblInfo.setText(self.format(format.params(song, values)))
-
- def updateFormat(self):
- try:
- self.format=format.compile(self.p.getSetting('format'))
- except Exception, e:
- self.format=lambda p: "Invalid format: %s"%(e)
+ values={'state':''}
+ try:
+ values['state']={'play':'playing', 'stop':'stopped', 'pause':'paused'}[status['state']]
+ if 'time' in status:
+ values['length']=sec2min(status['length'])
+ values['time']=sec2min(status['time'])
+ except:
+ pass
+
+ if song:
+ self.lblInfo.setText(self.format(format.params(song, values)))
+
+ def updateFormat(self):
+ try:
+ self.format=format.compile(self.p.getSetting('format'))
+ except Exception, e:
+ self.format=lambda p: "Invalid format: %s"%(e)
- def text(self):
- return self.lblInfo.text()
+ def text(self):
+ return self.lblInfo.text()
class pluginSongStatus(Plugin):
- o=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'SongStatus')
- self.addMontyListener('onSongChange', self.update)
- self.addMontyListener('onTimeChange', self.update)
- self.addMontyListener('onStateChange', self.update)
- self.addMontyListener('onConnect', self.update)
- self.addMontyListener('onDisconnect', self.update)
-
- def _load(self):
- self.o=wgSongStatus(self, None)
- self.update(None)
- def _unload(self):
- self.o=None
- def getInfo(self):
- return "Show information about the current song."
-
- def update(self, params):
- self.o.update(params)
-
- def _getDockWidget(self):
- return self._createDock(self.o)
+ o=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'SongStatus')
+ self.addMontyListener('onSongChange', self.update)
+ self.addMontyListener('onTimeChange', self.update)
+ self.addMontyListener('onStateChange', self.update)
+ self.addMontyListener('onConnect', self.update)
+ self.addMontyListener('onDisconnect', self.update)
+
+ def _load(self):
+ self.o=wgSongStatus(self, None)
+ self.update(None)
+ def _unload(self):
+ self.o=None
+ def getInfo(self):
+ return "Show information about the current song."
+
+ def update(self, params):
+ self.o.update(params)
+
+ def _getDockWidget(self):
+ return self._createDock(self.o)
- def _getSettings(self):
- format=QtGui.QTextEdit()
- format.insertPlainText(self.getSetting('format'))
- return [
- ['format', 'Format', 'Format of the song status. Possible tags: $title, $artist, $album, $track, $time, $length, $state', format]
- ]
- def afterSaveSettings(self):
- self.o.updateFormat()
- self.o.update(None)
+ def _getSettings(self):
+ format=QtGui.QTextEdit()
+ format.insertPlainText(self.getSetting('format'))
+ return [
+ ['format', 'Format', 'Format of the song status. Possible tags: $title, $artist, $album, $track, $time, $length, $state', format]
+ ]
+ def afterSaveSettings(self):
+ self.o.updateFormat()
+ self.o.update(None)
diff --git a/plugins/Systray.py b/plugins/Systray.py
index 75d2267..b933225 100644
--- a/plugins/Systray.py
+++ b/plugins/Systray.py
@@ -7,107 +7,107 @@ import format
SYSTRAY_FORMAT="$if($title,$title)$if($artist, by $artist) - [$album # $track] ($time/$length)"
class pluginSystray(Plugin):
- o=None
- format=None
- eventObj=None
- time=None # indicator of current time [0..64]
- appIcon=None
- pixmap=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'Systray')
- self.addMontyListener('onSongChange', self.update)
- self.addMontyListener('onReady', self.update)
- self.addMontyListener('onConnect', self.update)
- self.addMontyListener('onDisconnect', self.update)
- # TODO only update this when necessary, i.e. mouse-hover etc
- self.addMontyListener('onTimeChange', self.update)
- self.appIcon=appIcon
-
+ o=None
+ format=None
+ eventObj=None
+ time=None # indicator of current time [0..64]
+ appIcon=None
+ pixmap=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'Systray')
+ self.addMontyListener('onSongChange', self.update)
+ self.addMontyListener('onReady', self.update)
+ self.addMontyListener('onConnect', self.update)
+ self.addMontyListener('onDisconnect', self.update)
+ # TODO only update this when necessary, i.e. mouse-hover etc
+ self.addMontyListener('onTimeChange', self.update)
+ self.appIcon=appIcon
+
- def _load(self):
- self.format=format.compile(SYSTRAY_FORMAT)
- class SystrayWheelEventObject(QtCore.QObject):
- """This class listens for systray-wheel events"""
- def eventFilter(self, object, event):
- if type(event)==QtGui.QWheelEvent:
- numDegrees=event.delta() / 8
- numSteps=5*numDegrees/15
- monty.setVolume(int(monty.getStatus()['volume'])+numSteps)
- event.accept()
- return True
- return False
-
- self.o=QtGui.QSystemTrayIcon(appIcon, self.winMain)
- self.eventObj=SystrayWheelEventObject()
- self.o.installEventFilter(self.eventObj)
- self.winMain.connect(self.o, QtCore.SIGNAL('activated (QSystemTrayIcon::ActivationReason)')
- , self.onSysTrayClick)
- self.o.show()
- self.update(None)
-
- def _unload(self):
- self.o.hide()
- self.o.setIcon(QtGui.QIcon(None))
- self.o=None
- self.winMain._wheelEvent=None
- def getInfo(self):
- return "Display the montypc icon in the systray."
-
- def update(self, params):
- status=monty.getStatus()
- song=monty.getCurrentSong()
+ def _load(self):
+ self.format=format.compile(SYSTRAY_FORMAT)
+ class SystrayWheelEventObject(QtCore.QObject):
+ """This class listens for systray-wheel events"""
+ def eventFilter(self, object, event):
+ if type(event)==QtGui.QWheelEvent:
+ numDegrees=event.delta() / 8
+ numSteps=5*numDegrees/15
+ monty.setVolume(int(monty.getStatus()['volume'])+numSteps)
+ event.accept()
+ return True
+ return False
+
+ self.o=QtGui.QSystemTrayIcon(appIcon, self.winMain)
+ self.eventObj=SystrayWheelEventObject()
+ self.o.installEventFilter(self.eventObj)
+ self.winMain.connect(self.o, QtCore.SIGNAL('activated (QSystemTrayIcon::ActivationReason)')
+ , self.onSysTrayClick)
+ self.o.show()
+ self.update(None)
+
+ def _unload(self):
+ self.o.hide()
+ self.o.setIcon(QtGui.QIcon(None))
+ self.o=None
+ self.winMain._wheelEvent=None
+ def getInfo(self):
+ return "Display the montypc icon in the systray."
+
+ def update(self, params):
+ status=monty.getStatus()
+ song=monty.getCurrentSong()
- values={'state':''}
- try:
- values['state']={'play':'playing', 'stop':'stopped', 'pause':'paused'}[status['state']]
- if 'time' in status:
- values['length']=sec2min(status['length'])
- values['time']=sec2min(status['time'])
- except:
- pass
-
- if song:
- self.o.setToolTip(self.format(format.params(song, values)))
- else:
- self.o.setToolTip("montypc not playing")
-
- try:
- curTime=(64*status['time'])/status['length']
- except:
- curTime=-1
- if self.time!=curTime:
- self.time=curTime
- # redraw the systray icon
- self.pixmap=self.appIcon.pixmap(64,64)
- painter=QtGui.QPainter(self.pixmap)
- painter.fillRect(1, curTime, 63, 64, QtGui.QBrush(QtCore.Qt.white))
- appIcon.paint(painter, 1, 0, 63, 64)
- self.o.setIcon(QtGui.QIcon(self.pixmap))
-
- elif not song:
- self.time=None
- self.o.setIcon(appIcon)
+ values={'state':''}
+ try:
+ values['state']={'play':'playing', 'stop':'stopped', 'pause':'paused'}[status['state']]
+ if 'time' in status:
+ values['length']=sec2min(status['length'])
+ values['time']=sec2min(status['time'])
+ except:
+ pass
+
+ if song:
+ self.o.setToolTip(self.format(format.params(song, values)))
+ else:
+ self.o.setToolTip("montypc not playing")
+
+ try:
+ curTime=(64*status['time'])/status['length']
+ except:
+ curTime=-1
+ if self.time!=curTime:
+ self.time=curTime
+ # redraw the systray icon
+ self.pixmap=self.appIcon.pixmap(64,64)
+ painter=QtGui.QPainter(self.pixmap)
+ painter.fillRect(1, curTime, 63, 64, QtGui.QBrush(QtCore.Qt.white))
+ appIcon.paint(painter, 1, 0, 63, 64)
+ self.o.setIcon(QtGui.QIcon(self.pixmap))
+
+ elif not song:
+ self.time=None
+ self.o.setIcon(appIcon)
-
- def onSysTrayClick(self, reason):
- if reason==QtGui.QSystemTrayIcon.Trigger \
- or reason==QtGui.QSystemTrayIcon.Context:
- w=self.getWinMain()
- # left mouse button
- if w.isVisible():
- settings.setIntTuple('winMain.pos', w.x(), w.y())
- w.setVisible(False)
- else:
- w.setVisible(True)
- try:
- x,y=settings.getIntTuple('winMain.pos')
- except:
- x,y=0,0
- w.move(x, y)
- elif reason==QtGui.QSystemTrayIcon.MiddleClick:
- # middle mouse button
- if monty.isPlaying():
- monty.pause()
- else:
- monty.resume()
-
+
+ def onSysTrayClick(self, reason):
+ if reason==QtGui.QSystemTrayIcon.Trigger \
+ or reason==QtGui.QSystemTrayIcon.Context:
+ w=self.getWinMain()
+ # left mouse button
+ if w.isVisible():
+ settings.setIntTuple('winMain.pos', w.x(), w.y())
+ w.setVisible(False)
+ else:
+ w.setVisible(True)
+ try:
+ x,y=settings.getIntTuple('winMain.pos')
+ except:
+ x,y=0,0
+ w.move(x, y)
+ elif reason==QtGui.QSystemTrayIcon.MiddleClick:
+ # middle mouse button
+ if monty.isPlaying():
+ monty.pause()
+ else:
+ monty.resume()
+
diff --git a/plugins/Tabs.py b/plugins/Tabs.py
index 811096a..8914a4b 100644
--- a/plugins/Tabs.py
+++ b/plugins/Tabs.py
@@ -9,157 +9,157 @@ from clMonty import monty
from clPlugin import *
class ResetEvent(QtCore.QEvent):
- song=None
- def __init__(self, song=None):
- QtCore.QEvent.__init__(self,QtCore.QEvent.User)
- self.song=song
+ song=None
+ def __init__(self, song=None):
+ QtCore.QEvent.__init__(self,QtCore.QEvent.User)
+ self.song=song
class AddHtmlEvent(QtCore.QEvent):
- html=None
- def __init__(self,html):
- QtCore.QEvent.__init__(self,QtCore.QEvent.User)
- self.html=html
+ html=None
+ def __init__(self,html):
+ QtCore.QEvent.__init__(self,QtCore.QEvent.User)
+ self.html=html
TABS_DIR_DEFAULT='/jammin'
TABS_ENGINE_DEFAULT='http://www.google.com/search?q=tabs|chords+"$artist"+"$title"'
-TABS_SITES_DEFAULT='azchords.com <pre>(.*?)</pre>\n'\
- 'fretplay.com <P CLASS="tabs">(.*?)</P>\n'\
- 'guitaretab.com <pre style="COLOR: #000000; FONT-SIZE: 11px;">(.*)?</pre>'\
+TABS_SITES_DEFAULT='azchords.com <pre>(.*?)</pre>\n'\
+ 'fretplay.com <P CLASS="tabs">(.*?)</P>\n'\
+ 'guitaretab.com <pre style="COLOR: #000000; FONT-SIZE: 11px;">(.*)?</pre>'\
class wgTabs(QtGui.QWidget):
- " contains the tabs"
- txt=None
- p=None # plugin
- def __init__(self, p, parent=None):
- QtGui.QWidget.__init__(self, parent)
- self.p=p
- self.txt=QtGui.QTextEdit(parent)
- self.txt.setReadOnly(True)
-
- layout=QtGui.QVBoxLayout()
- layout.addWidget(self.txt)
- self.setLayout(layout)
+ " contains the tabs"
+ txt=None
+ p=None # plugin
+ def __init__(self, p, parent=None):
+ QtGui.QWidget.__init__(self, parent)
+ self.p=p
+ self.txt=QtGui.QTextEdit(parent)
+ self.txt.setReadOnly(True)
+
+ layout=QtGui.QVBoxLayout()
+ layout.addWidget(self.txt)
+ self.setLayout(layout)
-
- def refresh(self):
- song=monty.getCurrentSong()
- try:
- song._data['file']
- except:
- self.resetTxt()
- return
-
- self.resetTxt(song)
- start_new_thread(self.fetchTabs, (song,))
-
- def customEvent(self, event):
- if isinstance(event,ResetEvent):
- self.resetTxt(event.song)
- elif isinstance(event,AddHtmlEvent):
- self.txt.insertHtml(event.html)
+
+ def refresh(self):
+ song=monty.getCurrentSong()
+ try:
+ song._data['file']
+ except:
+ self.resetTxt()
+ return
+
+ self.resetTxt(song)
+ start_new_thread(self.fetchTabs, (song,))
+
+ def customEvent(self, event):
+ if isinstance(event,ResetEvent):
+ self.resetTxt(event.song)
+ elif isinstance(event,AddHtmlEvent):
+ self.txt.insertHtml(event.html)
- _mutex=QtCore.QMutex()
- _fetchCnt=0
- def fetchTabs(self, song):
- # only allow 1 instance to look tabs!
- self._mutex.lock()
- if self._fetchCnt:
- self._mutex.unlock()
- return
- self._fetchCnt=1
- self._mutex.unlock()
+ _mutex=QtCore.QMutex()
+ _fetchCnt=0
+ def fetchTabs(self, song):
+ # only allow 1 instance to look tabs!
+ self._mutex.lock()
+ if self._fetchCnt:
+ self._mutex.unlock()
+ return
+ self._fetchCnt=1
+ self._mutex.unlock()
- QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
-
- # save the data to file!
- save_dir=self.p.getSetting('dir')
- fInfo=QtCore.QFileInfo(save_dir)
- if fInfo.isDir():
- tabsFName=toAscii('%s/%s - %s.txt'%(save_dir,song.getArtist(),song.getTitle()))
- else:
- tabsFName=None
- # does the file exist? if yes, read that one!
- try:
- # we have it: load, and return!
- file=open(tabsFName, 'r')
- QtCore.QCoreApplication.postEvent(self, AddHtmlEvent(file.read()))
- file.close()
- self._fetchCnt=0
- return
- except:
- pass
+ QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
+
+ # save the data to file!
+ save_dir=self.p.getSetting('dir')
+ fInfo=QtCore.QFileInfo(save_dir)
+ if fInfo.isDir():
+ tabsFName=toAscii('%s/%s - %s.txt'%(save_dir,song.getArtist(),song.getTitle()))
+ else:
+ tabsFName=None
+ # does the file exist? if yes, read that one!
+ try:
+ # we have it: load, and return!
+ file=open(tabsFName, 'r')
+ QtCore.QCoreApplication.postEvent(self, AddHtmlEvent(file.read()))
+ file.close()
+ self._fetchCnt=0
+ return
+ except:
+ pass
- # fetch from inet
- QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('<i>Searching tabs ...</i>'))
-
- lines=self.p.getSetting('sites').split('\n')
- sites={}
- for line in lines:
- if line.strip():
- sites[line[0:line.find('\t')]]=line[line.find('\t'):].strip()
- # construct URL to search!
- SE=self.p.getSetting('engine')
- try:
- ret=fetch(SE, sites, song, {}, stripHTML=False)
- if ret:
- txt='<pre>%s<br /><br /><a href="%s">%s</a></pre>'%(ret[0],ret[1],ret[1])
- # save for later use!
- if tabsFName:
- # we can't save if the path isn't correct
- try:
- file=open(tabsFName, 'w')
- file.write(ret[0])
- file.close()
- except:
- pass
- else:
- txt="No tabs found :'("
+ # fetch from inet
+ QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('<i>Searching tabs ...</i>'))
+
+ lines=self.p.getSetting('sites').split('\n')
+ sites={}
+ for line in lines:
+ if line.strip():
+ sites[line[0:line.find('\t')]]=line[line.find('\t'):].strip()
+ # construct URL to search!
+ SE=self.p.getSetting('engine')
+ try:
+ ret=fetch(SE, sites, song, {}, stripHTML=False)
+ if ret:
+ txt='<pre>%s<br /><br /><a href="%s">%s</a></pre>'%(ret[0],ret[1],ret[1])
+ # save for later use!
+ if tabsFName:
+ # we can't save if the path isn't correct
+ try:
+ file=open(tabsFName, 'w')
+ file.write(ret[0])
+ file.close()
+ except:
+ pass
+ else:
+ txt="No tabs found :'("
- QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
- QtCore.QCoreApplication.postEvent(self, AddHtmlEvent(txt))
- except:
- print_exc()
- QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
- QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('Woops, site unavailable!'\
- '<br />You have an internet connection?'))
- self._fetchCnt=0
+ QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
+ QtCore.QCoreApplication.postEvent(self, AddHtmlEvent(txt))
+ except:
+ print_exc()
+ QtCore.QCoreApplication.postEvent(self, ResetEvent(song))
+ QtCore.QCoreApplication.postEvent(self, AddHtmlEvent('Woops, site unavailable!'\
+ '<br />You have an internet connection?'))
+ self._fetchCnt=0
- def resetTxt(self, song=None):
- self.txt.clear()
- if song:
- self.txt.insertHtml('<b>%s</b>\n<br /><u>%s</u><br />'\
- '<br />\n\n'%(song.getTitle(), song.getArtist()))
+ def resetTxt(self, song=None):
+ self.txt.clear()
+ if song:
+ self.txt.insertHtml('<b>%s</b>\n<br /><u>%s</u><br />'\
+ '<br />\n\n'%(song.getTitle(), song.getArtist()))
class pluginTabs(Plugin):
- o=None
- def __init__(self, winMain):
- Plugin.__init__(self, winMain, 'Tabs')
- self.addMontyListener('onSongChange', self.refresh)
- self.addMontyListener('onReady', self.refresh)
- self.addMontyListener('onDisconnect', self.onDisconnect)
- def _load(self):
- self.o=wgTabs(self, None)
- self.refresh(None)
- def _unload(self):
- self.o=None
- def getInfo(self):
- return "Show (and fetch) the tabs of the currently playing song."
-
- def _getDockWidget(self):
- return self._createDock(self.o)
-
- def refresh(self, params):
- self.o.refresh()
- def onDisconnect(self, params):
- self.o.resetTxt()
+ o=None
+ def __init__(self, winMain):
+ Plugin.__init__(self, winMain, 'Tabs')
+ self.addMontyListener('onSongChange', self.refresh)
+ self.addMontyListener('onReady', self.refresh)
+ self.addMontyListener('onDisconnect', self.onDisconnect)
+ def _load(self):
+ self.o=wgTabs(self, None)
+ self.refresh(None)
+ def _unload(self):
+ self.o=None
+ def getInfo(self):
+ return "Show (and fetch) the tabs of the currently playing song."
+
+ def _getDockWidget(self):
+ return self._createDock(self.o)
+
+ def refresh(self, params):
+ self.o.refresh()
+ def onDisconnect(self, params):
+ self.o.resetTxt()
- def _getSettings(self):
- sites=QtGui.QTextEdit()
- sites.insertPlainText(self.getSetting('sites'))
- return [
- ['engine', 'Search engine', 'The URL that is used to search. $artist, $title and $album are replaced in the URL.', QtGui.QLineEdit(self.getSetting('engine'))],
- ['sites', 'Sites & regexes', 'This field contains all sites, together with the regex needed to fetch the tabs.\nEvery line must look like this: $domain $regex-start(.*?)$regex-end\n$domain is the domain of the tabs website, $regex-start is the regex indicating the start of the tabs, $regex-end indicates the end. E.g. footabs.org <tabs>(.*?)</tabs>', sites],
- ['dir', 'Tabs directory', 'Directory where tabs should be stored and retrieved.', QtGui.QLineEdit(self.getSetting('dir'))],
- ]
- def afterSaveSettings(self):
- self.o.onSongChange(None)
+ def _getSettings(self):
+ sites=QtGui.QTextEdit()
+ sites.insertPlainText(self.getSetting('sites'))
+ return [
+ ['engine', 'Search engine', 'The URL that is used to search. $artist, $title and $album are replaced in the URL.', QtGui.QLineEdit(self.getSetting('engine'))],
+ ['sites', 'Sites & regexes', 'This field contains all sites, together with the regex needed to fetch the tabs.\nEvery line must look like this: $domain $regex-start(.*?)$regex-end\n$domain is the domain of the tabs website, $regex-start is the regex indicating the start of the tabs, $regex-end indicates the end. E.g. footabs.org <tabs>(.*?)</tabs>', sites],
+ ['dir', 'Tabs directory', 'Directory where tabs should be stored and retrieved.', QtGui.QLineEdit(self.getSetting('dir'))],
+ ]
+ def afterSaveSettings(self):
+ self.o.onSongChange(None)
diff --git a/plugins/__init__.py b/plugins/__init__.py
index aa940bc..0673752 100644
--- a/plugins/__init__.py
+++ b/plugins/__init__.py
@@ -11,69 +11,69 @@ PLUGIN_MSG=3
def loadPlugins():
- """(Re)load all modules in the plugins directory."""
- global _plugins
- _plugins={}
- for file in os.listdir('plugins'):
- if file[-3:]=='.py' and file!='__init__.py':
- name=file[:-3] # name without ext
- mod='plugins.%s'%(name) # mod name
- className='plugin%s'%(name) # classname
-
- _plugins[className.lower()]=[mod, className, None, None]
- loadPlugin(className, None)
+ """(Re)load all modules in the plugins directory."""
+ global _plugins
+ _plugins={}
+ for file in os.listdir('plugins'):
+ if file[-3:]=='.py' and file!='__init__.py':
+ name=file[:-3] # name without ext
+ mod='plugins.%s'%(name) # mod name
+ className='plugin%s'%(name) # classname
+
+ _plugins[className.lower()]=[mod, className, None, None]
+ loadPlugin(className, None)
def getPlugin(name):
- global _plugins
- try:
- return _plugins[name.lower()][PLUGIN_INSTANCE]
- except:
- try:
- return _plugins["plugin%s"%(name.lower())][PLUGIN_INSTANCE]
- except:
- return None
-
+ global _plugins
+ try:
+ return _plugins[name.lower()][PLUGIN_INSTANCE]
+ except:
+ try:
+ return _plugins["plugin%s"%(name.lower())][PLUGIN_INSTANCE]
+ except:
+ return None
+
def loadPlugin(className, parent):
- """Constructs a plugin."""
- global _plugins
- entry=_plugins[className.lower()]
- mod=entry[PLUGIN_MODULE]
- # ensure we get the latest version
- try:
- try:
- sys.modules[mod]
- reimport=True
- except:
- reimport=False
-
- if reimport:
- reload(sys.modules[mod])
- else:
- module=__import__(mod, globals(), locals(), className, -1)
-
- except Exception, e:
- _plugins[className.lower()][PLUGIN_MSG]=str(e)
- _plugins[className.lower()][PLUGIN_INSTANCE]=None
- log.important("Failed to load plugin %s: %s %s"%(className, str(type(e)), str(e)))
- return None
-
- module=sys.modules[mod]
- _plugins[className.lower()][PLUGIN_MSG]=None
-
- if parent:
- # instantiate the plugin
- _plugins[className.lower()][PLUGIN_INSTANCE]=module.__dict__[className](parent)
- else:
- _plugins[className.lower()][PLUGIN_INSTANCE]=None
- return _plugins[className.lower()][PLUGIN_INSTANCE]
-
+ """Constructs a plugin."""
+ global _plugins
+ entry=_plugins[className.lower()]
+ mod=entry[PLUGIN_MODULE]
+ # ensure we get the latest version
+ try:
+ try:
+ sys.modules[mod]
+ reimport=True
+ except:
+ reimport=False
+
+ if reimport:
+ reload(sys.modules[mod])
+ else:
+ module=__import__(mod, globals(), locals(), className, -1)
+
+ except Exception, e:
+ _plugins[className.lower()][PLUGIN_MSG]=str(e)
+ _plugins[className.lower()][PLUGIN_INSTANCE]=None
+ log.important("Failed to load plugin %s: %s %s"%(className, str(type(e)), str(e)))
+ return None
+
+ module=sys.modules[mod]
+ _plugins[className.lower()][PLUGIN_MSG]=None
+
+ if parent:
+ # instantiate the plugin
+ _plugins[className.lower()][PLUGIN_INSTANCE]=module.__dict__[className](parent)
+ else:
+ _plugins[className.lower()][PLUGIN_INSTANCE]=None
+ return _plugins[className.lower()][PLUGIN_INSTANCE]
+
def listPlugins():
- """Get the list of plugins available as { className => [mod, className, instance, msg] }."""
- global _plugins
- return _plugins
+ """Get the list of plugins available as { className => [mod, className, instance, msg] }."""
+ global _plugins
+ return _plugins
loadPlugins()