diff --git a/data/interfaces/default/config.html b/data/interfaces/default/config.html index 60aeaf99..d38fa710 100644 --- a/data/interfaces/default/config.html +++ b/data/interfaces/default/config.html @@ -124,7 +124,7 @@
Usenet - Sabnzbd NZBget Black Hole + Sabnzbd NZBget Black Hole
@@ -192,7 +192,7 @@
Torrents - Black Hole Transmission + Black Hole Transmission uTorrent (Beta)
@@ -237,7 +237,11 @@
- + +
+
+ +
@@ -293,7 +297,7 @@
- Newznab + Custom Newznab Providers
@@ -812,6 +816,14 @@
+
+

MPC

+
+ +
+
+ +
diff --git a/headphones/__init__.py b/headphones/__init__.py index b354291b..380a31bf 100644 --- a/headphones/__init__.py +++ b/headphones/__init__.py @@ -154,6 +154,7 @@ TRANSMISSION_PASSWORD = None UTORRENT_HOST = None UTORRENT_USERNAME = None UTORRENT_PASSWORD = None +UTORRENT_LABEL = None NEWZNAB = False NEWZNAB_HOST = None @@ -284,6 +285,7 @@ SONGKICK_ENABLED = False SONGKICK_APIKEY = None SONGKICK_LOCATION = None SONGKICK_FILTER_ENABLED = False +MPC_ENABLED = False CACHE_SIZEMB = 32 JOURNAL_MODE = None @@ -346,7 +348,7 @@ def initialize(): RUTRACKER, RUTRACKER_USER, RUTRACKER_PASSWORD, WHATCD, WHATCD_USERNAME, WHATCD_PASSWORD, DOWNLOAD_TORRENT_DIR, \ LIBRARYSCAN, LIBRARYSCAN_INTERVAL, DOWNLOAD_SCAN_INTERVAL, UPDATE_DB_INTERVAL, MB_IGNORE_AGE, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \ NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_HOST, HEADPHONES_INDEXER, NZBMATRIX, TRANSMISSION_HOST, TRANSMISSION_USERNAME, TRANSMISSION_PASSWORD, \ - UTORRENT_HOST, UTORRENT_USERNAME, UTORRENT_PASSWORD, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS, \ + UTORRENT_HOST, UTORRENT_USERNAME, UTORRENT_PASSWORD, UTORRENT_LABEL, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS, \ NZBSORG, NZBSORG_UID, NZBSORG_HASH, NZBSRUS, NZBSRUS_UID, NZBSRUS_APIKEY, OMGWTFNZBS, OMGWTFNZBS_UID, OMGWTFNZBS_APIKEY, \ NZB_DOWNLOADER, TORRENT_DOWNLOADER, PREFERRED_WORDS, REQUIRED_WORDS, IGNORED_WORDS, LASTFM_USERNAME, \ INTERFACE, FOLDER_PERMISSIONS, FILE_PERMISSIONS, ENCODERFOLDER, ENCODER_PATH, ENCODER, XLDPROFILE, BITRATE, SAMPLINGFREQUENCY, \ @@ -511,6 +513,7 @@ def initialize(): UTORRENT_HOST = check_setting_str(CFG, 'uTorrent', 'utorrent_host', '') UTORRENT_USERNAME = check_setting_str(CFG, 'uTorrent', 'utorrent_username', '') UTORRENT_PASSWORD = check_setting_str(CFG, 'uTorrent', 'utorrent_password', '') + UTORRENT_LABEL = check_setting_str(CFG, 'uTorrent', 'utorrent_label', '') NEWZNAB = bool(check_setting_int(CFG, 'Newznab', 'newznab', 0)) NEWZNAB_HOST = check_setting_str(CFG, 'Newznab', 'newznab_host', '') @@ -941,6 +944,7 @@ def config_write(): new_config['uTorrent']['utorrent_host'] = UTORRENT_HOST new_config['uTorrent']['utorrent_username'] = UTORRENT_USERNAME new_config['uTorrent']['utorrent_password'] = UTORRENT_PASSWORD + new_config['uTorrent']['utorrent_label'] = UTORRENT_LABEL new_config['Newznab'] = {} new_config['Newznab']['newznab'] = int(NEWZNAB) diff --git a/headphones/importer.py b/headphones/importer.py index 79026040..c442ff07 100644 --- a/headphones/importer.py +++ b/headphones/importer.py @@ -226,15 +226,11 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): skip_log = 0 #Make a user configurable variable to skip update of albums with release dates older than this date (in days) pause_delta = headphones.MB_IGNORE_AGE - - check_release_date = myDB.action("SELECT ReleaseDate, Status from albums WHERE ArtistID=? AND AlbumTitle=?", (artistid, al_title)).fetchone() - - #Skip update if Status set - if check_release_date and check_release_date[1]: - logger.info("[%s] Not updating: %s (Status is %s, skipping)" % (artist['artist_name'], rg['title'], check_release_date[1])) - continue + + rg_exists = myDB.action("SELECT * from albums WHERE AlbumID=?", [rg['id']]).fetchone() if not forcefull: + check_release_date = rg_exists['ReleaseDate'] if check_release_date: if check_release_date[0] is None: logger.info("[%s] Now updating: %s (No Release Date)" % (artist['artist_name'], rg['title'])) @@ -268,11 +264,6 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): logger.info("[%s] Now adding/updating: %s (Comprehensive Force)" % (artist['artist_name'], rg['title'])) new_releases = mb.get_new_releases(rgid,includeExtras,forcefull) - #What this does is adds new releases per artist to the allalbums + alltracks databases - #new_releases = mb.get_new_releases(rgid,includeExtras) - #print al_title - #print new_releases - if new_releases != 0: #Dump existing hybrid release since we're repackaging/replacing it myDB.action("DELETE from albums WHERE ReleaseID=?", [rg['id']]) @@ -381,7 +372,7 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): # If there's no release in the main albums tables, add the default (hybrid) # If there is a release, check the ReleaseID against the AlbumID to see if they differ (user updated) # check if the album already exists - rg_exists = myDB.action("SELECT * from albums WHERE AlbumID=?", [rg['id']]).fetchone() + if not rg_exists: releaseid = rg['id'] else: @@ -402,11 +393,14 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False): "ReleaseFormat": album['ReleaseFormat'] } - if not rg_exists: - + if rg_exists: + newValueDict['DateAdded'] = rg_exists['DateAdded'] + newValueDict['Status'] = rg_exists['Status'] + + else: today = helpers.today() - newValueDict['DateAdded']= today + newValueDict['DateAdded'] = today if headphones.AUTOWANT_ALL: newValueDict['Status'] = "Wanted" diff --git a/headphones/notifiers.py b/headphones/notifiers.py index a549db80..f5e4ff12 100644 --- a/headphones/notifiers.py +++ b/headphones/notifiers.py @@ -169,6 +169,17 @@ class PROWL: self.notify('ZOMG Lazors Pewpewpew!', 'Test Message') +class MPC: + + def __init__(self): + + pass + + def notify( self ): + + subprocess.call( ["mpc", "update"] ) + + class XBMC: def __init__(self): @@ -192,9 +203,9 @@ class XBMC: url = host + '/jsonrpc' if self.password: - response = request.request_json(url, method="POST", data=simplejson.dumps(data), headers=headers, auth=(self.username, self.password)) + response = request.request_json(url, method="post", data=simplejson.dumps(data), headers=headers, auth=(self.username, self.password)) else: - response = request.request_json(url, method="POST", data=simplejson.dumps(data), headers=headers) + response = request.request_json(url, method="post", data=simplejson.dumps(data), headers=headers) if response: return response[0]['result'] diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py index d1ccbee4..05e90880 100644 --- a/headphones/postprocessor.py +++ b/headphones/postprocessor.py @@ -31,7 +31,7 @@ from headphones import logger, helpers, request, mb, music_encoder postprocessor_lock = threading.Lock() def checkFolder(): - + with postprocessor_lock: myDB = db.DBConnection() @@ -42,30 +42,18 @@ def checkFolder(): 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 + download_dir = headphones.DOWNLOAD_DIR + else: + download_dir = headphones.DOWNLOAD_TORRENT_DIR - nzb_album_possibilities = [ album['FolderName'], - helpers.sab_replace_dots(album['FolderName']), - helpers.sab_replace_spaces(album['FolderName']), - helpers.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': + album_path = os.path.join(download_dir, album['FolderName']).encode(headphones.SYS_ENCODING,'replace') + logger.info("Checking if %s exists" % album_path) + if os.path.exists(album_path): + logger.info('Found "' + album['FolderName'] + '" in ' + album['Kind'] + ' download folder. Verifying....') + verify(album['AlbumID'], album_path, album['Kind']) - 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') + else: + logger.info("No folder name found for " + album['Title']) def verify(albumid, albumpath, Kind=None, forced=False): @@ -364,7 +352,7 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, headphones.MOVE_FILES: if not os.access(downloaded_track, os.W_OK): - logger.error("Track file is not writeable, which is equired for some post processing steps: %s", downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) + logger.error("Track file is not writeable, which is required for some post processing steps: %s", downloaded_track.decode(headphones.SYS_ENCODING, 'replace')) return #start encoding @@ -502,6 +490,10 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list, boxcar = notifiers.BOXCAR() boxcar.notify('Headphones processed: ' + pushmessage, "Download and Postprocessing completed", release['AlbumID']) + if headphones.MPC_ENABLED: + mpc = notifiers.MPC() + mpc.notify() + def embedAlbumArt(artwork, downloaded_track_list): logger.info('Embedding album art') diff --git a/headphones/searcher.py b/headphones/searcher.py index c1aee71a..20b5f16b 100644 --- a/headphones/searcher.py +++ b/headphones/searcher.py @@ -31,7 +31,7 @@ import subprocess import headphones from headphones.common import USER_AGENT from headphones import logger, db, helpers, classes, sab, nzbget, request -from headphones import transmission, notifiers +from headphones import utorrent, transmission, notifiers import lib.bencode as bencode @@ -307,13 +307,13 @@ def searchNZB(album, new=False, losslessOnly=False): params = { "t": "search", "cat": categories, - "apikey": '89edf227c1de9b3de50383fff11466c6', + "apikey": '964d601959918a578a670984bdee9357', "maxage": headphones.USENET_RETENTION, "q": term } data = request.request_feed( - url="http://headphones.codeshy.com/newznab/api", + url="http://indexer.codeshy.com/api", params=params, headers=headers, auth=(headphones.HPUSER, headphones.HPPASS) ) @@ -696,6 +696,30 @@ def send_to_downloader(data, bestqual, album): except Exception, e: logger.exception("Unhandled exception") + else: + logger.info("Sending torrent to uTorrent") + + # rutracker needs cookies to be set, pass the .torrent file instead of url + if bestqual[3] == 'rutracker.org': + file_or_url = rutracker.get_torrent(bestqual[2]) + else: + file_or_url = bestqual[2] + + folder_name = utorrent.addTorrent(file_or_url) + + if folder_name: + logger.info('Torrent folder name: %s' % folder_name) + else: + logger.error('Torrent folder name could not be determined') + return + + # remove temp .torrent file created above + if bestqual[3] == 'rutracker.org': + try: + shutil.rmtree(os.path.split(file_or_url)[0]) + except Exception, e: + logger.exception("Unhandled exception") + myDB = db.DBConnection() myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [album['AlbumID']]) myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?, ?)', [album['AlbumID'], bestqual[0], bestqual[1], bestqual[2], "Snatched", folder_name, kind]) @@ -1232,6 +1256,8 @@ def searchTorrent(album, new=False, losslessOnly=False): continue else: url = item.findAll("a")[3]['href'] + if url.lower().startswith("//"): + url = "http:" + url formatted_size = re.search('Size (.*),', unicode(item)).group(1).replace(u'\xa0', ' ') size = helpers.piratesize(formatted_size) if size < maxsize and minimumseeders < seeds and url != None: @@ -1424,4 +1450,4 @@ def preprocess(resultlist): logger.error("Couldn't retrieve the best nzb. Skipping.") continue - return (None, None) \ No newline at end of file + return (None, None) diff --git a/headphones/utorrent.py b/headphones/utorrent.py index fca5f5e3..7aad16f5 100644 --- a/headphones/utorrent.py +++ b/headphones/utorrent.py @@ -12,3 +12,87 @@ # # You should have received a copy of the GNU General Public License # along with Headphones. If not, see . + +import re +import os +import time +import base64 +import headphones + +import simplejson as json + +from headphones import logger, notifiers, request + +# This is just a simple script to send torrents to transmission. The +# intention is to turn this into a class where we can check the state +# of the download, set the download dir, etc. +# TODO: Store the session id so we don't need to make 2 calls +# Store torrent id so we can check up on it + + +def addTorrent(link): + + host = headphones.UTORRENT_HOST + username = headphones.UTORRENT_USERNAME + password = headphones.UTORRENT_PASSWORD + label = headphones.UTORRENT_LABEL + token = '' + + if not host.startswith('http'): + host = 'http://' + host + + if host.endswith('/'): + host = host[:-1] + + if host.endswith('/gui'): + host = host + '/' + else: + host = host + '/gui/' + + # Retrieve session id + auth = (username, password) if username and password else None + token_request = request.request_response(host + 'token.html', auth=auth) + + token = re.findall('(.*?)