Merge branch 'refs/heads/develop'
This commit is contained in:
@@ -12,6 +12,7 @@ config = [{
|
||||
'name': 'nzbget',
|
||||
'label': 'NZBGet',
|
||||
'description': 'Use <a href="http://nzbget.sourceforge.net/Main_Page" target="_blank">NZBGet</a> to download NZBs.',
|
||||
'wizard': True,
|
||||
'options': [
|
||||
{
|
||||
'name': 'enabled',
|
||||
|
||||
@@ -35,6 +35,11 @@ config = [{
|
||||
'name': 'label',
|
||||
'description': 'Label to apply on added torrents.',
|
||||
},
|
||||
{
|
||||
'name': 'directory',
|
||||
'type': 'directory',
|
||||
'description': 'Directory where rtorrent should download the files too.',
|
||||
},
|
||||
{
|
||||
'name': 'remove_complete',
|
||||
'label': 'Remove torrent',
|
||||
@@ -43,6 +48,14 @@ config = [{
|
||||
'type': 'bool',
|
||||
'description': 'Remove the torrent after it finishes seeding.',
|
||||
},
|
||||
{
|
||||
'name': 'append_label',
|
||||
'label': 'Append Label',
|
||||
'default': False,
|
||||
'advanced': True,
|
||||
'type': 'bool',
|
||||
'description': 'Append label to download location. Requires you to set the download location above.',
|
||||
},
|
||||
{
|
||||
'name': 'delete_files',
|
||||
'label': 'Remove files',
|
||||
|
||||
@@ -7,7 +7,7 @@ from datetime import timedelta
|
||||
from hashlib import sha1
|
||||
from rtorrent import RTorrent
|
||||
from rtorrent.err import MethodError
|
||||
import shutil
|
||||
import shutil, os
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -91,6 +91,7 @@ class rTorrent(Downloader):
|
||||
if self.conf('label'):
|
||||
torrent_params['label'] = self.conf('label')
|
||||
|
||||
|
||||
if not filedata and data.get('protocol') == 'torrent':
|
||||
log.error('Failed sending torrent, no data')
|
||||
return False
|
||||
@@ -116,10 +117,19 @@ class rTorrent(Downloader):
|
||||
# Send torrent to rTorrent
|
||||
torrent = self.rt.load_torrent(filedata)
|
||||
|
||||
if not torrent:
|
||||
log.error('Unable to find the torrent, did it fail to load?')
|
||||
return False
|
||||
|
||||
# Set label
|
||||
if self.conf('label'):
|
||||
torrent.set_custom(1, self.conf('label'))
|
||||
|
||||
if self.conf('directory') and self.conf('append_label'):
|
||||
torrent.set_directory(os.path.join(self.conf('directory'), self.conf('label')))
|
||||
elif self.conf('directory'):
|
||||
torrent.set_directory(self.conf('directory'))
|
||||
|
||||
# Set Ratio Group
|
||||
torrent.set_visible(group_name)
|
||||
|
||||
|
||||
@@ -90,9 +90,14 @@ class Sabnzbd(Downloader):
|
||||
|
||||
# Get busy releases
|
||||
for item in queue.get('slots', []):
|
||||
status = 'busy'
|
||||
if 'ENCRYPTED / ' in item['filename']:
|
||||
status = 'failed'
|
||||
|
||||
statuses.append({
|
||||
'id': item['nzo_id'],
|
||||
'name': item['filename'],
|
||||
'status': status,
|
||||
'original_status': item['status'],
|
||||
'timeleft': item['timeleft'] if not queue['paused'] else -1,
|
||||
})
|
||||
@@ -122,6 +127,12 @@ class Sabnzbd(Downloader):
|
||||
log.info('%s failed downloading, deleting...', item['name'])
|
||||
|
||||
try:
|
||||
self.call({
|
||||
'mode': 'queue',
|
||||
'name': 'delete',
|
||||
'del_files': '1',
|
||||
'value': item['id']
|
||||
}, use_json = False)
|
||||
self.call({
|
||||
'mode': 'history',
|
||||
'name': 'delete',
|
||||
|
||||
@@ -60,7 +60,7 @@ class Searcher(SearcherBase):
|
||||
|
||||
if downloader_enabled:
|
||||
|
||||
snatched_status = fireEvent('status.get', 'snatched', single = True)
|
||||
snatched_status, done_status, active_status = fireEvent('status.get', ['snatched', 'done', 'active'], single = True)
|
||||
|
||||
# Download movie to temp
|
||||
filedata = None
|
||||
@@ -79,9 +79,7 @@ class Searcher(SearcherBase):
|
||||
rls = db.query(Release).filter_by(identifier = md5(data['url'])).first()
|
||||
if rls:
|
||||
renamer_enabled = Env.setting('enabled', 'renamer')
|
||||
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
rls.status_id = done_status.get('id') if not renamer_enabled else snatched_status.get('id')
|
||||
fireEvent('release.update_status', rls.id, status = done_status if not renamer_enabled else snatched_status, single = True)
|
||||
|
||||
# Save download-id info if returned
|
||||
if isinstance(download_result, dict):
|
||||
@@ -100,20 +98,12 @@ class Searcher(SearcherBase):
|
||||
|
||||
# If renamer isn't used, mark movie done
|
||||
if not renamer_enabled:
|
||||
active_status = fireEvent('status.get', 'active', single = True)
|
||||
done_status = fireEvent('status.get', 'done', single = True)
|
||||
try:
|
||||
if movie['status_id'] == active_status.get('id'):
|
||||
for profile_type in movie['profile']['types']:
|
||||
if profile_type['quality_id'] == rls.quality.id and profile_type['finish']:
|
||||
log.info('Renamer disabled, marking movie as finished: %s', log_movie)
|
||||
|
||||
# Mark release done
|
||||
rls.status_id = done_status.get('id')
|
||||
rls.last_edit = int(time.time())
|
||||
db.commit()
|
||||
|
||||
# Mark movie done
|
||||
log.info('Renamer disabled, marking movie as finished: %s', log_movie)
|
||||
mvie = db.query(Movie).filter_by(id = movie['id']).first()
|
||||
mvie.status_id = done_status.get('id')
|
||||
mvie.last_edit = int(time.time())
|
||||
|
||||
@@ -241,7 +241,6 @@ MA.Release = new Class({
|
||||
}
|
||||
})
|
||||
).inject(self.release_container);
|
||||
|
||||
release['el'] = item;
|
||||
|
||||
if(status.identifier == 'ignored' || status.identifier == 'failed' || status.identifier == 'snatched'){
|
||||
@@ -251,6 +250,30 @@ MA.Release = new Class({
|
||||
else if(!self.next_release && status.identifier == 'available'){
|
||||
self.next_release = release;
|
||||
}
|
||||
|
||||
var update_handle = function(notification) {
|
||||
var q = self.movie.quality.getElement('.q_id' + release.quality_id),
|
||||
status = Status.get(release.status_id),
|
||||
new_status = Status.get(notification.data);
|
||||
|
||||
release.status_id = new_status.id
|
||||
release.el.set('class', 'item ' + new_status.identifier);
|
||||
|
||||
var status_el = release.el.getElement('.release_status');
|
||||
status_el.set('class', 'release_status ' + new_status.identifier);
|
||||
status_el.set('text', new_status.identifier);
|
||||
|
||||
if(!q && (new_status.identifier == 'snatched' || new_status.identifier == 'seeding' || new_status.identifier == 'done'))
|
||||
var q = self.addQuality(release.quality_id);
|
||||
|
||||
if(new_status && q && !q.hasClass(new_status.identifier)) {
|
||||
q.removeClass(status.identifier).addClass(new_status.identifier);
|
||||
q.set('title', q.get('title').replace(status.label, new_status.label));
|
||||
}
|
||||
}
|
||||
|
||||
App.addEvent('release.update_status.' + release.id, update_handle);
|
||||
|
||||
});
|
||||
|
||||
if(self.last_release)
|
||||
@@ -397,17 +420,6 @@ MA.Release = new Class({
|
||||
'data': {
|
||||
'id': release.id
|
||||
},
|
||||
'onComplete': function(){
|
||||
var el = release.el;
|
||||
if(el && (el.hasClass('failed') || el.hasClass('ignored'))){
|
||||
el.removeClass('failed').removeClass('ignored');
|
||||
el.getElement('.release_status').set('text', 'available');
|
||||
}
|
||||
else if(el) {
|
||||
el.addClass('ignored');
|
||||
el.getElement('.release_status').set('text', 'ignored');
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
@@ -419,22 +419,25 @@
|
||||
}
|
||||
|
||||
.movies .data .quality .available,
|
||||
.movies .data .quality .snatched {
|
||||
.movies .data .quality .snatched,
|
||||
.movies .data .quality .seeding {
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.movies .data .quality .available { background-color: #578bc3; }
|
||||
.movies .data .quality .failed { background-color: #a43d34; }
|
||||
.movies .data .quality .failed,
|
||||
.movies .data .quality .missing,
|
||||
.movies .data .quality .ignored { background-color: #a43d34; }
|
||||
.movies .data .quality .snatched { background-color: #a2a232; }
|
||||
.movies .data .quality .seeding { background-color: #0a6819; }
|
||||
.movies .data .quality .done {
|
||||
background-color: #369545;
|
||||
opacity: 1;
|
||||
}
|
||||
.movies .data .quality .seeding { background-color: #0a6819; }
|
||||
.movies .data .quality .finish {
|
||||
background-image: url('../images/sprite.png');
|
||||
background-repeat: no-repeat;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 2px;
|
||||
padding-left: 14px;
|
||||
background-size: 14px
|
||||
@@ -646,7 +649,7 @@
|
||||
margin-top: 25px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.trailer_container.hide {
|
||||
height: 0 !important;
|
||||
}
|
||||
@@ -1029,7 +1032,7 @@
|
||||
.movies .progress > div .folder {
|
||||
display: inline-block;
|
||||
padding: 5px 20px 5px 0;
|
||||
white-space: nowrap;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
width: 85%;
|
||||
|
||||
@@ -185,7 +185,7 @@ var Movie = new Class({
|
||||
var q = self.quality.getElement('.q_id'+ release.quality_id),
|
||||
status = Status.get(release.status_id);
|
||||
|
||||
if(!q && (status.identifier == 'snatched' || status.identifier == 'done'))
|
||||
if(!q && (status.identifier == 'snatched' || status.identifier == 'seeding' || status.identifier == 'done'))
|
||||
var q = self.addQuality(release.quality_id)
|
||||
|
||||
if (status && q && !q.hasClass(status.identifier)){
|
||||
|
||||
@@ -52,7 +52,7 @@ class Plex(Notification):
|
||||
clients.remove(server.get('name').lower())
|
||||
protocol = server.get('protocol', 'xbmchttp')
|
||||
|
||||
if protocol in ['xbmcjson', 'xbmchttp']:
|
||||
if protocol in ['plex', 'xbmcjson', 'xbmchttp']:
|
||||
self.clients[server.get('name')] = {
|
||||
'name': server.get('name'),
|
||||
'address': server.get('address'),
|
||||
@@ -164,7 +164,7 @@ class Plex(Notification):
|
||||
}
|
||||
|
||||
for name, client in self.clients.items():
|
||||
if client['protocol'] == 'xbmcjson':
|
||||
if client['protocol'] in ['xbmcjson', 'plex']:
|
||||
total += 1
|
||||
if self.sendJSON('GUI.ShowNotification', params, client):
|
||||
successful += 1
|
||||
|
||||
@@ -10,6 +10,7 @@ from sqlalchemy.orm import joinedload_all
|
||||
from sqlalchemy.sql.expression import and_, or_
|
||||
import os
|
||||
import traceback
|
||||
import time
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -47,6 +48,7 @@ class Release(Plugin):
|
||||
addEvent('release.for_movie', self.forMovie)
|
||||
addEvent('release.delete', self.delete)
|
||||
addEvent('release.clean', self.clean)
|
||||
addEvent('release.update_status', self.updateStatus)
|
||||
|
||||
def add(self, group):
|
||||
|
||||
@@ -159,8 +161,7 @@ class Release(Plugin):
|
||||
rel = db.query(Relea).filter_by(id = id).first()
|
||||
if rel:
|
||||
ignored_status, failed_status, available_status = fireEvent('status.get', ['ignored', 'failed', 'available'], single = True)
|
||||
rel.status_id = available_status.get('id') if rel.status_id in [ignored_status.get('id'), failed_status.get('id')] else ignored_status.get('id')
|
||||
db.commit()
|
||||
self.updateStatus(id, available_status if rel.status_id in [ignored_status.get('id'), failed_status.get('id')] else ignored_status)
|
||||
|
||||
return {
|
||||
'success': True
|
||||
@@ -199,14 +200,9 @@ class Release(Plugin):
|
||||
|
||||
if success:
|
||||
db.expunge_all()
|
||||
rel = db.query(Relea).filter_by(id = id).first() # Get release again
|
||||
|
||||
if rel.status_id != done_status.get('id'):
|
||||
rel.status_id = snatched_status.get('id')
|
||||
db.commit()
|
||||
rel = db.query(Relea).filter_by(id = id).first() # Get release again @RuudBurger why do we need to get it again??
|
||||
|
||||
fireEvent('notify.frontend', type = 'release.download', data = True, message = 'Successfully snatched "%s"' % item['name'])
|
||||
|
||||
return {
|
||||
'success': success
|
||||
}
|
||||
@@ -241,3 +237,23 @@ class Release(Plugin):
|
||||
'success': True
|
||||
}
|
||||
|
||||
def updateStatus(self, id, status = None):
|
||||
if not status: return
|
||||
|
||||
db = get_session()
|
||||
|
||||
rel = db.query(Relea).filter_by(id = id).first()
|
||||
if rel and status and rel.status_id != status.get('id'):
|
||||
|
||||
item = {}
|
||||
for info in rel.info:
|
||||
item[info.identifier] = info.value
|
||||
|
||||
#update status in Db
|
||||
log.debug('Marking release %s as %s', (item['name'], status.get("label")))
|
||||
rel.status_id = status.get('id')
|
||||
rel.last_edit = int(time.time())
|
||||
db.commit()
|
||||
|
||||
#Update all movie info as there is no release update function
|
||||
fireEvent('notify.frontend', type = 'release.update_status.%s' % rel.id, data = status.get('id'))
|
||||
|
||||
@@ -395,14 +395,8 @@ class Renamer(Plugin):
|
||||
break
|
||||
elif release.status_id is snatched_status.get('id'):
|
||||
if release.quality.id is group['meta_data']['quality']['id']:
|
||||
log.debug('Marking release as downloaded')
|
||||
try:
|
||||
release.status_id = downloaded_status.get('id')
|
||||
release.last_edit = int(time.time())
|
||||
except Exception, e:
|
||||
log.error('Failed marking release as finished: %s %s', (e, traceback.format_exc()))
|
||||
|
||||
db.commit()
|
||||
# Set the release to downloaded
|
||||
fireEvent('release.update_status', release.id, status = downloaded_status, single = True)
|
||||
|
||||
# Remove leftover files
|
||||
if not remove_leftovers: # Don't remove anything
|
||||
@@ -476,11 +470,18 @@ class Renamer(Plugin):
|
||||
log.error('Failed removing %s: %s', (release.identifier, traceback.format_exc()))
|
||||
|
||||
if group['dirname'] and group['parentdir'] and not self.downloadIsTorrent(download_info):
|
||||
if movie_folder:
|
||||
# Delete the movie folder
|
||||
group_folder = movie_folder
|
||||
else:
|
||||
# Delete the first empty subfolder in the tree relative to the 'from' folder
|
||||
group_folder = os.path.join(self.conf('from'), os.path.relpath(group['parentdir'], self.conf('from')).split(os.path.sep)[0])
|
||||
|
||||
try:
|
||||
log.info('Deleting folder: %s', group['parentdir'])
|
||||
self.deleteEmptyFolder(group['parentdir'])
|
||||
log.info('Deleting folder: %s', group_folder)
|
||||
self.deleteEmptyFolder(group_folder)
|
||||
except:
|
||||
log.error('Failed removing %s: %s', (group['parentdir'], traceback.format_exc()))
|
||||
log.error('Failed removing %s: %s', (group_folder, traceback.format_exc()))
|
||||
|
||||
# Notify on download, search for trailers etc
|
||||
download_message = 'Downloaded %s (%s)' % (movie_title, replacements['quality'])
|
||||
@@ -656,12 +657,13 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
|
||||
self.checking_snatched = True
|
||||
|
||||
snatched_status, ignored_status, failed_status, done_status, seeding_status, downloaded_status = \
|
||||
fireEvent('status.get', ['snatched', 'ignored', 'failed', 'done', 'seeding', 'downloaded'], single = True)
|
||||
snatched_status, ignored_status, failed_status, done_status, seeding_status, downloaded_status, missing_status = \
|
||||
fireEvent('status.get', ['snatched', 'ignored', 'failed', 'done', 'seeding', 'downloaded', 'missing'], single = True)
|
||||
|
||||
db = get_session()
|
||||
rels = db.query(Release).filter_by(status_id = snatched_status.get('id')).all()
|
||||
rels.extend(db.query(Release).filter_by(status_id = seeding_status.get('id')).all())
|
||||
rels = db.query(Release).filter(
|
||||
Release.status_id.in_([snatched_status.get('id'), seeding_status.get('id'), missing_status.get('id')])
|
||||
).all()
|
||||
|
||||
scan_items = []
|
||||
scan_required = False
|
||||
@@ -677,7 +679,6 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
try:
|
||||
for rel in rels:
|
||||
rel_dict = rel.to_dict({'info': {}})
|
||||
|
||||
movie_dict = fireEvent('movie.get', rel.movie_id, single = True)
|
||||
|
||||
# check status
|
||||
@@ -699,39 +700,36 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
log.debug('Found %s: %s, time to go: %s', (item['name'], item['status'].upper(), timeleft))
|
||||
|
||||
if item['status'] == 'busy':
|
||||
# Set the release to snatched if it was missing before
|
||||
fireEvent('release.update_status', rel.id, status = snatched_status, single = True)
|
||||
|
||||
# Tag folder if it is in the 'from' folder and it will not be processed because it is still downloading
|
||||
if item['folder'] and self.conf('from') in item['folder']:
|
||||
self.tagDir(item['folder'], 'downloading')
|
||||
|
||||
elif item['status'] == 'seeding':
|
||||
# Set the release to seeding
|
||||
fireEvent('release.update_status', rel.id, status = seeding_status, single = True)
|
||||
|
||||
#If linking setting is enabled, process release
|
||||
if self.conf('file_action') != 'move' and not rel.movie.status_id == done_status.get('id') and self.statusInfoComplete(item):
|
||||
if self.conf('file_action') != 'move' and not rel.status_id == seeding_status.get('id') and self.statusInfoComplete(item):
|
||||
log.info('Download of %s completed! It is now being processed while leaving the original files alone for seeding. Current ratio: %s.', (item['name'], item['seed_ratio']))
|
||||
|
||||
# Remove the downloading tag
|
||||
self.untagDir(item['folder'], 'downloading')
|
||||
|
||||
rel.status_id = seeding_status.get('id')
|
||||
rel.last_edit = int(time.time())
|
||||
db.commit()
|
||||
|
||||
# Scan and set the torrent to paused if required
|
||||
item.update({'pause': True, 'scan': True, 'process_complete': False})
|
||||
scan_items.append(item)
|
||||
else:
|
||||
if rel.status_id != seeding_status.get('id'):
|
||||
rel.status_id = seeding_status.get('id')
|
||||
rel.last_edit = int(time.time())
|
||||
db.commit()
|
||||
|
||||
#let it seed
|
||||
log.debug('%s is seeding with ratio: %s', (item['name'], item['seed_ratio']))
|
||||
|
||||
elif item['status'] == 'failed':
|
||||
# Set the release to failed
|
||||
fireEvent('release.update_status', rel.id, status = failed_status, single = True)
|
||||
|
||||
fireEvent('download.remove_failed', item, single = True)
|
||||
rel.status_id = failed_status.get('id')
|
||||
rel.last_edit = int(time.time())
|
||||
db.commit()
|
||||
|
||||
if self.conf('next_on_failed'):
|
||||
fireEvent('movie.searcher.try_next_release', movie_id = rel.movie_id)
|
||||
@@ -743,24 +741,23 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
if rel.status_id == seeding_status.get('id'):
|
||||
if rel.movie.status_id == done_status.get('id'):
|
||||
# Set the release to done as the movie has already been renamed
|
||||
rel.status_id = downloaded_status.get('id')
|
||||
rel.last_edit = int(time.time())
|
||||
db.commit()
|
||||
fireEvent('release.update_status', rel.id, status = downloaded_status, single = True)
|
||||
|
||||
# Allow the downloader to clean-up
|
||||
item.update({'pause': False, 'scan': False, 'process_complete': True})
|
||||
scan_items.append(item)
|
||||
else:
|
||||
# Set the release to snatched so that the renamer can process the release as if it was never seeding
|
||||
rel.status_id = snatched_status.get('id')
|
||||
rel.last_edit = int(time.time())
|
||||
db.commit()
|
||||
fireEvent('release.update_status', rel.id, status = snatched_status, single = True)
|
||||
|
||||
# Scan and Allow the downloader to clean-up
|
||||
item.update({'pause': False, 'scan': True, 'process_complete': True})
|
||||
scan_items.append(item)
|
||||
|
||||
else:
|
||||
# Set the release to snatched if it was missing before
|
||||
fireEvent('release.update_status', rel.id, status = snatched_status, single = True)
|
||||
|
||||
# Remove the downloading tag
|
||||
self.untagDir(item['folder'], 'downloading')
|
||||
|
||||
@@ -776,6 +773,14 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
if not found:
|
||||
log.info('%s not found in downloaders', nzbname)
|
||||
|
||||
#Check status if already missing and for how long, if > 1 week, set to ignored else to missing
|
||||
if rel.status_id == missing_status.get('id'):
|
||||
if rel.last_edit < int(time.time()) - 7 * 24 * 60 * 60:
|
||||
fireEvent('release.update_status', rel.id, status = ignored_status, single = True)
|
||||
else:
|
||||
# Set the release to missing
|
||||
fireEvent('release.update_status', rel.id, status = missing_status, single = True)
|
||||
|
||||
except:
|
||||
log.error('Failed checking for release in downloader: %s', traceback.format_exc())
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ class StatusPlugin(Plugin):
|
||||
'available': 'Available',
|
||||
'suggest': 'Suggest',
|
||||
'seeding': 'Seeding',
|
||||
'missing': 'Missing',
|
||||
}
|
||||
status_cached = {}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
from main import ILoveTorrents
|
||||
|
||||
def start():
|
||||
return ILoveTorrents()
|
||||
|
||||
config = [{
|
||||
'name': 'ilovetorrents',
|
||||
'groups': [
|
||||
{
|
||||
'tab': 'searcher',
|
||||
'list': 'torrent_providers',
|
||||
'name': 'ILoveTorrents',
|
||||
'description': 'Where the Love of Torrents is Born',
|
||||
'wizard': True,
|
||||
'options': [
|
||||
{
|
||||
'name': 'enabled',
|
||||
'type': 'enabler',
|
||||
'default': False
|
||||
},
|
||||
{
|
||||
'name': 'username',
|
||||
'label': 'Username',
|
||||
'type': 'string',
|
||||
'default': '',
|
||||
'description': 'The user name for your ILT account',
|
||||
},
|
||||
{
|
||||
'name': 'password',
|
||||
'label': 'Password',
|
||||
'type': 'password',
|
||||
'default': '',
|
||||
'description': 'The password for your ILT account.',
|
||||
},
|
||||
{
|
||||
'name': 'seed_ratio',
|
||||
'label': 'Seed ratio',
|
||||
'type': 'float',
|
||||
'default': 1,
|
||||
'description': 'Will not be (re)moved until this seed ratio is met.',
|
||||
},
|
||||
{
|
||||
'name': 'seed_time',
|
||||
'label': 'Seed time',
|
||||
'type': 'int',
|
||||
'default': 40,
|
||||
'description': 'Will not be (re)moved until this seed time (in hours) is met.',
|
||||
},
|
||||
{
|
||||
'name': 'extra_score',
|
||||
'advanced': True,
|
||||
'label': 'Extra Score',
|
||||
'type': 'int',
|
||||
'default': 0,
|
||||
'description': 'Starting score for each release found via this provider.',
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}]
|
||||
@@ -0,0 +1,128 @@
|
||||
from bs4 import BeautifulSoup
|
||||
from couchpotato.core.helpers.encoding import toUnicode, tryUrlencode
|
||||
from couchpotato.core.helpers.variable import tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.providers.torrent.base import TorrentProvider
|
||||
import re
|
||||
import traceback
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class ILoveTorrents(TorrentProvider):
|
||||
|
||||
urls = {
|
||||
'download': 'http://www.ilovetorrents.me/%s',
|
||||
'detail': 'http://www.ilovetorrents.me/%s',
|
||||
'search': 'http://www.ilovetorrents.me/browse.php?search=%s&page=%s&cat=%s',
|
||||
'test' : 'http://www.ilovetorrents.me/',
|
||||
'login' : 'http://www.ilovetorrents.me/takelogin.php',
|
||||
'login_check' : 'http://www.ilovetorrents.me'
|
||||
}
|
||||
|
||||
cat_ids = [
|
||||
(['41'], ['720p', '1080p', 'brrip']),
|
||||
(['19'], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']),
|
||||
(['20'], ['dvdr'])
|
||||
]
|
||||
|
||||
cat_backup_id = 200
|
||||
disable_provider = False
|
||||
http_time_between_calls = 1
|
||||
|
||||
def _searchOnTitle(self, title, movie, quality, results):
|
||||
|
||||
page = 0
|
||||
total_pages = 1
|
||||
cats = self.getCatId(quality['identifier'])
|
||||
|
||||
while page < total_pages:
|
||||
|
||||
movieTitle = tryUrlencode('"%s" %s' % (title, movie['library']['year']))
|
||||
search_url = self.urls['search'] % (movieTitle, page, cats[0])
|
||||
page += 1
|
||||
|
||||
data = self.getHTMLData(search_url, opener = self.login_opener)
|
||||
if data:
|
||||
try:
|
||||
soup = BeautifulSoup(data)
|
||||
|
||||
results_table = soup.find('table', attrs = {'class': 'koptekst'})
|
||||
if not results_table:
|
||||
return
|
||||
|
||||
try:
|
||||
pagelinks = soup.findAll(href = re.compile('page'))
|
||||
pageNumbers = [int(re.search('page=(?P<pageNumber>.+'')', i['href']).group('pageNumber')) for i in pagelinks]
|
||||
total_pages = max(pageNumbers)
|
||||
|
||||
except:
|
||||
pass
|
||||
|
||||
entries = results_table.find_all('tr')
|
||||
|
||||
for result in entries[1:]:
|
||||
prelink = result.find(href = re.compile('details.php'))
|
||||
link = prelink['href']
|
||||
download = result.find('a', href = re.compile('download.php'))['href']
|
||||
|
||||
if link and download:
|
||||
|
||||
def extra_score(item):
|
||||
trusted = (0, 10)[result.find('img', alt = re.compile('Trusted')) is not None]
|
||||
vip = (0, 20)[result.find('img', alt = re.compile('VIP')) is not None]
|
||||
confirmed = (0, 30)[result.find('img', alt = re.compile('Helpers')) is not None]
|
||||
moderated = (0, 50)[result.find('img', alt = re.compile('Moderator')) is not None]
|
||||
|
||||
return confirmed + trusted + vip + moderated
|
||||
|
||||
id = re.search('id=(?P<id>\d+)&', link).group('id')
|
||||
url = self.urls['download'] % (download)
|
||||
|
||||
fileSize = self.parseSize(result.select('td.rowhead')[5].text)
|
||||
results.append({
|
||||
'id': id,
|
||||
'name': toUnicode(prelink.find('b').text),
|
||||
'url': url,
|
||||
'detail_url': self.urls['detail'] % link,
|
||||
'size': fileSize,
|
||||
'seeders': tryInt(result.find_all('td')[2].string),
|
||||
'leechers': tryInt(result.find_all('td')[3].string),
|
||||
'extra_score': extra_score,
|
||||
'get_more_info': self.getMoreInfo
|
||||
})
|
||||
|
||||
except:
|
||||
log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc()))
|
||||
|
||||
def getLoginParams(self):
|
||||
return tryUrlencode({
|
||||
'username': self.conf('username'),
|
||||
'password': self.conf('password'),
|
||||
'submit': 'Welcome to ILT',
|
||||
})
|
||||
|
||||
def getMoreInfo(self, item):
|
||||
cache_key = 'ilt.%s' % item['id']
|
||||
description = self.getCache(cache_key)
|
||||
|
||||
if not description:
|
||||
|
||||
try:
|
||||
full_description = self.getHTMLData(item['detail_url'], opener = self.login_opener)
|
||||
html = BeautifulSoup(full_description)
|
||||
nfo_pre = html.find('td', attrs = {'class':'main'}).findAll('table')[1]
|
||||
description = toUnicode(nfo_pre.text) if nfo_pre else ''
|
||||
except:
|
||||
log.error('Failed getting more info for %s', item['name'])
|
||||
description = ''
|
||||
|
||||
self.setCache(cache_key, description, timeout = 25920000)
|
||||
|
||||
item['description'] = description
|
||||
return item
|
||||
|
||||
def loginSuccess(self, output):
|
||||
return 'logout.php' in output.lower()
|
||||
|
||||
loginCheckSuccess = loginSuccess
|
||||
@@ -16,7 +16,7 @@ config = [{
|
||||
{
|
||||
'name': 'enabled',
|
||||
'type': 'enabler',
|
||||
'default': True
|
||||
'default': False
|
||||
},
|
||||
{
|
||||
'name': 'domain',
|
||||
|
||||
@@ -12,7 +12,7 @@ class TorrentBytes(TorrentProvider):
|
||||
|
||||
urls = {
|
||||
'test' : 'https://www.torrentbytes.net/',
|
||||
'login' : 'https://www.torrentbytes.net/takelogin.php',
|
||||
'login' : 'https://www.torrentbytes.net/login.php',
|
||||
'login_check' : 'https://www.torrentbytes.net/inbox.php',
|
||||
'detail' : 'https://www.torrentbytes.net/details.php?id=%s',
|
||||
'search' : 'https://www.torrentbytes.net/browse.php?search=%s&cat=%d',
|
||||
|
||||
@@ -71,12 +71,10 @@ class RTorrent:
|
||||
|
||||
def _verify_conn(self):
|
||||
# check for rpc methods that should be available
|
||||
assert {"system.client_version",
|
||||
"system.library_version"}.issubset(set(self._get_rpc_methods())),\
|
||||
"Required RPC methods not available."
|
||||
assert "system.client_version" in self._get_rpc_methods(), "Required RPC method not available."
|
||||
assert "system.library_version" in self._get_rpc_methods(), "Required RPC method not available."
|
||||
|
||||
# minimum rTorrent version check
|
||||
|
||||
assert self._meets_version_requirement() is True,\
|
||||
"Error: Minimum rTorrent version required is {0}".format(
|
||||
MIN_RTORRENT_VERSION_STR)
|
||||
|
||||
@@ -90,9 +90,10 @@ class TorrentParser():
|
||||
def _calc_info_hash(self):
|
||||
self.info_hash = None
|
||||
if "info" in self._torrent_decoded.keys():
|
||||
info_dict = self._torrent_decoded["info"]
|
||||
self.info_hash = hashlib.sha1(bencode.encode(
|
||||
info_dict)).hexdigest().upper()
|
||||
info_encoded = bencode.encode(self._torrent_decoded["info"])
|
||||
|
||||
if info_encoded:
|
||||
self.info_hash = hashlib.sha1(info_encoded).hexdigest().upper()
|
||||
|
||||
return(self.info_hash)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user