Lyrics: allow dragging of items.
[nephilim.git] / nephilim / common.py
blob5bf562b0e2c1909a0eb318abf03b4939482cadcb
2 # Copyright (C) 2008 jerous <jerous@gmail.com>
3 # Copyright (C) 2009 Anton Khirnov <wyskas@gmail.com>
5 # Nephilim is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # Nephilim is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with Nephilim. If not, see <http://www.gnu.org/licenses/>.
19 from PyQt4 import QtCore, QtGui, QtNetwork
20 import socket
21 import logging
22 import os
23 import re
25 socket.setdefaulttimeout(8)
27 appIcon = ':icons/nephilim_small.png'
28 APPNAME = 'nephilim'
29 ORGNAME = 'nephilim'
31 # custom mimetypes used for drag&drop
32 MIMETYPES = {'songs' : 'application/x-mpd-songlist', 'plistsongs' : 'application/x-mpd-playlistsonglist'}
34 def sec2min(secs):
35 """Converts seconds to min:sec."""
36 min=int(secs/60)
37 sec=secs%60
38 if sec<10:sec='0'+str(sec)
39 return str(min)+':'+str(sec)
41 class Button(QtGui.QPushButton):
42 iconSize=32
43 """A simple Button class which calls $onClick when clicked."""
44 def __init__(self, caption, onClick=None, iconPath=None, iconOnly=False, parent=None):
45 QtGui.QPushButton.__init__(self, parent)
47 if onClick:
48 self.clicked.connect(onClick)
49 if iconPath:
50 self.changeIcon(iconPath)
52 if not(iconPath and iconOnly):
53 QtGui.QPushButton.setText(self, caption)
55 self.setToolTip(caption)
57 def setText(self, caption):
58 self.setToolTip(caption)
59 if self.icon()==None:
60 self.setText(caption)
62 def changeIcon(self, iconPath):
63 icon=QtGui.QIcon()
64 icon.addFile(iconPath, QtCore.QSize(self.iconSize, self.iconSize))
65 self.setIcon(icon)
67 def expand_tags(string, expanders):
68 for expander in expanders:
69 string = expander.expand_tags(string)
71 #remove unexpanded tags
72 return re.sub('\$\w+', '', string)
74 def generate_metadata_path(song, dir_tag, file_tag):
75 """Generate dirname and (db files only) full file path for reading/writing metadata files
76 (cover, lyrics) from $tags in dir/filename."""
77 if QtCore.QDir.isAbsolutePath(song['file']):
78 dirname = os.path.dirname(song['file'])
79 filepath = ''
80 elif '://' in song['file']: # we are streaming
81 dirname = ''
82 filepath = ''
83 else:
84 dirname = expand_tags(dir_tag, (QtGui.QApplication.instance(), song))
85 filepath = '%s/%s'%(dirname, expand_tags(file_tag, (QtGui.QApplication.instance(), song)).replace('/', '_'))
87 return dirname, filepath
89 class MetadataFetcher(QtCore.QObject):
90 """A basic class for metadata fetchers. Provides a fetch(song) function,
91 emits a finished(song, metadata) signal when done; lyrics is either a Python
92 unicode string or None if not found."""
93 #public, read-only
94 logger = None
95 name = ''
97 #private
98 nam = None # NetworkAccessManager
99 rep = None # current NetworkReply.
100 song = None # current song
102 # SIGNALS
103 finished = QtCore.pyqtSignal(['song', 'metadata'])
105 #### private ####
106 def __init__(self, plugin):
107 QtCore.QObject.__init__(self, plugin)
109 self.nam = QtNetwork.QNetworkAccessManager()
110 self.logger = plugin.logger
112 def fetch2(self, song, url):
113 """A private convenience function to initiate fetch process."""
114 # abort any existing connections
115 self.abort()
116 self.song = song
118 self.logger.info('Searching %s: %s.'%(self. name, url))
119 self.rep = self.nam.get(QtNetwork.QNetworkRequest(url))
120 self.rep.error.connect(self.handle_error)
122 def finish(self, metadata = None):
123 """A private convenience function to clean up and emit finished().
124 Feel free to reimplement/not use it."""
125 self.rep = None
126 self.rep = None
127 self.finished.emit(self.song, metadata)
128 self.song = None
130 def handle_error(self):
131 """Print the error and abort."""
132 self.logger.error(self.rep.errorString())
133 self.abort()
135 #### public ####
136 def fetch(self, song):
137 """Reimplement this in subclasses."""
138 pass
140 def abort(self):
141 """Abort all downloads currently in progress."""
142 if self.rep:
143 self.rep.finished.disconnect()
144 self.rep.abort()
145 self.rep = None