Merge branch 'develop': Fixed some torrent handling stuff, added Transmission support, Pirate

Bay support, changed db journal mode to write-ahead logging, fixed some stuff in the config
html"
This commit is contained in:
rembo10
2013-07-31 15:56:39 +05:30
16 changed files with 1610 additions and 1253 deletions

2
.gitignore vendored
View File

@@ -9,7 +9,7 @@
# Logs and databases #
######################
*.log
*.db
*.db*
*.db-journal
*.ini
logs/*

File diff suppressed because it is too large Load Diff

View File

@@ -365,7 +365,7 @@ form .row input[type=password] {
line-height: normal;
max-width: 230px;
margin-right: 5px;
padding: 3px 5px;
padding: 2px 5px;
}
form .row small {
color: #999;
@@ -378,28 +378,28 @@ form .row small {
form .left label {
float: none;
line-height: normal;
margin-bottom: 10px;
padding-top: 1px;
margin-bottom: 5px;
padding-top: 2px;
width: auto;
}
form .left input {
float: left;
margin-bottom: 10px;
margin-bottom: 5px;
clear: left;
display: block;
}
form .radio label {
float: none;
line-height: normal;
margin-bottom: 10px;
padding-top: 1px;
margin-bottom: 5px;
padding-top: 0px;
width: auto;
}
.override-float {
float: none !important;
margin-bottom: 0px !important;
}
form .radio input {
float: left;
margin-bottom: 10px;
margin-bottom: 5px;
clear: left;
display: block;
}
form .radio small {
display: inline !important;
@@ -413,6 +413,15 @@ form .checkbox small {
margin: 0 !important;
width: auto;
}
.override-float {
float: none !important;
margin-bottom: 0px !important;
clear: none !important;
display: inline !important;
font-size: 10px;
line-height: 10px;
height: 10px;
}
ul,
ol {
margin-left: 2em;
@@ -1486,3 +1495,6 @@ table tr td#status a {
.ie7 legend {
margin-left: -7px;
}
#preferred_bitrate_options {
margin-left: 20px;
}

View File

@@ -72,7 +72,7 @@
"bDestroy": true,
"oLanguage": {
"sLengthMenu":"Show _MENU_ items per page",
"sEmptyTable": "<em>No History to Display</em>",
"sEmptyTable": " ",
"sInfo":"Showing _START_ to _END_ of _TOTAL_ items",
"sInfoEmpty":"Showing 0 to 0 of 0 items",
"sInfoFiltered":"(filtered from _MAX_ total items)"},
@@ -88,4 +88,4 @@
initActions();
});
</script>
</%def>
</%def>

View File

@@ -109,7 +109,8 @@
"sLengthMenu":"Show _MENU_ artists per page",
"sInfo":"Showing _START_ to _END_ of _TOTAL_ artists",
"sInfoEmpty":"Showing 0 to 0 of 0 artists",
"sInfoFiltered":"(filtered from _MAX_ total artists)"
"sInfoFiltered":"(filtered from _MAX_ total artists)",
"sEmptyTable": " ",
},
"bStateSave": true,
"iDisplayLength": 50,

View File

@@ -98,7 +98,9 @@
null
],
"oLanguage": {
"sSearch" : ""},
"sSearch" : "",
"sEmptyTable": " "
},
"bStateSave": true,
"bPaginate": false
});

View File

@@ -93,6 +93,9 @@
function initThisPage() {
$('#wanted_table').dataTable({
"oLanguage": {
"sEmptyTable": " "
},
"bDestroy":true,
"bFilter": false,
"bInfo": false,

View File

@@ -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
@@ -232,6 +234,7 @@ HPUSER = None
HPPASS = None
CACHE_SIZEMB = 32
JOURNAL_MODE = None
UMASK = None
@@ -290,19 +293,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, JOURNAL_MODE, UMASK
if __INITIALIZED__:
return False
@@ -311,12 +313,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 +379,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 +399,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 +428,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 +449,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', '')
@@ -504,6 +508,7 @@ def initialize():
HPPASS = check_setting_str(CFG, 'General', 'hppass', '')
CACHE_SIZEMB = check_setting_int(CFG,'Advanced','cache_sizemb',32)
JOURNAL_MODE = check_setting_int(CFG,'Advanced', 'journal_mode', 'wal')
ALBUM_COMPLETION_PCT = check_setting_int(CFG, 'Advanced', 'album_completion_pct', 80)
@@ -727,6 +732,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 +747,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 +782,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 +814,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
@@ -872,6 +879,7 @@ def config_write():
new_config['Advanced'] = {}
new_config['Advanced']['album_completion_pct'] = ALBUM_COMPLETION_PCT
new_config['Advanced']['cache_sizemb'] = CACHE_SIZEMB
new_config['Advanced']['journal_mode'] = JOURNAL_MODE
new_config.write()

View File

@@ -50,7 +50,7 @@ class DBConnection:
#don't wait for the disk to finish writing
self.connection.execute("PRAGMA synchronous = OFF")
#journal disabled since we never do rollbacks
self.connection.execute("PRAGMA journal_mode = OFF")
self.connection.execute("PRAGMA journal_mode = %s" % headphones.JOURNAL_MODE)
#64mb of cache memory,probably need to make it user configurable
self.connection.execute("PRAGMA cache_size=-%s" % (getCacheSize()*1024))
self.connection.row_factory = sqlite3.Row

View File

@@ -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):

View File

@@ -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

View File

@@ -66,7 +66,7 @@ def checkFolder():
logger.debug('Found %s in torrent download folder. Verifying....' % album['FolderName'])
verify(album['AlbumID'], torrent_album_path, album['Kind'])
def verify(albumid, albumpath, Kind=None):
def verify(albumid, albumpath, Kind=None, forced=False):
myDB = db.DBConnection()
release = myDB.action('SELECT * from albums WHERE AlbumID=?', [albumid]).fetchone()
@@ -169,6 +169,11 @@ def verify(albumid, albumpath, Kind=None):
downloaded_track_list.append(os.path.join(r, files))
elif files.lower().endswith('.cue'):
downloaded_cuecount += 1
# if any of the files end in *.part, we know the torrent isn't done yet. Process if forced, though
elif files.lower().endswith('.part') and not forced:
logger.info("Looks like " + os.path.basename(albumpath).decode(headphones.SYS_ENCODING, 'replace') + " isn't complete yet. Will try again on the next run")
return
# use xld to split cue
@@ -320,14 +325,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 +879,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):
@@ -935,7 +957,7 @@ def forcePostProcess():
release = myDB.action('SELECT ArtistName, AlbumTitle, AlbumID from albums WHERE AlbumID=?', [rgid]).fetchone()
if release:
logger.info('Found a match in the database: %s - %s. Verifying to make sure it is the correct album' % (release['ArtistName'], release['AlbumTitle']))
verify(release['AlbumID'], folder)
verify(release['AlbumID'], folder, forced=True)
else:
logger.info('Found a (possibly) valid Musicbrainz identifier in album folder name - continuing post-processing')
verify(rgid, folder)
verify(rgid, folder, forced=True)

View File

@@ -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 <a href="%s">The Pirate Bay</a>' % 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,19 @@ 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
print resultlist
if headphones.PREFERRED_QUALITY == 2 and headphones.PREFERRED_BITRATE:
logger.debug('Target bitrate: %s kbps' % headphones.PREFERRED_BITRATE)
@@ -1231,31 +1298,61 @@ 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))
print newlist
torrentlist = sorted(newlist, key=lambda title: (-title[4], title[5]))
print torrentlist
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 +1378,16 @@ def searchTorrent(albumid=None, new=False, losslessOnly=False):
if data and bestqual:
logger.info(u'Found best result from %s: <a href="%s">%s</a> - %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:
if bestqual[2].startswith("magnet:"):
logger.error("Cannot save magnet files to blackhole. Please switch your torrent downloader to Transmission or uTorrent")
return
# Get torrent name from .torrent, this is usually used by the torrent client as the folder name
torrent_name = torrent_folder_name + '.torrent'
@@ -1318,47 +1415,43 @@ 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"])
def preprocesstorrent(resultlist, pre_sorted_list=False):
selresult = ""
# Get out of here if we're using Transmission or uTorrent
if headphones.TORRENT_DOWNLOADER != 0:
return True, resultlist[0]
for result in resultlist:
if selresult == "":
selresult = result
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
# get outta here if rutracker or piratebay
if result[3] == 'rutracker.org' or result[3] == 'The Pirate Bay':
return True, result
if selresult[3] == 'rutracker.org':
return True, selresult
try:
request = urllib2.Request(selresult[2])
request.add_header('Accept-encoding', 'gzip')
if result[3] == 'Kick Ass Torrent':
request.add_header('Referer', 'http://kat.ph/')
if pre_sorted_list:
selresult = resultlist[0]
else:
for result in resultlist:
if selresult == "":
selresult = result
elif int(selresult[1]) < int(result[1]): # if size is lower than new result replace previous selected result (bigger size = better quality?)
selresult = result
response = urllib2.urlopen(request)
if response.info().get('Content-Encoding') == 'gzip':
buf = StringIO(response.read())
f = gzip.GzipFile(fileobj=buf)
torrent = f.read()
else:
torrent = response.read()
except ExpatError:
logger.error('Unable to torrent %s' % result[2])
continue
try:
request = urllib2.Request(selresult[2])
request.add_header('Accept-encoding', 'gzip')
if selresult[3] == 'Kick Ass Torrent':
request.add_header('Referer', 'http://kat.ph/')
response = urllib2.urlopen(request)
if response.info().get('Content-Encoding') == 'gzip':
buf = StringIO(response.read())
f = gzip.GzipFile(fileobj=buf)
torrent = f.read()
else:
torrent = response.read()
except ExpatError:
logger.error('Unable to torrent %s' % selresult[2])
return torrent, selresult
return torrent, result

118
headphones/transmission.py Normal file
View File

@@ -0,0 +1,118 @@
# 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 <http://www.gnu.org/licenses/>.
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']
while percentdone == 0:
time.sleep(5)
response = torrentAction(method, arguments)
percentdone = response['arguments']['torrents'][0]['percentDone']
torrent_folder_name = response['arguments']['torrents'][0]['name']
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

14
headphones/utorrent.py Normal file
View File

@@ -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 <http://www.gnu.org/licenses/>.

View File

@@ -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,
@@ -702,14 +712,13 @@ class WebInterface(object):
return serve_template(templatename="config.html", title="Settings", config=config)
config.exposed = True
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,9 +747,15 @@ 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
headphones.USENET_RETENTION = usenet_retention
headphones.HEADPHONES_INDEXER = use_headphones_indexer
@@ -762,6 +777,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