-
-
- Folder your Download program watches for Torrents
-
-
-
-
- Number of minimum seeders a torrent must have to be accepted
-
-
-
-
- Full path where your torrent client downloads your music e.g. /Users/name/Downloads/music
-
-
-
-
+ Black Hole Transmission
+
+
+
+
+
+ Folder your Download program watches for Torrents
+
+
+
+
+ usually http://localhost:9091
+
+
+
+
+
+
+
+
+
+
+ Note: With Transmission, you can specify a different download directory for downloads sent from Headphones.
+ Set it in the Music Download Directory below
+
+
+
+
+
+ usually http://localhost:9091
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Number of minimum seeders a torrent must have to be accepted
+
+
+
+
+ Full path where your torrent client downloads your music e.g. /Users/name/Downloads/music
+
+
+
+
+
+
@@ -315,14 +355,18 @@
-
-
+
+
+
+
+
-
+
+
@@ -1068,6 +1112,22 @@
$("#sabnzbd_options,#nzbget_options").hide();
$("#blackhole_options").show();
}
+
+ if ($("#torrent_downloader_blackhole").is(":checked"))
+ {
+ $("#transmission_options,#utorrent_options").hide();
+ $("#torrent_blackhole_options").show();
+ }
+ if ($("#torrent_downloader_transmission").is(":checked"))
+ {
+ $("#torrent_blackhole_options,#utorrent_options").hide();
+ $("#transmission_options").show();
+ }
+ if ($("#torrent_downloader_utorrent").is(":checked"))
+ {
+ $("#torrent_blackhole_options,#transmission_options").hide();
+ $("#utorrent_options").show();
+ }
$('input[type=radio]').change(function(){
if ($("#preferred_bitrate").is(":checked"))
@@ -1098,7 +1158,19 @@
{
$("#sabnzbd_options,#nzbget_options").fadeOut("fast", function() { $("#blackhole_options").fadeIn() });
}
- });
+ if ($("#torrent_downloader_blackhole").is(":checked"))
+ {
+ $("#transmission_options,#utorrent_options").fadeOut("fast", function() { $("#torrent_blackhole_options").fadeIn() });
+ }
+ if ($("#torrent_downloader_transmission").is(":checked"))
+ {
+ $("#torrent_blackhole_options,#utorrent_options").fadeOut("fast", function() { $("#transmission_options").fadeIn() });
+ }
+ if ($("#torrent_downloader_utorrent").is(":checked"))
+ {
+ $("#torrent_blackhole_options,#transmission_options").fadeOut("fast", function() { $("#utorrent_options").fadeIn() });
+ }
+ });
$("#mirror").change(handleNewServerSelection);
handleNewServerSelection.apply($("#mirror"));
diff --git a/headphones/__init__.py b/headphones/__init__.py
index 4df3318d..540954a9 100644
--- a/headphones/__init__.py
+++ b/headphones/__init__.py
@@ -109,7 +109,8 @@ ADD_ALBUM_ART = False
ALBUM_ART_FORMAT = None
EMBED_ALBUM_ART = False
EMBED_LYRICS = False
-NZB_DOWNLOADER = None
+NZB_DOWNLOADER = None # 0: sabnzbd, 1: nzbget, 2: blackhole
+TORRENT_DOWNLOADER = None # 0: blackhole, 1: transmission, 2: utorrent
DOWNLOAD_DIR = None
BLACKHOLE = None
BLACKHOLE_DIR = None
@@ -138,9 +139,13 @@ NZBGET_HOST = None
HEADPHONES_INDEXER = False
-NZBMATRIX = False
-NZBMATRIX_USERNAME = None
-NZBMATRIX_APIKEY = None
+TRANSMISSION_HOST = None
+TRANSMISSION_USERNAME = None
+TRANSMISSION_PASSWORD = None
+
+UTORRENT_HOST = None
+UTORRENT_USERNAME = None
+UTORRENT_PASSWORD = None
NEWZNAB = False
NEWZNAB_HOST = None
@@ -152,10 +157,6 @@ NZBSORG = False
NZBSORG_UID = None
NZBSORG_HASH = None
-NEWZBIN = False
-NEWZBIN_UID = None
-NEWZBIN_PASSWORD = None
-
NZBSRUS = False
NZBSRUS_UID = None
NZBSRUS_APIKEY = None
@@ -177,6 +178,7 @@ NUMBEROFSEEDERS = 10
ISOHUNT = None
KAT = None
MININOVA = None
+PIRATEBAY = None
WAFFLES = None
WAFFLES_UID = None
WAFFLES_PASSKEY = None
@@ -290,19 +292,18 @@ def initialize():
LOSSLESS_DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, \
RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, CLEANUP_FILES, INCLUDE_EXTRAS, EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, KEEP_TORRENT_FILES, \
ADD_ALBUM_ART, ALBUM_ART_FORMAT, EMBED_ALBUM_ART, EMBED_LYRICS, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, SEARCH_INTERVAL, \
- TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, ISOHUNT, KAT, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, \
+ TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, ISOHUNT, KAT, PIRATEBAY, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, \
RUTRACKER, RUTRACKER_USER, RUTRACKER_PASSWORD, WHATCD, WHATCD_USERNAME, WHATCD_PASSWORD, DOWNLOAD_TORRENT_DIR, \
LIBRARYSCAN, LIBRARYSCAN_INTERVAL, DOWNLOAD_SCAN_INTERVAL, SAB_HOST, SAB_USERNAME, SAB_PASSWORD, SAB_APIKEY, SAB_CATEGORY, \
- NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_HOST, HEADPHONES_INDEXER, NZBMATRIX, NZBMATRIX_USERNAME, NZBMATRIX_APIKEY, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS, \
- NZBSORG, NZBSORG_UID, NZBSORG_HASH, NEWZBIN, NEWZBIN_UID, NEWZBIN_PASSWORD, NZBSRUS, NZBSRUS_UID, NZBSRUS_APIKEY, \
- NZB_DOWNLOADER, PREFERRED_WORDS, REQUIRED_WORDS, IGNORED_WORDS, \
+ 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, \
+ NZBSORG, NZBSORG_UID, NZBSORG_HASH, NZBSRUS, NZBSRUS_UID, NZBSRUS_APIKEY, NZB_DOWNLOADER, TORRENT_DOWNLOADER, PREFERRED_WORDS, REQUIRED_WORDS, IGNORED_WORDS, \
LASTFM_USERNAME, INTERFACE, FOLDER_PERMISSIONS, ENCODERFOLDER, ENCODER_PATH, ENCODER, XLDPROFILE, BITRATE, SAMPLINGFREQUENCY, \
MUSIC_ENCODER, ADVANCEDENCODER, ENCODEROUTPUTFORMAT, ENCODERQUALITY, ENCODERVBRCBR, ENCODERLOSSLESS, DELETE_LOSSLESS_FILES, \
PROWL_ENABLED, PROWL_PRIORITY, PROWL_KEYS, PROWL_ONSNATCH, PUSHOVER_ENABLED, PUSHOVER_PRIORITY, PUSHOVER_KEYS, PUSHOVER_ONSNATCH, MIRRORLIST, \
MIRROR, CUSTOMHOST, CUSTOMPORT, CUSTOMSLEEP, HPUSER, HPPASS, XBMC_ENABLED, XBMC_HOST, XBMC_USERNAME, XBMC_PASSWORD, XBMC_UPDATE, \
XBMC_NOTIFY, NMA_ENABLED, NMA_APIKEY, NMA_PRIORITY, NMA_ONSNATCH, SYNOINDEX_ENABLED, ALBUM_COMPLETION_PCT, PREFERRED_BITRATE_HIGH_BUFFER, \
- PREFERRED_BITRATE_LOW_BUFFER, PREFERRED_BITRATE_ALLOW_LOSSLESS, CACHE_SIZEMB, \
- UMASK
+ PREFERRED_BITRATE_LOW_BUFFER, PREFERRED_BITRATE_ALLOW_LOSSLESS, CACHE_SIZEMB, UMASK
if __INITIALIZED__:
return False
@@ -311,12 +312,12 @@ def initialize():
CheckSection('General')
CheckSection('SABnzbd')
CheckSection('NZBget')
+ CheckSection('Transmission')
+ CheckSection('uTorrent')
CheckSection('Headphones')
- CheckSection('NZBMatrix')
CheckSection('Newznab')
CheckSection('NZBsorg')
CheckSection('NZBsRus')
- CheckSection('Newzbin')
CheckSection('Waffles')
CheckSection('Rutracker')
CheckSection('What.cd')
@@ -377,6 +378,7 @@ def initialize():
EMBED_ALBUM_ART = bool(check_setting_int(CFG, 'General', 'embed_album_art', 0))
EMBED_LYRICS = bool(check_setting_int(CFG, 'General', 'embed_lyrics', 0))
NZB_DOWNLOADER = check_setting_int(CFG, 'General', 'nzb_downloader', 0)
+ TORRENT_DOWNLOADER = check_setting_int(CFG, 'General', 'torrent_downloader', 0)
DOWNLOAD_DIR = check_setting_str(CFG, 'General', 'download_dir', '')
BLACKHOLE = bool(check_setting_int(CFG, 'General', 'blackhole', 0))
BLACKHOLE_DIR = check_setting_str(CFG, 'General', 'blackhole_dir', '')
@@ -396,6 +398,7 @@ def initialize():
NUMBEROFSEEDERS = check_setting_str(CFG, 'General', 'numberofseeders', '10')
ISOHUNT = bool(check_setting_int(CFG, 'General', 'isohunt', 0))
KAT = bool(check_setting_int(CFG, 'General', 'kat', 0))
+ PIRATEBAY = bool(check_setting_int(CFG, 'General', 'piratebay', 0))
MININOVA = bool(check_setting_int(CFG, 'General', 'mininova', 0))
DOWNLOAD_TORRENT_DIR = check_setting_str(CFG, 'General', 'download_torrent_dir', '')
@@ -424,9 +427,13 @@ def initialize():
HEADPHONES_INDEXER = bool(check_setting_int(CFG, 'Headphones', 'headphones_indexer', 0))
- NZBMATRIX = bool(check_setting_int(CFG, 'NZBMatrix', 'nzbmatrix', 0))
- NZBMATRIX_USERNAME = check_setting_str(CFG, 'NZBMatrix', 'nzbmatrix_username', '')
- NZBMATRIX_APIKEY = check_setting_str(CFG, 'NZBMatrix', 'nzbmatrix_apikey', '')
+ TRANSMISSION_HOST = check_setting_str(CFG, 'Transmission', 'transmission_host', '')
+ TRANSMISSION_USERNAME = check_setting_str(CFG, 'Transmission', 'transmission_username', '')
+ TRANSMISSION_PASSWORD = check_setting_str(CFG, 'Transmission', 'transmission_password', '')
+
+ 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', '')
NEWZNAB = bool(check_setting_int(CFG, 'Newznab', 'newznab', 0))
NEWZNAB_HOST = check_setting_str(CFG, 'Newznab', 'newznab_host', '')
@@ -441,10 +448,6 @@ def initialize():
NZBSORG_UID = check_setting_str(CFG, 'NZBsorg', 'nzbsorg_uid', '')
NZBSORG_HASH = check_setting_str(CFG, 'NZBsorg', 'nzbsorg_hash', '')
- NEWZBIN = bool(check_setting_int(CFG, 'Newzbin', 'newzbin', 0))
- NEWZBIN_UID = check_setting_str(CFG, 'Newzbin', 'newzbin_uid', '')
- NEWZBIN_PASSWORD = check_setting_str(CFG, 'Newzbin', 'newzbin_password', '')
-
NZBSRUS = bool(check_setting_int(CFG, 'NZBsRus', 'nzbsrus', 0))
NZBSRUS_UID = check_setting_str(CFG, 'NZBsRus', 'nzbsrus_uid', '')
NZBSRUS_APIKEY = check_setting_str(CFG, 'NZBsRus', 'nzbsrus_apikey', '')
@@ -727,6 +730,7 @@ def config_write():
new_config['General']['embed_album_art'] = int(EMBED_ALBUM_ART)
new_config['General']['embed_lyrics'] = int(EMBED_LYRICS)
new_config['General']['nzb_downloader'] = NZB_DOWNLOADER
+ new_config['General']['torrent_downloader'] = TORRENT_DOWNLOADER
new_config['General']['download_dir'] = DOWNLOAD_DIR
new_config['General']['blackhole_dir'] = BLACKHOLE_DIR
new_config['General']['usenet_retention'] = USENET_RETENTION
@@ -741,6 +745,7 @@ def config_write():
new_config['General']['isohunt'] = int(ISOHUNT)
new_config['General']['kat'] = int(KAT)
new_config['General']['mininova'] = int(MININOVA)
+ new_config['General']['piratebay'] = int(PIRATEBAY)
new_config['General']['download_torrent_dir'] = DOWNLOAD_TORRENT_DIR
new_config['Waffles'] = {}
@@ -775,14 +780,19 @@ def config_write():
new_config['NZBget']['nzbget_password'] = NZBGET_PASSWORD
new_config['NZBget']['nzbget_category'] = NZBGET_CATEGORY
new_config['NZBget']['nzbget_host'] = NZBGET_HOST
-
+
new_config['Headphones'] = {}
new_config['Headphones']['headphones_indexer'] = int(HEADPHONES_INDEXER)
- new_config['NZBMatrix'] = {}
- new_config['NZBMatrix']['nzbmatrix'] = int(NZBMATRIX)
- new_config['NZBMatrix']['nzbmatrix_username'] = NZBMATRIX_USERNAME
- new_config['NZBMatrix']['nzbmatrix_apikey'] = NZBMATRIX_APIKEY
+ new_config['Transmission'] = {}
+ new_config['Transmission']['transmission_host'] = TRANSMISSION_HOST
+ new_config['Transmission']['transmission_username'] = TRANSMISSION_USERNAME
+ new_config['Transmission']['transmission_password'] = TRANSMISSION_PASSWORD
+
+ new_config['uTorrent'] = {}
+ new_config['uTorrent']['utorrent_host'] = UTORRENT_HOST
+ new_config['uTorrent']['utorrent_username'] = UTORRENT_USERNAME
+ new_config['uTorrent']['utorrent_password'] = UTORRENT_PASSWORD
new_config['Newznab'] = {}
new_config['Newznab']['newznab'] = int(NEWZNAB)
@@ -802,11 +812,6 @@ def config_write():
new_config['NZBsorg']['nzbsorg_uid'] = NZBSORG_UID
new_config['NZBsorg']['nzbsorg_hash'] = NZBSORG_HASH
- new_config['Newzbin'] = {}
- new_config['Newzbin']['newzbin'] = int(NEWZBIN)
- new_config['Newzbin']['newzbin_uid'] = NEWZBIN_UID
- new_config['Newzbin']['newzbin_password'] = NEWZBIN_PASSWORD
-
new_config['NZBsRus'] = {}
new_config['NZBsRus']['nzbsrus'] = int(NZBSRUS)
new_config['NZBsRus']['nzbsrus_uid'] = NZBSRUS_UID
diff --git a/headphones/helpers.py b/headphones/helpers.py
index c35e6a53..19c98b7e 100644
--- a/headphones/helpers.py
+++ b/headphones/helpers.py
@@ -142,6 +142,23 @@ def mb_to_bytes(mb_str):
result = re.search('^(\d+(?:\.\d+)?)\s?(?:mb)?', mb_str, flags=re.I)
if result:
return int(float(result.group(1))*1048576)
+
+def piratesize(size):
+ split = size.split(" ")
+ factor = float(split[0])
+ unit = split[1]
+ if unit == 'MiB':
+ size = factor * 1048576
+ elif unit == 'GiB':
+ size = factor * 1073741824
+ elif unit == 'KiB':
+ size = factor * 1024
+ elif unit == "B":
+ size = factor
+ else:
+ size = 0
+
+ return size
def replace_all(text, dic):
diff --git a/headphones/librarysync.py b/headphones/librarysync.py
index 05ff49a0..5ee57d8b 100644
--- a/headphones/librarysync.py
+++ b/headphones/librarysync.py
@@ -28,7 +28,10 @@ def libraryScan(dir=None, append=False, ArtistID=None, ArtistName=None, cron=Fal
return
if not dir:
- dir = headphones.MUSIC_DIR
+ if not headphones.MUSIC_DIR:
+ return
+ else:
+ dir = headphones.MUSIC_DIR
# If we're appending a dir, it's coming from the post processor which is
# already bytestring
diff --git a/headphones/postprocessor.py b/headphones/postprocessor.py
index fd54f17f..dea6773a 100644
--- a/headphones/postprocessor.py
+++ b/headphones/postprocessor.py
@@ -320,14 +320,28 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list,
logger.info('Starting post-processing for: %s - %s' % (release['ArtistName'], release['AlbumTitle']))
# Check to see if we're preserving the torrent dir
if headphones.KEEP_TORRENT_FILES and Kind=="torrent":
- new_folder = os.path.join(os.path.dirname(albumpath), ('temp' + release['AlbumTitle'][:8]).encode(headphones.SYS_ENCODING, 'replace'))
- new_folder = new_folder.strip()
+ new_folder = os.path.join(albumpath, 'headphones-modified').encode(headphones.SYS_ENCODING, 'replace')
+ logger.info("Copying files to 'headphones-modified' subfolder to preserve downleaded files for seeding")
try:
shutil.copytree(albumpath, new_folder)
+ # Update the album path with the new location
albumpath = new_folder
except Exception, e:
logger.warn("Cannot copy/move files to temp folder: " + new_folder.decode(headphones.SYS_ENCODING, 'replace') + ". Not continuing. Error: " + str(e))
return
+
+ # Need to update the downloaded track list with the new location.
+ # Could probably just throw in the "headphones-modified" folder,
+ # but this is good to make sure we're not counting files that may have failed to move
+ downloaded_track_list = []
+ downloaded_cuecount = 0
+
+ for r,d,f in os.walk(albumpath):
+ for files in f:
+ if any(files.lower().endswith('.' + x.lower()) for x in headphones.MEDIA_FORMATS):
+ downloaded_track_list.append(os.path.join(r, files))
+ elif files.lower().endswith('.cue'):
+ downloaded_cuecount += 1
#start encoding
if headphones.MUSIC_ENCODER:
downloaded_track_list=music_encoder.encode(albumpath)
@@ -860,6 +874,9 @@ def forcePostProcess():
# Get a list of folders in the download_dir
folders = []
for download_dir in download_dirs:
+ if not os.path.isdir(download_dir):
+ logger.warn('Directory ' + download_dir.decode(headphones.SYS_ENCODING, 'replace') + ' does not exist. Skipping')
+ continue
for folder in os.listdir(download_dir):
path_to_folder = os.path.join(download_dir, folder)
if os.path.isdir(path_to_folder):
diff --git a/headphones/searcher.py b/headphones/searcher.py
index c46504bf..3cbb3068 100644
--- a/headphones/searcher.py
+++ b/headphones/searcher.py
@@ -17,6 +17,7 @@
import urllib, urllib2, urlparse, httplib
import lib.feedparser as feedparser
+from bs4 import BeautifulSoup
from lib.pygazelle import api as gazelleapi
from lib.pygazelle import encoding as gazelleencoding
from lib.pygazelle import format as gazelleformat
@@ -32,6 +33,7 @@ import string
import headphones, exceptions
from headphones import logger, db, helpers, classes, sab, nzbget
+from headphones import transmission
import lib.bencode as bencode
@@ -117,7 +119,7 @@ def searchforalbum(albumid=None, new=False, lossless=False):
else:
foundNZB = searchNZB(result['AlbumID'], new)
- if (headphones.KAT or headphones.ISOHUNT or headphones.MININOVA or headphones.WAFFLES or headphones.RUTRACKER or headphones.WHATCD) and foundNZB == "none":
+ if (headphones.KAT or headphones.PIRATEBAY or headphones.ISOHUNT or headphones.MININOVA or headphones.WAFFLES or headphones.RUTRACKER or headphones.WHATCD) and foundNZB == "none":
if result['Status'] == "Wanted Lossless":
searchTorrent(result['AlbumID'], new, losslessOnly=True)
@@ -127,10 +129,11 @@ def searchforalbum(albumid=None, new=False, lossless=False):
else:
foundNZB = "none"
+
if (headphones.HEADPHONES_INDEXER or headphones.NEWZNAB or headphones.NZBSORG or headphones.NZBSRUS) and (headphones.SAB_HOST or headphones.BLACKHOLE_DIR or headphones.NZBGET_HOST):
foundNZB = searchNZB(albumid, new, lossless)
- if (headphones.KAT or headphones.ISOHUNT or headphones.MININOVA or headphones.WAFFLES or headphones.RUTRACKER or headphones.WHATCD) and foundNZB == "none":
+ if (headphones.KAT or headphones.PIRATEBAY or headphones.ISOHUNT or headphones.MININOVA or headphones.WAFFLES or headphones.RUTRACKER or headphones.WHATCD) and foundNZB == "none":
searchTorrent(albumid, new, lossless)
def searchNZB(albumid=None, new=False, losslessOnly=False):
@@ -483,7 +486,8 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
for result in resultlist:
if high_size_limit and (result[1] > high_size_limit):
- logger.info(result[0] + " is too large for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Maxsize: " + helpers.bytes_to_mb(high_size_limit))
+
+ logger.info(result[0] + " is too large for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Maxsize: " + helpers.bytes_to_mb(high_size_limit) + ")")
# Add lossless nzbs to the "flac list" which we can use if there are no good lossy matches
if 'flac' in result[0].lower():
@@ -492,7 +496,7 @@ def searchNZB(albumid=None, new=False, losslessOnly=False):
continue
if low_size_limit and (result[1] < low_size_limit):
- logger.info(result[0] + " is too small for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Minsize: " + helpers.bytes_to_mb(low_size_limit))
+ logger.info(result[0] + " is too small for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Minsize: " + helpers.bytes_to_mb(low_size_limit) + ")")
continue
delta = abs(targetsize - result[1])
@@ -1068,6 +1072,58 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
gazelle.generate_torrent_link(torrent.id),
provider))
+ # Pirate Bay
+ if headphones.PIRATEBAY and headphones.TORRENT_DOWNLOADER != 0:
+ provider = "The Pirate Bay"
+ providerurl = url_fix("http://thepiratebay.sx/search/" + term + "/0/99/")
+ if headphones.PREFERRED_QUALITY == 3 or losslessOnly:
+ category = '104' #flac
+ maxsize = 10000000000
+ elif headphones.PREFERRED_QUALITY:
+ category = '100' #audio cat
+ maxsize = 10000000000
+ else:
+ category = '101' #mp3
+ maxsize = 300000000
+
+ searchURL = providerurl + category
+
+ try:
+ data = urllib2.urlopen(searchURL, timeout=20).read()
+ except urllib2.URLError, e:
+ logger.warn('Error fetching data from The Pirate Bay: %s' % e)
+ data = False
+
+ if data:
+
+ logger.info(u'Parsing results from The Pirate Bay' % searchURL)
+
+ soup = BeautifulSoup(data)
+ table = soup.find('table')
+ rows = table.findAll('tr')
+
+ if len(rows) == '1':
+ logger.info(u"No results found from %s for %s" % (provider, term))
+ pass
+
+ else:
+ for item in rows[1:]:
+ try:
+ rightformat = True
+ title = ''.join(item.find("a", {"class" : "detLink"}))
+ seeds = int(''.join(item.find("td", {"align" : "right"})))
+ url = item.findAll("a")[3]['href']
+ formatted_size = re.search('Size (.*),', unicode(item)).group(1).replace(u'\xa0', ' ')
+ size = helpers.piratesize(formatted_size)
+ if size < maxsize and minimumseeders < seeds:
+ resultlist.append((title, size, url, provider))
+ logger.info('Found %s. Size: %s' % (title, formatted_size))
+ else:
+ logger.info('%s is larger than the maxsize or has too little seeders for this category, skipping. (Size: %i bytes, Seeders: %i)' % (title, size, int(seeds)))
+
+ except Exception, e:
+ logger.error(u"An unknown error occurred in the Pirate Bay parser: %s" % e)
+
if headphones.ISOHUNT:
provider = "isoHunt"
providerurl = url_fix("http://isohunt.com/js/rss/" + term)
@@ -1220,8 +1276,18 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
resultlist[:] = [result for result in resultlist if verifyresult(result[0], artistterm, term, losslessOnly)]
if len(resultlist):
-
- if headphones.PREFERRED_QUALITY == 2 and headphones.PREFERRED_BITRATE and not pre_sorted_results:
+
+ # Add a priority if it has any of the preferred words
+ temp_list = []
+ for result in resultlist:
+ if any(word.lower() in result[0].lower() for word in helpers.split_string(headphones.PREFERRED_WORDS)):
+ temp_list.append((result[0],result[1],result[2],result[3],1))
+ else:
+ temp_list.append((result[0],result[1],result[2],result[3],0))
+
+ resultlist = temp_list
+
+ if headphones.PREFERRED_QUALITY == 2 and headphones.PREFERRED_BITRATE:
logger.debug('Target bitrate: %s kbps' % headphones.PREFERRED_BITRATE)
@@ -1231,31 +1297,59 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
albumlength = sum([pair[0] for pair in tracks])
targetsize = albumlength/1000 * int(headphones.PREFERRED_BITRATE) * 128
- logger.info('Target size: %s' % helpers.bytes_to_mb(targetsize))
-
- newlist = []
-
- for result in resultlist:
- delta = abs(targetsize - result[1])
- newlist.append((result[0], result[1], result[2], result[3], delta))
-
- torrentlist = sorted(newlist, key=lambda title: title[4])
-
+
+ if not targetsize:
+ logger.info('No track information for %s - %s. Defaulting to highest quality' % (albums[0], albums[1]))
+ torrentlist = sorted(resultlist, key=lambda title: (-title[4] , -title[1]))
+
+ else:
+ logger.info('Target size: %s' % helpers.bytes_to_mb(targetsize))
+ newlist = []
+ flac_list = []
+
+ if headphones.PREFERRED_BITRATE_HIGH_BUFFER:
+ high_size_limit = targetsize * int(headphones.PREFERRED_BITRATE_HIGH_BUFFER)/100
+ else:
+ high_size_limit = None
+ if headphones.PREFERRED_BITRATE_LOW_BUFFER:
+ low_size_limit = targetsize * int(headphones.PREFERRED_BITRATE_LOW_BUFFER)/100
+ else:
+ low_size_limit = None
+
+ for result in resultlist:
+
+ if high_size_limit and (result[1] > high_size_limit):
+ logger.info(result[0] + " is too large for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Maxsize: " + helpers.bytes_to_mb(high_size_limit) + ")")
+
+ # Add lossless nzbs to the "flac list" which we can use if there are no good lossy matches
+ if 'flac' in result[0].lower():
+ flac_list.append((result[0], result[1], result[2], result[3], result[4]))
+
+ continue
+
+ if low_size_limit and (result[1] < low_size_limit):
+ logger.info(result[0] + " is too small for this album - not considering it. (Size: " + helpers.bytes_to_mb(result[1]) + ", Minsize: " + helpers.bytes_to_mb(low_size_limit) + ")")
+ continue
+
+ delta = abs(targetsize - result[1])
+ newlist.append((result[0], result[1], result[2], result[3], result[4], delta))
+
+ torrentlist = sorted(newlist, key=lambda title: (-title[4], title[5]))
+
+ if not len(torrentlist) and len(flac_list) and headphones.PREFERRED_BITRATE_ALLOW_LOSSLESS:
+ logger.info("Since there were no appropriate lossy matches (and at least one lossless match), going to use lossless instead")
+ torrentlist = sorted(flac_list, key=lambda title: (-title[4], -title[1]))
+
except Exception, e:
logger.debug('Error: %s' % str(e))
logger.info('No track information for %s - %s. Defaulting to highest quality' % (albums[0], albums[1]))
-
- torrentlist = sorted(resultlist, key=lambda title: title[1], reverse=True)
-
- elif pre_sorted_results:
-
- torrentlist = resultlist
-
+
+ torrentlist = sorted(resultlist, key=lambda title: (-title[4], -title[1]))
+
else:
-
- torrentlist = sorted(resultlist, key=lambda title: title[1], reverse=True)
-
+
+ torrentlist = sorted(resultlist, key=lambda title: (-title[4], -title[1]))
if new:
@@ -1281,16 +1375,12 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
if data and bestqual:
logger.info(u'Found best result from %s: %s - %s' % (bestqual[3], bestqual[2], bestqual[0], helpers.bytes_to_mb(bestqual[1])))
- torrent_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8').replace('/', '_'), helpers.latinToAscii(albums[1]).encode('UTF-8').replace('/', '_'), year)
- if headphones.TORRENTBLACKHOLE_DIR == "sendtracker":
- torrent = classes.TorrentDataSearchResult()
- torrent.extraInfo.append(data)
- torrent.name = torrent_folder_name
- sab.sendTorrent(torrent)
-
- elif headphones.TORRENTBLACKHOLE_DIR != "":
+ torrent_folder_name = '%s - %s [%s]' % (helpers.latinToAscii(albums[0]).encode('UTF-8').replace('/', '_'), helpers.latinToAscii(albums[1]).encode('UTF-8').replace('/', '_'), year)
+ # Blackhole
+ if headphones.TORRENT_DOWNLOADER == 0:
+
# Get torrent name from .torrent, this is usually used by the torrent client as the folder name
torrent_name = torrent_folder_name + '.torrent'
@@ -1318,6 +1408,11 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
except Exception, e:
logger.error('Couldn\'t get name from Torrent file: %s' % e)
break
+
+ elif headphones.TORRENT_DOWNLOADER == 1:
+ logger.info("Sending torrent to Transmission")
+ torrentid = transmission.addTorrent(bestqual[2])
+ torrent_folder_name = transmission.getTorrentFolder(torrentid)
myDB.action('UPDATE albums SET status = "Snatched" WHERE AlbumID=?', [albums[2]])
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?, ?)', [albums[2], bestqual[0], bestqual[1], bestqual[2], "Snatched", torrent_folder_name, "torrent"])
@@ -1330,9 +1425,10 @@ def preprocesstorrent(resultlist, pre_sorted_list=False):
elif int(selresult[1]) < int(result[1]): # if size is lower than new result replace previous selected result (bigger size = better quality?)
selresult = result
- # get outta here if rutracker
-
- if selresult[3] == 'rutracker.org':
+ # get outta here if rutracker or piratebay, or if we're using Transmission or uTorrent
+ if selresult[3] == 'rutracker.org' or selresult[3] == 'The Pirate Bay':
+ return True, selresult
+ if headphones.TORRENT_DOWNLOADER != 0:
return True, selresult
if pre_sorted_list:
diff --git a/headphones/transmission.py b/headphones/transmission.py
new file mode 100644
index 00000000..bace5517
--- /dev/null
+++ b/headphones/transmission.py
@@ -0,0 +1,121 @@
+# This file is part of Headphones.
+#
+# Headphones is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Headphones is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Headphones. If not, see .
+
+import headphones
+from headphones import logger, notifiers
+
+import urllib2
+import lib.simplejson as json
+import base64
+import time
+
+# 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):
+ method = 'torrent-add'
+ arguments = {'filename': link, 'download-dir':headphones.DOWNLOAD_TORRENT_DIR}
+
+ response = torrentAction(method,arguments)
+
+
+ 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 getTorrentFolder(torrentid):
+ method = 'torrent-get'
+ arguments = { 'ids': torrentid, 'fields': ['name','percentDone']}
+
+ response = torrentAction(method, arguments)
+ percentdone = response['arguments']['torrents'][0]['percentDone']
+ torrent_folder_name = response['arguments']['torrents'][0]['name']
+ print torrent_folder_name
+
+ while percentdone == 0:
+ print "In the while loop"
+ time.sleep(5)
+ response = torrentAction(method, arguments)
+ percentdone = response['arguments']['torrents'][0]['percentDone']
+ print "Attempting to get folder name, percent done: " + str(percentdone)
+
+ torrent_folder_name = response['arguments']['torrents'][0]['name']
+ print torrent_folder_name + " updated and finished!"
+ return torrent_folder_name
+
+def torrentAction(method, arguments):
+
+ host = headphones.TRANSMISSION_HOST
+ username = headphones.TRANSMISSION_USERNAME
+ password = headphones.TRANSMISSION_PASSWORD
+ sessionid = None
+
+ if not host.startswith('http'):
+ host = 'http://' + host
+
+ if host.endswith('/'):
+ host = host[:-1]
+
+ host = host + "/transmission/rpc"
+ request = urllib2.Request(host)
+ if username and password:
+ base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
+ request.add_header("Authorization", "Basic %s" % base64string)
+ opener = urllib2.build_opener()
+ try:
+ data = opener.open(request).read()
+ except urllib2.HTTPError, e:
+ if e.code == 409:
+ sessionid = e.hdrs['x-transmission-session-id']
+ else:
+ logger.error('Could not connect to Transmission. Error: ' + str(e))
+ except Exception, e:
+ logger.error('Could not connect to Transmission. Error: ' + str(e))
+
+ if not sessionid:
+ logger.error("Error getting Session ID from Transmission")
+ return
+
+ request.add_header('x-transmission-session-id', sessionid)
+
+ postdata = json.dumps({ 'method': method,
+ 'arguments': arguments })
+
+ request.add_data(postdata)
+
+ try:
+ response = json.loads(opener.open(request).read())
+ except Exception, e:
+ logger.error("Error sending torrent to Transmission: " + str(e))
+ return
+
+ return response
diff --git a/headphones/utorrent.py b/headphones/utorrent.py
new file mode 100644
index 00000000..fca5f5e3
--- /dev/null
+++ b/headphones/utorrent.py
@@ -0,0 +1,14 @@
+# This file is part of Headphones.
+#
+# Headphones is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Headphones is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Headphones. If not, see .
diff --git a/headphones/webserve.py b/headphones/webserve.py
index 31d3f26d..7015bd0e 100644
--- a/headphones/webserve.py
+++ b/headphones/webserve.py
@@ -579,9 +579,18 @@ class WebInterface(object):
"nzbget_user" : headphones.NZBGET_USERNAME,
"nzbget_pass" : headphones.NZBGET_PASSWORD,
"nzbget_cat" : headphones.NZBGET_CATEGORY,
+ "transmission_host" : headphones.TRANSMISSION_HOST,
+ "transmission_user" : headphones.TRANSMISSION_USERNAME,
+ "transmission_pass" : headphones.TRANSMISSION_PASSWORD,
+ "utorrent_host" : headphones.UTORRENT_HOST,
+ "utorrent_user" : headphones.UTORRENT_USERNAME,
+ "utorrent_pass" : headphones.UTORRENT_PASSWORD,
"nzb_downloader_sabnzbd" : radio(headphones.NZB_DOWNLOADER, 0),
"nzb_downloader_nzbget" : radio(headphones.NZB_DOWNLOADER, 1),
"nzb_downloader_blackhole" : radio(headphones.NZB_DOWNLOADER, 2),
+ "torrent_downloader_blackhole" : radio(headphones.TORRENT_DOWNLOADER, 0),
+ "torrent_downloader_transmission" : radio(headphones.TORRENT_DOWNLOADER, 1),
+ "torrent_downloader_utorrent" : radio(headphones.TORRENT_DOWNLOADER, 2),
"download_dir" : headphones.DOWNLOAD_DIR,
"use_blackhole" : checked(headphones.BLACKHOLE),
"blackhole_dir" : headphones.BLACKHOLE_DIR,
@@ -606,6 +615,7 @@ class WebInterface(object):
"numberofseeders" : headphones.NUMBEROFSEEDERS,
"use_isohunt" : checked(headphones.ISOHUNT),
"use_kat" : checked(headphones.KAT),
+ "use_piratebay" : checked(headphones.PIRATEBAY),
"use_mininova" : checked(headphones.MININOVA),
"use_waffles" : checked(headphones.WAFFLES),
"waffles_uid" : headphones.WAFFLES_UID,
@@ -703,13 +713,14 @@ class WebInterface(object):
config.exposed = True
+<<<<<<< HEAD
def configUpdate(self, http_host='0.0.0.0', http_username=None, http_port=8181, http_password=None, launch_browser=0, api_enabled=0, api_key=None,
download_scan_interval=None, nzb_search_interval=None, libraryscan_interval=None, sab_host=None, sab_username=None, sab_apikey=None, sab_password=None,
- sab_category=None, nzbget_host=None, nzbget_username='nzbget', nzbget_password=None, nzbget_category=None, nzb_downloader=0, download_dir=None, blackhole=0, blackhole_dir=None, usenet_retention=None,
- use_headphones_indexer=0,newznab=0, newznab_host=None, newznab_apikey=None,
- newznab_enabled=0, nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, nzbsrus=0, nzbsrus_uid=None, nzbsrus_apikey=None, preferred_words=None, required_words=None, ignored_words=None,
- preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0, torrentblackhole_dir=None, download_torrent_dir=None,
- numberofseeders=10, use_isohunt=0, use_kat=0, use_mininova=0, waffles=0, waffles_uid=None, waffles_passkey=None, whatcd=0, whatcd_username=None, whatcd_password=None,
+ sab_category=None, nzbget_host=None, nzbget_username=None, nzbget_password=None, nzbget_category=None, transmission_host=None, transmission_username=None, transmission_password=None,
+ utorrent_host=None, utorrent_username=None, utorrent_password=None, nzb_downloader=0, torrent_downloader=0, download_dir=None, blackhole_dir=None, usenet_retention=None,
+ use_headphones_indexer=0,newznab=0, newznab_host=None, newznab_apikey=None, newznab_enabled=0, nzbsorg=0, nzbsorg_uid=None, nzbsorg_hash=None, nzbsrus=0, nzbsrus_uid=None, nzbsrus_apikey=None,
+ preferred_words=None, required_words=None, ignored_words=None, preferred_quality=0, preferred_bitrate=None, detect_bitrate=0, move_files=0, torrentblackhole_dir=None, download_torrent_dir=None,
+ numberofseeders=None, use_piratebay=0, use_isohunt=0, use_kat=0, use_mininova=0, waffles=0, waffles_uid=None, waffles_passkey=None, whatcd=0, whatcd_username=None, whatcd_password=None,
rutracker=0, rutracker_user=None, rutracker_password=None, rename_files=0, correct_metadata=0, cleanup_files=0, add_album_art=0, album_art_format=None, embed_album_art=0, embed_lyrics=0,
destination_dir=None, lossless_destination_dir=None, folder_format=None, file_format=None, include_extras=0, single=0, ep=0, compilation=0, soundtrack=0, live=0,
remix=0, spokenword=0, audiobook=0, autowant_upcoming=False, autowant_all=False, keep_torrent_files=False, interface=None, log_dir=None, cache_dir=None, music_encoder=0, encoder=None, xldprofile=None,
@@ -738,7 +749,14 @@ class WebInterface(object):
headphones.NZBGET_USERNAME = nzbget_username
headphones.NZBGET_PASSWORD = nzbget_password
headphones.NZBGET_CATEGORY = nzbget_category
+ headphones.TRANSMISSION_HOST = transmission_host
+ headphones.TRANSMISSION_USERNAME = transmission_username
+ headphones.TRANSMISSION_PASSWORD = transmission_password
+ headphones.UTORRENT_HOST = utorrent_host
+ headphones.UTORRENT_USERNAME = utorrent_username
+ headphones.UTORRENT_PASSWORD = utorrent_password
headphones.NZB_DOWNLOADER = int(nzb_downloader)
+ headphones.TORRENT_DOWNLOADER = int(torrent_downloader)
headphones.DOWNLOAD_DIR = download_dir
headphones.BLACKHOLE = blackhole
headphones.BLACKHOLE_DIR = blackhole_dir
@@ -762,6 +780,7 @@ class WebInterface(object):
headphones.DOWNLOAD_TORRENT_DIR = download_torrent_dir
headphones.ISOHUNT = use_isohunt
headphones.KAT = use_kat
+ headphones.PIRATEBAY = use_piratebay
headphones.MININOVA = use_mininova
headphones.WAFFLES = waffles
headphones.WAFFLES_UID = waffles_uid