1 from enigma import eServiceReference, eConsoleAppContainer
2 from Components.MediaPlayer import PlayList
4 from Plugins.Plugin import PluginDescriptor
5 from Components.ActionMap import ActionMap
6 from Components.Sources.StaticText import StaticText
7 from Screens.Screen import Screen
8 from Components.config import config, ConfigSubsection, ConfigInteger, ConfigYesNo, ConfigText, getConfigListEntry
9 from Components.ConfigList import ConfigListScreen
11 config.plugins.CDInfo = ConfigSubsection()
12 config.plugins.CDInfo.useCDTEXT = ConfigYesNo(default = True)
13 config.plugins.CDInfo.useCDDB = ConfigYesNo(default = True)
14 config.plugins.CDInfo.displayString = ConfigText("$i - $t ($a)", fixed_size = False)
15 config.plugins.CDInfo.preferCDDB = ConfigYesNo(default = False)
16 config.plugins.CDInfo.CDDB_server = ConfigText("freedb.freedb.org", fixed_size = False)
17 config.plugins.CDInfo.CDDB_port = ConfigInteger(8880,limits = (1, 65536))
18 config.plugins.CDInfo.CDDB_timeout = ConfigInteger(20,limits = (-1, 60))
19 config.plugins.CDInfo.CDDB_cache = ConfigYesNo(default = True)
21 class CDInfo(ConfigListScreen,Screen):
23 <screen name="CDInfo" position="center,center" size="560,440" title="CDInfo" >
24 <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
25 <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
26 <ePixmap pixmap="skin_default/buttons/blue.png" position="420,0" size="140,40" alphatest="on" />
27 <widget source="key_red" render="Label" position="0,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#9f1313" transparent="1" />
28 <widget source="key_green" render="Label" position="140,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#1f771f" transparent="1" />
29 <widget source="key_blue" render="Label" position="420,0" zPosition="1" size="140,40" font="Regular;20" halign="center" valign="center" backgroundColor="#18188b" transparent="1" />
30 <widget source="info" render="Label" position="5,50" size="550,40" font="Regular;20" backgroundColor="#25062748" transparent="1" />
31 <widget name="config" position="5,115" size="550,200" scrollbarMode="showOnDemand" />
32 <ePixmap pixmap="skin_default/div-h.png" position="0,330" zPosition="1" size="560,2" />
33 <widget source="info2" render="Label" position="5,350" size="520,80" font="Regular;20" backgroundColor="#25062748" transparent="1" />
37 def __init__(self, session, args = None):
38 self.skin = CDInfo.skin
39 Screen.__init__(self, session)
41 self["info"] = StaticText("Gather audio cd album information and track listing from CDDB (online database) and / or CD-Text (from medium).")
43 self["info2"] = StaticText("Playlist string variables: $i=track, $t=title, $a=artist\nCDDB query will not delay start of audio CD playback. The request will be sent asynchronously and playlist text will be updated when match was found.")
46 self.list.append(getConfigListEntry("Try to extract CDTEXT", config.plugins.CDInfo.useCDTEXT))
47 self.list.append(getConfigListEntry("Try to query CDDB", config.plugins.CDInfo.useCDDB))
48 self.list.append(getConfigListEntry("Playlist string", config.plugins.CDInfo.displayString))
49 self.list.append(getConfigListEntry("CDDB overwrites CDTEXT info", config.plugins.CDInfo.preferCDDB))
50 self.list.append(getConfigListEntry("CDDB server hostname", config.plugins.CDInfo.CDDB_server))
51 self.list.append(getConfigListEntry("CDDB server port number", config.plugins.CDInfo.CDDB_port))
52 self.list.append(getConfigListEntry("CDDB retrieval timeout (s)", config.plugins.CDInfo.CDDB_timeout))
53 self.list.append(getConfigListEntry("store local CDDB cache", config.plugins.CDInfo.CDDB_cache))
55 ConfigListScreen.__init__(self, self.list)
56 self["key_red"] = StaticText(_("Cancel"))
57 self["key_green"] = StaticText(_("OK"))
58 self["key_blue"] = StaticText(_("Defaults"))
60 self["setupActions"] = ActionMap(["SetupActions", "ColorActions"],
64 "blue": self.defaults,
66 "cancel": self.cancel,
70 self.onLayoutFinish.append(self.layoutFinished)
72 def layoutFinished(self):
73 self.setTitle(_("CDInfo"))
76 for x in self["config"].list:
81 for x in self["config"].list:
86 config.plugins.CDInfo.useCDTEXT.setValue(True)
87 config.plugins.CDInfo.useCDDB.setValue(True)
88 config.plugins.CDInfo.displayString.setValue("$i - $t ($a)")
89 config.plugins.CDInfo.preferCDDB.setValue(False)
90 config.plugins.CDInfo.CDDB_server.setValue("freedb.freedb.org")
91 config.plugins.CDInfo.CDDB_port.setValue(8880)
92 config.plugins.CDInfo.CDDB_timeout.setValue(20)
93 config.plugins.CDInfo.CDDB_cache.setValue(True)
94 for x in self["config"].list:
99 def __init__(self, mediaplayer):
100 self.playlist = mediaplayer.playlist
101 self.mp = mediaplayer
102 self.cddb_container = eConsoleAppContainer()
103 self.cddb_output = ""
104 self.cdtext_container = eConsoleAppContainer()
105 self.cdtext_output = ""
106 self.tracklisting = {}
109 def getText(self, nodelist):
111 for node in nodelist:
112 if node.nodeType == node.TEXT_NODE:
114 return rc.encode("utf-8")
116 def xml_parse_output(self,string):
117 data = string.decode("utf-8","replace").replace('&',"&").encode("ascii",'xmlcharrefreplace')
119 cdinfodom = xml.dom.minidom.parseString(data)
121 print "[xml_parse_output] error, could not parse"
123 xmldata = cdinfodom.childNodes[0]
124 queries = xmldata.childNodes
125 self.xml_parse_query(queries)
126 print "[xml_parse_output] albuminfo: " + str(self.albuminfo)
127 print "[xml_parse_output] tracklisting: " + str(self.tracklisting)
130 def xml_parse_query(self, queries_xml):
131 for queries in queries_xml:
132 if queries.nodeType == xml.dom.minidom.Element.nodeType:
133 if queries.tagName == 'query':
134 print "[xml_parse_query] cdinfo source is %s, hit %s of %s" % (queries.getAttribute("source"),queries.getAttribute("match"),queries.getAttribute("num_matches"))
135 for query in queries.childNodes:
136 if query.nodeType == xml.dom.minidom.Element.nodeType:
137 if query.tagName == 'albuminfo':
138 self.xml_parse_albuminfo(query.childNodes)
139 elif query.tagName == 'tracklisting':
140 self.xml_parse_tracklisting(query.childNodes)
142 def xml_parse_albuminfo(self, albuminfo_xml):
143 for albuminfo in albuminfo_xml:
144 if albuminfo.nodeType == xml.dom.minidom.Element.nodeType:
145 if albuminfo.tagName == 'PERFORMER' or albuminfo.tagName == 'artist':
146 artist = self.getText(albuminfo.childNodes)
147 self.albuminfo["artist"] = artist
148 elif albuminfo.tagName.upper() == 'TITLE':
149 title = self.getText(albuminfo.childNodes)
150 self.albuminfo["title"] = title
151 elif albuminfo.tagName.upper() == 'YEAR':
152 year = self.getText(albuminfo.childNodes)
153 self.albuminfo["year"] = year
154 elif albuminfo.tagName.upper() == 'GENRE':
155 genre = self.getText(albuminfo.childNodes)
156 self.albuminfo["genre"] = genre
157 elif albuminfo.tagName == 'category' and not "GENRE" in self.albuminfo:
158 category = self.getText(albuminfo.childNodes)
159 self.albuminfo["genre"] = category
161 def xml_parse_tracklisting(self, tracklisting_xml):
162 for tracklist in tracklisting_xml:
163 if tracklist.nodeType == xml.dom.minidom.Element.nodeType:
164 if tracklist.tagName == 'track':
165 index = int(tracklist.getAttribute("number"))
167 for track in tracklist.childNodes:
168 if track.nodeType == xml.dom.minidom.Element.nodeType:
169 if track.tagName == 'PERFORMER' or track.tagName == 'artist':
170 artist = self.getText(track.childNodes)
171 trackinfo["artist"] = artist
172 if track.tagName.upper() == 'TITLE':
173 title = self.getText(track.childNodes)
174 trackinfo["title"] = title
175 #elif track.tagName == 'length':
176 #tracktext += "Dauer=%ss " % self.getText(track.childNodes)
177 self.tracklisting[index]=trackinfo
179 def updateAlbuminfo(self, replace = False):
180 for tag in self.albuminfo:
181 if tag not in self.mp.AudioCD_albuminfo or replace:
182 self.mp.AudioCD_albuminfo[tag] = self.albuminfo[tag]
184 def updatePlaylist(self, replace = False):
185 for idx in range(len(self.playlist)):
186 ref = self.playlist.getServiceRefList()[idx]
188 if idx < len(self.tracklisting):
189 if replace or not ref.getName():
190 trackinfo = self.tracklisting[track]
191 displayString = config.plugins.CDInfo.displayString.value.replace("$i", str(track))
192 if "title" in trackinfo:
193 displayString = displayString.replace("$t", trackinfo["title"])
194 if "artist" in trackinfo:
195 displayString = displayString.replace("$a", trackinfo["artist"])
196 ref.setName(displayString)
197 self.playlist.updateFile(idx, ref)
198 self.playlist.updateList()
201 if config.plugins.CDInfo.useCDTEXT.value:
203 if config.plugins.CDInfo.useCDDB.value:
206 def cdtext_scan(self):
207 cmd = "cdtextinfo -xalT"
208 print "[cdtext_scan] " + cmd
209 self.cdtext_container.appClosed.append(self.cdtext_finished)
210 self.cdtext_container.dataAvail.append(self.cdtext_avail)
211 self.cdtext_container.execute(cmd)
214 cmd = "cdtextinfo -xalD --cddb-port=%d --cddb-server=%s --cddb-timeout=%s" % (config.plugins.CDInfo.CDDB_port.value, config.plugins.CDInfo.CDDB_server.value, config.plugins.CDInfo.CDDB_timeout.value)
215 if not config.plugins.CDInfo.CDDB_cache.value:
216 cmd += " --no-cddb-cache"
217 print "[cddb_scan] " + cmd
218 self.cddb_container.appClosed.append(self.cddb_finished)
219 self.cddb_container.dataAvail.append(self.cddb_avail)
220 self.cddb_container.execute(cmd)
222 def cddb_avail(self,string):
223 self.cddb_output += string
225 def cdtext_avail(self,string):
226 self.cdtext_output += string
228 def cddb_finished(self,retval):
229 self.cddb_container.appClosed.remove(self.cddb_finished)
230 self.cddb_container.dataAvail.remove(self.cddb_avail)
231 if not self.xml_parse_output(self.cddb_output):
233 if config.plugins.CDInfo.preferCDDB.value:
234 self.updatePlaylist(replace = True)
235 self.updateAlbuminfo(replace = True)
237 self.updatePlaylist(replace = False)
238 self.updateAlbuminfo(replace = False)
239 self.mp.readTitleInformation()
240 self.cddb_output = ""
242 def cdtext_finished(self,retval):
243 self.cdtext_container.appClosed.remove(self.cdtext_finished)
244 self.cdtext_container.dataAvail.remove(self.cdtext_avail)
245 if not self.xml_parse_output(self.cdtext_output):
247 if not config.plugins.CDInfo.preferCDDB.value:
248 self.updatePlaylist(replace = True)
249 self.updateAlbuminfo(replace = True)
251 self.updatePlaylist(replace = False)
252 self.updateAlbuminfo(replace = False)
253 self.mp.readTitleInformation()
254 self.cdtext_output = ""
256 def main(session, **kwargs):
259 def Plugins(**kwargs):
260 return [ PluginDescriptor(name="CDInfo", description="AudioCD info from CDDB & CD-Text", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=main, icon="plugin.png") ]