def renameprocess(newname, debug=False): errors = [] # Renaming ourselves for ps et al. try: import procname procname.setprocname(newname) except: errors.append("Failed procname module") # Renaming ourselves for pkill et al. try: import ctypes # Linux style libc = ctypes.CDLL("libc.so.6") libc.prctl(15, newname, 0, 0, 0) except: errors.append("Failed linux style") try: import dl # FreeBSD style libc = dl.open("/lib/libc.so.6") libc.call("setproctitle", newname + "\0") renamed = True except: errors.append("Failed FreeBSD style") if debug and errors: msg = [_("Errors occured while trying to change process name:")] for i in errors: msg.append("%s" % (i,)) log.addwarning("\n".join(msg))
def foobar(self): """ Function to get foobar currently playing song on windows """ if sys.platform == "win32": try: from win32gui import GetWindowText, FindWindow except ImportError as error: log.addwarning(_("ERROR: foobar: failed to load win32gui module: %(error)s") % {"error": error}) return None else: log.addwarning(_("ERROR: foobar: is only supported on windows.")) return None wnd_ids = [ '{DA7CD0DE-1602-45e6-89A1-C2CA151E008E}', '{97E27FAA-C0B3-4b8e-A693-ED7881E99FC1}', '{E7076D1C-A7BF-4f39-B771-BCBE88F2A2A8}' ] metadata = None for wnd_id in wnd_ids: wnd_txt = GetWindowText(FindWindow(wnd_id, None)) if wnd_txt: m = re.match("(.*)\s+\[foobar.*", wnd_txt) if m: metadata = m.groups(0)[0].strip() if metadata: self.title["nowplaying"] = "now playing: " + metadata.decode('mbcs') return True else: return None
def amarok(self): """ Function to get amarok currently playing song """ try: import dbus import dbus.glib except ImportError as error: log.addwarning(_("ERROR: amarok: failed to load dbus module: %(error)s") % {"error": error}) return None self.bus = dbus.SessionBus() player = self.bus.get_object('org.mpris.amarok', '/Player') md = player.GetMetadata() for key, value in md.iteritems(): if key == 'mtime': # Convert seconds to minutes:seconds value = float(value) / 1000 m, s = divmod(value, 60) self.title['length'] = "%d:%02d" % (m, s) elif key == 'audio-bitrate': self.title['bitrate'] = value elif key == "location": self.title['filename'] else: self.title[key] = value self.title['nowplaying'] = self.title['artist'] + ' - ' + self.title['title'] return True
def amarok2(self): if not self.bus: log.addwarning("Failed to import DBus, cannot read out Amarok2") return player = self.bus.Interface(bus.get_object('org.mpris.amarok', '/Player'), dbus_interface='org.freedesktop.MediaPlayer') md = player.GetMetadata() for key, value in md.iteritems(): self.title[key] = unicode(value, 'iso8859-15', 'replace') return True
def other(self): try: othercommand = self.NPCommand.get_text() if othercommand == "": return None output = executeCommand(othercommand, returnoutput=True) self.title["nowplaying"] = output return True except Exception, error: log.addwarning(_("ERROR: Executing '%(command)s' failed: %(error)s") % {"command": othercommand, "error": error}) return None
def mpris(self): """ Function to get the currently playing song via dbus mpris v2 interface """ # https://media.readthedocs.org/pdf/mpris2/latest/mpris2.pdf try: import dbus import dbus.glib except ImportError as error: log.addwarning(_("ERROR: MPRIS: failed to load dbus module: %(error)s") % {"error": error}) return None self.bus = dbus.SessionBus() player = self.NPCommand.get_text() dbus_mpris_service = u'org.mpris.MediaPlayer2.' dbus_mpris_player_service = u'org.mpris.MediaPlayer2.Player' dbus_mpris_path = u'/org/mpris/MediaPlayer2' dbus_property = u'org.freedesktop.DBus.Properties' if not player: names = self.bus.list_names() players = [] for name in names: if name.startswith(dbus_mpris_service): players.append(name[len(dbus_mpris_service):]) if not players: log.addwarning(_("ERROR: MPRIS: Could not find a suitable MPRIS player.")) return None player = players[0] if len(players) > 1: log.addwarning(_("Found multiple MPRIS players: %(players)s. Using: %(player)s") % {'players': players, 'player': player}) else: log.addwarning(_("Auto-detected MPRIS player: %s.") % player) try: player_obj = self.bus.get_object(dbus_mpris_service + player, dbus_mpris_path) player_property_obj = dbus.Interface(player_obj, dbus_interface=dbus_property) metadata = player_property_obj.Get(dbus_mpris_player_service, "Metadata") except Exception, exception: log.addwarning(_("ERROR: MPRIS: Something went wrong while querying %(player)s: %(exception)s") % {'player': player, 'exception': exception}) return None
def OnAutoSearch(self, *args): # Wishlists supported by server? if self.interval == 0: log.addwarning("The server forbid us from doing wishlist searches.") return False searches = self.frame.np.config.sections["server"]["autosearch"] if not searches: return True # Search for a maximum of 3 items at each search interval for term in searches[0:3]: for i in self.searches.values(): if i[1] == term and i[4]: self.DoWishListSearch(i[0], term) oldsearch = searches.pop() searches.insert(0, oldsearch) return True
def compress_shares(self, sharestype): if sharestype == "normal": streams = self.config.sections["transfers"]["sharedfilesstreams"] elif sharestype == "buddy": streams = self.config.sections["transfers"]["bsharedfilesstreams"] if streams is None: log.addwarning( _("ERROR: No %(type)s shares database available") % {"type": sharestype}) return m = slskmessages.SharedFileList(None, streams) _thread.start_new_thread(m.makeNetworkMessage, (0, True)) if sharestype == "normal": self.CompressedSharesNormal = m elif sharestype == "buddy": self.CompressedSharesBuddy = m
def CreateMenu(self): try: self.tray_popup_menu_server = popup0 = PopupMenu(self, False) popup0.setup(("#" + _("Connect"), self.frame.OnConnect), ("#" + _("Disconnect"), self.frame.OnDisconnect)) self.tray_popup_menu = popup = PopupMenu(self, False) popup.setup( ("#" + _("Hide / Show Nicotine+"), self.HideUnhideWindow), (1, _("Server"), self.tray_popup_menu_server, self.OnPopupServer), ("#" + _("Settings"), self.frame.OnSettings), ("#" + _("Send Message"), self.OnOpenPrivateChat), ("#" + _("Lookup a User's IP"), self.OnGetAUsersIP), ("#" + _("Lookup a User's Info"), self.OnGetAUsersInfo), ("#" + _("Lookup a User's Shares"), self.OnGetAUsersShares), ("$" + _("Toggle Away"), self.frame.OnAway), ("#" + _("Quit"), self.frame.OnExit)) except Exception as e: log.addwarning(_('ERROR: tray menu, %(error)s') % {'error': e})
def writeAliases(self): try: f = open(self.filename + ".alias", "wb") except Exception as e: log.addwarning( _("Something went wrong while opening your alias file: %s") % e) else: try: pickle.dump(self.aliases, f, protocol=pickle.HIGHEST_PROTOCOL) f.close() except Exception as e: log.addwarning( _("Something went wrong while saving your alias file: %s") % e) finally: try: f.close() except Exception: pass
def amarok2(self): slist = self.NPFormat.child.get_text() if not self.bus: log.addwarning("Failed to import DBus, cannot read out Amarok2") return player = self.bus.get_object('org.mpris.amarok', '/Player') md = player.GetMetadata() for key, value in md.iteritems(): if key == 'mtime': # Convert seconds to minutes:seconds value = float(value) / 1000 m, s = divmod(value, 60) self.title['length'] = "%d:%02d" % (m, s) elif key == 'audio-bitrate': self.title['bitrate'] = value elif key == "location": self.title['filename'] else: self.title[key] = value self.title['nowplaying'] = self.title['artist']+' - '+self.title['title'] return True
def lastfm(self): """ Function to get the last song played via lastfm api """ import httplib import json try: conn = httplib.HTTPConnection("ws.audioscrobbler.com") except Exception as error: log.addwarning(_("ERROR: lastfm: Could not connect to audioscrobbler: %(error)s") % {"error": error}) return None try: (user, apikey) = self.NPCommand.get_text().split(';') except ValueError as error: log.addwarning(_("ERROR: lastfm: Please provide both your lastfm username and API key")) return None conn.request("GET", "/2.0/?method=user.getrecenttracks&user="******"&api_key=" + apikey + "&format=json") resp = conn.getresponse() data = resp.read() if resp.status != 200 or resp.reason != "OK": log.addwarning(_("ERROR: lastfm: Could not get recent track from audioscrobbler: %(error)s") % {"error": str(data)}) return None json_api = json.loads(data) lastplayed = json_api["recenttracks"]["track"][0] self.title["artist"] = lastplayed["artist"]["#text"] self.title["title"] = lastplayed["name"] self.title["album"] = lastplayed["album"]["#text"] self.title["nowplaying"] = "%s: %s - %s - %s" % (_("Last played"), self.title["artist"], self.title["album"], self.title["title"]) return True
def SetImage(self, status=None): if not self.IsTrayIconVisible(): return try: if status is not None: self.tray_status["status"] = status # Check for hilites, and display hilite icon if there is a room or private hilite if self.frame.hilites["rooms"] == [] and self.frame.hilites[ "private"] == []: # If there is no hilite, display the status icon_name = self.tray_status["status"] else: icon_name = "msg" if icon_name != self.tray_status["last"]: self.tray_status["last"] = icon_name if self.appindicator is not None: if self.custom_icons: icon_name = "trayicon_" + icon_name else: icon_name = "org.nicotine_plus.Nicotine-" + icon_name self.trayicon.set_icon_full(icon_name, "Nicotine+") else: # GtkStatusIcon fallback if self.custom_icons or self.local_icons: self.trayicon.set_from_pixbuf( self.frame.images["trayicon_" + icon_name]) else: self.trayicon.set_from_icon_name( "org.nicotine_plus.Nicotine-" + icon_name) except Exception as e: log.addwarning( _("ERROR: cannot set trayicon image: %(error)s") % {'error': e})
def getDirStream(self, dir): message = slskmessages.SlskMessage() stream = bytearray() stream.extend(message.packObject(len(dir))) for fileinfo in dir: stream.extend(bytes([1])) stream.extend(message.packObject(fileinfo[0])) stream.extend( message.packObject(fileinfo[1], unsignedlonglong=True)) if fileinfo[2] is not None: try: stream.extend(message.packObject('mp3')) stream.extend(message.packObject(3)) stream.extend(message.packObject(0)) stream.extend(message.packObject(fileinfo[2][0])) stream.extend(message.packObject(1)) stream.extend(message.packObject(fileinfo[3])) stream.extend(message.packObject(2)) stream.extend(message.packObject(fileinfo[2][1])) except Exception: log.addwarning( _( "Found meta data that couldn't be encoded, possible corrupt file: '%(file)s' has a bitrate of %(bitrate)s kbs, a length of %(length)s seconds and a VBR of %(vbr)s" % { 'file': fileinfo[0], 'bitrate': fileinfo[2][0], 'length': fileinfo[3], 'vbr': fileinfo[2][1] })) stream.extend(message.packObject('')) stream.extend(message.packObject(0)) else: stream.extend(message.packObject('')) stream.extend(message.packObject(0)) return stream
def detect(path): try: audio = mutagen.File(path) except IOError: return None except Exception as e: log.addwarning("Mutagen crashed on '%s': %s" % (path, e)) return None try: audio.info except Exception: # mutagen didn't think the file was audio return None if type(audio.info) == MPEGInfo: return processMPEG(audio) elif type(audio.info) == StreamInfo: return processFlac(audio) elif type(audio.info) == OggVorbisInfo: return processVorbis(audio) elif type(audio.info) == MusepackInfo: return processMusepack(audio) elif type(audio.info) == MonkeysAudioInfo: return processMonkeys(audio) elif type(audio.info) == MP4Info: return processMP4(audio) elif type(audio.info) == ASFInfo: return processASF(audio) elif type(audio.info) == OggOpusInfo: return processOpus(audio) else: print("EEK, what should I do with %(type)s (%(file)s)?" % { "type": str(type(audio.info)), "file": path }) return processGeneric(audio)
def enable_plugin(self, pluginname): if pluginname in self.enabled_plugins: return try: plugin = self.load_plugin(pluginname) if not plugin: raise Exception("Error loading plugin '%s'" % pluginname) plugin.enable(self) self.enabled_plugins[pluginname] = plugin log.add(_("Enabled plugin %s") % plugin.PLUGIN.__name__) except Exception: from traceback import print_exc print_exc() log.addwarning(_("Unable to enable plugin %s") % pluginname) # common.log_exception(logger) return False return True
def writeDownloadQueue(self): self.config_lock.acquire() realfile = os.path.join(self.data_dir, 'transfers.pickle') tmpfile = realfile + '.tmp' backupfile = realfile + ' .backup' try: handle = open(tmpfile, 'wb') except Exception as inst: log.addwarning( _("Something went wrong while opening your transfer list: %(error)s" ) % {'error': str(inst)}) else: try: pickle.dump(self.sections['transfers']['downloads'], handle, protocol=pickle.HIGHEST_PROTOCOL) handle.close() try: # Please let it be atomic... os.rename(tmpfile, realfile) except Exception: # ...ugh. Okay, how about... try: os.unlink(backupfile) except Exception: pass os.rename(realfile, backupfile) os.rename(tmpfile, realfile) except Exception as inst: log.addwarning( _("Something went wrong while writing your transfer list: %(error)s" ) % {'error': str(inst)}) finally: try: handle.close() except Exception: pass self.config_lock.release()
def load_shares(self, dbs): opened_shelves = [] errors = [] for shelvefile in dbs: try: opened_shelves.append( shelve.open(shelvefile, protocol=pickle.HIGHEST_PROTOCOL)) except Exception: errors.append(shelvefile) try: os.unlink(shelvefile) opened_shelves.append( shelve.open(shelvefile, flag='n', protocol=pickle.HIGHEST_PROTOCOL)) except Exception as ex: log.addwarning( ("Failed to unlink %s: %s" % (shelvefile, ex))) self.config.sections["transfers"]["sharedfiles"] = opened_shelves.pop( 0) self.config.sections["transfers"]["bsharedfiles"] = opened_shelves.pop( 0) self.config.sections["transfers"][ "sharedfilesstreams"] = opened_shelves.pop(0) self.config.sections["transfers"][ "bsharedfilesstreams"] = opened_shelves.pop(0) self.config.sections["transfers"]["wordindex"] = opened_shelves.pop(0) self.config.sections["transfers"]["bwordindex"] = opened_shelves.pop(0) self.config.sections["transfers"]["fileindex"] = opened_shelves.pop(0) self.config.sections["transfers"]["bfileindex"] = opened_shelves.pop(0) self.config.sections["transfers"]["sharedmtimes"] = opened_shelves.pop( 0) self.config.sections["transfers"][ "bsharedmtimes"] = opened_shelves.pop(0) if errors: log.addwarning( _("Failed to process the following databases: %(names)s") % {'names': '\n'.join(errors)}) self.set_shares(sharestype="normal", files={}, streams={}, mtimes={}, wordindex={}, fileindex={}) self.set_shares(sharestype="buddy", files={}, streams={}, mtimes={}, wordindex={}, fileindex={}) log.addwarning( _("Shared files database seems to be corrupted, rescan your shares" ))
def OnAutoSearch(self, *args): # Wishlists supported by server? if self.interval == 0: log.addwarning( _("The server forbid us from doing wishlist searches.")) return False searches = self.frame.np.config.sections["server"]["autosearch"] if not searches: return True # Search for a maximum of 1 item at each search interval term = searches.pop() searches.insert(0, term) for i in self.searches.searches.values(): if i[1] == term and i[4]: self.do_wishlist_search(i[0], term) break return True
def RescanShares(self, msg, rebuild=False): try: files, streams, wordindex, fileindex, mtimes = self.rescandirs( msg.shared, self.config.sections["transfers"]["sharedmtimes"], self.config.sections["transfers"]["sharedfiles"], self.config.sections["transfers"]["sharedfilesstreams"], msg.yieldfunction, self.np.frame.SharesProgress, name=_("Shares"), rebuild=rebuild) time.sleep(0.5) self.np.frame.RescanFinished( [files, streams, wordindex, fileindex, mtimes], "normal") except Exception as ex: config_dir, data_dir = GetUserDirectories() log.addwarning( _("Failed to rebuild share, serious error occurred. If this problem persists delete %s/*.db and try again. If that doesn't help please file a bug report with the stack trace included (see terminal output after this message). Technical details: %s" ) % (data_dir, ex)) raise
def set_shares(self, sharestype="normal", files=None, streams=None, mtimes=None, wordindex=None, fileindex=None): if sharestype == "normal": storable_objects = [(files, "sharedfiles", "files.db"), (streams, "sharedfilesstreams", "streams.db"), (mtimes, "sharedmtimes", "mtimes.db"), (wordindex, "wordindex", "wordindex.db"), (fileindex, "fileindex", "fileindex.db")] else: storable_objects = [(files, "bsharedfiles", "buddyfiles.db"), (streams, "bsharedfilesstreams", "buddystreams.db"), (mtimes, "bsharedmtimes", "buddymtimes.db"), (wordindex, "bwordindex", "buddywordindex.db"), (fileindex, "bfileindex", "buddyfileindex.db")] for source, destination, filename in storable_objects: if source is not None: try: self.config.sections["transfers"][destination].close() self.config.sections["transfers"][ destination] = shelve.open( os.path.join(self.config.data_dir, filename), flag='n', protocol=pickle.HIGHEST_PROTOCOL) self.config.sections["transfers"][destination].update( source) except Exception as e: log.addwarning(_("Can't save %s: %s") % (filename, e)) return
def foobar(self): """ Function to get foobar currently playing song on windows """ if sys.platform == "win32": try: from win32gui import GetWindowText, FindWindow except ImportError as error: log.addwarning( _("ERROR: foobar: failed to load win32gui module: %(error)s" ) % {"error": error}) return None else: log.addwarning(_("ERROR: foobar: is only supported on windows.")) return None wnd_ids = [ '{DA7CD0DE-1602-45e6-89A1-C2CA151E008E}', '{97E27FAA-C0B3-4b8e-A693-ED7881E99FC1}', '{E7076D1C-A7BF-4f39-B771-BCBE88F2A2A8}' ] metadata = None for wnd_id in wnd_ids: wnd_txt = GetWindowText(FindWindow(wnd_id, None)) if wnd_txt: m = re.match(r"(.*)\\s+\[foobar.*", wnd_txt) if m: metadata = m.groups()[0].strip() if metadata: self.title["nowplaying"] = "now playing: " + metadata.decode( 'mbcs') return True else: return None
def getByteStream(self, fileinfo): message = slskmessages.SlskMessage() stream = bytearray() stream.extend( bytes([1]) + message.packObject(fileinfo[0]) + message.packObject(NetworkLongLongType(fileinfo[1]))) if fileinfo[2] is not None: try: msgbytes = bytearray() msgbytes.extend( message.packObject('mp3') + message.packObject(3)) msgbytes.extend( message.packObject(0) + message.packObject(NetworkIntType(fileinfo[2][0])) + message.packObject(1) + message.packObject(NetworkIntType(fileinfo[3])) + message.packObject(2) + message.packObject(NetworkIntType(fileinfo[2][1]))) stream.extend(msgbytes) except Exception: log.addwarning( _( "Found meta data that couldn't be encoded, possible corrupt file: '%(file)s' has a bitrate of %(bitrate)s kbs, a length of %(length)s seconds and a VBR of %(vbr)s" % { 'file': fileinfo[0], 'bitrate': fileinfo[2][0], 'length': fileinfo[3], 'vbr': fileinfo[2][1] })) stream.extend(message.packObject('') + message.packObject(0)) else: stream.extend(message.packObject('') + message.packObject(0)) return stream
def lastfm(self): """ Function to get the last song played via lastfm api """ import http.client import json try: conn = http.client.HTTPSConnection("ws.audioscrobbler.com") except Exception as error: log.addwarning( _("ERROR: lastfm: Could not connect to audioscrobbler: %(error)s" ) % {"error": error}) return None try: (user, apikey) = self.NPCommand.get_text().split(';') except ValueError: log.addwarning( _("ERROR: lastfm: Please provide both your lastfm username and API key" )) return None conn.request("GET", "/2.0/?method=user.getrecenttracks&user="******"&api_key=" + apikey + "&format=json", headers={"User-Agent": "Nicotine+"}) resp = conn.getresponse() data = resp.read().decode("utf-8") if resp.status != 200 or resp.reason != "OK": log.addwarning( _("ERROR: lastfm: Could not get recent track from audioscrobbler: %(error)s" ) % {"error": str(data)}) return None json_api = json.loads(data) lastplayed = json_api["recenttracks"]["track"][0] self.title["artist"] = lastplayed["artist"]["#text"] self.title["title"] = lastplayed["name"] self.title["album"] = lastplayed["album"]["#text"] self.title["nowplaying"] = "%s: %s - %s - %s" % ( _("Last played"), self.title["artist"], self.title["album"], self.title["title"]) return True
def getFilesListUnicode(self, mtimes, oldmtimes, oldlist, yieldcall=None, progress=None, rebuild=False): """ Get a list of files with their filelength, bitrate and track length in seconds """ list = {} count = 0 for directory in mtimes: directory = os.path.expanduser(directory) virtualdir = self.real2virtual(directory) count += 1 if progress: percent = float(count) / len(mtimes) if percent <= 1.0: gobject.idle_add(progress.set_fraction, percent) # force Unicode for reading from disk u_directory = "%s" % directory str_directory = str(directory) # noqa: F841 if self.hiddenCheck({'dir': directory}): continue if directory in oldmtimes and directory not in oldlist: # Partial information, happened with unicode paths that N+ couldn't handle properly del oldmtimes[directory] if not rebuild and directory in oldmtimes: if mtimes[directory] == oldmtimes[directory]: if os.path.exists(directory): try: list[virtualdir] = oldlist[virtualdir] continue except KeyError: log.addwarning( _("Inconsistent cache for '%(vdir)s', rebuilding '%(dir)s'" ) % { 'vdir': virtualdir, 'dir': directory }) else: log.adddebug( _("Dropping missing directory %(dir)s") % {'dir': directory}) continue list[virtualdir] = [] try: contents = os.listdir(u_directory) except OSError as errtuple: print(str(errtuple)) self.logMessage(str(errtuple)) continue contents.sort() for filename in contents: if self.hiddenCheck({'dir': directory, 'file': filename}): continue path = os.path.join(directory, filename) s_path = str(path) ppath = str(path) s_filename = str(filename) try: # try to force Unicode for reading from disk isfile = os.path.isfile(ppath) except OSError as errtuple: message = _("Scanning Error: %(error)s Path: %(path)s") % { 'error': errtuple, 'path': path } print(str(message)) self.logMessage(message) displayTraceback(sys.exc_info()[2]) continue else: if isfile: # Get the metadata of the file via mutagen data = self.getFileInfoUnicode(s_filename, s_path) if data is not None: list[virtualdir].append(data) if yieldcall is not None: yieldcall() return list
def readConfig(self): self.sections['transfers']['downloads'] = [] if exists(os.path.join(self.data_dir, 'transfers.pickle')): # <1.2.13 stored transfers inside the main config try: handle = open(os.path.join(self.data_dir, 'transfers.pickle'), 'rb') except IOError as inst: log.addwarning( _("Something went wrong while opening your transfer list: %(error)s" ) % {'error': str(inst)}) else: try: self.sections['transfers'][ 'downloads'] = RestrictedUnpickler(handle).load() except Exception as inst: log.addwarning( _("Something went wrong while reading your transfer list: %(error)s" ) % {'error': str(inst)}) try: handle.close() except Exception: pass path, fn = os.path.split(self.filename) try: if not os.path.isdir(path): os.makedirs(path) except OSError as msg: log.addwarning("Can't create directory '%s', reported error: %s" % (path, msg)) try: if not os.path.isdir(self.data_dir): os.makedirs(self.data_dir) except OSError as msg: log.addwarning("Can't create directory '%s', reported error: %s" % (path, msg)) # Transition from 1.2.16 -> 1.4.0 # Do the cleanup early so we don't get the annoying # 'Unknown config option ...' message self.removeOldOption("transfers", "pmqueueddir") self.removeOldOption("server", "lastportstatuscheck") self.removeOldOption("server", "serverlist") self.removeOldOption("userinfo", "descrutf8") self.removeOldOption("ui", "enabletrans") self.removeOldOption("ui", "mozembed") self.removeOldOption("ui", "open_in_mozembed") self.removeOldOption("ui", "tooltips") self.removeOldOption("ui", "transalpha") self.removeOldOption("ui", "transfilter") self.removeOldOption("ui", "transtint") self.removeOldOption("language", "language") self.removeOldOption("language", "setlanguage") self.removeOldSection("language") # Transition from 1.4.1 -> 1.4.2 self.removeOldOption("columns", "downloads") self.removeOldOption("columns", "uploads") # Remove old encoding settings (1.4.3) self.removeOldOption("server", "enc") self.removeOldOption("server", "fallbackencodings") self.removeOldOption("server", "roomencoding") self.removeOldOption("server", "userencoding") # Remove soundcommand config, replaced by GSound (1.4.3) self.removeOldOption("ui", "soundcommand") # Remove old column widths in preparation for "group by folder"-feature self.removeOldOption("columns", "search") self.removeOldOption("columns", "search_widths") self.removeOldOption("columns", "downloads_columns") self.removeOldOption("columns", "downloads_widths") self.removeOldOption("columns", "uploads_columns") self.removeOldOption("columns", "uploads_widths") # Remove auto-retry failed downloads-option, this is now default behavior self.removeOldOption("transfers", "autoretry_downloads") # Remove old notification/sound settings self.removeOldOption("transfers", "shownotification") self.removeOldOption("transfers", "shownotificationperfolder") self.removeOldOption("ui", "soundenabled") self.removeOldOption("ui", "soundtheme") self.removeOldOption("ui", "tab_colors") self.removeOldOption("ui", "tab_icons") # Remove dropped offline user text color in search results self.removeOldOption("ui", "searchoffline") # Seems to be superseded by ("server", "private_chatrooms") self.removeOldOption("private_rooms", "enabled") # Remove everything ticker-related, no longer necessary after the introduction of room walls self.removeOldSection("ticker") # Checking for unknown section/options unknown1 = [ 'login', 'passw', 'enc', 'downloaddir', 'uploaddir', 'customban', 'descr', 'pic', 'logsdir', 'roomlogsdir', 'privatelogsdir', 'incompletedir', 'autoreply', 'afterfinish', 'downloadregexp', 'afterfolder', 'default', 'chatfont', 'npothercommand', 'npplayer', 'npformat', 'private_timestamp', 'rooms_timestamp', 'log_timestamp' ] unknown2 = { 'ui': [ "roomlistcollapsed", "tab_select_previous", "tabclosers", "tab_colors", "tab_reorderable", "buddylistinchatrooms", "trayicon", "showaway", "usernamehotspots", "exitdialog", "tab_icons", "spellcheck", "modes_order", "modes_visible", "chat_hidebuttons", "tab_status_icons", "notexists", "speechenabled", "enablefilters", "width", "height", "xposition", "yposition", "labelmain", "labelrooms", "labelprivate", "labelinfo", "labelbrowse", "labelsearch" ], 'words': [ "completion", "censorwords", "replacewords", "autoreplaced", "censored", "characters", "tab", "cycle", "dropdown", "roomnames", "buddies", "roomusers", "commands", "aliases", "onematch" ] } for i in self.parser.sections(): for j in self.parser.options(i): val = self.parser.get(i, j, raw=1) if i not in self.sections: log.addwarning(_("Unknown config section '%s'") % i) elif j not in self.sections[i] and not (j == "filter" or i in ('plugins', )): log.addwarning( _("Unknown config option '%(option)s' in section '%(section)s'" ) % { 'option': j, 'section': i }) elif j in unknown1 or (i in unknown2 and j not in unknown2[i]): if val is not None and val != "None": self.sections[i][j] = val else: self.sections[i][j] = None else: try: self.sections[i][j] = literal_eval(val) except Exception: self.sections[i][j] = None log.addwarning( "CONFIG ERROR: Couldn't decode '%s' section '%s' value '%s'" % (str(j), str(i), str(val))) # Convert fs-based shared to virtual shared (pre 1.4.0) def _convert_to_virtual(x): if isinstance(x, tuple): return x virtual = x.replace('/', '_').replace('\\', '_').strip('_') log.addwarning( "Renaming shared folder '%s' to '%s'. A rescan of your share is required." % (x, virtual)) return (virtual, x) self.sections["transfers"]["shared"] = [ _convert_to_virtual(x) for x in self.sections["transfers"]["shared"] ] self.sections["transfers"]["buddyshared"] = [ _convert_to_virtual(x) for x in self.sections["transfers"]["buddyshared"] ] sharedfiles = None bsharedfiles = None sharedfilesstreams = None bsharedfilesstreams = None wordindex = None bwordindex = None fileindex = None bfileindex = None sharedmtimes = None bsharedmtimes = None shelves = [ os.path.join(self.data_dir, "files.db"), os.path.join(self.data_dir, "buddyfiles.db"), os.path.join(self.data_dir, "streams.db"), os.path.join(self.data_dir, "buddystreams.db"), os.path.join(self.data_dir, "wordindex.db"), os.path.join(self.data_dir, "buddywordindex.db"), os.path.join(self.data_dir, "fileindex.db"), os.path.join(self.data_dir, "buddyfileindex.db"), os.path.join(self.data_dir, "mtimes.db"), os.path.join(self.data_dir, "buddymtimes.db") ] _opened_shelves = [] _errors = [] for shelvefile in shelves: try: _opened_shelves.append( shelve.open(shelvefile, protocol=pickle.HIGHEST_PROTOCOL)) except Exception: _errors.append(shelvefile) try: os.unlink(shelvefile) _opened_shelves.append( shelve.open(shelvefile, flag='n', protocol=pickle.HIGHEST_PROTOCOL)) except Exception as ex: print(("Failed to unlink %s: %s" % (shelvefile, ex))) sharedfiles = _opened_shelves.pop(0) bsharedfiles = _opened_shelves.pop(0) sharedfilesstreams = _opened_shelves.pop(0) bsharedfilesstreams = _opened_shelves.pop(0) wordindex = _opened_shelves.pop(0) bwordindex = _opened_shelves.pop(0) fileindex = _opened_shelves.pop(0) bfileindex = _opened_shelves.pop(0) sharedmtimes = _opened_shelves.pop(0) bsharedmtimes = _opened_shelves.pop(0) if _errors: log.addwarning( _("Failed to process the following databases: %(names)s") % {'names': '\n'.join(_errors)}) files = self.clearShares(sharedfiles, bsharedfiles, sharedfilesstreams, bsharedfilesstreams, wordindex, bwordindex, fileindex, bfileindex, sharedmtimes, bsharedmtimes) if files is not None: sharedfiles, bsharedfiles, sharedfilesstreams, bsharedfilesstreams, wordindex, bwordindex, fileindex, bfileindex, sharedmtimes, bsharedmtimes = files log.addwarning( _("Shared files database seems to be corrupted, rescan your shares" )) self.sections["transfers"]["sharedfiles"] = sharedfiles self.sections["transfers"]["sharedfilesstreams"] = sharedfilesstreams self.sections["transfers"]["wordindex"] = wordindex self.sections["transfers"]["fileindex"] = fileindex self.sections["transfers"]["sharedmtimes"] = sharedmtimes self.sections["transfers"]["bsharedfiles"] = bsharedfiles self.sections["transfers"]["bsharedfilesstreams"] = bsharedfilesstreams self.sections["transfers"]["bwordindex"] = bwordindex self.sections["transfers"]["bfileindex"] = bfileindex self.sections["transfers"]["bsharedmtimes"] = bsharedmtimes # Setting the port range in numerical order self.sections["server"]["portrange"] = ( min(self.sections["server"]["portrange"]), max(self.sections["server"]["portrange"]))
def _convert_to_virtual(x): if isinstance(x, tuple): return x virtual = x.replace('/', '_').replace('\\', '_').strip('_') log.addwarning("Renaming shared folder '%s' to '%s'. A rescan of your share is required." % (x, virtual)) return (virtual, x)
def mpris(self): """ Function to get the currently playing song via dbus mpris v2 interface """ # https://media.readthedocs.org/pdf/mpris2/latest/mpris2.pdf try: import dbus import dbus.glib except ImportError as error: log.addwarning( _("ERROR: MPRIS: failed to load dbus module: %(error)s") % {"error": error}) return None self.bus = dbus.SessionBus() player = self.NPCommand.get_text() dbus_mpris_service = u'org.mpris.MediaPlayer2.' dbus_mpris_player_service = u'org.mpris.MediaPlayer2.Player' dbus_mpris_path = u'/org/mpris/MediaPlayer2' dbus_property = u'org.freedesktop.DBus.Properties' if not player: names = self.bus.list_names() players = [] for name in names: if name.startswith(dbus_mpris_service): players.append(name[len(dbus_mpris_service):]) if not players: log.addwarning( _("ERROR: MPRIS: Could not find a suitable MPRIS player.")) return None player = players[0] if len(players) > 1: log.addwarning( _("Found multiple MPRIS players: %(players)s. Using: %(player)s" ) % { 'players': players, 'player': player }) else: log.addwarning(_("Auto-detected MPRIS player: %s.") % player) try: player_obj = self.bus.get_object(dbus_mpris_service + player, dbus_mpris_path) player_property_obj = dbus.Interface(player_obj, dbus_interface=dbus_property) metadata = player_property_obj.Get(dbus_mpris_player_service, "Metadata") except Exception, exception: log.addwarning( _("ERROR: MPRIS: Something went wrong while querying %(player)s: %(exception)s" ) % { 'player': player, 'exception': exception }) return None
def OnEnter(self, widget): bytes = widget.get_text() try: text = unicode(bytes, "UTF-8") except UnicodeDecodeError: log.addwarning(_("We have a problem, PyGTK get_text does not seem to return UTF-8. Please file a bug report. Bytes: %s") % (repr(bytes))) text = unicode(bytes, "UTF-8", "replace") if not text: widget.set_text("") return if is_alias(self.frame.np.config.aliases, text): import thread thread.start_new_thread(self.threadAlias, (text,)) widget.set_text("") return s = text.split(" ", 1) cmd = s[0] if len(s) == 2 and s[1]: realargs = args = s[1] else: args = self.user realargs = "" if cmd in ("/alias", "/al"): AppendLine(self.ChatScroll, self.frame.np.config.AddAlias(realargs), None, "") if self.frame.np.config.sections["words"]["aliases"]: self.frame.chatrooms.roomsctrl.UpdateCompletions() self.frame.privatechats.UpdateCompletions() elif cmd in ("/unalias", "/un"): AppendLine(self.ChatScroll, self.frame.np.config.Unalias(realargs), None, "") if self.frame.np.config.sections["words"]["aliases"]: self.frame.chatrooms.roomsctrl.UpdateCompletions() self.frame.privatechats.UpdateCompletions() elif cmd in ["/join", "/j"]: self.frame.np.queue.put(slskmessages.JoinRoom(args)) elif cmd in ["/w", "/whois", "/info"]: if args: self.frame.LocalUserInfoRequest(args) self.frame.OnUserInfo(None) elif cmd in ["/b", "/browse"]: if args: self.frame.BrowseUser(args) self.frame.OnUserBrowse(None) elif cmd == "/ip": if args: user = args if user not in self.frame.np.ip_requested: self.frame.np.ip_requested.append(user) self.frame.np.queue.put(slskmessages.GetPeerAddress(user)) elif cmd == "/pm": if realargs: self.frame.privatechats.SendMessage(realargs, None, 1) elif cmd in ["/m", "/msg"]: if realargs: s = realargs.split(" ", 1) user = s[0] if len(s) == 2: msg = s[1] else: msg = None self.frame.privatechats.SendMessage(user, msg) elif cmd in ["/s", "/search"]: if realargs: self.frame.Searches.DoSearch(realargs, 0) self.frame.OnSearch(None) elif cmd in ["/us", "/usearch"]: if realargs: self.frame.Searches.DoSearch(realargs, 3, [self.user]) self.frame.OnSearch(None) elif cmd in ["/rs", "/rsearch"]: if realargs: self.frame.Searches.DoSearch(realargs, 1) self.frame.OnSearch(None) elif cmd in ["/bs", "/bsearch"]: if realargs: self.frame.Searches.DoSearch(realargs, 2) self.frame.OnSearch(None) elif cmd in ["/ad", "/add", "/buddy"]: if args: self.frame.userlist.AddToList(args) elif cmd in ["/rem", "/unbuddy"]: if args: self.frame.userlist.RemoveFromList(args) elif cmd == "/ban": if args: self.frame.BanUser(args) elif cmd == "/ignore": if args: self.frame.IgnoreUser(args) elif cmd == "/ignoreip": if args: self.frame.IgnoreIP(args) elif cmd == "/unban": if args: self.frame.UnbanUser(args) elif cmd == "/unignore": if args: self.frame.UnignoreUser(args) elif cmd == "/ctcpversion": if args: self.frame.privatechats.SendMessage(args, CTCP_VERSION, 1, bytestring=True) elif cmd in ["/clear", "/cl"]: self.ChatScroll.get_buffer().set_text("") elif cmd in ["/a", "/away"]: self.frame.OnAway(None) elif cmd in ["/q", "/quit", "/exit"]: self.frame.OnExit(None) return elif cmd in ["/c", "/close"]: self.OnClose(None) elif cmd == "/now": self.NowPlayingThread() elif cmd == "/detach": self.Detach() elif cmd == "/attach": self.Attach() elif cmd == "/rescan": self.frame.OnRescan() elif cmd[:1] == "/" and self.frame.pluginhandler.TriggerPrivateCommandEvent(self.user, cmd[1:], args): pass elif cmd and cmd[:1] == "/" and cmd != "/me" and cmd[:2] != "//": self.frame.logMessage(_("Command %s is not recognized") % text) return else: if text[:2] == "//": text = text[1:] if self.chats.connected: self.SendMessage(text) widget.set_text("") return widget.set_text("")
def OnEnter(self, widget): bytes = widget.get_text() try: text = unicode(bytes, "UTF-8") except UnicodeDecodeError: log.addwarning( _("We have a problem, PyGTK get_text does not seem to return UTF-8. Please file a bug report. Bytes: %s" ) % (repr(bytes))) text = unicode(bytes, "UTF-8", "replace") if not text: widget.set_text("") return if is_alias(self.frame.np.config.aliases, text): import thread thread.start_new_thread(self.threadAlias, (text, )) widget.set_text("") return s = text.split(" ", 1) cmd = s[0] if len(s) == 2 and s[1]: realargs = args = s[1] else: args = self.user realargs = "" if cmd in ("/alias", "/al"): AppendLine(self.ChatScroll, self.frame.np.config.AddAlias(realargs), None, "") if self.frame.np.config.sections["words"]["aliases"]: self.frame.chatrooms.roomsctrl.UpdateCompletions() self.frame.privatechats.UpdateCompletions() elif cmd in ("/unalias", "/un"): AppendLine(self.ChatScroll, self.frame.np.config.Unalias(realargs), None, "") if self.frame.np.config.sections["words"]["aliases"]: self.frame.chatrooms.roomsctrl.UpdateCompletions() self.frame.privatechats.UpdateCompletions() elif cmd in ["/join", "/j"]: self.frame.np.queue.put(slskmessages.JoinRoom(args)) elif cmd in ["/w", "/whois", "/info"]: if args: self.frame.LocalUserInfoRequest(args) self.frame.OnUserInfo(None) elif cmd in ["/b", "/browse"]: if args: self.frame.BrowseUser(args) self.frame.OnUserBrowse(None) elif cmd == "/ip": if args: user = args if user not in self.frame.np.ip_requested: self.frame.np.ip_requested.append(user) self.frame.np.queue.put(slskmessages.GetPeerAddress(user)) elif cmd == "/pm": if realargs: self.frame.privatechats.SendMessage(realargs, None, 1) elif cmd in ["/m", "/msg"]: if realargs: s = realargs.split(" ", 1) user = s[0] if len(s) == 2: msg = s[1] else: msg = None self.frame.privatechats.SendMessage(user, msg) elif cmd in ["/s", "/search"]: if realargs: self.frame.Searches.DoSearch(realargs, 0) self.frame.OnSearch(None) elif cmd in ["/us", "/usearch"]: if realargs: self.frame.Searches.DoSearch(realargs, 3, [self.user]) self.frame.OnSearch(None) elif cmd in ["/rs", "/rsearch"]: if realargs: self.frame.Searches.DoSearch(realargs, 1) self.frame.OnSearch(None) elif cmd in ["/bs", "/bsearch"]: if realargs: self.frame.Searches.DoSearch(realargs, 2) self.frame.OnSearch(None) elif cmd in ["/ad", "/add", "/buddy"]: if args: self.frame.userlist.AddToList(args) elif cmd in ["/rem", "/unbuddy"]: if args: self.frame.userlist.RemoveFromList(args) elif cmd == "/ban": if args: self.frame.BanUser(args) elif cmd == "/ignore": if args: self.frame.IgnoreUser(args) elif cmd == "/ignoreip": if args: self.frame.IgnoreIP(args) elif cmd == "/unban": if args: self.frame.UnbanUser(args) elif cmd == "/unignore": if args: self.frame.UnignoreUser(args) elif cmd == "/ctcpversion": if args: self.frame.privatechats.SendMessage(args, CTCP_VERSION, 1, bytestring=True) elif cmd in ["/clear", "/cl"]: self.ChatScroll.get_buffer().set_text("") elif cmd in ["/a", "/away"]: self.frame.OnAway(None) elif cmd in ["/q", "/quit", "/exit"]: self.frame.OnExit(None) return elif cmd in ["/c", "/close"]: self.OnClose(None) elif cmd == "/now": self.NowPlayingThread() elif cmd == "/detach": self.Detach() elif cmd == "/attach": self.Attach() elif cmd == "/rescan": self.frame.OnRescan() elif cmd[: 1] == "/" and self.frame.pluginhandler.TriggerPrivateCommandEvent( self.user, cmd[1:], args): pass elif cmd and cmd[:1] == "/" and cmd != "/me" and cmd[:2] != "//": self.frame.logMessage(_("Command %s is not recognized") % text) return else: if text[:2] == "//": text = text[1:] if self.chats.connected: self.SendMessage(text) widget.set_text("") return widget.set_text("")
def mpris(self): """ Function to get the currently playing song via dbus mpris v2 interface """ # https://media.readthedocs.org/pdf/mpris2/latest/mpris2.pdf try: import dbus import dbus.glib except ImportError as error: log.addwarning( _("ERROR: MPRIS: failed to load dbus module: %(error)s") % {"error": error}) return None self.bus = dbus.SessionBus() player = self.NPCommand.get_text() dbus_mpris_service = 'org.mpris.MediaPlayer2.' dbus_mpris_player_service = 'org.mpris.MediaPlayer2.Player' dbus_mpris_path = '/org/mpris/MediaPlayer2' dbus_property = 'org.freedesktop.DBus.Properties' if not player: names = self.bus.list_names() players = [] for name in names: if name.startswith(dbus_mpris_service): players.append(name[len(dbus_mpris_service):]) if not players: log.addwarning( _("ERROR: MPRIS: Could not find a suitable MPRIS player.")) return None player = players[0] if len(players) > 1: log.addwarning( _("Found multiple MPRIS players: %(players)s. Using: %(player)s" ) % { 'players': players, 'player': player }) else: log.addwarning(_("Auto-detected MPRIS player: %s.") % player) try: player_obj = self.bus.get_object(dbus_mpris_service + player, dbus_mpris_path) player_property_obj = dbus.Interface(player_obj, dbus_interface=dbus_property) metadata = player_property_obj.Get(dbus_mpris_player_service, "Metadata") except Exception as exception: log.addwarning( _("ERROR: MPRIS: Something went wrong while querying %(player)s: %(exception)s" ) % { 'player': player, 'exception': exception }) return None self.title['program'] = player list_mapping = [('xesam:artist', 'artist')] for (source, dest) in list_mapping: try: self.title[dest] = '+'.join(metadata[source]) except KeyError: self.title[dest] = '?' mapping = [('xesam:title', 'title'), ('xesam:album', 'album'), ('xesam:comment', 'comment'), ('xesam:audioBitrate', 'bitrate'), ('xesak:trackNumber', 'track')] for (source, dest) in mapping: try: self.title[dest] = str(metadata[source]) except KeyError: self.title[dest] = '?' # The length is in microseconds, and be represented as a signed 64-bit integer. try: self.title['length'] = self.get_length_time( metadata['mpris:length'] / 1000000) except KeyError: self.title['length'] = '?' if self.title['artist'] != "": self.title['nowplaying'] += self.title['artist'] if self.title['title'] != "": self.title['nowplaying'] += " - " + self.title['title'] return True
def xmms2(self): """ Function to get xmms2 currently playing song """ # To communicate with xmms2d, you need an instance of the xmmsclient.XMMS object, which abstracts the connection try: import xmmsclient except ImportError as error: log.addwarning( _("ERROR: xmms2: failed to load xmmsclient module: %(error)s") % {"error": error}) return None xmms = xmmsclient.XMMS("NPP") # Now we need to connect to xmms2d try: xmms.connect(os.getenv("XMMS_PATH")) except IOError as error: log.addwarning( _("ERROR: xmms2: connecting failed: %(error)s") % {"error": error}) return None # Retrieve the current playing entry result = xmms.playback_current_id() result.wait() if result.iserror(): log.addwarning( _("ERROR: xmms2: playback current id error: %(error)s") % {"error": result.get_error()}) return None id = result.value() # Entry 0 is non valid if id == 0: log.addwarning(_("ERROR: xmms2: nothing is playing")) return None result = xmms.medialib_get_info(id) result.wait() # This can return error if the id is not in the medialib if result.iserror(): log.addwarning( _("ERROR: xmms2: medialib get info error: %(error)s") % {"error": result.get_error()}) return None # Extract entries from the dict minfo = result.value() try: self.title["artist"] = str(minfo["artist"]) self.title["nowplaying"] = str(minfo["artist"]) except KeyError: pass try: self.title["title"] = str(minfo["title"]) self.title["nowplaying"] += " - " + str(minfo["title"]) except KeyError: pass try: self.title["album"] = str(minfo["album"]) except KeyError: pass try: self.title["bitrate"] = str(minfo["bitrate"]) except KeyError: pass try: self.title["filename"] = str(minfo["url"]) except KeyError: pass try: self.title["length"] = self.get_length_time(minfo["duration"] / 1000) except KeyError: pass try: self.title["track"] = str(minfo["tracknr"]) except KeyError: pass try: self.title["year"] = str(minfo["date"]) except KeyError: pass return True
def writeConfiguration(self): external_sections = [ "sharedfiles", "sharedfilesstreams", "wordindex", "fileindex", "sharedmtimes", "bsharedfiles", "bsharedfilesstreams", "bwordindex", "bfileindex", "bsharedmtimes", "downloads" ] for i in self.sections: if not self.parser.has_section(i): self.parser.add_section(i) for j in self.sections[i]: if j not in external_sections: self.parser.set(i, j, self.sections[i][j]) else: self.parser.remove_option(i, j) path, fn = os.path.split(self.filename) try: if not os.path.isdir(path): os.makedirs(path) except OSError as msg: log.addwarning(_("Can't create directory '%(path)s', reported error: %(error)s") % {'path': path, 'error': msg}) oldumask = os.umask(0o077) try: f = open(self.filename + ".new", "w", encoding="utf-8") except IOError as e: log.addwarning(_("Can't save config file, I/O error: %s") % e) return else: try: self.parser.write(f) except IOError as e: log.addwarning(_("Can't save config file, I/O error: %s") % e) return else: f.close() os.umask(oldumask) # A paranoid precaution since config contains the password try: os.chmod(self.filename, 0o600) except Exception: pass try: s = os.stat(self.filename) if s.st_size > 0: try: if os.path.exists(self.filename + ".old"): os.remove(self.filename + ".old") except OSError: log.addwarning(_("Can't remove %s" % self.filename + ".old")) try: os.rename(self.filename, self.filename + ".old") except OSError as error: log.addwarning(_("Can't back config file up, error: %s") % error) except OSError: pass try: os.rename(self.filename + ".new", self.filename) except OSError as error: log.addwarning(_("Can't rename config file, error: %s") % error)
def readConfig(self): self.sections['transfers']['downloads'] = [] if exists(os.path.join(self.data_dir, 'transfers.pickle')): # <1.2.13 stored transfers inside the main config try: handle = open(os.path.join(self.data_dir, 'transfers.pickle'), 'rb') except IOError as inst: log.addwarning(_("Something went wrong while opening your transfer list: %(error)s") % {'error': str(inst)}) else: try: self.sections['transfers']['downloads'] = RestrictedUnpickler(handle).load() except Exception as inst: log.addwarning(_("Something went wrong while reading your transfer list: %(error)s") % {'error': str(inst)}) try: handle.close() except Exception: pass path, fn = os.path.split(self.filename) try: if not os.path.isdir(path): os.makedirs(path) except OSError as msg: log.addwarning("Can't create directory '%s', reported error: %s" % (path, msg)) try: if not os.path.isdir(self.data_dir): os.makedirs(self.data_dir) except OSError as msg: log.addwarning("Can't create directory '%s', reported error: %s" % (path, msg)) # Transition from 1.2.16 -> 1.4.0 # Do the cleanup early so we don't get the annoying # 'Unknown config option ...' message self.removeOldOption("transfers", "pmqueueddir") self.removeOldOption("server", "lastportstatuscheck") self.removeOldOption("server", "serverlist") self.removeOldOption("userinfo", "descrutf8") self.removeOldOption("ui", "enabletrans") self.removeOldOption("ui", "mozembed") self.removeOldOption("ui", "open_in_mozembed") self.removeOldOption("ui", "tooltips") self.removeOldOption("ui", "transalpha") self.removeOldOption("ui", "transfilter") self.removeOldOption("ui", "transtint") self.removeOldOption("language", "language") self.removeOldOption("language", "setlanguage") self.removeOldSection("language") # Transition from 1.4.1 -> 1.4.2 self.removeOldOption("columns", "downloads") self.removeOldOption("columns", "uploads") # Remove old encoding settings (1.4.3) self.removeOldOption("server", "enc") self.removeOldOption("server", "fallbackencodings") self.removeOldOption("server", "roomencoding") self.removeOldOption("server", "userencoding") # Remove soundcommand config, replaced by GSound (1.4.3) self.removeOldOption("ui", "soundcommand") # Remove old column widths in preparation for "group by folder"-feature self.removeOldOption("columns", "search") self.removeOldOption("columns", "search_widths") self.removeOldOption("columns", "downloads_columns") self.removeOldOption("columns", "downloads_widths") self.removeOldOption("columns", "uploads_columns") self.removeOldOption("columns", "uploads_widths") # Remove auto-retry failed downloads-option, this is now default behavior self.removeOldOption("transfers", "autoretry_downloads") # Remove old notification/sound settings self.removeOldOption("transfers", "shownotification") self.removeOldOption("transfers", "shownotificationperfolder") self.removeOldOption("ui", "soundenabled") self.removeOldOption("ui", "soundtheme") self.removeOldOption("ui", "tab_colors") self.removeOldOption("ui", "tab_icons") # Remove dropped offline user text color in search results self.removeOldOption("ui", "searchoffline") # Seems to be superseded by ("server", "private_chatrooms") self.removeOldOption("private_rooms", "enabled") # Remove everything ticker-related, no longer necessary after the introduction of room walls self.removeOldSection("ticker") # Remove old log folder option, superseded by individual log folders for transfers and debug messages self.removeOldOption("logging", "logsdir") # Checking for unknown section/options unknown1 = [ 'login', 'passw', 'enc', 'downloaddir', 'uploaddir', 'customban', 'descr', 'pic', 'transferslogsdir', 'roomlogsdir', 'privatelogsdir', 'incompletedir', 'autoreply', 'afterfinish', 'downloadregexp', 'afterfolder', 'default', 'chatfont', 'npothercommand', 'npplayer', 'npformat', 'private_timestamp', 'rooms_timestamp', 'log_timestamp', 'debuglogsdir' ] unknown2 = { 'ui': [ "roomlistcollapsed", "tab_select_previous", "tabclosers", "tab_colors", "tab_reorderable", "buddylistinchatrooms", "trayicon", "showaway", "usernamehotspots", "exitdialog", "tab_icons", "spellcheck", "modes_order", "modes_visible", "chat_hidebuttons", "tab_status_icons", "notexists", "speechenabled", "enablefilters", "width", "height", "xposition", "yposition", "labelmain", "labelrooms", "labelprivate", "labelinfo", "labelbrowse", "labelsearch", "maximized", "dark_mode" ], 'words': [ "completion", "censorwords", "replacewords", "autoreplaced", "censored", "characters", "tab", "cycle", "dropdown", "roomnames", "buddies", "roomusers", "commands", "aliases", "onematch" ] } for i in self.parser.sections(): for j in self.parser.options(i): val = self.parser.get(i, j, raw=1) if i not in self.sections: log.addwarning(_("Unknown config section '%s'") % i) elif j not in self.sections[i] and not (j == "filter" or i in ('plugins',)): log.addwarning(_("Unknown config option '%(option)s' in section '%(section)s'") % {'option': j, 'section': i}) elif j in unknown1 or (i in unknown2 and j not in unknown2[i]): if val is not None and val != "None": self.sections[i][j] = val else: self.sections[i][j] = None else: try: self.sections[i][j] = literal_eval(val) except Exception: self.sections[i][j] = None log.addwarning("CONFIG ERROR: Couldn't decode '%s' section '%s' value '%s'" % (str(j), str(i), str(val))) # Setting the port range in numerical order self.sections["server"]["portrange"] = (min(self.sections["server"]["portrange"]), max(self.sections["server"]["portrange"]))
def run(): renameprocess("nicotine") import locale # Win32 hack to fix LANG and LC_ALL environnment variables with unix compliant language code # See pynicotine/libi18n.py if win32: import pynicotine.libi18n pynicotine.libi18n.fix_locale() del pynicotine.libi18n try: locale.setlocale(locale.LC_ALL, "") except: log.addwarning(_("Cannot set locale")) import gettext gettext.textdomain("nicotine") import sys, getopt, os.path try: opts, args = getopt.getopt( sys.argv[1:], "hc:p:tdvswb:", [ "help", "config=", "plugins=", "profile", "enable-trayicon", "disable-trayicon", "enable-rgba", "disable-rgba", "version", "hidden", "disable-webbrowser", "bindip=", ], ) except getopt.GetoptError: # print help information and exit: usage() sys.exit(2) if win32: try: mydir = os.path.join(os.environ["APPDATA"], "nicotine") except KeyError: # windows 9x? mydir, x = os.path.split(sys.argv[0]) config = os.path.join(mydir, "config", "config") plugins = os.path.join(mydir, "plugins") else: config = os.path.join(os.path.expanduser("~"), ".nicotine", "config") plugins = os.path.join(os.path.expanduser("~"), ".nicotine", "plugins") profile = 0 trayicon = 1 webbrowser = True tryrgba = False hidden = False bindip = None for o, a in opts: if o in ("-h", "--help"): usage() sys.exit() if o in ("-c", "--config"): config = a if o in ("-p", "--plugins"): plugins = a if o in ("-b", "--bindip"): bindip = a if o == "--profile": profile = 1 if o in ("-t", "--enable-trayicon"): trayicon = 1 if o in ("-d", "--disable-trayicon"): trayicon = 0 if o in ("-w", "--disable-webbrowser"): webbrowser = False if o in ("-r", "--enable-rgba"): tryrgba = True if o in ("-x", "--disable-rgba"): tryrgba = False if o in ("-s", "--hidden"): hidden = True if o in ("-v", "--version"): version() sys.exit() result = checkenv() if result is None: from pynicotine.gtkgui import frame app = frame.MainApp(config, plugins, trayicon, tryrgba, hidden, webbrowser, bindip) if profile: import hotshot logfile = os.path.expanduser(config) + ".profile" profiler = hotshot.Profile(logfile) log.add(_("Starting using the profiler (saving log to %s)") % logfile) profiler.runcall(app.MainLoop) else: app.MainLoop() else: print result
def AddPortMapping(self, frame, np): """Wrapper to redirect the Port Mapping creation to either: - The MiniUPnPc binary: upnpc. - The python binding to the MiniUPnPc binary: miniupnpc. Both method support creating a Port Mapping via the UPnP IGDv1 and IGDv2 protocol. Need a reference to NicotineFrame to update the interface with the WAN external port chosen and connect to the slsk network. Also need a reference to the np object to extract the internal LAN local from the protothread socket. From the UPnP IGD reference: http://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v2-Service.pdf IGDv1 and IGDV2: AddPortMapping: This action creates a new port mapping or overwrites an existing mapping with the same internal client. If the ExternalPort and PortMappingProtocol pair is already mapped to another internal client, an error is returned. IGDv1: NewLeaseDuration: This argument defines the duration of the port mapping. If the value of this argument is 0, it means it's a static port mapping that never expire. IGDv2: NewLeaseDuration: This argument defines the duration of the port mapping. The value of this argument MUST be greater than 0. A NewLeaseDuration with value 0 means static port mapping, but static port mappings can only be created through an out-of-band mechanism. If this parameter is set to 0, default value of 604800 MUST be used. BTW since we don't recheck periodically ports mappings while nicotine+ runs, any UPnP port mapping done with IGDv2 (any modern router does that) will expire after 7 days. The client won't be able to send/receive files anymore... """ log.add(_('Creating Port Mapping rule via UPnP...')) # Hack to found out the local LAN IP # See https://stackoverflow.com/questions/166506/finding-local-ip-addresses-using-pythons-stdlib/28950776#28950776 # Create a UDP socket s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Send a broadcast packet on a local address (doesn't need to be reachable, but MacOS requires port to be non-zero) s.connect(('10.255.255.255', 1)) # This returns the "primary" IP on the local box, even if that IP is a NAT/private/internal IP. self.internalipaddress = s.getsockname()[0] # Close the socket s.close() # Store the Local LAN port self.internallanport = np.protothread._p.getsockname()[1] # The function depends on what method of configuring port mapping is # available functiontocall = getattr(self, 'AddPortMapping' + self.mode) try: functiontocall() except Exception as e: log.addwarning(_('UPnP exception: %(error)s') % {'error': str(e)}) log.addwarning( _('Failed to automate the creation of ' + 'UPnP Port Mapping rule.')) return log.add( _('Managed to map external WAN port %(externalwanport)s ' + 'on your external IP %(externalipaddress)s ' + 'to your local host %(internalipaddress)s ' + 'port %(internallanport)s.') % { 'externalwanport': self.externalwanport, 'externalipaddress': self.externalipaddress, 'internalipaddress': self.internalipaddress, 'internallanport': self.internallanport } ) # Set the external WAN port in the GUI frame.networkcallback([slskmessages.IncPort(self.externalwanport)]) # Establish the connection to the slsk network frame.OnConnect(-1)
if v < ((1 << 16) + (99 << 8) + 16): return _("Your PyGTK is too old, upgrade to at least PyGTK 1.99.16") try: import GeoIP except ImportError: try: import _GeoIP except ImportError: msg = _( """Nicotine supports a country code blocker but that requires a (GPL'ed) library called GeoIP. You can find it here: C library: http://www.maxmind.com/app/c Python bindings: http://www.maxmind.com/app/python (the python bindings require the C library)""" ) log.addwarning(msg) import pynicotine.upnp as upnp if not upnp.upnppossible: log.addwarning( _("Disabled UPnP support due to errors: %(errors)s") % {"errors": ". Also: ".join(upnp.miniupnpc_errors)} ) return None def version(): try: import pynicotine.utils
def GetNP(self, widget, test=None, callback=None): self.title_clear() result = None try: if self.NP_amarok.get_active(): result = self.amarok() elif self.NP_audacious.get_active(): result = self.audacious() elif self.NP_mpd.get_active(): result = self.mpd() elif self.NP_exaile.get_active(): result = self.exaile() elif self.NP_lastfm.get_active(): result = self.lastfm() elif self.NP_foobar.get_active(): result = self.foobar() elif self.NP_xmms2.get_active(): result = self.xmms2() elif self.NP_other.get_active(): result = self.other() elif self.NP_mpris.get_active(): result = self.mpris() except RuntimeError: log.addwarning( _("ERROR: Could not execute now playing code. Are you sure you picked the right player?" )) result = None if not result: return None # Since we need unicode instead of bytes we'll try to force such a # conversion. Individual player commands should have done this already # - this is a failsafe. oldtitle = copy.copy(self.title) self.title_clear() for key, value in oldtitle.items(): try: self.title[key] = str(value, "UTF-8", "replace") except TypeError: self.title[key] = value # already unicode title = self.NPFormat.get_child().get_text() title = title.replace("%", "%%") # Escaping user supplied % symbols title = title.replace("$t", "%(title)s") title = title.replace("$a", "%(artist)s") title = title.replace("$b", "%(album)s") title = title.replace("$c", "%(comment)s") title = title.replace("$n", "%(nowplaying)s") title = title.replace("$k", "%(track)s") title = title.replace("$l", "%(length)s") title = title.replace("$y", "%(year)s") title = title.replace("$r", "%(bitrate)s") title = title.replace("$f", "%(filename)s") title = title.replace("$p", "%(program)s") title = title % self.title title = ' '.join( [x for x in title.replace('\r', '\n').split('\n') if x]) if test: self.Example.set_text(title) return None if title: if callback: callback(title) return title return None
def clearShares(self, sharedfiles, bsharedfiles, sharedfilesstreams, bsharedfilesstreams, wordindex, bwordindex, fileindex, bfileindex, sharedmtimes, bsharedmtimes): try: if sharedfiles: sharedfiles.close() try: os.unlink(os.path.join(self.data_dir, 'files.db')) except Exception: pass sharedfiles = shelve.open(os.path.join(self.data_dir, "files.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) if bsharedfiles: bsharedfiles.close() try: os.unlink(os.path.join(self.data_dir, 'buddyfiles.db')) except Exception: pass bsharedfiles = shelve.open(os.path.join(self.data_dir, "buddyfiles.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) if sharedfilesstreams: sharedfilesstreams.close() try: os.unlink(os.path.join(self.data_dir, 'streams.db')) except Exception: pass sharedfilesstreams = shelve.open(os.path.join( self.data_dir, "streams.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) if bsharedfilesstreams: bsharedfilesstreams.close() try: os.unlink(os.path.join(self.data_dir, 'buddystreams.db')) except Exception: pass bsharedfilesstreams = shelve.open(os.path.join( self.data_dir, "buddystreams.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) if wordindex: wordindex.close() try: os.unlink(os.path.join(self.data_dir, 'wordindex.db')) except Exception: pass wordindex = shelve.open(os.path.join(self.data_dir, "wordindex.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) if bwordindex: bwordindex.close() try: os.unlink(os.path.join(self.data_dir, 'buddywordindex.db')) except Exception: pass bwordindex = shelve.open(os.path.join(self.data_dir, "buddywordindex.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) if fileindex: fileindex.close() try: os.unlink(os.path.join(self.data_dir, 'fileindex.db')) except Exception: pass fileindex = shelve.open(os.path.join(self.data_dir, "fileindex.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) if bfileindex: bfileindex.close() try: os.unlink(os.path.join(self.data_dir, 'buddyfileindex.db')) except Exception: pass bfileindex = shelve.open(os.path.join(self.data_dir, "buddyfileindex.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) if sharedmtimes: sharedmtimes.close() try: os.unlink(os.path.join(self.data_dir, 'mtimes.db')) except Exception: pass sharedmtimes = shelve.open(os.path.join(self.data_dir, "mtimes.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) if bsharedmtimes: bsharedmtimes.close() try: os.unlink(os.path.join(self.data_dir, 'buddymtimes.db')) except Exception: pass bsharedmtimes = shelve.open(os.path.join(self.data_dir, "buddymtimes.db"), flag='n', protocol=pickle.HIGHEST_PROTOCOL) except Exception as error: log.addwarning(_("Error while writing database files: %s") % error) return None return sharedfiles, bsharedfiles, sharedfilesstreams, bsharedfilesstreams, wordindex, bwordindex, fileindex, bfileindex, sharedmtimes, bsharedmtimes
def audacious(self): """ Function to get audacious currently playing song """ slist = self.NPFormat.get_child().get_text() output = "" self.audacious_running = True if "$n" in slist: artist = self.audacious_command('current-song-tuple-data', 'artist') title = self.audacious_command('current-song-tuple-data', 'title') if artist and title: self.title["nowplaying"] = artist + ' - ' + title if "$t" in slist: output = self.audacious_command('current-song-tuple-data', 'title') if output: self.title["title"] = output if "$l" in slist: output = self.audacious_command('current-song-length') if output: self.title["length"] = output if "$a" in slist: output = self.audacious_command('current-song-tuple-data', 'artist') if output: self.title["artist"] = output if "$b" in slist: output = self.audacious_command('current-song-tuple-data', 'album') if output: self.title["album"] = output if "$c" in slist: output = self.audacious_command('current-song-tuple-data', 'comment') if output: self.title["comment"] = output if "$k" in slist: output = self.audacious_command('current-song-tuple-data', 'track-number') if output: self.title["track"] = output if "$y" in slist: output = self.audacious_command('current-song-tuple-data', 'year') if output and not output == "0": self.title["year"] = output if "$r" in slist: output = self.audacious_command('current-song-bitrate-kbps') if output: self.title["bitrate"] = output if "$f" in slist: path = self.audacious_command( 'current-song-filename') # noqa: F841 if not self.audacious_running: log.addwarning( _("ERROR: audacious: audtool didn't detect a running Audacious session." )) return False return True
what version of python was used to build the Nicotine binary package and what you try to run Nicotine+ with).""") # Require GeoIP try: import GeoIP except ImportError: try: import _GeoIP except ImportError: msg = _("""Nicotine+ supports a country code blocker. That requires a (GPL'ed) library called GeoIP. You can find it here: C library: http://www.maxmind.com/app/c Python bindings: http://www.maxmind.com/app/python (the python bindings require the C library)""") log.addwarning(msg) return None def version(): try: import pynicotine.utils print _("Nicotine+ version %s" % pynicotine.utils.version) except ImportError, error: print _("Cannot find the pynicotine.utils module.") def usage(): print _("""Nicotine+ is a Soulseek client. Usage: nicotine [OPTION]...
def GetNP(self, widget, test=None, callback=None): self.title_clear() try: if self.NP_infopipe.get_active(): result = self.xmms() elif self.NP_amarok.get_active(): result = self.amarok() elif self.NP_amarok2.get_active(): result = self.amarok2() elif self.NP_audacious.get_active(): result = self.audacious() elif self.NP_mpd.get_active(): result = self.mpd() elif self.NP_banshee.get_active(): result = self.banshee() elif self.NP_rhythmbox.get_active(): result = self.rhythmbox() elif self.NP_bmpx.get_active(): result = self.bmpx() elif self.NP_exaile.get_active(): result = self.exaile() elif self.NP_lastfm.get_active(): result = self.lastfm() elif self.NP_foobar.get_active(): result = self.foobar() elif self.NP_other.get_active(): result = self.other() elif self.NP_mpris.get_active(): result = self.mpris() except RuntimeError: log.addwarning("ERROR: Could not execute now playing code. Are you sure you picked the right player?") result = None if not result: return None # Since we need unicode instead of bytes we'll try to force such a # conversion. Individual player commands should have done this already # - this is a failsafe. oldtitle = copy.copy(self.title) self.title_clear() for key, value in oldtitle.iteritems(): try: self.title[key] = unicode(value, "UTF-8", "replace") except TypeError: self.title[key] = value # already unicode title = self.NPFormat.child.get_text() title = title.replace("%", "%%") # Escaping user supplied % symbols #print "Title1: " + title title = title.replace("$t", "%(title)s") title = title.replace("$a", "%(artist)s") title = title.replace("$b", "%(album)s") title = title.replace("$c", "%(comment)s") title = title.replace("$n", "%(nowplaying)s") title = title.replace("$k", "%(track)s") title = title.replace("$l", "%(length)s") title = title.replace("$y", "%(year)s") title = title.replace("$r", "%(bitrate)s") title = title.replace("$s", "%(status)s") title = title.replace("$f", "%(filename)s") title = title.replace("$p", "%(program)s") #print "Title2: " + title title = title % self.title title = ' '.join([x for x in title.replace('\r', '\n').split('\n') if x]) if test: self.Example.set_text(title) return None if title: if callback: callback(title) return title return None
def xmms2(self): """ Function to get xmms2 currently playing song """ # To communicate with xmms2d, you need an instance of the xmmsclient.XMMS object, which abstracts the connection try: import xmmsclient except ImportError as error: log.addwarning(_("ERROR: xmms2: failed to load xmmsclient module: %(error)s") % {"error": error}) return None xmms = xmmsclient.XMMS("NPP") # Now we need to connect to xmms2d try: xmms.connect(os.getenv("XMMS_PATH")) except IOError as detail: log.addwarning(_("ERROR: xmms2: connecting failed: %(error)s") % {"error": error}) return None # Retrieve the current playing entry result = xmms.playback_current_id() result.wait() if result.iserror(): log.addwarning(_("ERROR: xmms2: playback current id error: %(error)s") % {"error": result.get_error()}) return None id = result.value() # Entry 0 is non valid if id == 0: log.addwarning(_("ERROR: xmms2: nothing is playing")) return None result = xmms.medialib_get_info(id) result.wait() # This can return error if the id is not in the medialib if result.iserror(): log.addwarning(_("ERROR: xmms2: medialib get info error: %(error)s") % {"error": result.get_error()}) return None # Extract entries from the dict minfo = result.value() try: self.title["artist"] = str(minfo["artist"]) self.title["nowplaying"] = str(minfo["artist"]) except KeyError: pass try: self.title["title"] = str(minfo["title"]) self.title["nowplaying"] += " - " + str(minfo["title"]) except KeyError: pass try: self.title["album"] = str(minfo["album"]) except KeyError: pass try: self.title["bitrate"] = str(minfo["bitrate"]) except KeyError: pass try: self.title["filename"] = str(minfo["url"]) except KeyError: pass try: self.title["length"] = self.get_length_time(minfo["duration"] / 1000) except KeyError: pass try: self.title["track"] = str(minfo["tracknr"]) except KeyError: pass try: self.title["year"] = str(minfo["date"]) except KeyError: pass return True
def AddPortMapping(self, frame, np): """Wrapper to redirect the Port Mapping creation to either: - The MiniUPnPc binary: upnpc. - The python binding to the MiniUPnPc binary: miniupnpc. Both method support creating a Port Mapping via the UPnP IGDv1 and IGDv2 protocol. Need a reference to NicotineFrame to update the interface with the WAN external port chosen and connect to the slsk network. Also need a reference to the np object to extract the internal LAN local from the protothread socket. From the UPnP IGD reference: http://upnp.org/specs/gw/UPnP-gw-WANIPConnection-v2-Service.pdf IGDv1 and IGDV2: AddPortMapping: This action creates a new port mapping or overwrites an existing mapping with the same internal client. If the ExternalPort and PortMappingProtocol pair is already mapped to another internal client, an error is returned. IGDv1: NewLeaseDuration: This argument defines the duration of the port mapping. If the value of this argument is 0, it means it's a static port mapping that never expire. IGDv2: NewLeaseDuration: This argument defines the duration of the port mapping. The value of this argument MUST be greater than 0. A NewLeaseDuration with value 0 means static port mapping, but static port mappings can only be created through an out-of-band mechanism. If this parameter is set to 0, default value of 604800 MUST be used. BTW since we don't recheck periodically ports mappings while nicotine+ runs, any UPnP port mapping done with IGDv2 (any modern router does that) will expire after 7 days. The client won't be able to send/receive files anymore... """ log.add(_('Creating Port Mapping rule via UPnP...')) # Placeholder LAN IP address, updated in AddPortMappingBinary or AddPortMappingModule self.internalipaddress = "127.0.0.1" # Store the Local LAN port self.internallanport = np.protothread._p.getsockname()[1] self.externalwanport = self.internallanport # The function depends on what method of configuring port mapping is # available functiontocall = getattr(self, 'AddPortMapping' + self.mode) try: functiontocall() except Exception as e: log.addwarning(_('UPnP exception: %(error)s') % {'error': str(e)}) log.addwarning( _('Failed to automate the creation of ' + 'UPnP Port Mapping rule.')) return log.adddebug( _('Managed to map external WAN port %(externalwanport)s ' + 'on your external IP %(externalipaddress)s ' + 'to your local host %(internalipaddress)s ' + 'port %(internallanport)s.') % { 'externalwanport': self.externalwanport, 'externalipaddress': self.externalipaddress, 'internalipaddress': self.internalipaddress, 'internallanport': self.internallanport })
def audacious(self): """ Function to get audacious currently playing song """ slist = self.NPFormat.child.get_text() output = "" self.audacious_running = True if "$n" in slist: artist = self.audacious_command('current-song-tuple-data', 'artist') title = self.audacious_command('current-song-tuple-data', 'title') if artist and title: self.title["nowplaying"] = artist + ' - ' + title if "$t" in slist: output = self.audacious_command('current-song-tuple-data', 'title') if output: self.title["title"] = output if "$l" in slist: output = self.audacious_command('current-song-length') if output: self.title["length"] = output if "$a" in slist: output = self.audacious_command('current-song-tuple-data', 'artist') if output: self.title["artist"] = output if "$b" in slist: output = self.audacious_command('current-song-tuple-data', 'album') if output: self.title["album"] = output if "$c" in slist: output = self.audacious_command('current-song-tuple-data', 'comment') if output: self.title["comment"] = output if "$k" in slist: output = self.audacious_command('current-song-tuple-data', 'track-number') if output: self.title["track"] = output if "$y" in slist: output = self.audacious_command('current-song-tuple-data', 'year') if output and not output == "0": self.title["year"] = output if "$r" in slist: output = self.audacious_command('current-song-bitrate-kbps') if output: self.title["bitrate"] = output if "$f" in slist: path = self.audacious_command('current-song-filename') if not self.audacious_running: log.addwarning(_("ERROR: audacious: audtool didn't detect a running Audacious session.")) return False return True