Merge remote-tracking branch 'origin/develop'

This commit is contained in:
Pieter Janssens
2014-08-14 13:45:54 +02:00
16 changed files with 569 additions and 153 deletions

View File

@@ -128,7 +128,7 @@
%if album['Status'] == 'Skipped' or album['Status'] == 'Ignored':
[<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}',$(this),'table')" data-success="'${album['AlbumTitle']}' added to Wanted list">want</a>]
%elif (album['Status'] == 'Wanted' or album['Status'] == 'Wanted Lossless'):
[<a href="#" onclick="doAjaxCall('unqueueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}',$(this),'table')" data-success="'${album['AlbumTitle']}' skipped">skip</a>]
[<a href="#" onclick="doAjaxCall('unqueueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}',$(this),'table')" data-success="'${album['AlbumTitle']}' skipped">skip</a>] [<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}', $(this),'table')" data-success="Trying to download'${album['AlbumTitle']}'" title="Search if available for download">search</a>]
%else:
[<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}', $(this),'table')" data-success="Retrying the same version of '${album['AlbumTitle']}'" title="Retry the same download again">retry</a>][<a href="#" onclick="doAjaxCall('queueAlbum?AlbumID=${album['AlbumID']}&ArtistID=${album['ArtistID']}&new=True', $(this),'table')" title="Try a new download, skipping all previously tried nzbs" data-success="Downloading new version for '${album['AlbumTitle']}'" data-success="Looking for a new version of '${album['AlbumTitle']}'">new</a>]
%endif

View File

@@ -168,6 +168,68 @@
<label>NZBget Category:</label>
<input type="text" name="nzbget_category" value="${config['nzbget_cat']}" size="20">
</div>
<%
if config['nzbget_priority'] == -100:
prio_verylow = 'selected="selected"'
prio_low = ''
prio_normal = ''
prio_high = ''
prio_veryhigh = ''
prio_force = ''
elif config['nzbget_priority'] == -50:
prio_verylow = ''
prio_low = 'selected="selected"'
prio_normal = ''
prio_high = ''
prio_veryhigh = ''
prio_force = ''
elif config['nzbget_priority'] == 0:
prio_verylow = ''
prio_low = ''
prio_normal = 'selected="selected"'
prio_high = ''
prio_veryhigh = ''
prio_force = ''
elif config['nzbget_priority'] == 50:
prio_verylow = ''
prio_low = ''
prio_normal = ''
prio_high = 'selected="selected"'
prio_veryhigh = ''
prio_force = ''
elif config['nzbget_priority'] == 100:
prio_verylow = ''
prio_low = ''
prio_normal = ''
prio_high = ''
prio_veryhigh = 'selected="selected"'
prio_force = ''
elif config['nzbget_priority'] == 900:
prio_verylow = ''
prio_low = ''
prio_normal = ''
prio_high = ''
prio_veryhigh = ''
prio_force = 'selected="selected"'
else:
prio_verylow = ''
prio_low = ''
prio_normal = 'selected="selected"'
prio_high = ''
prio_veryhigh = ''
prio_force = ''
%>
<div class="row">
<label>NZBget Priority:</label>
<select name="nzbget_priority" id="nzbget_priority">
<option value="-100" ${prio_verylow}>Very Low</option>
<option value="-50" ${prio_low}>Low</option>
<option value="0" ${prio_normal}>Normal</option>
<option value="50" ${prio_high}>High</option>
<option value="100" ${prio_veryhigh}>Very High</option>
<option value="900" ${prio_force}>Force</option>
</select>
</div>
</fieldset>
<fieldset id="blackhole_options">
@@ -382,66 +444,117 @@
<td>
<fieldset>
<legend>Torrents</legend>
<div class="row checkbox">
<input id="usepiratebay" type="checkbox" name="use_piratebay" value="1" ${config['use_piratebay']} /><label>The Pirate Bay</label>
</div>
<div class="config">
<div class="row">
<label>Proxy URL (Optional): </label>
<input type="text" name="piratebay_proxy_url" value="${config['piratebay_proxy_url']}" size="36">
<fieldset>
<legend>The Pirate Bay</legend>
<div class="row checkbox">
<input id="usepiratebay" type="checkbox" name="use_piratebay" value="1" ${config['use_piratebay']} /><label>Use The Pirate Bay</label>
</div>
</div>
<div class="row checkbox">
<input type="checkbox" name="use_mininova" value="1" ${config['use_mininova']} /><label>Mininova</label>
</div>
<div class="row checkbox">
<input id="usekat" type="checkbox" name="use_kat" value="1" ${config['use_kat']} /><label>Kick Ass Torrents</label>
</div>
<div class="config">
<div class="row">
<label>Proxy URL (Optional): </label>
<input type="text" name="kat_proxy_url" value="${config['kat_proxy_url']}" size="36">
<div class="config">
<div class="row">
<label>Proxy URL (Optional): </label>
<input type="text" name="piratebay_proxy_url" value="${config['piratebay_proxy_url']}" size="36">
</div>
<div class="row">
<label>Seed Ratio: </label>
<input type="text" class="override-float" name="piratebay_ratio" value="${config['piratebay_ratio']}" size="10" title="Stop seeding when ratio met, 0 = unlimited. Scheduled job will remove torrent when post processed and finished seeding">
</div>
</div>
</div>
<div class="row checkbox">
<input id="usewaffles" type="checkbox" name="waffles" onclick="initConfigCheckbox($(this));" value="1" ${config['use_waffles']} /><label>Waffles.fm</label>
</div>
<div class="config">
<div class="row">
<label>Waffles UID Number: </label>
<input type="text" name="waffles_uid" value="${config['waffles_uid']}" size="36">
</fieldset>
<fieldset>
<legend>Kick Ass Torrents</legend>
<div class="row checkbox">
<input id="usekat" type="checkbox" name="use_kat" value="1" ${config['use_kat']} /><label>Use Kick Ass Torrents</label>
</div>
<div class="row">
<label>Waffles Passkey: </label>
<input type="password" name="waffles_passkey" value="${config['waffles_passkey']}" size="36">
<div class="config">
<div class="row">
<label>Proxy URL (Optional): </label>
<input type="text" name="kat_proxy_url" value="${config['kat_proxy_url']}" size="36">
</div>
<div class="row">
<label>Seed Ratio: </label>
<input type="text" class="override-float" name="kat_ratio" value="${config['kat_ratio']}" size="10" title="Stop seeding when ratio met, 0 = unlimited. Scheduled job will remove torrent when post processed and finished seeding">
</div>
</div>
</div>
<div class="row checkbox">
<input id="userutracker" type="checkbox" name="rutracker" onclick="initConfigCheckbox($(this));" value="1" ${config['use_rutracker']} /><label>rutracker.org</label>
</div>
<div class="config">
<div class="row">
<label>rutracker User Name: </label>
<input type="text" name="rutracker_user" value="${config['rutracker_user']}" size="36">
</fieldset>
<fieldset>
<legend>Waffles.fm</legend>
<div class="row checkbox">
<input id="usewaffles" type="checkbox" name="waffles" onclick="initConfigCheckbox($(this));" value="1" ${config['use_waffles']} /><label>Use Waffles.fm</label>
</div>
<div class="row">
<label>rutracker Password: </label>
<input type="password" name="rutracker_password" value="${config['rutracker_password']}" size="36">
<div class="config">
<div class="row">
<label>UID Number: </label>
<input type="text" name="waffles_uid" value="${config['waffles_uid']}" size="36">
</div>
<div class="row">
<label>Passkey: </label>
<input type="password" name="waffles_passkey" value="${config['waffles_passkey']}" size="36">
</div>
<div class="row">
<label>Seed Ratio: </label>
<input type="text" class="override-float" name="waffles_ratio" value="${config['waffles_ratio']}" size="10" title="Stop seeding when ratio met, 0 = unlimited. Scheduled job will remove torrent when post processed and finished seeding">
</div>
</div>
</div>
<div class="row checkbox">
<input id="usewhatcd" type="checkbox" name="whatcd" onclick="initConfigCheckbox($(this));" value="1" ${config['use_whatcd']} /><label>What.cd</label>
</div>
<div class="config">
<div class="row">
<label>What.cd Username: </label>
<input type="text" name="whatcd_username" value="${config['whatcd_username']}" size="36">
</fieldset>
<fieldset>
<legend>rutracker.org</legend>
<div class="row checkbox">
<input id="userutracker" type="checkbox" name="rutracker" onclick="initConfigCheckbox($(this));" value="1" ${config['use_rutracker']} /><label>Use rutracker.org</label>
</div>
<div class="row">
<label>What.cd Password: </label>
<input type="password" name="whatcd_password" value="${config['whatcd_password']}" size="36">
<div class="config">
<div class="row">
<label>User Name: </label>
<input type="text" name="rutracker_user" value="${config['rutracker_user']}" size="36">
</div>
<div class="row">
<label>Password: </label>
<input type="password" name="rutracker_password" value="${config['rutracker_password']}" size="36">
</div>
<div class="row">
<label>Seed Ratio: </label>
<input type="text" class="override-float" name="rutracker_ratio" value="${config['rutracker_ratio']}" size="10" title="Stop seeding when ratio met, 0 = unlimited. Scheduled job will remove torrent when post processed and finished seeding">
</div>
</div>
</div>
</fieldset>
<fieldset>
<legend>What.cd</legend>
<div class="row checkbox">
<input id="usewhatcd" type="checkbox" name="whatcd" onclick="initConfigCheckbox($(this));" value="1" ${config['use_whatcd']} /><label>Use What.cd</label>
</div>
<div class="config">
<div class="row">
<label>Username: </label>
<input type="text" name="whatcd_username" value="${config['whatcd_username']}" size="36">
</div>
<div class="row">
<label>Password: </label>
<input type="password" name="whatcd_password" value="${config['whatcd_password']}" size="36">
</div>
<div class="row">
<label>Seed Ratio: </label>
<input type="text" class="override-float" name="whatcd_ratio" value="${config['whatcd_ratio']}" size="10" title="Stop seeding when ratio met, 0 = unlimited. Scheduled job will remove torrent when post processed and finished seeding">
</div>
</div>
</fieldset>
<fieldset>
<legend>Mininova</legend>
<div class="row checkbox">
<input id="usemininova" type="checkbox" name="use_mininova" value="1" ${config['use_mininova']} /><label>Use Mininova</label>
</div>
<div class="config">
<div class="row">
<label>Seed Ratio: </label>
<input type="text" class="override-float" name="mininova_ratio" value="${config['mininova_ratio']}" size="10" title="Stop seeding when ratio met, 0 = unlimited. Scheduled job will remove torrent when post processed and finished seeding">
</div>
</div>
</fieldset>
</fieldset>
</td>
</tr>
@@ -514,6 +627,7 @@
<input type="checkbox" name="rename_files" value="1" ${config['rename_files']} /><label>Rename files</label>
<input type="checkbox" name="correct_metadata" value="1" ${config['correct_metadata']} /><label>Correct metadata</label>
<input type="checkbox" name="cleanup_files" value="1" ${config['cleanup_files']} /><label>Delete leftover files <small>(.m3u, .nfo, .sfv, .nzb, etc.)</small></label>
<input type="checkbox" name="keep_nfo" value="1" ${config['keep_nfo']} /><label>Keep original nfo <small>(extension changed to .orig.nfo)</small></label>
<input type="checkbox" name="add_album_art" id="add_album_art" value="1" ${config['add_album_art']}><label>Add album art jpeg to album folder</label>
<div id="album_art_options">
<div class="row">
@@ -834,7 +948,7 @@
<div class="row">
<label>Folder Format</label>
<input type="text" name="folder_format" value="${config['folder_format']}" size="43">
<small>Use: $Artist/$artist, $Album/$album, $Year/$year, $Type/$type (release type) and $First/$first (first letter in artist name)
<small>Use: $Artist/$artist, $Album/$album, $Year/$year, $Type/$type (release type) and $First/$first (first letter in artist name), $OriginalFolder/$originalfolder (downloaded directory name)
E.g.: $Type/$First/$artist/$album [$year] = Album/G/girl talk/all day [2010]</small>
</div>
@@ -1744,6 +1858,7 @@
initConfigCheckbox("#useomgwtfnzbs");
initConfigCheckbox("#usekat");
initConfigCheckbox("#usepiratebay");
initConfigCheckbox("#usemininova");
initConfigCheckbox("#usewaffles");
initConfigCheckbox("#userutracker");
initConfigCheckbox("#usewhatcd");

View File

@@ -111,6 +111,7 @@ CORRECT_METADATA = False
MOVE_FILES = False
RENAME_FILES = False
CLEANUP_FILES = False
KEEP_NFO = False
ADD_ALBUM_ART = False
ALBUM_ART_FORMAT = None
EMBED_ALBUM_ART = False
@@ -147,6 +148,7 @@ NZBGET_USERNAME = None
NZBGET_PASSWORD = None
NZBGET_CATEGORY = None
NZBGET_HOST = None
NZBGET_PRIORITY = 0
HEADPHONES_INDEXER = False
@@ -189,18 +191,24 @@ TORRENTBLACKHOLE_DIR = None
NUMBEROFSEEDERS = 10
KAT = None
KAT_PROXY_URL = None
KAT_RATIO = None
MININOVA = None
MININOVA_RATIO = None
PIRATEBAY = None
PIRATEBAY_PROXY_URL = None
PIRATEBAY_RATIO = None
WAFFLES = None
WAFFLES_UID = None
WAFFLES_PASSKEY = None
WAFFLES_RATIO = None
RUTRACKER = None
RUTRACKER_USER = None
RUTRACKER_PASSWORD = None
RUTRACKER_RATIO = None
WHATCD = None
WHATCD_USERNAME = None
WHATCD_PASSWORD = None
WHATCD_RATIO = None
DOWNLOAD_TORRENT_DIR = None
INTERFACE = None
@@ -343,12 +351,12 @@ def initialize():
HTTP_PORT, HTTP_HOST, HTTP_USERNAME, HTTP_PASSWORD, HTTP_ROOT, HTTP_PROXY, LAUNCH_BROWSER, API_ENABLED, API_KEY, GIT_PATH, GIT_USER, GIT_BRANCH, DO_NOT_OVERRIDE_GIT_BRANCH, \
CURRENT_VERSION, LATEST_VERSION, CHECK_GITHUB, CHECK_GITHUB_ON_STARTUP, CHECK_GITHUB_INTERVAL, MUSIC_DIR, DESTINATION_DIR, \
LOSSLESS_DESTINATION_DIR, PREFERRED_QUALITY, PREFERRED_BITRATE, DETECT_BITRATE, ADD_ARTISTS, CORRECT_METADATA, MOVE_FILES, \
RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, FILE_UNDERSCORES, CLEANUP_FILES, INCLUDE_EXTRAS, EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, KEEP_TORRENT_FILES, PREFER_TORRENTS, OPEN_MAGNET_LINKS, \
RENAME_FILES, FOLDER_FORMAT, FILE_FORMAT, FILE_UNDERSCORES, CLEANUP_FILES, KEEP_NFO, INCLUDE_EXTRAS, EXTRAS, AUTOWANT_UPCOMING, AUTOWANT_ALL, KEEP_TORRENT_FILES, PREFER_TORRENTS, OPEN_MAGNET_LINKS, \
ADD_ALBUM_ART, ALBUM_ART_FORMAT, EMBED_ALBUM_ART, EMBED_LYRICS, REPLACE_EXISTING_FOLDERS, DOWNLOAD_DIR, BLACKHOLE, BLACKHOLE_DIR, USENET_RETENTION, SEARCH_INTERVAL, \
TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, KAT, KAT_PROXY_URL, PIRATEBAY, PIRATEBAY_PROXY_URL, MININOVA, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, \
RUTRACKER, RUTRACKER_USER, RUTRACKER_PASSWORD, WHATCD, WHATCD_USERNAME, WHATCD_PASSWORD, DOWNLOAD_TORRENT_DIR, \
TORRENTBLACKHOLE_DIR, NUMBEROFSEEDERS, KAT, KAT_PROXY_URL, KAT_RATIO, PIRATEBAY, PIRATEBAY_PROXY_URL, PIRATEBAY_RATIO, MININOVA, MININOVA_RATIO, WAFFLES, WAFFLES_UID, WAFFLES_PASSKEY, WAFFLES_RATIO, \
RUTRACKER, RUTRACKER_USER, RUTRACKER_PASSWORD, RUTRACKER_RATIO, WHATCD, WHATCD_USERNAME, WHATCD_PASSWORD, WHATCD_RATIO, 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, \
NZBGET_USERNAME, NZBGET_PASSWORD, NZBGET_CATEGORY, NZBGET_PRIORITY, NZBGET_HOST, HEADPHONES_INDEXER, NZBMATRIX, TRANSMISSION_HOST, TRANSMISSION_USERNAME, TRANSMISSION_PASSWORD, \
UTORRENT_HOST, UTORRENT_USERNAME, UTORRENT_PASSWORD, UTORRENT_LABEL, NEWZNAB, NEWZNAB_HOST, NEWZNAB_APIKEY, NEWZNAB_ENABLED, EXTRA_NEWZNABS, \
NZBSORG, NZBSORG_UID, NZBSORG_HASH, OMGWTFNZBS, OMGWTFNZBS_UID, OMGWTFNZBS_APIKEY, \
NZB_DOWNLOADER, TORRENT_DOWNLOADER, PREFERRED_WORDS, REQUIRED_WORDS, IGNORED_WORDS, LASTFM_USERNAME, \
@@ -377,6 +385,9 @@ def initialize():
CheckSection('Newznab')
CheckSection('NZBsorg')
CheckSection('omgwtfnzbs')
CheckSection('Piratebay')
CheckSection('Kat')
CheckSection('Mininova')
CheckSection('Waffles')
CheckSection('Rutracker')
CheckSection('What.cd')
@@ -448,6 +459,7 @@ def initialize():
FILE_FORMAT = check_setting_str(CFG, 'General', 'file_format', 'Track Artist - Album [Year] - Title')
FILE_UNDERSCORES = bool(check_setting_int(CFG, 'General', 'file_underscores', 0))
CLEANUP_FILES = bool(check_setting_int(CFG, 'General', 'cleanup_files', 0))
KEEP_NFO = bool(check_setting_int(CFG, 'General', 'keep_nfo', 0))
ADD_ALBUM_ART = bool(check_setting_int(CFG, 'General', 'add_album_art', 0))
ALBUM_ART_FORMAT = check_setting_str(CFG, 'General', 'album_art_format', 'folder')
EMBED_ALBUM_ART = bool(check_setting_int(CFG, 'General', 'embed_album_art', 0))
@@ -476,24 +488,33 @@ def initialize():
TORRENTBLACKHOLE_DIR = check_setting_str(CFG, 'General', 'torrentblackhole_dir', '')
NUMBEROFSEEDERS = check_setting_str(CFG, 'General', 'numberofseeders', '10')
KAT = bool(check_setting_int(CFG, 'General', 'kat', 0))
KAT_PROXY_URL = check_setting_str(CFG, 'General', 'kat_proxy_url', '')
PIRATEBAY = bool(check_setting_int(CFG, 'General', 'piratebay', 0))
PIRATEBAY_PROXY_URL = check_setting_str(CFG, 'General', 'piratebay_proxy_url', '')
MININOVA = bool(check_setting_int(CFG, 'General', 'mininova', 0))
DOWNLOAD_TORRENT_DIR = check_setting_str(CFG, 'General', 'download_torrent_dir', '')
KAT = bool(check_setting_int(CFG, 'Kat', 'kat', 0))
KAT_PROXY_URL = check_setting_str(CFG, 'Kat', 'kat_proxy_url', '')
KAT_RATIO = check_setting_str(CFG, 'Kat', 'kat_ratio', '')
PIRATEBAY = bool(check_setting_int(CFG, 'Piratebay', 'piratebay', 0))
PIRATEBAY_PROXY_URL = check_setting_str(CFG, 'Piratebay', 'piratebay_proxy_url', '')
PIRATEBAY_RATIO = check_setting_str(CFG, 'Piratebay', 'piratebay_ratio', '')
MININOVA = bool(check_setting_int(CFG, 'Mininova', 'mininova', 0))
MININOVA_RATIO = check_setting_str(CFG, 'Mininova', 'mininova_ratio', '')
WAFFLES = bool(check_setting_int(CFG, 'Waffles', 'waffles', 0))
WAFFLES_UID = check_setting_str(CFG, 'Waffles', 'waffles_uid', '')
WAFFLES_PASSKEY = check_setting_str(CFG, 'Waffles', 'waffles_passkey', '')
WAFFLES_RATIO = check_setting_str(CFG, 'Waffles', 'waffles_ratio', '')
RUTRACKER = bool(check_setting_int(CFG, 'Rutracker', 'rutracker', 0))
RUTRACKER_USER = check_setting_str(CFG, 'Rutracker', 'rutracker_user', '')
RUTRACKER_PASSWORD = check_setting_str(CFG, 'Rutracker', 'rutracker_password', '')
RUTRACKER_RATIO = check_setting_str(CFG, 'Rutracker', 'rutracker_ratio', '')
WHATCD = bool(check_setting_int(CFG, 'What.cd', 'whatcd', 0))
WHATCD_USERNAME = check_setting_str(CFG, 'What.cd', 'whatcd_username', '')
WHATCD_PASSWORD = check_setting_str(CFG, 'What.cd', 'whatcd_password', '')
WHATCD_RATIO = check_setting_str(CFG, 'What.cd', 'whatcd_ratio', '')
SAB_HOST = check_setting_str(CFG, 'SABnzbd', 'sab_host', '')
SAB_USERNAME = check_setting_str(CFG, 'SABnzbd', 'sab_username', '')
@@ -505,6 +526,7 @@ def initialize():
NZBGET_PASSWORD = check_setting_str(CFG, 'NZBget', 'nzbget_password', '')
NZBGET_CATEGORY = check_setting_str(CFG, 'NZBget', 'nzbget_category', '')
NZBGET_HOST = check_setting_str(CFG, 'NZBget', 'nzbget_host', '')
NZBGET_PRIORITY = check_setting_int(CFG, 'NZBget', 'nzbget_priority', 0)
HEADPHONES_INDEXER = bool(check_setting_int(CFG, 'Headphones', 'headphones_indexer', 0))
@@ -872,6 +894,7 @@ def config_write():
new_config['General']['file_format'] = FILE_FORMAT
new_config['General']['file_underscores'] = int(FILE_UNDERSCORES)
new_config['General']['cleanup_files'] = int(CLEANUP_FILES)
new_config['General']['keep_nfo'] = int(KEEP_NFO)
new_config['General']['add_album_art'] = int(ADD_ALBUM_ART)
new_config['General']['album_art_format'] = ALBUM_ART_FORMAT
new_config['General']['embed_album_art'] = int(EMBED_ALBUM_ART)
@@ -892,27 +915,39 @@ def config_write():
new_config['General']['numberofseeders'] = NUMBEROFSEEDERS
new_config['General']['torrentblackhole_dir'] = TORRENTBLACKHOLE_DIR
new_config['General']['kat'] = int(KAT)
new_config['General']['kat_proxy_url'] = KAT_PROXY_URL
new_config['General']['mininova'] = int(MININOVA)
new_config['General']['piratebay'] = int(PIRATEBAY)
new_config['General']['piratebay_proxy_url'] = PIRATEBAY_PROXY_URL
new_config['General']['download_torrent_dir'] = DOWNLOAD_TORRENT_DIR
new_config['Kat'] = {}
new_config['Kat']['kat'] = int(KAT)
new_config['Kat']['kat_proxy_url'] = KAT_PROXY_URL
new_config['Kat']['kat_ratio'] = KAT_RATIO
new_config['Mininova'] = {}
new_config['Mininova']['mininova'] = int(MININOVA)
new_config['Mininova']['mininova_ratio'] = MININOVA_RATIO
new_config['Piratebay'] = {}
new_config['Piratebay']['piratebay'] = int(PIRATEBAY)
new_config['Piratebay']['piratebay_proxy_url'] = PIRATEBAY_PROXY_URL
new_config['Piratebay']['piratebay_ratio'] = PIRATEBAY_RATIO
new_config['Waffles'] = {}
new_config['Waffles']['waffles'] = int(WAFFLES)
new_config['Waffles']['waffles_uid'] = WAFFLES_UID
new_config['Waffles']['waffles_passkey'] = WAFFLES_PASSKEY
new_config['Waffles']['waffles_ratio'] = WAFFLES_RATIO
new_config['Rutracker'] = {}
new_config['Rutracker']['rutracker'] = int(RUTRACKER)
new_config['Rutracker']['rutracker_user'] = RUTRACKER_USER
new_config['Rutracker']['rutracker_password'] = RUTRACKER_PASSWORD
new_config['Rutracker']['rutracker_ratio'] = RUTRACKER_RATIO
new_config['What.cd'] = {}
new_config['What.cd']['whatcd'] = int(WHATCD)
new_config['What.cd']['whatcd_username'] = WHATCD_USERNAME
new_config['What.cd']['whatcd_password'] = WHATCD_PASSWORD
new_config['What.cd']['whatcd_ratio'] = WHATCD_RATIO
new_config['General']['search_interval'] = SEARCH_INTERVAL
new_config['General']['libraryscan'] = int(LIBRARYSCAN)
@@ -933,6 +968,7 @@ 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['NZBget']['nzbget_priority'] = NZBGET_PRIORITY
new_config['Headphones'] = {}
new_config['Headphones']['headphones_indexer'] = int(HEADPHONES_INDEXER)
@@ -1101,7 +1137,7 @@ def start():
if __INITIALIZED__:
# Start our scheduled background tasks
from headphones import updater, searcher, librarysync, postprocessor
from headphones import updater, searcher, librarysync, postprocessor, torrentfinished
SCHED.add_interval_job(updater.dbUpdate, hours=UPDATE_DB_INTERVAL)
SCHED.add_interval_job(searcher.searchforalbum, minutes=SEARCH_INTERVAL)
@@ -1113,6 +1149,9 @@ def start():
if DOWNLOAD_SCAN_INTERVAL > 0:
SCHED.add_interval_job(postprocessor.checkFolder, minutes=DOWNLOAD_SCAN_INTERVAL)
# Remove Torrent + data if Post Processed and finished Seeding
SCHED.add_interval_job(torrentfinished.checkTorrentFinished, hours=12)
SCHED.start()
started = True

View File

@@ -145,7 +145,7 @@ class Api(object):
return
def _getHistory(self, **kwargs):
self.data = self._dic_from_query('SELECT * from snatched order by DateAdded DESC')
self.data = self._dic_from_query('SELECT * from snatched WHERE status NOT LIKE "Seed%" order by DateAdded DESC')
return
def _getUpcoming(self, **kwargs):

View File

@@ -20,7 +20,7 @@ import headphones
from headphones import db, helpers, logger, lastfm, request
lastfm_apikey = "690e1ed3bc00bc91804cd8f7fe5ed6d4"
LASTFM_API_KEY = "690e1ed3bc00bc91804cd8f7fe5ed6d4"
class Cache(object):
"""
@@ -59,7 +59,6 @@ class Cache(object):
info_content = None
def __init__(self):
pass
def _findfilesstartingwith(self,pattern,folder):
@@ -209,7 +208,7 @@ class Cache(object):
if ArtistID:
self.id_type = 'artist'
data = lastfm.request_lastfm("artist.getinfo", mbid=ArtistID, api_key=lastfm_apikey)
data = lastfm.request_lastfm("artist.getinfo", mbid=ArtistID, api_key=LASTFM_API_KEY)
if not data:
return
@@ -227,7 +226,7 @@ class Cache(object):
else:
self.id_type = 'album'
data = lastfm.request_lastfm("album.getinfo", mbid=AlbumID, api_key=lastfm_apikey)
data = lastfm.request_lastfm("album.getinfo", mbid=AlbumID, api_key=LASTFM_API_KEY)
if not data:
return
@@ -254,7 +253,7 @@ class Cache(object):
# Since lastfm uses release ids rather than release group ids for albums, we have to do a artist + album search for albums
if self.id_type == 'artist':
data = lastfm.request_lastfm("artist.getinfo", mbid=self.id, api_key=lastfm_apikey)
data = lastfm.request_lastfm("artist.getinfo", mbid=self.id, api_key=LASTFM_API_KEY)
if not data:
return
@@ -282,7 +281,7 @@ class Cache(object):
else:
dbartist = myDB.action('SELECT ArtistName, AlbumTitle FROM albums WHERE AlbumID=?', [self.id]).fetchone()
data = lastfm.request_lastfm("album.getinfo", artist=dbartist['ArtistName'], album=dbartist['AlbumTitle'], api_key=lastfm_apikey)
data = lastfm.request_lastfm("album.getinfo", artist=dbartist['ArtistName'], album=dbartist['AlbumTitle'], api_key=LASTFM_API_KEY)
if not data:
return

View File

@@ -59,37 +59,32 @@ class DBConnection:
return
sqlResult = None
attempt = 0
while attempt < 5:
try:
try:
with self.connection as c:
if args == None:
#logger.debug(self.filename+": "+query)
sqlResult = self.connection.execute(query)
sqlResult = c.execute(query)
else:
#logger.debug(self.filename+": "+query+" with args "+str(args))
sqlResult = self.connection.execute(query, args)
self.connection.commit()
break
except sqlite3.OperationalError, e:
if "unable to open database file" in e.message or "database is locked" in e.message:
logger.warn('Database Error: %s', e)
attempt += 1
time.sleep(1)
else:
logger.error('Database error: %s', e)
raise
except sqlite3.DatabaseError, e:
logger.error('Fatal Error executing %s :: %s', query, e)
sqlResult = c.execute(query, args)
except sqlite3.OperationalError, e:
if "unable to open database file" in e.message or "database is locked" in e.message:
logger.warn('Database Error: %s', e)
else:
logger.error('Database error: %s', e)
raise
except sqlite3.DatabaseError, e:
logger.error('Fatal Error executing %s :: %s', query, e)
raise
return sqlResult
def select(self, query, args=None):
sqlResults = self.action(query, args).fetchall()
if sqlResults == None:
if sqlResults == None or sqlResults == [None]:
return []
return sqlResults

View File

@@ -98,9 +98,10 @@ def artistlist_to_mbids(artistlist, forced=False):
# Update the similar artist tag cloud:
logger.info('Updating artist information from Last.fm')
try:
lastfm.getSimilar()
except Exception, e:
except Exception as e:
logger.warn('Failed to update arist information from Last.fm: %s' % e)
def addArtistIDListToDB(artistidlist):
@@ -431,9 +432,16 @@ def addArtisttoDB(artistid, extrasonly=False, forcefull=False):
tracks = myDB.action('SELECT * from alltracks WHERE ReleaseID=?', [releaseid]).fetchall()
# This is used to see how many tracks you have from an album - to mark it as downloaded. Default is 80%, can be set in config as ALBUM_COMPLETION_PCT
# This is used to see how many tracks you have from an album - to
# mark it as downloaded. Default is 80%, can be set in config as
# ALBUM_COMPLETION_PCT
total_track_count = len(tracks)
if total_track_count == 0:
logger.warning("Total track count is zero for Release ID " +
"'%s', skipping.", releaseid)
continue
for track in tracks:
controlValueDict = {"TrackID": track['TrackID'],

View File

@@ -21,8 +21,9 @@ from headphones import db, logger, request
from collections import defaultdict
ENTRY_POINT = 'http://ws.audioscrobbler.com/2.0/'
API_KEY = '395e6ec6bb557382fc41fde867bce66f'
TIMEOUT = 60 # seconds
ENTRY_POINT = "http://ws.audioscrobbler.com/2.0/"
API_KEY = "395e6ec6bb557382fc41fde867bce66f"
def request_lastfm(method, **kwargs):
"""
@@ -40,7 +41,7 @@ def request_lastfm(method, **kwargs):
# Send request
logger.debug("Calling Last.FM method: %s", method)
data = request.request_json(ENTRY_POINT, timeout=20, params=kwargs)
data = request.request_json(ENTRY_POINT, timeout=TIMEOUT, params=kwargs)
# Parse response and check for errors.
if not data:
@@ -55,13 +56,13 @@ def request_lastfm(method, **kwargs):
def getSimilar():
myDB = db.DBConnection()
results = myDB.select('SELECT ArtistID from artists ORDER BY HaveTracks DESC')
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'])
data = request_lastfm("artist.getsimilar", mbid=result["ArtistId"])
time.sleep(10)
if data and "similarartists" in data:
@@ -94,13 +95,13 @@ def getSimilar():
artist_name, artist_mbid = item[0]
count = item[1]
myDB.action('INSERT INTO lastfmcloud VALUES( ?, ?, ?)', [artist_name, artist_mbid, count])
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 getArtists():
myDB = db.DBConnection()
results = myDB.select('SELECT ArtistID from artists')
results = myDB.select("SELECT ArtistID from artists")
if not headphones.LASTFM_USERNAME:
logger.warn("Last.FM username not set, not importing artists.")
@@ -129,7 +130,7 @@ def getArtists():
def getTagTopArtists(tag, limit=50):
myDB = db.DBConnection()
results = myDB.select('SELECT ArtistID from artists')
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)

View File

@@ -69,25 +69,60 @@ def sendNZB(nzb):
logger.error(u"Protocol Error: " + e.errmsg)
return False
# if it's a normal result need to download the NZB content
if nzb.resultType == "nzb":
genProvider = GenericProvider("")
data = genProvider.getURL(nzb.url)
if (data == None):
return False
# if we get a raw data result thats even better
elif nzb.resultType == "nzbdata":
nzbcontent64 = None
if nzb.resultType == "nzbdata":
data = nzb.extraInfo[0]
nzbcontent64 = standard_b64encode(data)
nzbcontent64 = standard_b64encode(data)
logger.info(u"Sending NZB to NZBget")
logger.debug(u"URL: " + url)
if nzbGetRPC.append(nzb.name + ".nzb", headphones.NZBGET_CATEGORY, addToTop, nzbcontent64):
logger.debug(u"NZB sent to NZBget successfully")
return True
else:
logger.error(u"NZBget could not add %s to the queue" % (nzb.name + ".nzb"))
dupekey = ""
dupescore = 0
try:
# Find out if nzbget supports priority (Version 9.0+), old versions beginning with a 0.x will use the old command
nzbget_version_str = nzbGetRPC.version()
nzbget_version = int(nzbget_version_str[:nzbget_version_str.find(".")])
if nzbget_version == 0:
if nzbcontent64 is not None:
nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", headphones.NZBGET_CATEGORY, addToTop, nzbcontent64)
else:
if nzb.resultType == "nzb":
genProvider = GenericProvider("")
data = genProvider.getURL(nzb.url)
if (data == None):
return False
nzbcontent64 = standard_b64encode(data)
nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", headphones.NZBGET_CATEGORY, addToTop, nzbcontent64)
elif nzbget_version == 12:
if nzbcontent64 is not None:
nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", headphones.NZBGET_CATEGORY, headphones.NZBGET_PRIORITY, False,
nzbcontent64, False, dupekey, dupescore, "score")
else:
nzbget_result = nzbGetRPC.appendurl(nzb.name + ".nzb", headphones.NZBGET_CATEGORY, headphones.NZBGET_PRIORITY, False,
nzb.url, False, dupekey, dupescore, "score")
# v13+ has a new combined append method that accepts both (url and content)
# also the return value has changed from boolean to integer
# (Positive number representing NZBID of the queue item. 0 and negative numbers represent error codes.)
elif nzbget_version >= 13:
nzbget_result = True if nzbGetRPC.append(nzb.name + ".nzb", nzbcontent64 if nzbcontent64 is not None else nzb.url,
headphones.NZBGET_CATEGORY, headphones.NZBGET_PRIORITY, False, False, dupekey, dupescore,
"score") > 0 else False
else:
if nzbcontent64 is not None:
nzbget_result = nzbGetRPC.append(nzb.name + ".nzb", headphones.NZBGET_CATEGORY, headphones.NZBGET_PRIORITY, False,
nzbcontent64)
else:
nzbget_result = nzbGetRPC.appendurl(nzb.name + ".nzb", headphones.NZBGET_CATEGORY, headphones.NZBGET_PRIORITY, False,
nzb.url)
if nzbget_result:
logger.debug(u"NZB sent to NZBget successfully")
return True
else:
logger.error(u"NZBget could not add %s to the queue" % (nzb.name + ".nzb"))
return False
except:
logger.error(u"Connect Error to NZBget: could not add %s to the queue" % (nzb.name + ".nzb"))
return False

View File

@@ -24,7 +24,7 @@ import headphones
from beets import autotag
from beets.mediafile import MediaFile, FileTypeError, UnreadableFileError
from headphones import notifiers
from headphones import notifiers, utorrent, transmission
from headphones import db, albumart, librarysync, lyrics
from headphones import logger, helpers, request, mb, music_encoder
@@ -305,7 +305,7 @@ def verify(albumid, albumpath, Kind=None, forced=False):
return
logger.warn(u'Could not identify album: %s. It may not be the intended album.' % albumpath.decode(headphones.SYS_ENCODING, 'replace'))
myDB.action('UPDATE snatched SET status = "Unprocessed" WHERE AlbumID=?', [albumid])
myDB.action('UPDATE snatched SET status = "Unprocessed" WHERE status NOT LIKE "Seed%" and AlbumID=?', [albumid])
processed = re.search(r' \(Unprocessed\)(?:\[\d+\])?', albumpath)
if not processed:
renameUnprocessedFolder(albumpath)
@@ -389,6 +389,9 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list,
if headphones.CLEANUP_FILES:
cleanupFiles(albumpath)
if headphones.KEEP_NFO:
renameNFO(albumpath)
if headphones.ADD_ALBUM_ART and artwork:
addAlbumArt(artwork, albumpath, release)
@@ -413,7 +416,24 @@ def doPostProcessing(albumid, albumpath, release, tracks, downloaded_track_list,
myDB = db.DBConnection()
myDB.action('UPDATE albums SET status = "Downloaded" WHERE AlbumID=?', [albumid])
myDB.action('UPDATE snatched SET status = "Processed" WHERE AlbumID=?', [albumid])
myDB.action('UPDATE snatched SET status = "Processed" WHERE Status NOT LIKE "Seed%" and AlbumID=?', [albumid])
# Check if torrent has finished seeding
if headphones.TORRENT_DOWNLOADER == 1 or headphones.TORRENT_DOWNLOADER == 2:
seed_snatched = myDB.action('SELECT * from snatched WHERE Status="Seed_Snatched" and AlbumID=?', [albumid]).fetchone()
if seed_snatched:
hash = seed_snatched['FolderName']
torrent_removed = False
if headphones.TORRENT_DOWNLOADER == 1:
torrent_removed = transmission.removeTorrent(hash, True)
else:
torrent_removed = utorrent.removeTorrent(hash, True)
# Torrent removed, delete the snatched record, else update Status for scheduled job to check
if torrent_removed:
myDB.action('DELETE from snatched WHERE status = "Seed_Snatched" and AlbumID=?', [albumid])
else:
myDB.action('UPDATE snatched SET status = "Seed_Processed" WHERE status = "Seed_Snatched" and AlbumID=?', [albumid])
# Update the have tracks for all created dirs:
for albumpath in albumpaths:
@@ -560,11 +580,23 @@ def cleanupFiles(albumpath):
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))
if not (headphones.KEEP_NFO and files.lower().endswith('.nfo')):
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 renameNFO(albumpath):
for r,d,f in os.walk(albumpath):
for file in f:
if file.lower().endswith('.nfo'):
logger.debug('Renaming: "%s" to "%s"' % (file.decode(headphones.SYS_ENCODING, 'replace'), file.decode(headphones.SYS_ENCODING, 'replace') + '-orig'))
try:
new_file_name = os.path.join(r, file)[:-3] + 'orig.nfo'
os.rename(os.path.join(r, file), new_file_name)
except Exception, e:
logger.error(u'Could not remove file: %s. Error: %s' % (files.decode(headphones.SYS_ENCODING, 'replace'), e))
logger.error(u'Could not rename file: %s. Error: %s' % (os.path.join(r, file).decode(headphones.SYS_ENCODING, 'replace'), e))
def moveFiles(albumpath, release, tracks):
@@ -590,20 +622,27 @@ def moveFiles(albumpath, release, tracks):
firstchar = '0-9'
else:
firstchar = sortname[0]
for r,d,f in os.walk(albumpath):
try:
origfolder = os.path.basename(os.path.normpath(r))
except:
origfolder = ''
values = { '$Artist': artist,
'$SortArtist': sortname,
'$Album': album,
'$Year': year,
'$Type': releasetype,
'$OriginalFolder': origfolder,
'$First': firstchar.upper(),
'$artist': artist.lower(),
'$sortartist': sortname.lower(),
'$album': album.lower(),
'$year': year,
'$type': releasetype.lower(),
'$first': firstchar.lower()
'$first': firstchar.lower(),
'$originalfolder': origfolder.lower()
}
folder = helpers.replace_all(headphones.FOLDER_FORMAT.strip(), values)

View File

@@ -1,3 +1,18 @@
# 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/>.
from headphones import logger
from xml.dom import minidom

View File

@@ -569,6 +569,8 @@ def send_to_downloader(data, bestqual, album):
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]))
# Get rid of any dodgy chars here so we can prevent sab from renaming our downloads
kind = bestqual[4]
seed_ratio = None
torrentid = None
if kind == 'nzb':
folder_name = helpers.sab_sanitize_foldername(bestqual[0])
@@ -674,7 +676,7 @@ def send_to_downloader(data, bestqual, album):
# rutracker needs cookies to be set, pass the .torrent file instead of url
if bestqual[3] == 'rutracker.org':
file_or_url, _hash = rutracker.get_torrent(bestqual[2])
file_or_url, torrentid = rutracker.get_torrent(bestqual[2])
else:
file_or_url = bestqual[2]
@@ -698,19 +700,24 @@ def send_to_downloader(data, bestqual, album):
except Exception as e:
logger.exception("Unhandled exception")
# Set Seed Ratio
seed_ratio = getSeedRatio(bestqual[3])
if seed_ratio != None:
transmission.setSeedRatio(torrentid, seed_ratio)
else:# if headphones.TORRENT_DOWNLOADER == 2:
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, _hash = rutracker.get_torrent(bestqual[2])
folder_name, cacheid = utorrent.dirTorrent(_hash)
file_or_url, torrentid = rutracker.get_torrent(bestqual[2])
folder_name, cacheid = utorrent.dirTorrent(torrentid)
folder_name = os.path.basename(os.path.normpath(folder_name))
utorrent.labelTorrent(_hash)
utorrent.labelTorrent(torrentid)
else:
file_or_url = bestqual[2]
_hash = CalculateTorrentHash(file_or_url, data)
folder_name = utorrent.addTorrent(file_or_url, _hash)
torrentid = CalculateTorrentHash(file_or_url, data)
folder_name = utorrent.addTorrent(file_or_url, torrentid)
if folder_name:
logger.info('Torrent folder name: %s' % folder_name)
@@ -725,10 +732,19 @@ def send_to_downloader(data, bestqual, album):
except Exception as e:
logger.exception("Unhandled exception")
# Set Seed Ratio
seed_ratio = getSeedRatio(bestqual[3])
if seed_ratio != None:
utorrent.setSeedRatio(torrentid, seed_ratio)
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])
# Store the torrent id so we can check later if it's finished seeding and can be removed
if seed_ratio != None and seed_ratio != 0 and torrentid:
myDB.action('INSERT INTO snatched VALUES( ?, ?, ?, ?, DATETIME("NOW", "localtime"), ?, ?, ?)', [album['AlbumID'], bestqual[0], bestqual[1], bestqual[2], "Seed_Snatched", torrentid, kind])
# notify
artist = album[1]
albumname = album[2]
@@ -1351,3 +1367,27 @@ def CalculateTorrentHash(link, data):
logger.debug('Torrent Hash: ' + str(tor_hash))
return tor_hash
def getSeedRatio(provider):
seed_ratio = ''
if provider == 'rutracker.org':
seed_ratio = headphones.RUTRACKER_RATIO
elif provider == 'Kick Ass Torrents':
seed_ratio = headphones.KAT_RATIO
elif provider == 'What.cd':
seed_ratio = headphones.WHATCD_RATIO
elif provider == 'The Pirate Bay':
seed_ratio = headphones.PIRATEBAY_RATIO
elif provider == 'Waffles.fm':
seed_ratio = headphones.WAFFLES_RATIO
elif provider == 'Mininova':
seed_ratio = headphones.MININOVA_RATIO
if seed_ratio != '':
try:
seed_ratio_float = float(seed_ratio)
except:
seed_ratio_float = None
logger.warn('Could not get Seed Ratio for %s' % provider)
return seed_ratio_float
else:
return None

View File

@@ -0,0 +1,40 @@
# 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 threading
import headphones
from headphones import db, utorrent, transmission, logger
postprocessor_lock = threading.Lock()
# Remove Torrent + data if Post Processed and finished Seeding
def checkTorrentFinished():
with postprocessor_lock:
myDB = db.DBConnection()
results = myDB.select('SELECT * from snatched WHERE Status="Seed_Processed"')
for album in results:
hash = album['FolderName']
albumid = album['AlbumID']
torrent_removed = False
if headphones.TORRENT_DOWNLOADER == 1:
torrent_removed = transmission.removeTorrent(hash, True)
else:
torrent_removed = utorrent.removeTorrent(hash, True)
if torrent_removed:
myDB.action('DELETE from snatched WHERE status = "Seed_Processed" and AlbumID=?', [albumid])

View File

@@ -47,10 +47,10 @@ def addTorrent(link):
if response['result'] == 'success':
if 'torrent-added' in response['arguments']:
name = response['arguments']['torrent-added']['name']
retid = response['arguments']['torrent-added']['id']
retid = response['arguments']['torrent-added']['hashString']
elif 'torrent-duplicate' in response['arguments']:
name = response['arguments']['torrent-duplicate']['name']
retid = response['arguments']['torrent-duplicate']['id']
retid = response['arguments']['torrent-duplicate']['hashString']
else:
name = link
retid = False
@@ -82,6 +82,39 @@ def getTorrentFolder(torrentid):
return torrent_folder_name
def setSeedRatio(torrentid, ratio):
method = 'torrent-set'
if ratio != 0:
arguments = {'seedRatioLimit': ratio, 'seedRatioMode': 1, 'ids': torrentid}
else:
arguments = {'seedRatioMode': 2, 'ids': torrentid}
response = torrentAction(method, arguments)
if not response:
return False
def removeTorrent(torrentid, remove_data = False):
method = 'torrent-get'
arguments = { 'ids': torrentid, 'fields': ['isFinished', 'name']}
response = torrentAction(method, arguments)
finished = response['arguments']['torrents'][0]['isFinished']
name = response['arguments']['torrents'][0]['name']
if finished:
logger.info('%s has finished seeding, removing torrent and data' % name)
method = 'torrent-remove'
if remove_data:
arguments = {'delete-local-data': True, 'ids': torrentid}
else:
arguments = {'ids': torrentid}
response = torrentAction(method, arguments)
return True
return False
def torrentAction(method, arguments):
host = headphones.TRANSMISSION_HOST

View File

@@ -132,6 +132,13 @@ class utorrentclient(object):
return settings[key]
return settings
def remove(self, hash, remove_data = False):
if remove_data:
params = [('action', 'removedata'), ('hash', hash)]
else:
params = [('action', 'remove'), ('hash', hash)]
return self._action(params)
def _action(self, params, body=None, content_type=None):
url = self.base_url + '/gui/' + '?token=' + self.token + '&' + urllib.urlencode(params)
request = urllib2.Request(url)
@@ -155,7 +162,27 @@ def labelTorrent(hash):
if label:
uTorrentClient.setprops(hash,'label',label)
def dirTorrent(hash, cacheid=None):
def removeTorrent(hash, remove_data = False):
uTorrentClient = utorrentclient()
status, torrentList = uTorrentClient.list()
torrents = torrentList['torrents']
for torrent in torrents:
if torrent[0].lower() == hash and torrent[21] == 'Finished':
logger.info('%s has finished seeding, removing torrent and data' % torrent[2])
uTorrentClient.remove(hash, remove_data)
return True
return False
def setSeedRatio(hash, ratio):
uTorrentClient = utorrentclient()
uTorrentClient.setprops(hash, 'seed_override', '1')
if ratio != 0:
uTorrentClient.setprops(hash,'seed_ratio', ratio * 10)
else:
# TODO passing -1 should be unlimited
uTorrentClient.setprops(hash,'seed_ratio', -1.00)
def dirTorrent(hash, cacheid=None, return_name=None):
uTorrentClient = utorrentclient()
@@ -174,7 +201,10 @@ def dirTorrent(hash, cacheid=None):
for torrent in torrents:
if (torrent[0].lower() == hash):
return torrent[26], cacheid
if not return_name:
return torrent[26], cacheid
else:
return torrent[2], cacheid
return None, None
@@ -184,6 +214,10 @@ def addTorrent(link, hash):
# Get Active Directory from settings
active_dir, completed_dir = getSettingsDirectories()
if not active_dir or not completed_dir:
logger.error('Could not get "Put new downloads in:" or "Move completed downloads to:" directories from uTorrent settings, please ensure they are set')
return None
uTorrentClient.add_url(link)
# Get Torrent Folder Name
@@ -197,8 +231,10 @@ def addTorrent(link, hash):
time.sleep(6)
torrent_folder, cacheid = dirTorrent(hash, cacheid)
if torrent_folder == active_dir:
return None
if torrent_folder == active_dir or not torrent_folder:
torrent_folder, cacheid = dirTorrent(hash, cacheid, return_name=True)
labelTorrent(hash)
return torrent_folder
else:
labelTorrent(hash)
return os.path.basename(os.path.normpath(torrent_folder))
@@ -206,6 +242,11 @@ def addTorrent(link, hash):
def getSettingsDirectories():
uTorrentClient = utorrentclient()
settings = uTorrentClient.get_settings()
active = settings['dir_active_download'][2]
completed = settings['dir_completed_download'][2]
active = None
completed = None
if 'dir_active_download' in settings:
active = settings['dir_active_download'][2]
if 'dir_completed_download' in settings:
completed = settings['dir_completed_download'][2]
return active, completed

View File

@@ -735,7 +735,7 @@ class WebInterface(object):
def history(self):
myDB = db.DBConnection()
history = myDB.select('''SELECT * from snatched order by DateAdded DESC''')
history = myDB.select('''SELECT * from snatched WHERE Status NOT LIKE "Seed%" order by DateAdded DESC''')
return serve_template(templatename="history.html", title="History", history=history)
history.exposed = True
@@ -901,13 +901,13 @@ class WebInterface(object):
if type:
if type == 'all':
logger.info(u"Clearing all history")
myDB.action('DELETE from snatched')
myDB.action('DELETE from snatched WHERE Status NOT LIKE "Seed%"')
else:
logger.info(u"Clearing history where status is %s" % type)
myDB.action('DELETE from snatched WHERE Status=?', [type])
else:
logger.info(u"Deleting '%s' from history" % title)
myDB.action('DELETE from snatched WHERE Title=? AND DateAdded=?', [title, date_added])
myDB.action('DELETE from snatched WHERE Status NOT LIKE "Seed%" AND Title=? AND DateAdded=?', [title, date_added])
raise cherrypy.HTTPRedirect("history")
clearhistory.exposed = True
@@ -971,6 +971,7 @@ class WebInterface(object):
"nzbget_user" : headphones.NZBGET_USERNAME,
"nzbget_pass" : headphones.NZBGET_PASSWORD,
"nzbget_cat" : headphones.NZBGET_CATEGORY,
"nzbget_priority" : headphones.NZBGET_PRIORITY,
"transmission_host" : headphones.TRANSMISSION_HOST,
"transmission_user" : headphones.TRANSMISSION_USERNAME,
"transmission_pass" : headphones.TRANSMISSION_PASSWORD,
@@ -1008,18 +1009,24 @@ class WebInterface(object):
"numberofseeders" : headphones.NUMBEROFSEEDERS,
"use_kat" : checked(headphones.KAT),
"kat_proxy_url" : headphones.KAT_PROXY_URL,
"kat_ratio": headphones.KAT_RATIO,
"use_piratebay" : checked(headphones.PIRATEBAY),
"piratebay_proxy_url" : headphones.PIRATEBAY_PROXY_URL,
"piratebay_ratio": headphones.PIRATEBAY_RATIO,
"use_mininova" : checked(headphones.MININOVA),
"mininova_ratio": headphones.MININOVA_RATIO,
"use_waffles" : checked(headphones.WAFFLES),
"waffles_uid" : headphones.WAFFLES_UID,
"waffles_passkey": headphones.WAFFLES_PASSKEY,
"waffles_ratio": headphones.WAFFLES_RATIO,
"use_rutracker" : checked(headphones.RUTRACKER),
"rutracker_user" : headphones.RUTRACKER_USER,
"rutracker_password": headphones.RUTRACKER_PASSWORD,
"rutracker_ratio": headphones.RUTRACKER_RATIO,
"use_whatcd" : checked(headphones.WHATCD),
"whatcd_username" : headphones.WHATCD_USERNAME,
"whatcd_password": headphones.WHATCD_PASSWORD,
"whatcd_ratio": headphones.WHATCD_RATIO,
"pref_qual_0" : radio(headphones.PREFERRED_QUALITY, 0),
"pref_qual_1" : radio(headphones.PREFERRED_QUALITY, 1),
"pref_qual_3" : radio(headphones.PREFERRED_QUALITY, 3),
@@ -1035,6 +1042,7 @@ class WebInterface(object):
"rename_files" : checked(headphones.RENAME_FILES),
"correct_metadata" : checked(headphones.CORRECT_METADATA),
"cleanup_files" : checked(headphones.CLEANUP_FILES),
"keep_nfo" : checked(headphones.KEEP_NFO),
"add_album_art" : checked(headphones.ADD_ALBUM_ART),
"album_art_format" : headphones.ALBUM_ART_FORMAT,
"embed_album_art" : checked(headphones.EMBED_ALBUM_ART),
@@ -1154,12 +1162,12 @@ class WebInterface(object):
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, update_db_interval=None, mb_ignore_age=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=None, nzbget_password=None, nzbget_category=None, transmission_host=None, transmission_username=None, transmission_password=None,
sab_category=None, nzbget_host=None, nzbget_username=None, nzbget_password=None, nzbget_category=None, nzbget_priority=0, transmission_host=None, transmission_username=None, transmission_password=None,
utorrent_host=None, utorrent_username=None, utorrent_password=None, utorrent_label=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, omgwtfnzbs=0, omgwtfnzbs_uid=None, omgwtfnzbs_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, piratebay_proxy_url=None, use_kat=0, kat_proxy_url=None, 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, replace_existing_folders=False,
numberofseeders=None, use_piratebay=0, piratebay_proxy_url=None, piratebay_ratio=None, use_kat=0, kat_proxy_url=None, kat_ratio=None, use_mininova=0, mininova_ratio=None, waffles=0, waffles_uid=None, waffles_passkey=None, waffles_ratio=None, whatcd=0, whatcd_username=None, whatcd_password=None, whatcd_ratio=None,
rutracker=0, rutracker_user=None, rutracker_password=None, rutracker_ratio=None, rename_files=0, correct_metadata=0, cleanup_files=0, keep_nfo=0, add_album_art=0, album_art_format=None, embed_album_art=0, embed_lyrics=0, replace_existing_folders=False,
destination_dir=None, lossless_destination_dir=None, folder_format=None, file_format=None, file_underscores=0, include_extras=0, single=0, ep=0, compilation=0, soundtrack=0, live=0,
remix=0, djmix=0, mixtape_street=0, broadcast=0, interview=0, spokenword=0, audiobook=0, other=0, autowant_upcoming=False, autowant_all=False, keep_torrent_files=False, prefer_torrents=0, open_magnet_links=0, interface=None, log_dir=None, cache_dir=None, music_encoder=0, encoder=None, xldprofile=None,
bitrate=None, samplingfrequency=None, encoderfolder=None, advancedencoder=None, encoderoutputformat=None, encodervbrcbr=None, encoderquality=None, encoderlossless=0,
@@ -1195,6 +1203,7 @@ class WebInterface(object):
headphones.NZBGET_USERNAME = nzbget_username
headphones.NZBGET_PASSWORD = nzbget_password
headphones.NZBGET_CATEGORY = nzbget_category
headphones.NZBGET_PRIORITY = int(nzbget_priority)
headphones.TRANSMISSION_HOST = transmission_host
headphones.TRANSMISSION_USERNAME = transmission_username
headphones.TRANSMISSION_PASSWORD = transmission_password
@@ -1226,18 +1235,24 @@ class WebInterface(object):
headphones.DOWNLOAD_TORRENT_DIR = download_torrent_dir
headphones.KAT = use_kat
headphones.KAT_PROXY_URL = kat_proxy_url
headphones.KAT_RATIO = kat_ratio
headphones.PIRATEBAY = use_piratebay
headphones.PIRATEBAY_PROXY_URL = piratebay_proxy_url
headphones.PIRATEBAY_RATIO = piratebay_ratio
headphones.MININOVA = use_mininova
headphones.MININOVA_RATIO = mininova_ratio
headphones.WAFFLES = waffles
headphones.WAFFLES_UID = waffles_uid
headphones.WAFFLES_PASSKEY = waffles_passkey
headphones.WAFFLES_RATIO = waffles_ratio
headphones.RUTRACKER = rutracker
headphones.RUTRACKER_USER = rutracker_user
headphones.RUTRACKER_PASSWORD = rutracker_password
headphones.RUTRACKER_RATIO = rutracker_ratio
headphones.WHATCD = whatcd
headphones.WHATCD_USERNAME = whatcd_username
headphones.WHATCD_PASSWORD = whatcd_password
headphones.WHATCD_RATIO = whatcd_ratio
headphones.PREFERRED_QUALITY = int(preferred_quality)
headphones.PREFERRED_BITRATE = preferred_bitrate
headphones.PREFERRED_BITRATE_HIGH_BUFFER = preferred_bitrate_high_buffer
@@ -1250,6 +1265,7 @@ class WebInterface(object):
headphones.CORRECT_METADATA = correct_metadata
headphones.RENAME_FILES = rename_files
headphones.CLEANUP_FILES = cleanup_files
headphones.KEEP_NFO = keep_nfo
headphones.ADD_ALBUM_ART = add_album_art
headphones.ALBUM_ART_FORMAT = album_art_format
headphones.EMBED_ALBUM_ART = embed_album_art