def notify(self, artist=None, album=None, snatched=None): title = 'Headphones' api = headphones.NMA_APIKEY nma_priority = headphones.NMA_PRIORITY logger.debug(u"NMA title: " + title) logger.debug(u"NMA API: " + api) logger.debug(u"NMA Priority: " + str(nma_priority)) if snatched: event = snatched + " snatched!" message = "Headphones has snatched: " + snatched else: event = artist + ' - ' + album + ' complete!' message = "Headphones has downloaded and postprocessed: " + artist + ' [' + album + ']' logger.debug(u"NMA event: " + event) logger.debug(u"NMA message: " + message) batch = False p = pynma.PyNMA() keys = api.split(',') p.addkey(keys) if len(keys) > 1: batch = True response = p.push(title, event, message, priority=nma_priority, batch_mode=batch) if not response[api][u'code'] == u'200': logger.error(u'Could not send notification to NotifyMyAndroid') return False else: return True
def _parseMetaTokenValue(self, value): if not value: logger.warn('config.meta.parse.token: empty value') return None r = re.compile('(?P<name>[-\w]+)\s*(?:\((?P<params>[-;\w\d\s]+)\))?') m = r.match(value) if not m: logger.warn('config.meta.parse.token: raw-value has syntax error') return None t = {} t['name'] = m.groupdict()['name'] params = m.groupdict()['params'] if params: params = params.split(';') params = map(lambda x: x.strip(), params) else: params = [] t['params'] = params logger.debug('config.meta.parse.token parsed token: {0}'.format(t)) return t
def addTorrent(link): method = 'torrent-add' arguments = {'filename': link, 'download-dir':headphones.DOWNLOAD_TORRENT_DIR} response = torrentAction(method,arguments) if not response: return False if response['result'] == 'success': name = response['arguments']['torrent-added']['name'] logger.info(u"Torrent sent to Transmission successfully") if headphones.PROWL_ENABLED and headphones.PROWL_ONSNATCH: logger.info(u"Sending Prowl notification") prowl = notifiers.PROWL() prowl.notify(name,"Download started") if headphones.PUSHOVER_ENABLED and headphones.PUSHOVER_ONSNATCH: logger.info(u"Sending Pushover notification") prowl = notifiers.PUSHOVER() prowl.notify(name,"Download started") if headphones.NMA_ENABLED and headphones.NMA_ONSNATCH: logger.debug(u"Sending NMA notification") nma = notifiers.NMA() nma.notify(snatched_nzb=name) return response['arguments']['torrent-added']['id']
def embedLyrics(downloaded_track_list): logger.info('Adding lyrics') # TODO: If adding lyrics for flac & lossy, only fetch the lyrics once # and apply it to both files for downloaded_track in downloaded_track_list: try: f = MediaFile(downloaded_track) except: logger.error('Could not read %s. Not checking lyrics' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) continue if f.albumartist and f.title: metalyrics = lyrics.getLyrics(f.albumartist, f.title) elif f.artist and f.title: metalyrics = lyrics.getLyrics(f.artist, f.title) else: logger.info('No artist/track metadata found for track: %s. Not fetching lyrics' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) metalyrics = None if lyrics: logger.debug('Adding lyrics to: %s' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) f.lyrics = metalyrics f.save()
def setSeedRatio(result): logger.debug('Deluge: Setting seed ratio') if not any(delugeweb_auth): _get_auth() ratio = None if result['ratio']: ratio = result['ratio'] try: if ratio: post_data = json.dumps({"method": "core.set_torrent_stop_at_ratio", "params": [result['hash'], True], "id": 5}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) post_data = json.dumps({"method": "core.set_torrent_stop_ratio", "params": [result['hash'], float(ratio)], "id": 6}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) return not json.loads(response.text)['error'] return True except Exception as e: logger.error('Deluge: Setting seed ratio failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) return None
def markArtists(self, action=None, **args): myDB = db.DBConnection() artistsToAdd = [] for ArtistID in args: if action == 'delete': myDB.action('DELETE from artists WHERE ArtistID=?', [ArtistID]) myDB.action('DELETE from albums WHERE ArtistID=?', [ArtistID]) myDB.action('DELETE from tracks WHERE ArtistID=?', [ArtistID]) myDB.action('DELETE from allalbums WHERE AlbumID=?', [AlbumID]) myDB.action('DELETE from alltracks WHERE AlbumID=?', [AlbumID]) myDB.action('INSERT OR REPLACE into blacklist VALUES (?)', [ArtistID]) elif action == 'pause': controlValueDict = {'ArtistID': ArtistID} newValueDict = {'Status': 'Paused'} myDB.upsert("artists", newValueDict, controlValueDict) elif action == 'resume': controlValueDict = {'ArtistID': ArtistID} newValueDict = {'Status': 'Active'} myDB.upsert("artists", newValueDict, controlValueDict) else: artistsToAdd.append(ArtistID) if len(artistsToAdd) > 0: logger.debug("Refreshing artists: %s" % artistsToAdd) threading.Thread(target=importer.addArtistIDListToDB, args=[artistsToAdd]).start() raise cherrypy.HTTPRedirect("home")
def checkFolder(): with postprocessor_lock: myDB = db.DBConnection() snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"') for album in snatched: if album["FolderName"]: nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, album["FolderName"]).encode( headphones.SYS_ENCODING ) torrent_album_path = os.path.join(headphones.DOWNLOAD_TORRENT_DIR, album["FolderName"]).encode( headphones.SYS_ENCODING ) if os.path.exists(nzb_album_path): logger.debug("Found %s in NZB download folder. Verifying...." % album["FolderName"]) verify(album["AlbumID"], nzb_album_path) elif os.path.exists(torrent_album_path): logger.debug("Found %s in torrent download folder. Verifying...." % album["FolderName"]) verify(album["AlbumID"], torrent_album_path)
def startmb(forcemb=False): mbuser = None mbpass = None # Can use headphones mirror for queries if headphones.MIRROR == "headphones": forcemb=False if forcemb or headphones.MIRROR == "musicbrainz.org": mbhost = "musicbrainz.org" mbport = 80 sleepytime = 1 elif headphones.MIRROR == "custom": mbhost = headphones.CUSTOMHOST mbport = int(headphones.CUSTOMPORT) sleepytime = int(headphones.CUSTOMSLEEP) elif headphones.MIRROR == "headphones": mbhost = "178.63.142.150" mbport = 8181 mbuser = headphones.HPUSER mbpass = headphones.HPPASS sleepytime = 0 else: mbhost = "tbueter.com" mbport = 5000 sleepytime = 0 service = ws.WebService(host=mbhost, port=mbport, username=mbuser, password=mbpass, mirror=headphones.MIRROR) q = ws.Query(service) logger.debug('Using the following server values:\nMBHost: %s ; MBPort: %i ; Sleep Interval: %i ' % (mbhost, mbport, sleepytime)) return (q, sleepytime)
def setTorrentPath(result): logger.debug('Deluge: Setting download path') if not any(delugeweb_auth): _get_auth() if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR: post_data = json.dumps({"method": "core.set_torrent_move_completed", "params": [result['hash'], True], "id": 7}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) if headphones.CONFIG.DELUGE_DONE_DIRECTORY: move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY else: move_to = headphones.CONFIG.DOWNLOAD_TORRENT_DIR if not os.path.exists(move_to): logger.debug('Deluge: %s directory doesn\'t exist, let\'s create it' % move_to) os.makedirs(move_to) post_data = json.dumps({"method": "core.set_torrent_move_completed_path", "params": [result['hash'], move_to], "id": 8}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) return not json.loads(response.text)['error'] return True
def addReleaseById(rid): myDB = db.DBConnection() rgid = None artistid = None release_dict = None results = myDB.select( "SELECT albums.ArtistID, releases.ReleaseGroupID from releases, albums WHERE releases.ReleaseID=? and releases.ReleaseGroupID=albums.AlbumID LIMIT 1", [rid], ) for result in results: rgid = result["ReleaseGroupID"] artistid = result["ArtistID"] logger.debug("Found a cached releaseid : releasegroupid relationship: " + rid + " : " + rgid) if not rgid: # didn't find it in the cache, get the information from MB logger.debug("Didn't find releaseID " + rid + " in the cache. Looking up its ReleaseGroupID") try: release_dict = mb.getRelease(rid) except Exception, e: logger.info("Unable to get release information for Release: " + str(rid) + " " + str(e)) return if not release_dict: logger.info("Unable to get release information for Release: " + str(rid) + " no dict") return rgid = release_dict["rgid"] artistid = release_dict["artist_id"]
def utorrent_add_file(self, data): host = headphones.CONFIG.UTORRENT_HOST if not host.startswith('http'): host = 'http://' + host if host.endswith('/'): host = host[:-1] if host.endswith('/gui'): host = host[:-4] base_url = host url = base_url + '/gui/' self.session.auth = (headphones.CONFIG.UTORRENT_USERNAME, headphones.CONFIG.UTORRENT_PASSWORD) try: r = self.session.get(url + 'token.html') except Exception as e: logger.error('Error getting token: %s', e) return if r.status_code == 401: logger.debug('Error reaching utorrent') return regex = re.search(r'.+>([^<]+)</div></html>', r.text) if regex is None: logger.debug('Error reading token') return self.session.params = {'token': regex.group(1)} files = {'torrent_file': ("", data)} try: self.session.post(url, params={'action': 'add-file'}, files=files) except Exception as e: logger.exception('Error adding file to utorrent %s', e)
def setTorrentPath(result): logger.debug('Deluge: Setting download path') if not any(delugeweb_auth): _get_auth() try: if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR: post_data = json.dumps({"method": "core.set_torrent_move_completed", "params": [result['hash'], True], "id": 7}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) if headphones.CONFIG.DELUGE_DONE_DIRECTORY: move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY else: move_to = headphones.CONFIG.DOWNLOAD_TORRENT_DIR if not os.path.exists(move_to): logger.debug('Deluge: %s directory doesn\'t exist, let\'s create it' % move_to) os.makedirs(move_to) post_data = json.dumps({"method": "core.set_torrent_move_completed_path", "params": [result['hash'], move_to], "id": 8}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) return not json.loads(response.text)['error'] return True except Exception as e: logger.error('Deluge: Setting torrent move-to directory failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) return None
def getTagTopArtists(tag, limit=50): myDB = db.DBConnection() results = myDB.select("SELECT ArtistID from artists") logger.info("Fetching top artists from Last.FM for tag: %s", tag) data = request_lastfm("tag.gettopartists", limit=limit, tag=tag) if data and "topartists" in data: artistlist = [] artists = data["topartists"]["artist"] logger.debug("Fetched %d artists from Last.FM", len(artists)) for artist in artists: try: artist_mbid = artist["mbid"] except KeyError: continue if not any(artist_mbid in x for x in results): artistlist.append(artist_mbid) from headphones import importer for artistid in artistlist: importer.addArtisttoDB(artistid) logger.debug("Added %d new artists from Last.FM", len(artistlist))
def sab_api_call(request_type=None, params={}, **kwargs): if not headphones.CONFIG.SAB_HOST.startswith('http'): headphones.CONFIG.SAB_HOST = 'http://' + headphones.CONFIG.SAB_HOST if headphones.CONFIG.SAB_HOST.endswith('/'): headphones.CONFIG.SAB_HOST = headphones.CONFIG.SAB_HOST[ 0:len(headphones.CONFIG.SAB_HOST) - 1] url = headphones.CONFIG.SAB_HOST + "/" + "api?" if headphones.CONFIG.SAB_USERNAME: params['ma_username'] = headphones.CONFIG.SAB_USERNAME if headphones.CONFIG.SAB_PASSWORD: params['ma_password'] = headphones.CONFIG.SAB_PASSWORD if headphones.CONFIG.SAB_APIKEY: params['apikey'] = headphones.CONFIG.SAB_APIKEY if request_type == 'send_nzb' and headphones.CONFIG.SAB_CATEGORY: params['cat'] = headphones.CONFIG.SAB_CATEGORY params['output'] = 'json' response = request.request_json(url, params=params, **kwargs) if not response: logger.error("Error connecting to SABnzbd on url: %s" % headphones.CONFIG.SAB_HOST) return False else: logger.debug("Successfully connected to SABnzbd on url: %s" % headphones.CONFIG.SAB_HOST) return response
def getFolder(hash): logger.debug('getFolder(%s)' % hash) qbclient = qbittorrentclient() # Get Active Directory from settings settings = qbclient._get_settings() active_dir = settings['temp_path'] completed_dir = settings['save_path'] if not active_dir: logger.error('Could not get "Keep incomplete torrents in:" directory from QBitTorrent settings, please ensure it is set') return None # Get Torrent Folder Name torrent_folder = qbclient.get_savepath(hash) # If there's no folder yet then it's probably a magnet, try until folder is populated if torrent_folder == active_dir or not torrent_folder: tries = 1 while (torrent_folder == active_dir or torrent_folder is None) and tries <= 10: tries += 1 time.sleep(6) torrent_folder = qbclient.get_savepath(hash) if torrent_folder == active_dir or not torrent_folder: torrent_folder = qbclient.get_savepath(hash) return torrent_folder else: if headphones.SYS_PLATFORM != "win32": torrent_folder = torrent_folder.replace('\\', '/') return os.path.basename(os.path.normpath(torrent_folder))
def sendNZB(nzb): addToTop = False nzbgetXMLrpc = "%(username)s:%(password)s@%(host)s/xmlrpc" if headphones.CONFIG.NZBGET_HOST is None: logger.error(u"No NZBget host found in configuration. Please configure it.") return False if headphones.CONFIG.NZBGET_HOST.startswith('https://'): nzbgetXMLrpc = 'https://' + nzbgetXMLrpc headphones.CONFIG.NZBGET_HOST.replace('https://', '', 1) else: nzbgetXMLrpc = 'http://' + nzbgetXMLrpc headphones.CONFIG.NZBGET_HOST.replace('http://', '', 1) url = nzbgetXMLrpc % {"host": headphones.CONFIG.NZBGET_HOST, "username": headphones.CONFIG.NZBGET_USERNAME, "password": headphones.CONFIG.NZBGET_PASSWORD} nzbGetRPC = xmlrpclib.ServerProxy(url) try: if nzbGetRPC.writelog("INFO", "headphones connected to drop of %s any moment now." % (nzb.name + ".nzb")): logger.debug(u"Successfully connected to NZBget") else: logger.info(u"Successfully connected to NZBget, but unable to send a message" % (nzb.name + ".nzb")) except httplib.socket.error: logger.error(u"Please check your NZBget host and port (if it is running). NZBget is not responding to this combination") return False except xmlrpclib.ProtocolError, e: if (e.errmsg == "Unauthorized"): logger.error(u"NZBget password is incorrect.") else: logger.error(u"Protocol Error: " + e.errmsg) return False
def request_lastfm(method, **kwargs): """ Call a Last.FM API method. Automatically sets the method and API key. Method will return the result if no error occured. By default, this method will request the JSON format, since it is more lightweight than XML. """ # Prepare request kwargs["method"] = method kwargs.setdefault("api_key", API_KEY) kwargs.setdefault("format", "json") # Send request logger.debug("Calling Last.FM method: %s", method) logger.debug("Last.FM call parameters: %s", kwargs) data = request.request_json(ENTRY_POINT, timeout=TIMEOUT, params=kwargs, rate_limit=(lock, REQUEST_LIMIT)) # Parse response and check for errors. if not data: logger.error("Error calling Last.FM method: %s", method) return if "error" in data: logger.error("Last.FM returned an error: %s", data["message"]) return return data
def checkFolder(): with postprocessor_lock: myDB = db.DBConnection() snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"') for album in snatched: if album['FolderName']: # Need to check for variations due to sab renaming. Ideally we'd check the sab config via api to # figure out which options are checked, but oh well nzb_album_possibilities = [ album['FolderName'], sab_replace_dots(album['FolderName']), sab_replace_spaces(album['FolderName']), sab_replace_dots(sab_replace_spaces(album['FolderName'])) ] torrent_album_path = os.path.join(headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING) for nzb_folder_name in nzb_album_possibilities: nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, nzb_folder_name).encode(headphones.SYS_ENCODING) if os.path.exists(nzb_album_path): logger.debug('Found %s in NZB download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], nzb_album_path) if os.path.exists(torrent_album_path): logger.debug('Found %s in torrent download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], torrent_album_path)
def __init__(self, config_file): """ Initialize the config with values from a file """ self._config_file = config_file self._config = ConfigObj(self._config_file, encoding='utf-8') # meta config preparation (_basename, _ext) = os.path.splitext(self._config_file) meta_config_name = _basename + '.meta' + _ext logger.debug('Read meta config from: [{0}]'.format(meta_config_name)) self._meta_config = MetaConfig(meta_config_name) # used in opt-register , it helps to make the names of sections correct self._section_name_spell_check_dic = {} self._options = {} self._uiparser = ViewParser() # ------------------------------------------------------------- # register options from definition's files: # ------------------------------------------------------------- self._initOptions() logger.info('All options registered. Total options: {0}'.format(len(self._options))) self._upgrade()
def search(self, searchurl, maxsize, minseeders, albumid, bitrate): if not self.login_done: self._doLogin( headphones.CONFIG.TONZE_LOGIN, headphones.CONFIG.TONZE_PASSWORD ) results = [] logger.debug(u"Search string: " + searchurl) r = self.opener.open( searchurl ) soup = BeautifulSoup( r, "html.parser" ) resultsTable = soup.find("table", { "class" : "results" }) if resultsTable: rows = resultsTable.find("tbody").findAll("tr") for row in rows: link = row.find("a", title=True) title = link['title'] size = row.find_all('td')[5].text seeders = row.find_all('td')[7].text size = parseSize(size) size = tryInt(size) seeders = tryInt(seeders) id = row.find_all('td')[2].find_all('a')[0]['href'][1:].replace('torrents/nfo/?id=','') downloadURL = ('http://www.t411.me/torrents/download/?id=%s' % id) results.append( T411SearchResult( self.opener, title, downloadURL,size, seeders) ) return results
def search(self, searchurl, maxsize, minseeders, albumid, bitrate): if not self.login_done: self._doLogin( headphones.CONFIG.FTDB_LOGIN, headphones.CONFIG.FTDB_PASSWORD ) results = [] logger.debug(u"Search string: " + searchurl) URL = self.url + '/?section=TORRENTS&' + searchurl.replace('!','') r = self.opener.open(URL) soup = BeautifulSoup( r, "html5lib" ) resultsTable = soup.find("div", { "class" : "DataGrid" }) if resultsTable: rows = resultsTable.findAll("ul") for row in rows: link = row.find("a", title=True) title = link['title'] size= row.findAll('li')[3].text size = parseSize(size) size = tryInt(size) leecher=row.findAll('li')[5].text seeder=row.findAll('li')[4].text seeders = tryInt(seeder) autogetURL = self.url +'/'+ (row.find("li", { "class" : "torrents_name"}).find('a')['href'][1:]).replace('#FTD_MENU','&menu=4') r = self.opener.open( autogetURL , 'wb').read() soup = BeautifulSoup( r, "html5lib" ) downloadURL = soup.find("div", { "class" : "autoget"}).find('a')['href'] results.append( FTDBSearchResult( self.opener, title, downloadURL,size, seeders) ) return results
def split_baby(split_file, split_cmd): '''Let's split baby''' logger.info('Splitting %s...', split_file.decode(headphones.SYS_ENCODING, 'replace')) logger.debug(subprocess.list2cmdline(split_cmd)) # Prevent Windows from opening a terminal window startupinfo = None if headphones.SYS_PLATFORM == "win32": startupinfo = subprocess.STARTUPINFO() try: startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW except AttributeError: startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW env = os.environ.copy() if 'xld' in split_cmd: env['PATH'] += os.pathsep + '/Applications' elif headphones.CONFIG.CUE_SPLIT_FLAC_PATH: env['PATH'] += os.pathsep + headphones.CONFIG.CUE_SPLIT_FLAC_PATH process = subprocess.Popen(split_cmd, startupinfo=startupinfo, stdin=open(os.devnull, 'rb'), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env) stdout, stderr = process.communicate() if process.returncode: logger.error('Split failed for %s', split_file.decode(headphones.SYS_ENCODING, 'replace')) out = stdout if stdout else stderr logger.error('Error details: %s', out.decode(headphones.SYS_ENCODING, 'replace')) return False else: logger.info('Split success %s', split_file.decode(headphones.SYS_ENCODING, 'replace')) return True
def getArtists(): myDB = db.DBConnection() results = myDB.select("SELECT ArtistID from artists") if not headphones.LASTFM_USERNAME: logger.warn("Last.FM username not set, not importing artists.") return logger.info("Fetching artists from Last.FM for username: %s", headphones.LASTFM_USERNAME) data = request_lastfm("library.getartists", limit=10000, user=headphones.LASTFM_USERNAME) if data and "artists" in data: artistlist = [] artists = data["artists"]["artist"] logger.debug("Fetched %d artists from Last.FM", len(artists)) for artist in artists: artist_mbid = artist["mbid"] if not any(artist_mbid in x for x in results): artistlist.append(artist_mbid) from headphones import importer for artistid in artistlist: importer.addArtisttoDB(artistid) logger.info("Imported %d new artists from Last.FM", len(artistlist))
def checkFolder(): with postprocessor_lock: myDB = db.DBConnection() snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"') for album in snatched: if album['FolderName']: # We're now checking sab config options after sending to determine renaming - but we'll keep the # iterations in just in case we can't read the config for some reason nzb_album_possibilities = [ album['FolderName'], sab_replace_dots(album['FolderName']), sab_replace_spaces(album['FolderName']), sab_replace_spaces(sab_replace_dots(album['FolderName'])) ] torrent_album_path = os.path.join(headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING) for nzb_folder_name in nzb_album_possibilities: nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, nzb_folder_name).encode(headphones.SYS_ENCODING) if os.path.exists(nzb_album_path): logger.debug('Found %s in NZB download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], nzb_album_path, album['Kind']) if os.path.exists(torrent_album_path): logger.debug('Found %s in torrent download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], torrent_album_path, album['Kind'])
def addFile(data): logger.debug('addFile(data)') qbclient = qbittorrentclient() files = {'torrents': {'filename': '', 'content': data}} return qbclient._command('command/upload', filelist=files)
def findArtist(name, limit=1): with mb_lock: artistlist = [] attempt = 0 artistResults = None chars = set('!?*') if any((c in chars) for c in name): name = '"'+name+'"' q, sleepytime = startmb() while attempt < 5: try: artistResults = q.getArtists(ws.ArtistFilter(query=name, limit=limit)) break except WebServiceError, e: logger.warn('Attempt to query MusicBrainz for %s failed: %s [%s:%i]' % (name, e, mbhost, mbport)) attempt += 1 time.sleep(5) time.sleep(sleepytime) if not artistResults: return False for result in artistResults: if result.artist.name != result.artist.getUniqueName() and limit == 1: logger.debug('Found an artist with a disambiguation: %s - doing an album based search' % name) artistdict = findArtistbyAlbum(name) if not artistdict: logger.debug('Cannot determine the best match from an artist/album search. Using top match instead') artistlist.append({ 'name': result.artist.name, 'uniquename': result.artist.getUniqueName(), 'id': u.extractUuid(result.artist.id), 'url': result.artist.id, 'score': result.score }) else: artistlist.append(artistdict) else: artistlist.append({ 'name': result.artist.name, 'uniquename': result.artist.getUniqueName(), 'id': u.extractUuid(result.artist.id), 'url': result.artist.id, 'score': result.score }) return artistlist
def getSimilar(): myDB = db.DBConnection() results = myDB.select('SELECT ArtistID from artists ORDER BY HaveTracks DESC') artistlist = [] for result in results[:12]: url = 'http://ws.audioscrobbler.com/2.0/?method=artist.getsimilar&mbid=%s&api_key=%s' % (result['ArtistID'], api_key) try: data = urllib2.urlopen(url, timeout=20).read() except: time.sleep(1) continue if len(data) < 200: continue try: d = minidom.parseString(data) except: logger.debug("Could not parse similar artist data from last.fm") node = d.documentElement artists = d.getElementsByTagName("artist") for artist in artists: namenode = artist.getElementsByTagName("name")[0].childNodes mbidnode = artist.getElementsByTagName("mbid")[0].childNodes for node in namenode: artist_name = node.data for node in mbidnode: artist_mbid = node.data try: if not any(artist_mbid in x for x in results): artistlist.append((artist_name, artist_mbid)) except: continue count = defaultdict(int) for artist, mbid in artistlist: count[artist, mbid] += 1 items = count.items() top_list = sorted(items, key=lambda x: x[1], reverse=True)[:25] random.shuffle(top_list) myDB.action('''DELETE from lastfmcloud''') for tuple in top_list: artist_name, artist_mbid = tuple[0] count = tuple[1] myDB.action('INSERT INTO lastfmcloud VALUES( ?, ?, ?)', [artist_name, artist_mbid, count])
def addTorrent(link): logger.debug('addTorrent(%s)' % link) qbclient = qbittorrentclient() args = { 'urls':link, 'savepath':headphones.CONFIG.DOWNLOAD_TORRENT_DIR } if headphones.CONFIG.QBITTORRENT_LABEL: args['label'] = headphones.CONFIG.QBITTORRENT_LABEL return qbclient._command('command/download', args, 'application/x-www-form-urlencoded' )
def get_savepath(self, hash): logger.debug('qb.get_savepath(%s)' % hash) status, torrentList = self._get_list() for torrent in torrentList: if torrent['hash']: if torrent['hash'].upper() == hash.upper(): return torrent['save_path'] return None
def renameFiles(albumpath, downloaded_track_list, release): logger.info('Renaming files') try: year = release['ReleaseDate'][:4] except TypeError: year = '' # Until tagging works better I'm going to rely on the already provided metadata for downloaded_track in downloaded_track_list: try: f = MediaFile(downloaded_track) except: logger.info("MediaFile couldn't parse: " + downloaded_track) continue if not f.track: tracknumber = '' else: tracknumber = '%02d' % f.track if not f.title: basename = unicode(os.path.basename(downloaded_track), headphones.SYS_ENCODING, errors='replace') title = os.path.splitext(basename)[0] ext = os.path.splitext(basename)[1] new_file_name = helpers.cleanTitle(title) + ext else: title = f.title values = { 'Track': tracknumber, 'Title': title, 'Artist': release['ArtistName'], 'Album': release['AlbumTitle'], 'Year': year, 'track': tracknumber, 'title': title.lower(), 'artist': release['ArtistName'].lower(), 'album': release['AlbumTitle'].lower(), 'year': year } ext = os.path.splitext(downloaded_track)[1] new_file_name = helpers.replace_all(headphones.FILE_FORMAT, values).replace('/','_') + ext new_file_name = new_file_name.replace('?','_').replace(':', '_').encode(headphones.SYS_ENCODING) new_file = os.path.join(albumpath, new_file_name) logger.debug('Renaming %s ---> %s' % (downloaded_track, new_file_name)) try: os.rename(downloaded_track, new_file) except Exception, e: logger.error('Error renaming file: %s. Error: %s' % (downloaded_track, e)) continue
def check_setting_int(config, cfg_name, item_name, def_val): try: my_val = int(config[cfg_name][item_name]) except: my_val = def_val try: config[cfg_name][item_name] = my_val except: config[cfg_name] = {} config[cfg_name][item_name] = my_val logger.debug(item_name + " -> " + str(my_val)) return my_val
def startmb(): mbuser = None mbpass = None if headphones.CONFIG.MIRROR == "musicbrainz.org": mbhost = "musicbrainz.org" mbport = 80 sleepytime = 1 elif headphones.CONFIG.MIRROR == "custom": mbhost = headphones.CONFIG.CUSTOMHOST mbport = int(headphones.CONFIG.CUSTOMPORT) mbuser = headphones.CONFIG.CUSTOMUSER mbpass = headphones.CONFIG.CUSTOMPASS sleepytime = int(headphones.CONFIG.CUSTOMSLEEP) elif headphones.CONFIG.MIRROR == "headphones": mbhost = "144.76.94.239" mbport = 8181 mbuser = headphones.CONFIG.HPUSER mbpass = headphones.CONFIG.HPPASS sleepytime = 0 else: return False musicbrainzngs.set_useragent("headphones", "0.0", "https://github.com/rembo10/headphones") musicbrainzngs.set_hostname(mbhost + ":" + str(mbport)) # Their rate limiting should be redundant to our lock if sleepytime == 0: musicbrainzngs.set_rate_limit(False) else: #calling it with an it ends up blocking all requests after the first musicbrainzngs.set_rate_limit(limit_or_interval=float(sleepytime)) mb_lock.minimum_delta = sleepytime # Add headphones credentials if headphones.CONFIG.MIRROR == "headphones" or headphones.CONFIG.CUSTOMAUTH: if not mbuser or not mbpass: logger.warn("No username or password set for MusicBrainz server") else: musicbrainzngs.hpauth(mbuser, mbpass) # Let us know if we disable custom authentication if not headphones.CONFIG.CUSTOMAUTH and headphones.CONFIG.MIRROR == "custom": musicbrainzngs.disable_hpauth() logger.debug( 'Using the following server values: MBHost: %s, MBPort: %i, Sleep Interval: %i', mbhost, mbport, sleepytime) return True
def cleanupFiles(albumpath): logger.info('Cleaning up files') for r, d, f in os.walk(albumpath): for files in f: if not any(files.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS): logger.debug('Removing: %s' % files) try: os.remove(os.path.join(r, files)) except Exception, e: logger.error( u'Could not remove file: %s. Error: %s' % (files.decode(headphones.SYS_ENCODING, 'replace'), e))
def embedAlbumArt(artwork, downloaded_track_list): logger.info('Embedding album art') for downloaded_track in downloaded_track_list: try: f = MediaFile(downloaded_track) except: logger.error('Could not read %s. Not adding album art' % downloaded_track) continue logger.debug('Adding album art to: %s' % downloaded_track) f.art = artwork f.save()
def embedAlbumArt(artwork, downloaded_track_list): logger.info('Embedding album art') for downloaded_track in downloaded_track_list: try: f = MediaFile(downloaded_track) except: logger.error(u'Could not read %s. Not adding album art' % downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) continue logger.debug('Adding album art to: %s' % downloaded_track) f.art = artwork f.save()
def startmb(forcemb=False): mbuser = None mbpass = None # Can use headphones mirror for queries if headphones.MIRROR == "headphones" or "custom": forcemb = False if forcemb or headphones.MIRROR == "musicbrainz.org": mbhost = "musicbrainz.org" mbport = 80 sleepytime = 1 elif headphones.MIRROR == "custom": mbhost = headphones.CUSTOMHOST mbport = int(headphones.CUSTOMPORT) sleepytime = int(headphones.CUSTOMSLEEP) elif headphones.MIRROR == "headphones": mbhost = "178.63.142.150" mbport = 8181 mbuser = headphones.HPUSER mbpass = headphones.HPPASS sleepytime = 0 else: mbhost = "tbueter.com" mbport = 5000 sleepytime = 0 musicbrainzngs.set_useragent("headphones", "0.0", "https://github.com/rembo10/headphones") musicbrainzngs.set_hostname(mbhost + ":" + str(mbport)) if sleepytime == 0: musicbrainzngs.set_rate_limit(False) else: musicbrainzngs.set_rate_limit(True) # Add headphones credentials if headphones.MIRROR == "headphones": if not mbuser and mbpass: logger.warn("No username or password set for VIP server") else: musicbrainzngs.hpauth(mbuser, mbpass) # Don't really need to return q anymore since ngs, but maybe we can return an 'initialized=True' instead? q = musicbrainzngs logger.debug( 'Using the following server values:\nMBHost: %s ; MBPort: %i ; Sleep Interval: %i ' % (mbhost, mbport, sleepytime)) return (q, sleepytime)
def initialize_scheduler(): """ Start the scheduled background tasks. Because this method can be called multiple times, the old tasks will be first removed. """ from headphones import updater, searcher, librarysync, postprocessor, \ torrentfinished with SCHED_LOCK: # Remove all jobs count = len(SCHED.get_jobs()) if count > 0: logger.debug("Current number of background tasks: %d", count) SCHED.shutdown() SCHED.remove_all_jobs() # Regular jobs if CONFIG.UPDATE_DB_INTERVAL > 0: SCHED.add_job( updater.dbUpdate, trigger=IntervalTrigger(hours=CONFIG.UPDATE_DB_INTERVAL)) if CONFIG.SEARCH_INTERVAL > 0: SCHED.add_job( searcher.searchforalbum, trigger=IntervalTrigger(minutes=CONFIG.SEARCH_INTERVAL)) if CONFIG.LIBRARYSCAN_INTERVAL > 0: SCHED.add_job( librarysync.libraryScan, trigger=IntervalTrigger(hours=CONFIG.LIBRARYSCAN_INTERVAL)) if CONFIG.DOWNLOAD_SCAN_INTERVAL > 0: SCHED.add_job( postprocessor.checkFolder, trigger=IntervalTrigger(minutes=CONFIG.DOWNLOAD_SCAN_INTERVAL)) # Update check if CONFIG.CHECK_GITHUB: SCHED.add_job( versioncheck.checkGithub, trigger=IntervalTrigger(minutes=CONFIG.CHECK_GITHUB_INTERVAL)) # Remove Torrent + data if Post Processed and finished Seeding if CONFIG.TORRENT_REMOVAL_INTERVAL > 0: SCHED.add_job(torrentfinished.checkTorrentFinished, trigger=IntervalTrigger( minutes=CONFIG.TORRENT_REMOVAL_INTERVAL)) # Start scheduler logger.info("(Re-)Scheduling background tasks") SCHED.start()
def getTorrentFolder(result): logger.debug('Deluge: Get torrent folder name') if not any(delugeweb_auth): _get_auth() try: post_data = json.dumps({ "method": "web.get_torrent_status", "params": [result['hash'], ["total_done"]], "id": 22 }) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['total_done'] = json.loads( response.text)['result']['total_done'] tries = 0 while result['total_done'] == 0 and tries < 10: tries += 1 time.sleep(5) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['total_done'] = json.loads( response.text)['result']['total_done'] post_data = json.dumps({ "method": "web.get_torrent_status", "params": [ result['hash'], [ "name", "save_path", "total_size", "num_files", "message", "tracker", "comment" ] ], "id": 23 }) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) result['save_path'] = json.loads(response.text)['result']['save_path'] result['name'] = json.loads(response.text)['result']['name'] return json.loads(response.text)['result']['name'] except Exception as e: logger.debug('Deluge: Could not get torrent folder name: %s' % str(e))
def checkGithub(): headphones.COMMITS_BEHIND = 0 # Get the latest version available from github logger.info('Retrieving latest version information from GitHub') url = 'https://api.github.com/repos/%s/headphones/commits/%s' % ( headphones.CONFIG.GIT_USER, headphones.CONFIG.GIT_BRANCH) version = request.request_json(url, timeout=20, validator=lambda x: type(x) == dict) if version is None: logger.warn( 'Could not get the latest version from GitHub. Are you running a local development version?') return headphones.CURRENT_VERSION headphones.LATEST_VERSION = version['sha'] logger.debug("Latest version is %s", headphones.LATEST_VERSION) # See how many commits behind we are if not headphones.CURRENT_VERSION: logger.info( 'You are running an unknown version of Headphones. Run the updater to identify your version') return headphones.LATEST_VERSION if headphones.LATEST_VERSION == headphones.CURRENT_VERSION: logger.info('Headphones is up to date') return headphones.LATEST_VERSION logger.info('Comparing currently installed version with latest GitHub version') url = 'https://api.github.com/repos/%s/headphones/compare/%s...%s' % ( headphones.CONFIG.GIT_USER, headphones.LATEST_VERSION, headphones.CURRENT_VERSION) commits = request.request_json(url, timeout=20, whitelist_status_code=404, validator=lambda x: type(x) == dict) if commits is None: logger.warn('Could not get commits behind from GitHub.') return headphones.LATEST_VERSION try: headphones.COMMITS_BEHIND = int(commits['behind_by']) logger.debug("In total, %d commits behind", headphones.COMMITS_BEHIND) except KeyError: logger.info('Cannot compare versions. Are you running a local development version?') headphones.COMMITS_BEHIND = 0 if headphones.COMMITS_BEHIND > 0: logger.info( 'New version is available. You are %s commits behind' % headphones.COMMITS_BEHIND) elif headphones.COMMITS_BEHIND == 0: logger.info('Headphones is up to date') return headphones.LATEST_VERSION
def encode_multipart(args, files, boundary=None): logger.debug('encode_multipart()') def escape_quote(s): return s.replace('"', '\\"') if boundary is None: boundary = ''.join(random.choice(_BOUNDARY_CHARS) for i in range(30)) lines = [] if args: for name, value in args.items(): lines.extend(( '--{0}'.format(boundary), 'Content-Disposition: form-data; name="{0}"'.format( escape_quote(name)), '', str(value), )) logger.debug(''.join(lines)) if files: for name, value in files.items(): filename = value['filename'] if 'mimetype' in value: mimetype = value['mimetype'] else: mimetype = mimetypes.guess_type( filename)[0] or 'application/octet-stream' lines.extend(( '--{0}'.format(boundary), 'Content-Disposition: form-data; name="{0}"; filename="{1}"'. format(escape_quote(name), escape_quote(filename)), 'Content-Type: {0}'.format(mimetype), '', value['content'], )) lines.extend(( '--{0}--'.format(boundary), '', )) body = '\r\n'.join(lines) headers = { 'Content-Type': 'multipart/form-data; boundary={0}'.format(boundary), 'Content-Length': str(len(body)), } return (body, headers)
def setTorrentPath(result): logger.debug('Deluge: Setting download path') if not any(delugeweb_auth): _get_auth() try: if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR: post_data = json.dumps({ "method": "core.set_torrent_move_completed", "params": [result['hash'], True], "id": 7 }) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) if headphones.CONFIG.DELUGE_DONE_DIRECTORY: move_to = headphones.CONFIG.DELUGE_DONE_DIRECTORY else: move_to = headphones.CONFIG.DOWNLOAD_TORRENT_DIR # If Deluge host is remote, disable path checks for now if headphones.CONFIG.DELUGE_FOREIGN != 1: if not os.path.exists(move_to): logger.debug( 'Deluge: %s directory doesn\'t exist, let\'s create it' % move_to) os.makedirs(move_to) post_data = json.dumps({ "method": "core.set_torrent_move_completed_path", "params": [result['hash'], move_to], "id": 8 }) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) return not json.loads(response.text)['error'] return True except Exception as e: logger.error('Deluge: Setting torrent move-to directory failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) return None
def removeTorrent(torrentid, remove_data=False): logger.debug('Deluge: Remove torrent %s' % torrentid) if not any(delugeweb_auth): _get_auth() try: logger.debug('Deluge: Checking if torrent %s finished seeding' % str(torrentid)) post_data = json.dumps({"method": "web.get_torrent_status", "params": [ torrentid, [ "name", "ratio", "state" ] ], "id": 26}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) try: state = json.loads(response.text)['result']['state'] except KeyError as e: logger.debug('Deluge: "state" KeyError when trying to remove torrent %s' % str(torrentid)) return False not_finished = ["queued", "seeding", "downloading", "checking", "error"] result = False if state.lower() in not_finished: logger.debug('Deluge: Torrent %s is either queued or seeding, not removing yet' % str(torrentid)) return False else: logger.debug('Deluge: Removing torrent %s' % str(torrentid)) post_data = json.dumps({"method": "core.remove_torrent", "params": [ torrentid, remove_data ], "id": 25}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) result = json.loads(response.text)['result'] return result except Exception as e: logger.error('Deluge: Removing torrent failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) return None
def _add_torrent_file(result): logger.debug('Deluge: Adding file') if not any(delugeweb_auth): _get_auth() try: # content is torrent file contents that needs to be encoded to base64 post_data = json.dumps({ "method": "core.add_torrent_file", "params": [ result['name'] + '.torrent', b64encode(result['content'].encode('utf8')), {} ], "id": 2 }) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert) result['hash'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was %s' % str(json.loads(response.text))) return json.loads(response.text)['result'] except UnicodeDecodeError: try: # content is torrent file contents that needs to be encoded to base64 # this time let's try leaving the encoding as is logger.debug( 'Deluge: There was a decoding issue, let\'s try again') post_data = json.dumps({ "method": "core.add_torrent_file", "params": [ result['name'] + '.torrent', b64encode(result['content']), {} ], "id": 22 }) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert) result['hash'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was %s' % str(json.loads(response.text))) return json.loads(response.text)['result'] except Exception as e: logger.error( 'Deluge: Adding torrent file failed after decode: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) return False except Exception as e: logger.error('Deluge: Adding torrent file failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) return False
def checkFolder(): with postprocessor_lock: myDB = db.DBConnection() snatched = myDB.select( 'SELECT * from snatched WHERE Status="Snatched"') for album in snatched: if album['FolderName']: if album['Kind'] == 'nzb': # We're now checking sab config options after sending to determine renaming - but we'll keep the # iterations in just in case we can't read the config for some reason nzb_album_possibilities = [ album['FolderName'], sab_replace_dots(album['FolderName']), sab_replace_spaces(album['FolderName']), sab_replace_spaces( sab_replace_dots(album['FolderName'])) ] for nzb_folder_name in nzb_album_possibilities: nzb_album_path = os.path.join( headphones.DOWNLOAD_DIR, nzb_folder_name).encode(headphones.SYS_ENCODING, 'replace') if os.path.exists(nzb_album_path): logger.debug( 'Found %s in NZB download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], nzb_album_path, 'nzb') if album['Kind'] == 'torrent': torrent_album_path = os.path.join( headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING, 'replace') if os.path.exists(torrent_album_path): logger.debug( 'Found %s in torrent download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], torrent_album_path, 'torrent')
def getName(hash): logger.debug('getName(%s)' % hash) qbclient = qbittorrentclient() tries = 1 while tries <= 6: time.sleep(10) status, torrentlist = qbclient._get_list() for torrent in torrentlist: if torrent['hash'].lower() == hash.lower(): return torrent['name'] tries += 1 return None
def check_setting_str(config, cfg_name, item_name, def_val, log=True): try: my_val = config[cfg_name][item_name] except: my_val = def_val try: config[cfg_name][item_name] = my_val except: config[cfg_name] = {} config[cfg_name][item_name] = my_val if log: logger.debug(item_name + " -> " + my_val) else: logger.debug(item_name + " -> ******") return my_val
def removeTorrent(hash, remove_data=False): logger.debug('removeTorrent(%s,%s)' % (hash, remove_data)) qbclient = qbittorrentclient() status, torrentList = qbclient._get_list() for torrent in torrentList: if torrent['hash'].upper() == hash.upper(): if torrent['state'] == 'uploading' or torrent['state'] == 'stalledUP': logger.info('%s has finished seeding, removing torrent and data' % torrent['name']) qbclient.remove(hash, remove_data) return True else: logger.info('%s has not finished seeding yet, torrent will not be removed, will try again on next run' % torrent['name']) return False return False
def checkFolder(): myDB = db.DBConnection() snatched = myDB.select('SELECT * from snatched WHERE Status="Snatched"') for album in snatched: if album['FolderName']: album_path = os.path.join(headphones.DOWNLOAD_DIR, album['FolderName']).encode( headphones.SYS_ENCODING) if os.path.exists(album_path): logger.debug('Found %s. Verifying....' % album['FolderName']) verify(album['AlbumID'], album_path)
def sendNZB(nzb): addToTop = False nzbgetXMLrpc = "%(protocol)s://%(username)s:%(password)s@%(host)s/xmlrpc" if not headphones.CONFIG.NZBGET_HOST: logger.error( u"No NZBget host found in configuration. Please configure it.") return False if headphones.CONFIG.NZBGET_HOST.startswith('https://'): protocol = 'https' host = headphones.CONFIG.NZBGET_HOST.replace('https://', '', 1) else: protocol = 'http' host = headphones.CONFIG.NZBGET_HOST.replace('http://', '', 1) url = nzbgetXMLrpc % { "protocol": protocol, "host": host, "username": headphones.CONFIG.NZBGET_USERNAME, "password": headphones.CONFIG.NZBGET_PASSWORD } nzbGetRPC = xmlrpclib.ServerProxy(url) try: if nzbGetRPC.writelog( "INFO", "headphones connected to drop of %s any moment now." % (nzb.name + ".nzb")): logger.debug(u"Successfully connected to NZBget") else: logger.info( u"Successfully connected to NZBget, but unable to send a message" % (nzb.name + ".nzb")) except httplib.socket.error: logger.error( u"Please check your NZBget host and port (if it is running). NZBget is not responding to this combination" ) return False except xmlrpclib.ProtocolError, e: if e.errmsg == "Unauthorized": logger.error(u"NZBget password is incorrect.") else: logger.error(u"Protocol Error: " + e.errmsg) return False
def _add_torrent_file(result): logger.debug('Deluge: Adding file') options = {} if headphones.CONFIG.DELUGE_DOWNLOAD_DIRECTORY: options['download_location'] = headphones.CONFIG.DELUGE_DOWNLOAD_DIRECTORY if headphones.CONFIG.DELUGE_DONE_DIRECTORY or headphones.CONFIG.DOWNLOAD_TORRENT_DIR: options['move_completed'] = 1 if headphones.CONFIG.DELUGE_DONE_DIRECTORY: options['move_completed_path'] = headphones.CONFIG.DELUGE_DONE_DIRECTORY else: options['move_completed_path'] = headphones.CONFIG.DOWNLOAD_TORRENT_DIR if headphones.CONFIG.DELUGE_PAUSED: options['add_paused'] = headphones.CONFIG.DELUGE_PAUSED if not any(delugeweb_auth): _get_auth() try: # content is torrent file contents that needs to be encoded to base64 post_data = json.dumps({"method": "core.add_torrent_file", "params": [result['name'] + '.torrent', b64encode(result['content'].encode('utf8')), options], "id": 2}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) result['hash'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was %s' % str(json.loads(response.text))) return json.loads(response.text)['result'] except UnicodeDecodeError: try: # content is torrent file contents that needs to be encoded to base64 # this time let's try leaving the encoding as is logger.debug('Deluge: There was a decoding issue, let\'s try again') post_data = json.dumps({"method": "core.add_torrent_file", "params": [result['name'].decode('utf8') + '.torrent', b64encode(result['content']), options], "id": 22}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) result['hash'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was %s' % str(json.loads(response.text))) return json.loads(response.text)['result'] except Exception as e: logger.error('Deluge: Adding torrent file failed after decode: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) return False except Exception as e: logger.error('Deluge: Adding torrent file failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) return False
def setTorrentLabel(result): logger.debug('Deluge: Setting label') label = headphones.CONFIG.DELUGE_LABEL if not any(delugeweb_auth): _get_auth() if ' ' in label: logger.error('Deluge: Invalid label. Label can\'t contain spaces - replacing with underscores') label = label.replace(' ', '_') if label: # check if label already exists and create it if not post_data = json.dumps({"method": 'label.get_labels', "params": [], "id": 3}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) labels = json.loads(response.text)['result'] if labels is not None: if label not in labels: try: logger.debug('Deluge: %s label doesn\'t exist in Deluge, let\'s add it' % label) post_data = json.dumps({"method": 'label.add', "params": [label], "id": 4}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) logger.debug('Deluge: %s label added to Deluge' % label) except Exception as e: logger.error('Deluge: Setting label failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) # add label to torrent post_data = json.dumps({"method": 'label.set_torrent', "params": [result['hash'], label], "id": 5}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) logger.debug('Deluge: %s label added to torrent' % label) else: logger.debug('Deluge: Label plugin not detected') return False return not json.loads(response.text)['error']
def getSimilar(): myDB = db.DBConnection() results = myDB.select( "SELECT ArtistID from artists ORDER BY HaveTracks DESC") logger.info("Fetching similar artists from Last.FM for tag cloud") artistlist = [] for result in results[:12]: data = request_lastfm("artist.getsimilar", mbid=result["ArtistId"]) time.sleep(10) if data and "similarartists" in data: artists = data["similarartists"]["artist"] for artist in artists: try: artist_mbid = artist["mbid"] artist_name = artist["name"] except TypeError: continue if not any(artist_mbid in x for x in results): artistlist.append((artist_name, artist_mbid)) # Add new artists to tag cloud logger.debug("Fetched %d artists from Last.FM", len(artistlist)) count = defaultdict(int) for artist, mbid in artistlist: count[artist, mbid] += 1 items = count.items() top_list = sorted(items, key=lambda x: x[1], reverse=True)[:25] random.shuffle(top_list) myDB.action("DELETE from lastfmcloud") for item in top_list: artist_name, artist_mbid = item[0] count = item[1] myDB.action("INSERT INTO lastfmcloud VALUES( ?, ?, ?)", [artist_name, artist_mbid, count]) logger.debug("Inserted %d artists into Last.FM tag cloud", len(top_list))
def _get_sid(self, base_url, username, password): # login so we can capture SID cookie login_data = urllib.urlencode({ 'username': username, 'password': password }) try: self.opener.open(base_url + '/login', login_data) except urllib2.URLError as err: logger.debug( 'Error getting SID. qBittorrent responded with error: ' + str(err.reason)) return for cookie in self.cookiejar: logger.debug('login cookie: ' + cookie.name + ', value: ' + cookie.value) return
def _add_torrent_url(result): logger.debug('Deluge: Adding URL') if not any(delugeweb_auth): _get_auth() try: post_data = json.dumps({"method": "web.download_torrent_from_url", "params": [result['url'], {}], "id": 32}) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth, verify=deluge_verify_cert, headers=headers) result['location'] = json.loads(response.text)['result'] logger.debug('Deluge: Response was %s' % str(json.loads(response.text))) return json.loads(response.text)['result'] except Exception as e: logger.error('Deluge: Adding torrent URL failed: %s' % str(e)) formatted_lines = traceback.format_exc().splitlines() logger.error('; '.join(formatted_lines)) return False
def _action(self, params, body=None, content_type=None): url = self.base_url + '/gui/' + '?token=' + self.token + '&' + urllib.urlencode( params) request = urllib2.Request(url) if body: request.add_data(body) request.add_header('Content-length', len(body)) if content_type: request.add_header('Content-type', content_type) try: response = self.opener.open(request) return response.code, json.loads(response.read()) except urllib2.HTTPError as err: logger.debug('URL: ' + str(url)) logger.debug('uTorrent webUI raised the following error: ' + str(err))
def setTorrentPause(result): logger.debug('Deluge: Pausing torrent') if not any(delugeweb_auth): _get_auth() if headphones.CONFIG.DELUGE_PAUSED: post_data = json.dumps({ "method": "core.pause_torrent", "params": [[result['hash']]], "id": 9 }) response = requests.post(delugeweb_url, data=post_data.encode('utf-8'), cookies=delugeweb_auth) return not json.loads(response.text)['error'] return True
def startmb(): mbuser = None mbpass = None if headphones.MIRROR == "musicbrainz.org": mbhost = "musicbrainz.org" mbport = 80 sleepytime = 1 elif headphones.MIRROR == "custom": mbhost = headphones.CUSTOMHOST mbport = int(headphones.CUSTOMPORT) sleepytime = int(headphones.CUSTOMSLEEP) elif headphones.MIRROR == "headphones": mbhost = "178.63.142.150" mbport = 8181 mbuser = headphones.HPUSER mbpass = headphones.HPPASS sleepytime = 0 else: return False musicbrainzngs.set_useragent("headphones", "0.0", "https://github.com/rembo10/headphones") musicbrainzngs.set_hostname(mbhost + ":" + str(mbport)) if sleepytime == 0: musicbrainzngs.set_rate_limit(False) else: #calling it with an it ends up blocking all requests after the first musicbrainzngs.set_rate_limit(limit_or_interval=float(sleepytime)) # Add headphones credentials if headphones.MIRROR == "headphones": if not mbuser and mbpass: logger.warn("No username or password set for VIP server") else: musicbrainzngs.hpauth(mbuser, mbpass) logger.debug( 'Using the following server values:\nMBHost: %s ; MBPort: %i ; Sleep Interval: %i ' % (mbhost, mbport, sleepytime)) return True
def addTorrent(link): logger.debug('addTorrent(%s)' % link) qbclient = qbittorrentclient() if qbclient.version == 2: return qbclient.qb.download_from_link( link, savepath=headphones.CONFIG.DOWNLOAD_TORRENT_DIR, category=headphones.CONFIG.QBITTORRENT_LABEL) else: args = { 'urls': link, 'savepath': headphones.CONFIG.DOWNLOAD_TORRENT_DIR } if headphones.CONFIG.QBITTORRENT_LABEL: args['category'] = headphones.CONFIG.QBITTORRENT_LABEL return qbclient._command('command/download', args, 'multipart/form-data')
def _inner(root, directories, files): for directory in directories: path = os.path.join(root, directory) if followlinks and os.path.islink(path): real_path = os.path.abspath(os.readlink(path)) if real_path in traversed: logger.debug("Skipping '%s' since it is a symlink to " \ "'%s', which is already visited.", path, real_path) else: traversed.append(real_path) for args in os.walk(real_path): for result in _inner(*args): yield result # Pass on actual result yield root, directories, files
def checkFolder(): with postprocessor_lock: myDB = db.DBConnection() snatched = myDB.select( 'SELECT * from snatched WHERE Status="Snatched"') for album in snatched: if album['FolderName']: # Need to check for variations due to sab renaming. Ideally we'd check the sab config via api to # figure out which options are checked, but oh well nzb_album_possibilities = [ album['FolderName'], sab_replace_dots(album['FolderName']), sab_replace_spaces(album['FolderName']), sab_replace_dots(sab_replace_spaces(album['FolderName'])) ] torrent_album_path = os.path.join( headphones.DOWNLOAD_TORRENT_DIR, album['FolderName']).encode(headphones.SYS_ENCODING) for nzb_folder_name in nzb_album_possibilities: nzb_album_path = os.path.join(headphones.DOWNLOAD_DIR, nzb_folder_name).encode( headphones.SYS_ENCODING) if os.path.exists(nzb_album_path): logger.debug( 'Found %s in NZB download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], nzb_album_path) if os.path.exists(torrent_album_path): logger.debug( 'Found %s in torrent download folder. Verifying....' % album['FolderName']) verify(album['AlbumID'], torrent_album_path)