Merge branch 'refs/heads/develop' into tv
This commit is contained in:
@@ -15,6 +15,7 @@ log = CPLog(__name__)
|
||||
views = {}
|
||||
template_loader = template.Loader(os.path.join(os.path.dirname(__file__), 'templates'))
|
||||
|
||||
|
||||
class BaseHandler(RequestHandler):
|
||||
|
||||
def get_current_user(self):
|
||||
|
||||
@@ -189,7 +189,7 @@ class Core(Plugin):
|
||||
def signalHandler(self):
|
||||
if Env.get('daemonized'): return
|
||||
|
||||
def signal_handler(signal, frame):
|
||||
def signal_handler(*args, **kwargs):
|
||||
fireEvent('app.shutdown', single = True)
|
||||
|
||||
signal.signal(signal.SIGINT, signal_handler)
|
||||
|
||||
@@ -57,13 +57,12 @@ class ClientScript(Plugin):
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
urls = {'style': {}, 'script': {}}
|
||||
minified = {'style': {}, 'script': {}}
|
||||
paths = {'style': {}, 'script': {}}
|
||||
comment = {
|
||||
'style': '/*** %s:%d ***/\n',
|
||||
'script': '// %s:%d\n'
|
||||
'style': '/*** %s:%d ***/\n',
|
||||
'script': '// %s:%d\n'
|
||||
}
|
||||
|
||||
html = {
|
||||
@@ -95,7 +94,6 @@ class ClientScript(Plugin):
|
||||
else:
|
||||
self.registerStyle(core_url, file_path, position = 'front')
|
||||
|
||||
|
||||
def minify(self):
|
||||
|
||||
# Create cache dir
|
||||
@@ -129,7 +127,7 @@ class ClientScript(Plugin):
|
||||
data = cssmin(data)
|
||||
data = data.replace('../images/', '../static/images/')
|
||||
data = data.replace('../fonts/', '../static/fonts/')
|
||||
data = data.replace('../../static/', '../static/') # Replace inside plugins
|
||||
data = data.replace('../../static/', '../static/') # Replace inside plugins
|
||||
|
||||
raw.append({'file': file_path, 'date': int(os.path.getmtime(file_path)), 'data': data})
|
||||
|
||||
@@ -192,6 +190,7 @@ class ClientScript(Plugin):
|
||||
|
||||
prefix_properties = ['border-radius', 'transform', 'transition', 'box-shadow']
|
||||
prefix_tags = ['ms', 'moz', 'webkit']
|
||||
|
||||
def prefix(self, data):
|
||||
|
||||
trimmed_data = re.sub('(\t|\n|\r)+', '', data)
|
||||
|
||||
@@ -164,14 +164,14 @@ class BaseUpdater(Plugin):
|
||||
|
||||
def info(self):
|
||||
|
||||
version = self.getVersion()
|
||||
current_version = self.getVersion()
|
||||
|
||||
return {
|
||||
'last_check': self.last_check,
|
||||
'update_version': self.update_version,
|
||||
'version': version,
|
||||
'version': current_version,
|
||||
'repo_name': '%s/%s' % (self.repo_user, self.repo_name),
|
||||
'branch': version.get('branch', self.branch),
|
||||
'branch': current_version.get('branch', self.branch),
|
||||
}
|
||||
|
||||
def getVersion(self):
|
||||
|
||||
@@ -5,7 +5,7 @@ var UpdaterBase = new Class({
|
||||
initialize: function(){
|
||||
var self = this;
|
||||
|
||||
App.addEvent('load', self.info.bind(self, 2000))
|
||||
App.addEvent('load', self.info.bind(self, 2000));
|
||||
App.addEvent('unload', function(){
|
||||
if(self.timer)
|
||||
clearTimeout(self.timer);
|
||||
@@ -66,7 +66,7 @@ var UpdaterBase = new Class({
|
||||
|
||||
var changelog = 'https://github.com/'+data.repo_name+'/compare/'+data.version.hash+'...'+data.branch;
|
||||
if(data.update_version.changelog)
|
||||
changelog = data.update_version.changelog + '#' + data.version.hash+'...'+data.update_version.hash
|
||||
changelog = data.update_version.changelog + '#' + data.version.hash+'...'+data.update_version.hash;
|
||||
|
||||
self.message = new Element('div.message.update').adopt(
|
||||
new Element('span', {
|
||||
|
||||
@@ -21,6 +21,8 @@ class Database(object):
|
||||
def __init__(self):
|
||||
|
||||
addApiView('database.list_documents', self.listDocuments)
|
||||
addApiView('database.reindex', self.reindex)
|
||||
addApiView('database.compact', self.compact)
|
||||
addApiView('database.document.update', self.updateDocument)
|
||||
addApiView('database.document.delete', self.deleteDocument)
|
||||
|
||||
@@ -114,9 +116,36 @@ class Database(object):
|
||||
results[key] = []
|
||||
results[key].append(document)
|
||||
|
||||
|
||||
return results
|
||||
|
||||
def reindex(self, **kwargs):
|
||||
|
||||
success = True
|
||||
try:
|
||||
db = self.getDB()
|
||||
db.reindex()
|
||||
except:
|
||||
log.error('Failed index: %s', traceback.format_exc())
|
||||
success = False
|
||||
|
||||
return {
|
||||
'success': success
|
||||
}
|
||||
|
||||
def compact(self, **kwargs):
|
||||
|
||||
success = True
|
||||
try:
|
||||
db = self.getDB()
|
||||
db.compact()
|
||||
except:
|
||||
log.error('Failed compact: %s', traceback.format_exc())
|
||||
success = False
|
||||
|
||||
return {
|
||||
'success': success
|
||||
}
|
||||
|
||||
def migrate(self):
|
||||
|
||||
from couchpotato import Env
|
||||
@@ -181,7 +210,7 @@ class Database(object):
|
||||
migrate_data[ml][p[0]] = [migrate_data[ml][p[0]]]
|
||||
migrate_data[ml][p[0]].append(columns)
|
||||
|
||||
c.close()
|
||||
conn.close()
|
||||
|
||||
log.info('Getting data took %s', time.time() - migrate_start)
|
||||
|
||||
@@ -297,6 +326,11 @@ class Database(object):
|
||||
releaseinfos = migrate_data['releaseinfo']
|
||||
for x in releaseinfos:
|
||||
info = releaseinfos[x]
|
||||
|
||||
# Skip if release doesn't exist for this info
|
||||
if not migrate_data['release'].get(info.get('release_id')):
|
||||
continue
|
||||
|
||||
if not migrate_data['release'][info.get('release_id')].get('info'):
|
||||
migrate_data['release'][info.get('release_id')]['info'] = {}
|
||||
|
||||
@@ -438,7 +472,6 @@ class Database(object):
|
||||
log.info('Total migration took %s', time.time() - migrate_start)
|
||||
log.info('=' * 30)
|
||||
|
||||
|
||||
# rename old database
|
||||
log.info('Renaming old database to %s ', old_db + '.old')
|
||||
os.rename(old_db, old_db + '.old')
|
||||
|
||||
@@ -24,17 +24,21 @@ class Downloader(Provider):
|
||||
]
|
||||
|
||||
torrent_trackers = [
|
||||
'http://tracker.publicbt.com/announce',
|
||||
'udp://tracker.istole.it:80/announce',
|
||||
'udp://fr33domtracker.h33t.com:3310/announce',
|
||||
'http://tracker.istole.it/announce',
|
||||
'http://tracker.ccc.de/announce',
|
||||
'udp://fr33domtracker.h33t.com:3310/announce',
|
||||
'http://tracker.publicbt.com/announce',
|
||||
'udp://tracker.publicbt.com:80/announce',
|
||||
'http://tracker.ccc.de/announce',
|
||||
'udp://tracker.ccc.de:80/announce',
|
||||
'http://exodus.desync.com/announce',
|
||||
'http://exodus.desync.com:6969/announce',
|
||||
'http://tracker.publichd.eu/announce',
|
||||
'udp://tracker.publichd.eu:80/announce',
|
||||
'http://tracker.openbittorrent.com/announce',
|
||||
'udp://tracker.openbittorrent.com/announce',
|
||||
'udp://tracker.openbittorrent.com:80/announce',
|
||||
'udp://open.demonii.com:1337/announce',
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
@@ -184,6 +188,7 @@ class Downloader(Provider):
|
||||
def pause(self, release_download, pause):
|
||||
return
|
||||
|
||||
|
||||
class ReleaseDownloadList(list):
|
||||
|
||||
provider = None
|
||||
|
||||
@@ -146,7 +146,7 @@ class NZBGet(Downloader):
|
||||
'timeleft': timeleft,
|
||||
})
|
||||
|
||||
for nzb in queue: # 'Parameters' is not passed in rpc.postqueue
|
||||
for nzb in queue: # 'Parameters' is not passed in rpc.postqueue
|
||||
if nzb['NZBID'] in ids:
|
||||
log.debug('Found %s in NZBGet postprocessing queue', nzb['NZBFilename'])
|
||||
release_downloads.append({
|
||||
|
||||
@@ -14,6 +14,7 @@ log = CPLog(__name__)
|
||||
|
||||
autoload = 'qBittorrent'
|
||||
|
||||
|
||||
class qBittorrent(Downloader):
|
||||
|
||||
protocol = ['torrent', 'torrent_magnet']
|
||||
@@ -107,25 +108,24 @@ class qBittorrent(Downloader):
|
||||
for torrent in torrents:
|
||||
if torrent.hash in ids:
|
||||
torrent.update_general() # get extra info
|
||||
torrent_filelist = torrent.get_files()
|
||||
|
||||
torrent_files = []
|
||||
t_files = torrent.get_files()
|
||||
torrent_dir = os.path.join(torrent.save_path, torrent.name)
|
||||
|
||||
check_dir = os.path.join(torrent.save_path, torrent.name)
|
||||
if os.path.isdir(check_dir):
|
||||
torrent.save_path = check_dir
|
||||
if os.path.isdir(torrent_dir):
|
||||
torrent.save_path = torrent_dir
|
||||
|
||||
if len(t_files) > 1 and os.path.isdir(torrent.save_path): # multi file torrent
|
||||
if len(torrent_filelist) > 1 and os.path.isdir(torrent_dir): # multi file torrent, path.isdir check makes sure we're not in the root download folder
|
||||
for root, _, files in os.walk(torrent.save_path):
|
||||
for f in files:
|
||||
p = os.path.join(root, f)
|
||||
if os.path.isfile(p):
|
||||
torrent_files.append(sp(p))
|
||||
torrent_files.append(sp(os.path.join(root, f)))
|
||||
|
||||
else: # multi or single file placed directly in torrent.save_path
|
||||
for f in t_files:
|
||||
p = os.path.join(torrent.save_path, f.name)
|
||||
if os.path.isfile(p):
|
||||
torrent_files.append(sp(p))
|
||||
for f in torrent_filelist:
|
||||
file_path = os.path.join(torrent.save_path, f.name)
|
||||
if os.path.isfile(file_path):
|
||||
torrent_files.append(sp(file_path))
|
||||
|
||||
release_downloads.append({
|
||||
'id': torrent.hash,
|
||||
|
||||
@@ -78,10 +78,10 @@ class rTorrent(Downloader):
|
||||
|
||||
self.error_msg = ''
|
||||
try:
|
||||
self.rt._verify_conn()
|
||||
self.rt._verify_conn()
|
||||
except AssertionError as e:
|
||||
self.error_msg = e.message
|
||||
self.rt = None
|
||||
self.error_msg = e.message
|
||||
self.rt = None
|
||||
|
||||
return self.rt
|
||||
|
||||
@@ -94,44 +94,6 @@ class rTorrent(Downloader):
|
||||
|
||||
return False
|
||||
|
||||
def updateProviderGroup(self, name, data):
|
||||
if data.get('seed_time'):
|
||||
log.info('seeding time ignored, not supported')
|
||||
|
||||
if not name:
|
||||
return False
|
||||
|
||||
if not self.connect():
|
||||
return False
|
||||
|
||||
views = self.rt.get_views()
|
||||
|
||||
if name not in views:
|
||||
self.rt.create_group(name)
|
||||
|
||||
group = self.rt.get_group(name)
|
||||
|
||||
try:
|
||||
if data.get('seed_ratio'):
|
||||
ratio = int(float(data.get('seed_ratio')) * 100)
|
||||
log.debug('Updating provider ratio to %s, group name: %s', (ratio, name))
|
||||
|
||||
# Explicitly set all group options to ensure it is setup correctly
|
||||
group.set_upload('1M')
|
||||
group.set_min(ratio)
|
||||
group.set_max(ratio)
|
||||
group.set_command('d.stop')
|
||||
group.enable()
|
||||
else:
|
||||
# Reset group action and disable it
|
||||
group.set_command()
|
||||
group.disable()
|
||||
except MethodError as err:
|
||||
log.error('Unable to set group options: %s', err.msg)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def download(self, data = None, media = None, filedata = None):
|
||||
if not media: media = {}
|
||||
@@ -142,10 +104,6 @@ class rTorrent(Downloader):
|
||||
if not self.connect():
|
||||
return False
|
||||
|
||||
group_name = 'cp_' + data.get('provider').lower()
|
||||
if not self.updateProviderGroup(group_name, data):
|
||||
return False
|
||||
|
||||
torrent_params = {}
|
||||
if self.conf('label'):
|
||||
torrent_params['label'] = self.conf('label')
|
||||
@@ -186,9 +144,6 @@ class rTorrent(Downloader):
|
||||
if self.conf('directory'):
|
||||
torrent.set_directory(self.conf('directory'))
|
||||
|
||||
# Set Ratio Group
|
||||
torrent.set_visible(group_name)
|
||||
|
||||
# Start torrent
|
||||
if not self.conf('paused', default = 0):
|
||||
torrent.start()
|
||||
|
||||
@@ -160,6 +160,7 @@ class Transmission(Downloader):
|
||||
log.debug('Requesting Transmission to remove the torrent %s%s.', (release_download['name'], ' and cleanup the downloaded files' if delete_files else ''))
|
||||
return self.trpc.remove_torrent(release_download['id'], delete_files)
|
||||
|
||||
|
||||
class TransmissionRPC(object):
|
||||
|
||||
"""TransmissionRPC lite library"""
|
||||
|
||||
@@ -29,14 +29,14 @@ class uTorrent(Downloader):
|
||||
protocol = ['torrent', 'torrent_magnet']
|
||||
utorrent_api = None
|
||||
status_flags = {
|
||||
'STARTED' : 1,
|
||||
'CHECKING' : 2,
|
||||
'CHECK-START' : 4,
|
||||
'CHECKED' : 8,
|
||||
'ERROR' : 16,
|
||||
'PAUSED' : 32,
|
||||
'QUEUED' : 64,
|
||||
'LOADED' : 128
|
||||
'STARTED': 1,
|
||||
'CHECKING': 2,
|
||||
'CHECK-START': 4,
|
||||
'CHECKED': 8,
|
||||
'ERROR': 16,
|
||||
'PAUSED': 32,
|
||||
'QUEUED': 64,
|
||||
'LOADED': 128
|
||||
}
|
||||
|
||||
def connect(self):
|
||||
|
||||
@@ -2,7 +2,7 @@ from urllib import unquote
|
||||
import re
|
||||
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.variable import natsortKey, tryInt
|
||||
from couchpotato.core.helpers.variable import natsortKey
|
||||
|
||||
|
||||
def getParams(params):
|
||||
@@ -44,6 +44,7 @@ def getParams(params):
|
||||
return dictToList(temp)
|
||||
|
||||
non_decimal = re.compile(r'[^\d.]+')
|
||||
|
||||
def dictToList(params):
|
||||
|
||||
if type(params) is dict:
|
||||
|
||||
@@ -216,6 +216,7 @@ def tryFloat(s):
|
||||
return float(s)
|
||||
except: return 0
|
||||
|
||||
|
||||
def natsortKey(string_):
|
||||
"""See http://www.codinghorror.com/blog/archives/001018.html"""
|
||||
return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', string_)]
|
||||
@@ -247,9 +248,6 @@ def getTitle(media_dict):
|
||||
except:
|
||||
log.error('Could not get title for %s', getIdentifier(media_dict))
|
||||
return None
|
||||
|
||||
log.error('Could not get title for %s', getIdentifier(media_dict))
|
||||
return None
|
||||
except:
|
||||
log.error('Could not get title for library item: %s', media_dict)
|
||||
return None
|
||||
@@ -296,8 +294,11 @@ def isSubFolder(sub_folder, base_folder):
|
||||
# Returns True if sub_folder is the same as or inside base_folder
|
||||
return base_folder and sub_folder and ss(os.path.normpath(base_folder).rstrip(os.path.sep) + os.path.sep) in ss(os.path.normpath(sub_folder).rstrip(os.path.sep) + os.path.sep)
|
||||
|
||||
|
||||
# From SABNZBD
|
||||
re_password = [re.compile(r'(.+){{([^{}]+)}}$'), re.compile(r'(.+)\s+password\s*=\s*(.+)$', re.I)]
|
||||
|
||||
|
||||
def scanForPassword(name):
|
||||
m = None
|
||||
for reg in re_password:
|
||||
|
||||
@@ -12,19 +12,22 @@ log = CPLog(__name__)
|
||||
|
||||
|
||||
class Loader(object):
|
||||
plugins = {}
|
||||
providers = {}
|
||||
modules = {}
|
||||
|
||||
def __init__(self):
|
||||
self.plugins = {}
|
||||
self.providers = {}
|
||||
self.modules = {}
|
||||
self.paths = {}
|
||||
|
||||
def preload(self, root = ''):
|
||||
core = os.path.join(root, 'couchpotato', 'core')
|
||||
|
||||
self.paths = {
|
||||
self.paths.update({
|
||||
'core': (0, 'couchpotato.core._base', os.path.join(core, '_base')),
|
||||
'plugin': (1, 'couchpotato.core.plugins', os.path.join(core, 'plugins')),
|
||||
'notifications': (20, 'couchpotato.core.notifications', os.path.join(core, 'notifications')),
|
||||
'downloaders': (20, 'couchpotato.core.downloaders', os.path.join(core, 'downloaders')),
|
||||
}
|
||||
})
|
||||
|
||||
# Add media to loader
|
||||
self.addPath(root, ['couchpotato', 'core', 'media'], 25, recursive = True)
|
||||
|
||||
@@ -5,6 +5,7 @@ from couchpotato import get_db, CPLog
|
||||
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
import six
|
||||
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from .main import Library
|
||||
|
||||
|
||||
def autoload():
|
||||
return Library()
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from .main import Matcher
|
||||
|
||||
|
||||
def autoload():
|
||||
return Matcher()
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ from couchpotato.core.helpers.encoding import simplifyString"""
|
||||
|
||||
|
||||
class TitleIndex(TreeBasedIndex):
|
||||
_version = 1
|
||||
_version = 2
|
||||
|
||||
custom_header = """from CodernityDB.tree_index import TreeBasedIndex
|
||||
from string import ascii_letters
|
||||
@@ -113,14 +113,14 @@ from couchpotato.core.helpers.encoding import toUnicode, simplifyString"""
|
||||
return self.simplify(key)
|
||||
|
||||
def make_key_value(self, data):
|
||||
if data.get('_t') == 'media' and data.get('title') is not None:
|
||||
if data.get('_t') == 'media' and data.get('title') is not None and len(data.get('title')) > 0:
|
||||
return self.simplify(data['title']), None
|
||||
|
||||
def simplify(self, title):
|
||||
|
||||
title = toUnicode(title)
|
||||
|
||||
nr_prefix = '' if title[0] in ascii_letters else '#'
|
||||
nr_prefix = '' if title and len(title) > 0 and title[0] in ascii_letters else '#'
|
||||
title = simplifyString(title)
|
||||
|
||||
for prefix in ['the ']:
|
||||
@@ -132,7 +132,7 @@ from couchpotato.core.helpers.encoding import toUnicode, simplifyString"""
|
||||
|
||||
|
||||
class StartsWithIndex(TreeBasedIndex):
|
||||
_version = 1
|
||||
_version = 2
|
||||
|
||||
custom_header = """from CodernityDB.tree_index import TreeBasedIndex
|
||||
from string import ascii_letters
|
||||
@@ -158,4 +158,4 @@ from couchpotato.core.helpers.encoding import toUnicode, simplifyString"""
|
||||
title = title[len(prefix):]
|
||||
break
|
||||
|
||||
return str(title[0] if title[0] in ascii_letters else '#').lower()
|
||||
return str(title[0] if title and len(title) > 0 and title[0] in ascii_letters else '#').lower()
|
||||
|
||||
@@ -132,10 +132,10 @@ class MediaPlugin(MediaBase):
|
||||
if media:
|
||||
|
||||
# Attach category
|
||||
if media.get('category_id'):
|
||||
media['category'] = db.get('id', media.get('category_id'))
|
||||
try: media['category'] = db.get('id', media.get('category_id'))
|
||||
except: pass
|
||||
|
||||
media['releases'] = list(fireEvent('release.for_media', media['_id'], single = True))
|
||||
media['releases'] = fireEvent('release.for_media', media['_id'], single = True)
|
||||
|
||||
return media
|
||||
|
||||
@@ -222,7 +222,6 @@ class MediaPlugin(MediaBase):
|
||||
del filter_by['media_status']
|
||||
del filter_by['release_status']
|
||||
|
||||
|
||||
# Filter by combining ids
|
||||
for x in filter_by:
|
||||
media_ids = [n for n in media_ids if n in filter_by[x]]
|
||||
@@ -281,7 +280,7 @@ class MediaPlugin(MediaBase):
|
||||
|
||||
for media_type in fireEvent('media.types', merge = True):
|
||||
def tempList(*args, **kwargs):
|
||||
return self.listView(types = media_type, *args, **kwargs)
|
||||
return self.listView(types = media_type, **kwargs)
|
||||
addApiView('%s.list' % media_type, tempList)
|
||||
|
||||
def availableChars(self, types = None, status = None, release_status = None):
|
||||
@@ -350,7 +349,7 @@ class MediaPlugin(MediaBase):
|
||||
|
||||
for media_type in fireEvent('media.types', merge = True):
|
||||
def tempChar(*args, **kwargs):
|
||||
return self.charView(types = media_type, *args, **kwargs)
|
||||
return self.charView(types = media_type, **kwargs)
|
||||
addApiView('%s.available_chars' % media_type, tempChar)
|
||||
|
||||
def delete(self, media_id, delete_from = None):
|
||||
@@ -366,7 +365,7 @@ class MediaPlugin(MediaBase):
|
||||
deleted = True
|
||||
else:
|
||||
|
||||
media_releases = list(fireEvent('release.for_media', media['_id'], single = True))
|
||||
media_releases = fireEvent('release.for_media', media['_id'], single = True)
|
||||
|
||||
total_releases = len(media_releases)
|
||||
total_deleted = 0
|
||||
@@ -382,9 +381,8 @@ class MediaPlugin(MediaBase):
|
||||
if release.get('status') == 'done':
|
||||
db.delete(release)
|
||||
total_deleted += 1
|
||||
new_media_status = 'active'
|
||||
|
||||
if total_releases == total_deleted:
|
||||
if (total_releases == total_deleted and media['status'] != 'active') or (delete_from == 'wanted' and media['status'] == 'active'):
|
||||
db.delete(media)
|
||||
deleted = True
|
||||
elif new_media_status:
|
||||
@@ -432,7 +430,7 @@ class MediaPlugin(MediaBase):
|
||||
move_to_wanted = True
|
||||
|
||||
profile = db.get('id', m['profile_id'])
|
||||
media_releases = list(fireEvent('release.for_media', m['_id'], single = True))
|
||||
media_releases = fireEvent('release.for_media', m['_id'], single = True)
|
||||
|
||||
for q_identifier in profile['qualities']:
|
||||
index = profile['qualities'].index(q_identifier)
|
||||
|
||||
@@ -244,10 +244,16 @@ class YarrProvider(Provider):
|
||||
|
||||
return 0
|
||||
|
||||
def getCatId(self, identifier):
|
||||
def getCatId(self, quality = None):
|
||||
if not quality: quality = {}
|
||||
identifier = quality.get('identifier')
|
||||
|
||||
want_3d = False
|
||||
if quality.get('custom'):
|
||||
want_3d = quality['custom'].get('3d')
|
||||
|
||||
for ids, qualities in self.cat_ids:
|
||||
if identifier in qualities:
|
||||
if identifier in qualities or (want_3d and '3d' in qualities):
|
||||
return ids
|
||||
|
||||
if self.cat_backup_id:
|
||||
|
||||
@@ -18,7 +18,7 @@ class Base(NZBProvider):
|
||||
'search': 'https://www.binsearch.info/index.php?%s',
|
||||
}
|
||||
|
||||
http_time_between_calls = 4 # Seconds
|
||||
http_time_between_calls = 4 # Seconds
|
||||
|
||||
def _search(self, media, quality, results):
|
||||
|
||||
@@ -28,7 +28,7 @@ class Base(NZBProvider):
|
||||
try:
|
||||
|
||||
html = BeautifulSoup(data)
|
||||
main_table = html.find('table', attrs = {'id':'r2'})
|
||||
main_table = html.find('table', attrs = {'id': 'r2'})
|
||||
|
||||
if not main_table:
|
||||
return
|
||||
@@ -36,11 +36,11 @@ class Base(NZBProvider):
|
||||
items = main_table.find_all('tr')
|
||||
|
||||
for row in items:
|
||||
title = row.find('span', attrs = {'class':'s'})
|
||||
title = row.find('span', attrs = {'class': 's'})
|
||||
|
||||
if not title: continue
|
||||
|
||||
nzb_id = row.find('input', attrs = {'type':'checkbox'})['name']
|
||||
nzb_id = row.find('input', attrs = {'type': 'checkbox'})['name']
|
||||
info = row.find('span', attrs = {'class':'d'})
|
||||
size_match = re.search('size:.(?P<size>[0-9\.]+.[GMB]+)', info.text)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class Base(NZBProvider, RSS):
|
||||
'search': 'https://www.nzbclub.com/nzbfeeds.aspx?%s',
|
||||
}
|
||||
|
||||
http_time_between_calls = 4 #seconds
|
||||
http_time_between_calls = 4 # seconds
|
||||
|
||||
def _search(self, media, quality, results):
|
||||
|
||||
@@ -55,7 +55,7 @@ class Base(NZBProvider, RSS):
|
||||
def getMoreInfo(self, item):
|
||||
full_description = self.getCache('nzbclub.%s' % item['id'], item['detail_url'], cache_timeout = 25920000)
|
||||
html = BeautifulSoup(full_description)
|
||||
nfo_pre = html.find('pre', attrs = {'class':'nfo'})
|
||||
nfo_pre = html.find('pre', attrs = {'class': 'nfo'})
|
||||
description = toUnicode(nfo_pre.text) if nfo_pre else ''
|
||||
|
||||
item['description'] = description
|
||||
|
||||
@@ -91,7 +91,7 @@ class Base(NZBProvider, RSS):
|
||||
nfo_url = re.search('href=\"(?P<nfo>.+)\" ', item['description']).group('nfo')
|
||||
full_description = self.getCache('nzbindex.%s' % item['id'], url = nfo_url, cache_timeout = 25920000)
|
||||
html = BeautifulSoup(full_description)
|
||||
item['description'] = toUnicode(html.find('pre', attrs = {'id':'nfo0'}).text)
|
||||
item['description'] = toUnicode(html.find('pre', attrs = {'id': 'nfo0'}).text)
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class Base(NZBProvider, RSS):
|
||||
'detail_url': 'https://omgwtfnzbs.org/details.php?id=%s',
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
|
||||
cat_ids = [
|
||||
([15], ['dvdrip']),
|
||||
@@ -42,7 +42,7 @@ class Base(NZBProvider, RSS):
|
||||
q = '%s %s' % (title, movie['info']['year'])
|
||||
params = tryUrlencode({
|
||||
'search': q,
|
||||
'catid': ','.join([str(x) for x in self.getCatId(quality['identifier'])]),
|
||||
'catid': ','.join([str(x) for x in self.getCatId(quality)]),
|
||||
'user': self.conf('username', default = ''),
|
||||
'api': self.conf('api_key', default = ''),
|
||||
})
|
||||
|
||||
@@ -21,7 +21,7 @@ class Base(TorrentProvider):
|
||||
}
|
||||
|
||||
# Searches for movies only - BiT-HDTV's subcategory and resolution search filters appear to be broken
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
|
||||
def _search(self, media, quality, results):
|
||||
|
||||
@@ -40,7 +40,7 @@ class Base(TorrentProvider):
|
||||
html = BeautifulSoup(data)
|
||||
|
||||
try:
|
||||
result_table = html.find('table', attrs = {'width' : '750', 'class' : ''})
|
||||
result_table = html.find('table', attrs = {'width': '750', 'class': ''})
|
||||
if result_table is None:
|
||||
return
|
||||
|
||||
@@ -74,7 +74,7 @@ class Base(TorrentProvider):
|
||||
def getMoreInfo(self, item):
|
||||
full_description = self.getCache('bithdtv.%s' % item['id'], item['detail_url'], cache_timeout = 25920000)
|
||||
html = BeautifulSoup(full_description)
|
||||
nfo_pre = html.find('table', attrs = {'class':'detail'})
|
||||
nfo_pre = html.find('table', attrs = {'class': 'detail'})
|
||||
description = toUnicode(nfo_pre.text) if nfo_pre else ''
|
||||
|
||||
item['description'] = description
|
||||
|
||||
@@ -20,7 +20,7 @@ class Base(TorrentProvider):
|
||||
'baseurl': 'https://www.bitsoup.me/%s',
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
|
||||
def _searchOnTitle(self, title, movie, quality, results):
|
||||
|
||||
@@ -30,7 +30,7 @@ class Base(TorrentProvider):
|
||||
})
|
||||
url = "%s&%s" % (self.urls['search'], arguments)
|
||||
|
||||
url = self.urls['search'] % self.buildUrl(media, quality)
|
||||
url = self.urls['search'] % self.buildUrl(movie, quality)
|
||||
data = self.getHTMLData(url)
|
||||
|
||||
if data:
|
||||
@@ -74,7 +74,6 @@ class Base(TorrentProvider):
|
||||
except:
|
||||
log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc()))
|
||||
|
||||
|
||||
def getLoginParams(self):
|
||||
return {
|
||||
'username': self.conf('username'),
|
||||
@@ -82,7 +81,6 @@ class Base(TorrentProvider):
|
||||
'ssl': 'yes',
|
||||
}
|
||||
|
||||
|
||||
def loginSuccess(self, output):
|
||||
return 'logout.php' in output.lower()
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ class Base(TorrentProvider):
|
||||
'api': 'https://hdbits.org/api/torrents'
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
|
||||
def _post_query(self, **params):
|
||||
|
||||
@@ -56,7 +56,7 @@ class Base(TorrentProvider):
|
||||
'name': result['name'],
|
||||
'url': self.urls['download'] % (result['id'], self.conf('passkey')),
|
||||
'detail_url': self.urls['detail'] % result['id'],
|
||||
'size': tryInt(result['size'])/1024/1024,
|
||||
'size': tryInt(result['size']) / 1024 / 1024,
|
||||
'seeders': tryInt(result['seeders']),
|
||||
'leechers': tryInt(result['leechers'])
|
||||
})
|
||||
|
||||
@@ -23,9 +23,9 @@ class Base(TorrentProvider):
|
||||
}
|
||||
|
||||
cat_ids = [
|
||||
(['41'], ['720p', '1080p', 'brrip']),
|
||||
(['19'], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']),
|
||||
(['20'], ['dvdr'])
|
||||
(['41'], ['720p', '1080p', 'brrip']),
|
||||
(['19'], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']),
|
||||
(['20'], ['dvdr'])
|
||||
]
|
||||
|
||||
cat_backup_id = 200
|
||||
@@ -36,7 +36,7 @@ class Base(TorrentProvider):
|
||||
|
||||
page = 0
|
||||
total_pages = 1
|
||||
cats = self.getCatId(quality['identifier'])
|
||||
cats = self.getCatId(quality)
|
||||
|
||||
while page < total_pages:
|
||||
|
||||
@@ -79,7 +79,7 @@ class Base(TorrentProvider):
|
||||
return confirmed + trusted + vip + moderated
|
||||
|
||||
id = re.search('id=(?P<id>\d+)&', link).group('id')
|
||||
url = self.urls['download'] % (download)
|
||||
url = self.urls['download'] % download
|
||||
|
||||
fileSize = self.parseSize(result.select('td.rowhead')[5].text)
|
||||
results.append({
|
||||
@@ -113,7 +113,7 @@ class Base(TorrentProvider):
|
||||
try:
|
||||
full_description = self.getHTMLData(item['detail_url'])
|
||||
html = BeautifulSoup(full_description)
|
||||
nfo_pre = html.find('td', attrs = {'class':'main'}).findAll('table')[1]
|
||||
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'])
|
||||
|
||||
@@ -18,21 +18,21 @@ class Base(TorrentProvider):
|
||||
'base_url': 'https://www.iptorrents.com',
|
||||
'login': 'https://www.iptorrents.com/torrents/',
|
||||
'login_check': 'https://www.iptorrents.com/inbox.php',
|
||||
'search': 'https://www.iptorrents.com/torrents/?l%d=1%s&q=%s&qf=ti&p=%d',
|
||||
'search': 'https://www.iptorrents.com/torrents/?%s%%s&q=%s&qf=ti&p=%%d',
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
cat_backup_id = None
|
||||
|
||||
def buildUrl(self, title, media, quality):
|
||||
return self._buildUrl(title.replace(':', ''), quality['identifier'])
|
||||
return self._buildUrl(title.replace(':', ''), quality)
|
||||
|
||||
def _buildUrl(self, query, quality_identifier):
|
||||
def _buildUrl(self, query, quality):
|
||||
|
||||
cat_ids = self.getCatId(quality_identifier)
|
||||
cat_ids = self.getCatId(quality)
|
||||
|
||||
if not cat_ids:
|
||||
log.warning('Unable to find category ids for identifier "%s"', quality_identifier)
|
||||
log.warning('Unable to find category ids for identifier "%s"', quality.get('identifier'))
|
||||
return None
|
||||
|
||||
return self.urls['search'] % ("&".join(("l%d=" % x) for x in cat_ids), tryUrlencode(query).replace('%', '%%'))
|
||||
@@ -53,14 +53,14 @@ class Base(TorrentProvider):
|
||||
html = BeautifulSoup(data)
|
||||
|
||||
try:
|
||||
page_nav = html.find('span', attrs = {'class' : 'page_nav'})
|
||||
page_nav = html.find('span', attrs = {'class': 'page_nav'})
|
||||
if page_nav:
|
||||
next_link = page_nav.find("a", text = "Next")
|
||||
if next_link:
|
||||
final_page_link = next_link.previous_sibling.previous_sibling
|
||||
pages = int(final_page_link.string)
|
||||
|
||||
result_table = html.find('table', attrs = {'class' : 'torrents'})
|
||||
result_table = html.find('table', attrs = {'class': 'torrents'})
|
||||
|
||||
if not result_table or 'nothing found!' in data.lower():
|
||||
return
|
||||
@@ -80,8 +80,8 @@ class Base(TorrentProvider):
|
||||
torrent_download_url = self.urls['base_url'] + (result.find_all('td')[3].find('a'))['href'].replace(' ', '.')
|
||||
torrent_details_url = self.urls['base_url'] + torrent['href']
|
||||
torrent_size = self.parseSize(result.find_all('td')[5].string)
|
||||
torrent_seeders = tryInt(result.find('td', attrs = {'class' : 'ac t_seeders'}).string)
|
||||
torrent_leechers = tryInt(result.find('td', attrs = {'class' : 'ac t_leechers'}).string)
|
||||
torrent_seeders = tryInt(result.find('td', attrs = {'class': 'ac t_seeders'}).string)
|
||||
torrent_leechers = tryInt(result.find('td', attrs = {'class': 'ac t_leechers'}).string)
|
||||
|
||||
results.append({
|
||||
'id': torrent_id,
|
||||
|
||||
@@ -26,7 +26,7 @@ class Base(TorrentMagnetProvider):
|
||||
(['dvd'], ['dvdr']),
|
||||
]
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
cat_backup_id = None
|
||||
|
||||
proxy_list = [
|
||||
@@ -42,7 +42,7 @@ class Base(TorrentMagnetProvider):
|
||||
|
||||
if data:
|
||||
|
||||
cat_ids = self.getCatId(quality['identifier'])
|
||||
cat_ids = self.getCatId(quality)
|
||||
table_order = ['name', 'size', None, 'age', 'seeds', 'leechers']
|
||||
|
||||
try:
|
||||
|
||||
@@ -18,12 +18,12 @@ log = CPLog(__name__)
|
||||
class Base(TorrentProvider):
|
||||
|
||||
urls = {
|
||||
'domain': 'https://tls.passthepopcorn.me',
|
||||
'detail': 'https://tls.passthepopcorn.me/torrents.php?torrentid=%s',
|
||||
'torrent': 'https://tls.passthepopcorn.me/torrents.php',
|
||||
'login': 'https://tls.passthepopcorn.me/ajax.php?action=login',
|
||||
'login_check': 'https://tls.passthepopcorn.me/ajax.php?action=login',
|
||||
'search': 'https://tls.passthepopcorn.me/search/%s/0/7/%d'
|
||||
'domain': 'https://tls.passthepopcorn.me',
|
||||
'detail': 'https://tls.passthepopcorn.me/torrents.php?torrentid=%s',
|
||||
'torrent': 'https://tls.passthepopcorn.me/torrents.php',
|
||||
'login': 'https://tls.passthepopcorn.me/ajax.php?action=login',
|
||||
'login_check': 'https://tls.passthepopcorn.me/ajax.php?action=login',
|
||||
'search': 'https://tls.passthepopcorn.me/search/%s/0/7/%d'
|
||||
}
|
||||
|
||||
http_time_between_calls = 2
|
||||
@@ -136,23 +136,23 @@ class Base(TorrentProvider):
|
||||
|
||||
def htmlToUnicode(self, text):
|
||||
def fixup(m):
|
||||
text = m.group(0)
|
||||
if text[:2] == "&#":
|
||||
txt = m.group(0)
|
||||
if txt[:2] == "&#":
|
||||
# character reference
|
||||
try:
|
||||
if text[:3] == "&#x":
|
||||
return unichr(int(text[3:-1], 16))
|
||||
if txt[:3] == "&#x":
|
||||
return unichr(int(txt[3:-1], 16))
|
||||
else:
|
||||
return unichr(int(text[2:-1]))
|
||||
return unichr(int(txt[2:-1]))
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
# named entity
|
||||
try:
|
||||
text = unichr(htmlentitydefs.name2codepoint[text[1:-1]])
|
||||
txt = unichr(htmlentitydefs.name2codepoint[txt[1:-1]])
|
||||
except KeyError:
|
||||
pass
|
||||
return text # leave as is
|
||||
return txt # leave as is
|
||||
return re.sub("&#?\w+;", fixup, six.u('%s') % text)
|
||||
|
||||
def unicodeToASCII(self, text):
|
||||
@@ -164,11 +164,11 @@ class Base(TorrentProvider):
|
||||
|
||||
def getLoginParams(self):
|
||||
return {
|
||||
'username': self.conf('username'),
|
||||
'password': self.conf('password'),
|
||||
'passkey': self.conf('passkey'),
|
||||
'keeplogged': '1',
|
||||
'login': 'Login'
|
||||
'username': self.conf('username'),
|
||||
'password': self.conf('password'),
|
||||
'passkey': self.conf('passkey'),
|
||||
'keeplogged': '1',
|
||||
'login': 'Login'
|
||||
}
|
||||
|
||||
def loginSuccess(self, output):
|
||||
|
||||
@@ -34,7 +34,7 @@ class Base(TorrentMagnetProvider):
|
||||
query = self.buildUrl(media)
|
||||
|
||||
params = tryUrlencode({
|
||||
'page':'torrents',
|
||||
'page': 'torrents',
|
||||
'search': query,
|
||||
'active': 1,
|
||||
})
|
||||
|
||||
@@ -22,7 +22,7 @@ class Base(TorrentProvider):
|
||||
'download': 'https://www.sceneaccess.eu/%s',
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
|
||||
def _search(self, media, quality, results):
|
||||
|
||||
@@ -33,16 +33,16 @@ class Base(TorrentProvider):
|
||||
html = BeautifulSoup(data)
|
||||
|
||||
try:
|
||||
resultsTable = html.find('table', attrs = {'id' : 'torrents-table'})
|
||||
resultsTable = html.find('table', attrs = {'id': 'torrents-table'})
|
||||
if resultsTable is None:
|
||||
return
|
||||
|
||||
entries = resultsTable.find_all('tr', attrs = {'class' : 'tt_row'})
|
||||
entries = resultsTable.find_all('tr', attrs = {'class': 'tt_row'})
|
||||
for result in entries:
|
||||
|
||||
link = result.find('td', attrs = {'class' : 'ttr_name'}).find('a')
|
||||
url = result.find('td', attrs = {'class' : 'td_dl'}).find('a')
|
||||
leechers = result.find('td', attrs = {'class' : 'ttr_leechers'}).find('a')
|
||||
link = result.find('td', attrs = {'class': 'ttr_name'}).find('a')
|
||||
url = result.find('td', attrs = {'class': 'td_dl'}).find('a')
|
||||
leechers = result.find('td', attrs = {'class': 'ttr_leechers'}).find('a')
|
||||
torrent_id = link['href'].replace('details?id=', '')
|
||||
|
||||
results.append({
|
||||
@@ -50,8 +50,8 @@ class Base(TorrentProvider):
|
||||
'name': link['title'],
|
||||
'url': self.urls['download'] % url['href'],
|
||||
'detail_url': self.urls['detail'] % torrent_id,
|
||||
'size': self.parseSize(result.find('td', attrs = {'class' : 'ttr_size'}).contents[0]),
|
||||
'seeders': tryInt(result.find('td', attrs = {'class' : 'ttr_seeders'}).find('a').string),
|
||||
'size': self.parseSize(result.find('td', attrs = {'class': 'ttr_size'}).contents[0]),
|
||||
'seeders': tryInt(result.find('td', attrs = {'class': 'ttr_seeders'}).find('a').string),
|
||||
'leechers': tryInt(leechers.string) if leechers else 0,
|
||||
'get_more_info': self.getMoreInfo,
|
||||
})
|
||||
@@ -62,7 +62,7 @@ class Base(TorrentProvider):
|
||||
def getMoreInfo(self, item):
|
||||
full_description = self.getCache('sceneaccess.%s' % item['id'], item['detail_url'], cache_timeout = 25920000)
|
||||
html = BeautifulSoup(full_description)
|
||||
nfo_pre = html.find('div', attrs = {'id':'details_table'})
|
||||
nfo_pre = html.find('div', attrs = {'id': 'details_table'})
|
||||
description = toUnicode(nfo_pre.text) if nfo_pre else ''
|
||||
|
||||
item['description'] = description
|
||||
|
||||
@@ -39,7 +39,7 @@ class Base(TorrentMagnetProvider):
|
||||
|
||||
page = 0
|
||||
total_pages = 1
|
||||
cats = self.getCatId(quality['identifier'])
|
||||
cats = self.getCatId(quality)
|
||||
|
||||
base_search_url = self.urls['search'] % self.getDomain()
|
||||
|
||||
@@ -108,7 +108,7 @@ class Base(TorrentMagnetProvider):
|
||||
def getMoreInfo(self, item):
|
||||
full_description = self.getCache('tpb.%s' % item['id'], item['detail_url'], cache_timeout = 25920000)
|
||||
html = BeautifulSoup(full_description)
|
||||
nfo_pre = html.find('div', attrs = {'class':'nfo'})
|
||||
nfo_pre = html.find('div', attrs = {'class': 'nfo'})
|
||||
description = toUnicode(nfo_pre.text) if nfo_pre else ''
|
||||
|
||||
item['description'] = description
|
||||
|
||||
@@ -31,19 +31,19 @@ class Base(TorrentProvider):
|
||||
([20], ['dvdr']),
|
||||
]
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
cat_backup_id = None
|
||||
|
||||
def _searchOnTitle(self, title, movie, quality, results):
|
||||
|
||||
url = self.urls['search'] % (tryUrlencode('%s %s' % (title.replace(':', ''), movie['info']['year'])), self.getCatId(quality['identifier'])[0])
|
||||
url = self.urls['search'] % (tryUrlencode('%s %s' % (title.replace(':', ''), movie['info']['year'])), self.getCatId(quality)[0])
|
||||
data = self.getHTMLData(url)
|
||||
|
||||
if data:
|
||||
html = BeautifulSoup(data)
|
||||
|
||||
try:
|
||||
result_table = html.find('table', attrs = {'border' : '1'})
|
||||
result_table = html.find('table', attrs = {'border': '1'})
|
||||
if not result_table:
|
||||
return
|
||||
|
||||
@@ -52,7 +52,7 @@ class Base(TorrentProvider):
|
||||
for result in entries[1:]:
|
||||
cells = result.find_all('td')
|
||||
|
||||
link = cells[1].find('a', attrs = {'class' : 'index'})
|
||||
link = cells[1].find('a', attrs = {'class': 'index'})
|
||||
|
||||
full_id = link['href'].replace('details.php?id=', '')
|
||||
torrent_id = full_id[:6]
|
||||
|
||||
@@ -16,7 +16,7 @@ class Base(TorrentProvider):
|
||||
'download': 'http://www.td.af/download.php/%s/%s',
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
|
||||
def _search(self, media, quality, results):
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ class Base(TorrentProvider):
|
||||
'download': 'http://www.torrentleech.org%s',
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
cat_backup_id = None
|
||||
|
||||
def _search(self, media, quality, results):
|
||||
@@ -34,7 +34,7 @@ class Base(TorrentProvider):
|
||||
html = BeautifulSoup(data)
|
||||
|
||||
try:
|
||||
result_table = html.find('table', attrs = {'id' : 'torrenttable'})
|
||||
result_table = html.find('table', attrs = {'id': 'torrenttable'})
|
||||
if not result_table:
|
||||
return
|
||||
|
||||
@@ -42,9 +42,9 @@ class Base(TorrentProvider):
|
||||
|
||||
for result in entries[1:]:
|
||||
|
||||
link = result.find('td', attrs = {'class' : 'name'}).find('a')
|
||||
url = result.find('td', attrs = {'class' : 'quickdownload'}).find('a')
|
||||
details = result.find('td', attrs = {'class' : 'name'}).find('a')
|
||||
link = result.find('td', attrs = {'class': 'name'}).find('a')
|
||||
url = result.find('td', attrs = {'class': 'quickdownload'}).find('a')
|
||||
details = result.find('td', attrs = {'class': 'name'}).find('a')
|
||||
|
||||
results.append({
|
||||
'id': link['href'].replace('/torrent/', ''),
|
||||
@@ -52,8 +52,8 @@ class Base(TorrentProvider):
|
||||
'url': self.urls['download'] % url['href'],
|
||||
'detail_url': self.urls['download'] % details['href'],
|
||||
'size': self.parseSize(result.find_all('td')[4].string),
|
||||
'seeders': tryInt(result.find('td', attrs = {'class' : 'seeders'}).string),
|
||||
'leechers': tryInt(result.find('td', attrs = {'class' : 'leechers'}).string),
|
||||
'seeders': tryInt(result.find('td', attrs = {'class': 'seeders'}).string),
|
||||
'leechers': tryInt(result.find('td', attrs = {'class': 'leechers'}).string),
|
||||
})
|
||||
|
||||
except:
|
||||
|
||||
@@ -21,7 +21,7 @@ class Base(TorrentProvider):
|
||||
'download': 'https://torrentshack.net/%s',
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # Seconds
|
||||
|
||||
def _search(self, media, quality, results):
|
||||
|
||||
@@ -32,16 +32,16 @@ class Base(TorrentProvider):
|
||||
html = BeautifulSoup(data)
|
||||
|
||||
try:
|
||||
result_table = html.find('table', attrs = {'id' : 'torrent_table'})
|
||||
result_table = html.find('table', attrs = {'id': 'torrent_table'})
|
||||
if not result_table:
|
||||
return
|
||||
|
||||
entries = result_table.find_all('tr', attrs = {'class' : 'torrent'})
|
||||
entries = result_table.find_all('tr', attrs = {'class': 'torrent'})
|
||||
|
||||
for result in entries:
|
||||
|
||||
link = result.find('span', attrs = {'class' : 'torrent_name_link'}).parent
|
||||
url = result.find('td', attrs = {'class' : 'torrent_td'}).find('a')
|
||||
link = result.find('span', attrs = {'class': 'torrent_name_link'}).parent
|
||||
url = result.find('td', attrs = {'class': 'torrent_td'}).find('a')
|
||||
|
||||
results.append({
|
||||
'id': link['href'].replace('torrents.php?torrentid=', ''),
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
import re
|
||||
import traceback
|
||||
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.rss import RSS
|
||||
from couchpotato.core.helpers.variable import tryInt, splitString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.providers.torrent.base import TorrentMagnetProvider
|
||||
import six
|
||||
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Base(TorrentMagnetProvider, RSS):
|
||||
|
||||
urls = {
|
||||
'detail': 'https://torrentz.eu/%s',
|
||||
'search': 'https://torrentz.eu/feed?q=%s',
|
||||
'verified_search': 'https://torrentz.eu/feed_verified?q=%s'
|
||||
}
|
||||
|
||||
http_time_between_calls = 0
|
||||
|
||||
def _search(self, media, quality, results):
|
||||
|
||||
search_url = self.urls['verified_search'] if self.conf('verified_only') else self.urls['search']
|
||||
|
||||
# Create search parameters
|
||||
search_params = self.buildUrl(media)
|
||||
|
||||
smin = quality.get('size_min')
|
||||
smax = quality.get('size_max')
|
||||
if smin and smax:
|
||||
search_params += ' size %sm - %sm' % (smin, smax)
|
||||
|
||||
min_seeds = tryInt(self.conf('minimal_seeds'))
|
||||
if min_seeds:
|
||||
search_params += ' seed > %s' % (min_seeds - 1)
|
||||
|
||||
rss_data = self.getRSSData(search_url % search_params)
|
||||
|
||||
if rss_data:
|
||||
try:
|
||||
|
||||
for result in rss_data:
|
||||
|
||||
name = self.getTextElement(result, 'title')
|
||||
detail_url = self.getTextElement(result, 'link')
|
||||
description = self.getTextElement(result, 'description')
|
||||
|
||||
magnet = splitString(detail_url, '/')[-1]
|
||||
magnet_url = 'magnet:?xt=urn:btih:%s&dn=%s&tr=%s' % (magnet.upper(), tryUrlencode(name), tryUrlencode('udp://tracker.openbittorrent.com/announce'))
|
||||
|
||||
reg = re.search('Size: (?P<size>\d+) MB Seeds: (?P<seeds>[\d,]+) Peers: (?P<peers>[\d,]+)', six.text_type(description))
|
||||
size = reg.group('size')
|
||||
seeds = reg.group('seeds').replace(',', '')
|
||||
peers = reg.group('peers').replace(',', '')
|
||||
|
||||
results.append({
|
||||
'id': magnet,
|
||||
'name': six.text_type(name),
|
||||
'url': magnet_url,
|
||||
'detail_url': detail_url,
|
||||
'size': tryInt(size),
|
||||
'seeders': tryInt(seeds),
|
||||
'leechers': tryInt(peers),
|
||||
})
|
||||
|
||||
except:
|
||||
log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc()))
|
||||
|
||||
|
||||
config = [{
|
||||
'name': 'torrentz',
|
||||
'groups': [
|
||||
{
|
||||
'tab': 'searcher',
|
||||
'list': 'torrent_providers',
|
||||
'name': 'Torrentz',
|
||||
'description': 'Torrentz is a free, fast and powerful meta-search engine. <a href="https://torrentz.eu/">Torrentz</a>',
|
||||
'wizard': True,
|
||||
'options': [
|
||||
{
|
||||
'name': 'enabled',
|
||||
'type': 'enabler',
|
||||
'default': False
|
||||
},
|
||||
{
|
||||
'name': 'verified_only',
|
||||
'type': 'bool',
|
||||
'default': True,
|
||||
'advanced': True,
|
||||
'description': 'Only search verified releases',
|
||||
},
|
||||
{
|
||||
'name': 'minimal_seeds',
|
||||
'type': 'int',
|
||||
'default': 1,
|
||||
'advanced': True,
|
||||
'description': 'Only return releases with minimal X seeds',
|
||||
},
|
||||
{
|
||||
'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.',
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}]
|
||||
@@ -16,7 +16,7 @@ class Base(TorrentProvider):
|
||||
'detail': '%s/api/movie.json?id=%s'
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 #seconds
|
||||
http_time_between_calls = 1 # seconds
|
||||
|
||||
proxy_list = [
|
||||
'http://yify.unlocktorrent.com',
|
||||
@@ -81,7 +81,7 @@ config = [{
|
||||
{
|
||||
'name': 'enabled',
|
||||
'type': 'enabler',
|
||||
'default': 0
|
||||
'default': False
|
||||
},
|
||||
{
|
||||
'name': 'domain',
|
||||
|
||||
@@ -6,12 +6,11 @@
|
||||
top: 0;
|
||||
text-align: right;
|
||||
height: 100%;
|
||||
border-bottom: 4px solid transparent;
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1);
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
border: 1px solid transparent;
|
||||
border-width: 0 0 4px;
|
||||
border: 0 solid transparent;
|
||||
border-bottom-width: 4px;
|
||||
}
|
||||
.search_form:hover {
|
||||
border-color: #047792;
|
||||
@@ -22,19 +21,19 @@
|
||||
right: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.search_form.focused,
|
||||
.search_form.shown {
|
||||
border-color: #04bce6;
|
||||
}
|
||||
|
||||
|
||||
.search_form .input {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
width: 45px;
|
||||
transition: all .4s cubic-bezier(0.9,0,0.1,1);
|
||||
}
|
||||
|
||||
|
||||
.search_form.focused .input,
|
||||
.search_form.shown .input {
|
||||
width: 380px;
|
||||
@@ -49,7 +48,6 @@
|
||||
color: #FFF;
|
||||
font-size: 25px;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
width: 100%;
|
||||
opacity: 0;
|
||||
padding: 0 40px 0 10px;
|
||||
@@ -59,23 +57,23 @@
|
||||
.search_form.shown .input input {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
.search_form input::-ms-clear {
|
||||
width : 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.search_form .input input {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
|
||||
.search_form.focused .input,
|
||||
.search_form.shown .input {
|
||||
width: 277px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.search_form .input a {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -89,7 +87,7 @@
|
||||
font-size: 15px;
|
||||
color: #FFF;
|
||||
}
|
||||
|
||||
|
||||
.search_form .input a:after {
|
||||
content: "\e03e";
|
||||
}
|
||||
@@ -97,7 +95,7 @@
|
||||
.search_form.shown.filled .input a:after {
|
||||
content: "\e04e";
|
||||
}
|
||||
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
.search_form .input a {
|
||||
line-height: 44px;
|
||||
@@ -167,13 +165,13 @@
|
||||
.media_result .options select[name=title] { width: 170px; }
|
||||
.media_result .options select[name=profile] { width: 90px; }
|
||||
.media_result .options select[name=category] { width: 80px; }
|
||||
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
|
||||
|
||||
.media_result .options select[name=title] { width: 90px; }
|
||||
.media_result .options select[name=profile] { width: 50px; }
|
||||
.media_result .options select[name=category] { width: 50px; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
.media_result .options .button {
|
||||
@@ -227,14 +225,14 @@
|
||||
right: 7px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
.media_result .info h2 {
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
|
||||
.search_form .info h2 {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
@@ -247,12 +245,12 @@
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
.search_form .info h2 .title {
|
||||
position: absolute;
|
||||
width: 88%;
|
||||
}
|
||||
|
||||
|
||||
.media_result .info h2 .year {
|
||||
padding: 0 5px;
|
||||
text-align: center;
|
||||
@@ -260,14 +258,14 @@
|
||||
width: 12%;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
|
||||
@media all and (max-width: 480px) {
|
||||
|
||||
|
||||
.search_form .info h2 .year {
|
||||
font-size: 12px;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.search_form .mask,
|
||||
@@ -277,4 +275,4 @@
|
||||
width: 100%;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ Block.Search = new Class({
|
||||
'keyup': self.keyup.bind(self),
|
||||
'focus': function(){
|
||||
if(focus_timer) clearTimeout(focus_timer);
|
||||
self.el.addClass('focused')
|
||||
self.el.addClass('focused');
|
||||
if(this.get('value'))
|
||||
self.hideResults(false)
|
||||
},
|
||||
@@ -57,17 +57,17 @@ Block.Search = new Class({
|
||||
(e).preventDefault();
|
||||
|
||||
if(self.last_q === ''){
|
||||
self.input.blur()
|
||||
self.input.blur();
|
||||
self.last_q = null;
|
||||
}
|
||||
else {
|
||||
|
||||
self.last_q = '';
|
||||
self.input.set('value', '');
|
||||
self.input.focus()
|
||||
self.input.focus();
|
||||
|
||||
self.media = {}
|
||||
self.results.empty()
|
||||
self.media = {};
|
||||
self.results.empty();
|
||||
self.el.removeClass('filled')
|
||||
|
||||
}
|
||||
@@ -92,16 +92,16 @@ Block.Search = new Class({
|
||||
self.hidden = bool;
|
||||
},
|
||||
|
||||
keyup: function(e){
|
||||
keyup: function(){
|
||||
var self = this;
|
||||
|
||||
self.el[self.q() ? 'addClass' : 'removeClass']('filled')
|
||||
self.el[self.q() ? 'addClass' : 'removeClass']('filled');
|
||||
|
||||
if(self.q() != self.last_q){
|
||||
if(self.api_request && self.api_request.isRunning())
|
||||
self.api_request.cancel();
|
||||
|
||||
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer)
|
||||
if(self.autocomplete_timer) clearTimeout(self.autocomplete_timer);
|
||||
self.autocomplete_timer = self.autocomplete.delay(300, self)
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ Block.Search = new Class({
|
||||
var self = this;
|
||||
|
||||
if(!self.q()){
|
||||
self.hideResults(true)
|
||||
self.hideResults(true);
|
||||
return
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ Block.Search = new Class({
|
||||
})
|
||||
}
|
||||
else
|
||||
self.fill(q, cache)
|
||||
self.fill(q, cache);
|
||||
|
||||
self.last_q = q;
|
||||
|
||||
@@ -148,31 +148,31 @@ Block.Search = new Class({
|
||||
fill: function(q, json){
|
||||
var self = this;
|
||||
|
||||
self.cache[q] = json
|
||||
self.cache[q] = json;
|
||||
|
||||
self.media = {}
|
||||
self.results.empty()
|
||||
|
||||
Object.each(json, function(media, type){
|
||||
self.media = {};
|
||||
self.results.empty();
|
||||
|
||||
Object.each(json, function(media){
|
||||
if(typeOf(media) == 'array'){
|
||||
Object.each(media, function(m){
|
||||
|
||||
|
||||
var m = new Block.Search[m.type.capitalize() + 'Item'](m);
|
||||
$(m).inject(self.results)
|
||||
self.media[m.imdb || 'r-'+Math.floor(Math.random()*10000)] = m
|
||||
|
||||
$(m).inject(self.results);
|
||||
self.media[m.imdb || 'r-'+Math.floor(Math.random()*10000)] = m;
|
||||
|
||||
if(q == m.imdb)
|
||||
m.showOptions()
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Calculate result heights
|
||||
var w = window.getSize(),
|
||||
rc = self.result_container.getCoordinates();
|
||||
|
||||
self.results.setStyle('max-height', (w.y - rc.top - 50) + 'px')
|
||||
self.results.setStyle('max-height', (w.y - rc.top - 50) + 'px');
|
||||
self.mask.fade('out')
|
||||
|
||||
},
|
||||
@@ -185,4 +185,4 @@ Block.Search = new Class({
|
||||
return this.input.get('value').trim();
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,9 +14,11 @@ log = CPLog(__name__)
|
||||
|
||||
class Searcher(SearcherBase):
|
||||
|
||||
# noinspection PyMissingConstructor
|
||||
def __init__(self):
|
||||
addEvent('searcher.protocols', self.getSearchProtocols)
|
||||
addEvent('searcher.contains_other_quality', self.containsOtherQuality)
|
||||
addEvent('searcher.correct_3d', self.correct3D)
|
||||
addEvent('searcher.correct_year', self.correctYear)
|
||||
addEvent('searcher.correct_name', self.correctName)
|
||||
addEvent('searcher.correct_words', self.correctWords)
|
||||
@@ -123,6 +125,17 @@ class Searcher(SearcherBase):
|
||||
|
||||
return not (found.get(preferred_quality['identifier']) and len(found) == 1)
|
||||
|
||||
def correct3D(self, nzb, preferred_quality = None):
|
||||
if not preferred_quality: preferred_quality = {}
|
||||
if not preferred_quality.get('custom'): return
|
||||
|
||||
threed = preferred_quality['custom'].get('3d')
|
||||
|
||||
# Try guessing via quality tags
|
||||
guess = fireEvent('quality.guess', [nzb.get('name')], single = True)
|
||||
|
||||
return threed == guess.get('is_3d')
|
||||
|
||||
def correctYear(self, haystack, year, year_range):
|
||||
|
||||
if not isinstance(haystack, (list, tuple, set)):
|
||||
@@ -183,7 +196,7 @@ class Searcher(SearcherBase):
|
||||
req = splitString(req_set, '&')
|
||||
req_match += len(list(set(rel_words) & set(req))) == len(req)
|
||||
|
||||
if len(required_words) > 0 and req_match == 0:
|
||||
if len(required_words) > 0 and req_match == 0:
|
||||
log.info2('Wrong: Required word missing: %s', rel_name)
|
||||
return False
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ var MovieList = new Class({
|
||||
self.filter = self.options.filter || {
|
||||
'starts_with': null,
|
||||
'search': null
|
||||
}
|
||||
};
|
||||
|
||||
self.el = new Element('div.movies').adopt(
|
||||
self.title = self.options.title ? new Element('h2', {
|
||||
@@ -52,7 +52,7 @@ var MovieList = new Class({
|
||||
|
||||
self.getMovies();
|
||||
|
||||
App.on('movie.added', self.movieAdded.bind(self))
|
||||
App.on('movie.added', self.movieAdded.bind(self));
|
||||
App.on('movie.deleted', self.movieDeleted.bind(self))
|
||||
},
|
||||
|
||||
@@ -96,7 +96,7 @@ var MovieList = new Class({
|
||||
if(self.options.load_more)
|
||||
self.scrollspy = new ScrollSpy({
|
||||
min: function(){
|
||||
var c = self.load_more.getCoordinates()
|
||||
var c = self.load_more.getCoordinates();
|
||||
return c.top - window.document.getSize().y - 300
|
||||
},
|
||||
onEnter: self.loadMore.bind(self)
|
||||
@@ -179,7 +179,7 @@ var MovieList = new Class({
|
||||
|
||||
m.fireEvent('injected');
|
||||
|
||||
self.movies.include(m)
|
||||
self.movies.include(m);
|
||||
self.movies_added[movie._id] = true;
|
||||
},
|
||||
|
||||
@@ -187,7 +187,7 @@ var MovieList = new Class({
|
||||
var self = this;
|
||||
var chars = '#ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||||
|
||||
self.el.addClass('with_navigation')
|
||||
self.el.addClass('with_navigation');
|
||||
|
||||
self.navigation = new Element('div.alph_nav').adopt(
|
||||
self.mass_edit_form = new Element('div.mass_edit_form').adopt(
|
||||
@@ -242,7 +242,7 @@ var MovieList = new Class({
|
||||
this.addClass(a);
|
||||
|
||||
el.inject(el.getParent(), 'top');
|
||||
el.getSiblings().hide()
|
||||
el.getSiblings().hide();
|
||||
setTimeout(function(){
|
||||
el.getSiblings().setStyle('display', null);
|
||||
}, 100)
|
||||
@@ -286,7 +286,7 @@ var MovieList = new Class({
|
||||
'status': self.options.status
|
||||
}, self.filter),
|
||||
'onSuccess': function(json){
|
||||
available_chars = json.chars
|
||||
available_chars = json.chars;
|
||||
|
||||
available_chars.each(function(c){
|
||||
self.letters[c.capitalize()].addClass('available')
|
||||
@@ -300,7 +300,7 @@ var MovieList = new Class({
|
||||
self.navigation_alpha = new Element('ul.numbers', {
|
||||
'events': {
|
||||
'click:relay(li.available)': function(e, el){
|
||||
self.activateLetter(el.get('data-letter'))
|
||||
self.activateLetter(el.get('data-letter'));
|
||||
self.getMovies(true)
|
||||
}
|
||||
}
|
||||
@@ -318,7 +318,7 @@ var MovieList = new Class({
|
||||
|
||||
// All
|
||||
self.letters['all'] = new Element('li.letter_all.available.active', {
|
||||
'text': 'ALL',
|
||||
'text': 'ALL'
|
||||
}).inject(self.navigation_alpha);
|
||||
|
||||
// Chars
|
||||
@@ -334,7 +334,7 @@ var MovieList = new Class({
|
||||
if (self.options.menu.length > 0)
|
||||
self.options.menu.each(function(menu_item){
|
||||
self.navigation_menu.addLink(menu_item);
|
||||
})
|
||||
});
|
||||
else
|
||||
self.navigation_menu.hide();
|
||||
|
||||
@@ -347,15 +347,15 @@ var MovieList = new Class({
|
||||
movies = self.movies.length;
|
||||
self.movies.each(function(movie){
|
||||
selected += movie.isSelected() ? 1 : 0
|
||||
})
|
||||
});
|
||||
|
||||
var indeterminate = selected > 0 && selected < movies,
|
||||
checked = selected == movies && selected > 0;
|
||||
|
||||
self.mass_edit_select.set('indeterminate', indeterminate)
|
||||
self.mass_edit_select.set('indeterminate', indeterminate);
|
||||
|
||||
self.mass_edit_select_class[checked ? 'check' : 'uncheck']()
|
||||
self.mass_edit_select_class.element[indeterminate ? 'addClass' : 'removeClass']('indeterminate')
|
||||
self.mass_edit_select_class[checked ? 'check' : 'uncheck']();
|
||||
self.mass_edit_select_class.element[indeterminate ? 'addClass' : 'removeClass']('indeterminate');
|
||||
|
||||
self.mass_edit_selected.set('text', selected);
|
||||
},
|
||||
@@ -371,7 +371,7 @@ var MovieList = new Class({
|
||||
'events': {
|
||||
'click': function(e){
|
||||
(e).preventDefault();
|
||||
this.set('text', 'Deleting..')
|
||||
this.set('text', 'Deleting..');
|
||||
Api.request('media.delete', {
|
||||
'data': {
|
||||
'id': ids.join(','),
|
||||
@@ -383,7 +383,7 @@ var MovieList = new Class({
|
||||
var erase_movies = [];
|
||||
self.movies.each(function(movie){
|
||||
if (movie.isSelected()){
|
||||
$(movie).destroy()
|
||||
$(movie).destroy();
|
||||
erase_movies.include(movie);
|
||||
}
|
||||
});
|
||||
@@ -410,7 +410,7 @@ var MovieList = new Class({
|
||||
|
||||
changeQualitySelected: function(){
|
||||
var self = this;
|
||||
var ids = self.getSelectedMovies()
|
||||
var ids = self.getSelectedMovies();
|
||||
|
||||
Api.request('movie.edit', {
|
||||
'data': {
|
||||
@@ -423,11 +423,11 @@ var MovieList = new Class({
|
||||
|
||||
refreshSelected: function(){
|
||||
var self = this;
|
||||
var ids = self.getSelectedMovies()
|
||||
var ids = self.getSelectedMovies();
|
||||
|
||||
Api.request('media.refresh', {
|
||||
'data': {
|
||||
'id': ids.join(','),
|
||||
'id': ids.join(',')
|
||||
}
|
||||
});
|
||||
},
|
||||
@@ -435,7 +435,7 @@ var MovieList = new Class({
|
||||
getSelectedMovies: function(){
|
||||
var self = this;
|
||||
|
||||
var ids = []
|
||||
var ids = [];
|
||||
self.movies.each(function(movie){
|
||||
if (movie.isSelected())
|
||||
ids.include(movie.get('_id'))
|
||||
@@ -459,11 +459,11 @@ var MovieList = new Class({
|
||||
reset: function(){
|
||||
var self = this;
|
||||
|
||||
self.movies = []
|
||||
self.movies = [];
|
||||
if(self.mass_edit_select)
|
||||
self.calculateSelected()
|
||||
self.calculateSelected();
|
||||
if(self.navigation_alpha)
|
||||
self.navigation_alpha.getElements('.active').removeClass('active')
|
||||
self.navigation_alpha.getElements('.active').removeClass('active');
|
||||
|
||||
self.offset = 0;
|
||||
if(self.scrollspy){
|
||||
@@ -475,7 +475,7 @@ var MovieList = new Class({
|
||||
activateLetter: function(letter){
|
||||
var self = this;
|
||||
|
||||
self.reset()
|
||||
self.reset();
|
||||
|
||||
self.letters[letter || 'all'].addClass('active');
|
||||
self.filter.starts_with = letter;
|
||||
@@ -487,7 +487,7 @@ var MovieList = new Class({
|
||||
|
||||
self.el
|
||||
.removeClass(self.current_view+'_list')
|
||||
.addClass(new_view+'_list')
|
||||
.addClass(new_view+'_list');
|
||||
|
||||
self.current_view = new_view;
|
||||
Cookie.write(self.options.identifier+'_view2', new_view, {duration: 1000});
|
||||
@@ -504,9 +504,9 @@ var MovieList = new Class({
|
||||
if(self.search_timer) clearTimeout(self.search_timer);
|
||||
self.search_timer = (function(){
|
||||
var search_value = self.navigation_search_input.get('value');
|
||||
if (search_value == self.last_search_value) return
|
||||
if (search_value == self.last_search_value) return;
|
||||
|
||||
self.reset()
|
||||
self.reset();
|
||||
|
||||
self.activateLetter();
|
||||
self.filter.search = search_value;
|
||||
@@ -563,7 +563,7 @@ var MovieList = new Class({
|
||||
|
||||
if(self.loader_first){
|
||||
var lf = self.loader_first;
|
||||
self.loader_first.addClass('hide')
|
||||
self.loader_first.addClass('hide');
|
||||
self.loader_first = null;
|
||||
setTimeout(function(){
|
||||
lf.destroy();
|
||||
@@ -603,10 +603,10 @@ var MovieList = new Class({
|
||||
var is_empty = self.movies.length == 0 && (self.total_movies == 0 || self.total_movies === undefined);
|
||||
|
||||
if(self.title)
|
||||
self.title[is_empty ? 'hide' : 'show']()
|
||||
self.title[is_empty ? 'hide' : 'show']();
|
||||
|
||||
if(self.description)
|
||||
self.description.setStyle('display', [is_empty ? 'none' : ''])
|
||||
self.description.setStyle('display', [is_empty ? 'none' : '']);
|
||||
|
||||
if(is_empty && self.options.on_empty_element){
|
||||
self.options.on_empty_element.inject(self.loader_first || self.title || self.movie_list, 'after');
|
||||
|
||||
@@ -60,22 +60,6 @@ var MovieAction = new Class({
|
||||
'z-index': '1'
|
||||
}
|
||||
}).inject(self.movie, 'top').fade('hide');
|
||||
//self.positionMask();
|
||||
},
|
||||
|
||||
positionMask: function(){
|
||||
var self = this,
|
||||
movie = $(self.movie),
|
||||
s = movie.getSize()
|
||||
|
||||
return;
|
||||
|
||||
return self.mask.setStyles({
|
||||
'width': s.x,
|
||||
'height': s.y
|
||||
}).position({
|
||||
'relativeTo': movie
|
||||
})
|
||||
},
|
||||
|
||||
toElement: function(){
|
||||
@@ -122,7 +106,7 @@ MA.Release = new Class({
|
||||
});
|
||||
|
||||
if(!self.movie.data.releases || self.movie.data.releases.length == 0)
|
||||
self.el.hide()
|
||||
self.el.hide();
|
||||
else
|
||||
self.showHelper();
|
||||
|
||||
@@ -164,7 +148,7 @@ MA.Release = new Class({
|
||||
new Element('span.age', {'text': 'Age'}),
|
||||
new Element('span.score', {'text': 'Score'}),
|
||||
new Element('span.provider', {'text': 'Provider'})
|
||||
).inject(self.release_container)
|
||||
).inject(self.release_container);
|
||||
|
||||
if(self.movie.data.releases)
|
||||
self.movie.data.releases.each(function(release){
|
||||
@@ -186,13 +170,13 @@ MA.Release = new Class({
|
||||
}
|
||||
|
||||
// Create release
|
||||
var item = new Element('div', {
|
||||
release['el'] = new Element('div', {
|
||||
'class': 'item '+release.status,
|
||||
'id': 'release_'+release._id
|
||||
}).adopt(
|
||||
new Element('span.name', {'text': release_name, 'title': release_name}),
|
||||
new Element('span.status', {'text': release.status, 'class': 'release_status '+release.status}),
|
||||
new Element('span.quality', {'text': quality.label || 'n/a'}),
|
||||
new Element('span.quality', {'text': quality.label + (release.is_3d ? ' 3D' : '') || 'n/a'}),
|
||||
new Element('span.size', {'text': info['size'] ? Math.floor(self.get(release, 'size')) : 'n/a'}),
|
||||
new Element('span.age', {'text': self.get(release, 'age')}),
|
||||
new Element('span.score', {'text': self.get(release, 'score')}),
|
||||
@@ -219,7 +203,6 @@ MA.Release = new Class({
|
||||
}
|
||||
})
|
||||
).inject(self.release_container);
|
||||
release['el'] = item;
|
||||
|
||||
if(release.status == 'ignored' || release.status == 'failed' || release.status == 'snatched'){
|
||||
if(!self.last_release || (self.last_release && self.last_release.status != 'snatched' && release.status == 'snatched'))
|
||||
@@ -242,13 +225,13 @@ MA.Release = new Class({
|
||||
status_el.set('text', new_status);
|
||||
|
||||
if(!q && (new_status == 'snatched' || new_status == 'seeding' || new_status == 'done'))
|
||||
var q = self.addQuality(release.quality_id);
|
||||
q = self.addQuality(release.quality_id);
|
||||
|
||||
if(q && !q.hasClass(new_status)) {
|
||||
q.removeClass(release.status).addClass(new_status);
|
||||
q.set('title', q.get('title').replace(release.status, new_status));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
App.on('release.update_status', update_handle);
|
||||
|
||||
@@ -391,12 +374,11 @@ MA.Release = new Class({
|
||||
},
|
||||
|
||||
ignore: function(release){
|
||||
var self = this;
|
||||
|
||||
Api.request('release.ignore', {
|
||||
'data': {
|
||||
'id': release._id
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
},
|
||||
@@ -456,7 +438,7 @@ MA.Trailer = new Class({
|
||||
watch: function(offset){
|
||||
var self = this;
|
||||
|
||||
var data_url = 'https://gdata.youtube.com/feeds/videos?vq="{title}" {year} trailer&max-results=1&alt=json-in-script&orderby=relevance&sortorder=descending&format=5&fmt=18'
|
||||
var data_url = 'https://gdata.youtube.com/feeds/videos?vq="{title}" {year} trailer&max-results=1&alt=json-in-script&orderby=relevance&sortorder=descending&format=5&fmt=18';
|
||||
var url = data_url.substitute({
|
||||
'title': encodeURI(self.getTitle()),
|
||||
'year': self.get('year'),
|
||||
@@ -515,7 +497,7 @@ MA.Trailer = new Class({
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
self.player.addEventListener('onStateChange', change_quality);
|
||||
|
||||
}
|
||||
@@ -532,7 +514,7 @@ MA.Trailer = new Class({
|
||||
$(self.movie).setStyle('height', null);
|
||||
|
||||
setTimeout(function(){
|
||||
self.container.destroy()
|
||||
self.container.destroy();
|
||||
self.close_button.destroy();
|
||||
}, 1800)
|
||||
}
|
||||
@@ -661,7 +643,7 @@ MA.Edit = new Class({
|
||||
self.movie.slide('out');
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
MA.Refresh = new Class({
|
||||
|
||||
@@ -847,7 +829,7 @@ MA.Files = new Class({
|
||||
new Element('div.item.head').adopt(
|
||||
new Element('span.name', {'text': 'File'}),
|
||||
new Element('span.type', {'text': 'Type'})
|
||||
).inject(self.files_container)
|
||||
).inject(self.files_container);
|
||||
|
||||
if(self.movie.data.releases)
|
||||
Array.each(self.movie.data.releases, function(release){
|
||||
|
||||
@@ -398,7 +398,6 @@
|
||||
|
||||
.movies .data .quality span {
|
||||
padding: 2px 3px;
|
||||
font-weight: bold;
|
||||
opacity: 0.5;
|
||||
font-size: 10px;
|
||||
height: 16px;
|
||||
@@ -451,7 +450,6 @@
|
||||
right: 20px;
|
||||
line-height: 0;
|
||||
top: 0;
|
||||
display: block;
|
||||
width: auto;
|
||||
opacity: 0;
|
||||
display: none;
|
||||
@@ -833,7 +831,6 @@
|
||||
}
|
||||
|
||||
.movies .alph_nav .search input {
|
||||
padding: 6px 5px;
|
||||
width: 100%;
|
||||
height: 44px;
|
||||
display: inline-block;
|
||||
@@ -841,7 +838,6 @@
|
||||
background: none;
|
||||
color: #444;
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
padding: 0 10px 0 30px;
|
||||
border-bottom: 1px solid rgba(0,0,0,.08);
|
||||
}
|
||||
@@ -1043,7 +1039,6 @@
|
||||
}
|
||||
|
||||
.movies .progress > div .percentage {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
text-transform: uppercase;
|
||||
font-weight: normal;
|
||||
|
||||
@@ -23,7 +23,7 @@ var Movie = new Class({
|
||||
addEvents: function(){
|
||||
var self = this;
|
||||
|
||||
self.global_events = {}
|
||||
self.global_events = {};
|
||||
|
||||
// Do refresh with new data
|
||||
self.global_events['movie.update'] = function(notification){
|
||||
@@ -32,7 +32,7 @@ var Movie = new Class({
|
||||
self.busy(false);
|
||||
self.removeView();
|
||||
self.update.delay(2000, self, notification);
|
||||
}
|
||||
};
|
||||
App.on('movie.update', self.global_events['movie.update']);
|
||||
|
||||
// Add spinner on load / search
|
||||
@@ -40,20 +40,20 @@ var Movie = new Class({
|
||||
self.global_events[listener] = function(notification){
|
||||
if(notification.data && (self.data._id == notification.data._id || (typeOf(notification.data._id) == 'array' && notification.data._id.indexOf(self.data._id) > -1)))
|
||||
self.busy(true);
|
||||
}
|
||||
};
|
||||
App.on(listener, self.global_events[listener]);
|
||||
})
|
||||
});
|
||||
|
||||
// Remove spinner
|
||||
self.global_events['movie.searcher.ended'] = function(notification){
|
||||
if(notification.data && self.data._id == notification.data._id)
|
||||
self.busy(false)
|
||||
}
|
||||
};
|
||||
App.on('movie.searcher.ended', self.global_events['movie.searcher.ended']);
|
||||
|
||||
// Reload when releases have updated
|
||||
self.global_events['release.update_status'] = function(notification){
|
||||
var data = notification.data
|
||||
var data = notification.data;
|
||||
if(data && self.data._id == data.movie_id){
|
||||
|
||||
if(!self.data.releases)
|
||||
@@ -62,7 +62,7 @@ var Movie = new Class({
|
||||
self.data.releases.push({'quality': data.quality, 'status': data.status});
|
||||
self.updateReleases();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
App.on('release.update_status', self.global_events['release.update_status']);
|
||||
|
||||
@@ -73,7 +73,7 @@ var Movie = new Class({
|
||||
|
||||
self.el.destroy();
|
||||
delete self.list.movies_added[self.get('id')];
|
||||
self.list.movies.erase(self)
|
||||
self.list.movies.erase(self);
|
||||
|
||||
self.list.checkIfEmpty();
|
||||
|
||||
@@ -117,18 +117,6 @@ var Movie = new Class({
|
||||
}).inject(self.el, 'top').fade('hide');
|
||||
},
|
||||
|
||||
positionMask: function(){
|
||||
var self = this,
|
||||
s = self.el.getSize()
|
||||
|
||||
return self.mask.setStyles({
|
||||
'width': s.x,
|
||||
'height': s.y
|
||||
}).position({
|
||||
'relativeTo': self.el
|
||||
})
|
||||
},
|
||||
|
||||
update: function(notification){
|
||||
var self = this;
|
||||
|
||||
@@ -197,7 +185,7 @@ var Movie = new Class({
|
||||
if(self.profile.data)
|
||||
self.profile.getTypes().each(function(type){
|
||||
|
||||
var q = self.addQuality(type.get('quality'));
|
||||
var q = self.addQuality(type.get('quality'), type.get('3d'));
|
||||
if((type.finish == true || type.get('finish')) && !q.hasClass('finish')){
|
||||
q.addClass('finish');
|
||||
q.set('title', q.get('title') + ' Will finish searching for this movie if this quality is found.')
|
||||
@@ -209,7 +197,7 @@ var Movie = new Class({
|
||||
self.updateReleases();
|
||||
|
||||
Object.each(self.options.actions, function(action, key){
|
||||
self.action[key.toLowerCase()] = action = new self.options.actions[key](self)
|
||||
self.action[key.toLowerCase()] = action = new self.options.actions[key](self);
|
||||
if(action.el)
|
||||
self.actions.adopt(action)
|
||||
});
|
||||
@@ -222,11 +210,11 @@ var Movie = new Class({
|
||||
|
||||
self.data.releases.each(function(release){
|
||||
|
||||
var q = self.quality.getElement('.q_'+ release.quality),
|
||||
var q = self.quality.getElement('.q_'+ release.quality+(release.is_3d ? '.is_3d' : ':not(.is_3d)')),
|
||||
status = release.status;
|
||||
|
||||
if(!q && (status == 'snatched' || status == 'seeding' || status == 'done'))
|
||||
var q = self.addQuality(release.quality)
|
||||
q = self.addQuality(release.quality, release.is_3d || false);
|
||||
|
||||
if (q && !q.hasClass(status)){
|
||||
q.addClass(status);
|
||||
@@ -236,13 +224,13 @@ var Movie = new Class({
|
||||
});
|
||||
},
|
||||
|
||||
addQuality: function(quality){
|
||||
addQuality: function(quality, is_3d){
|
||||
var self = this;
|
||||
|
||||
var q = Quality.getQuality(quality);
|
||||
return new Element('span', {
|
||||
'text': q.label,
|
||||
'class': 'q_'+q.identifier,
|
||||
'text': q.label + (is_3d ? ' 3D' : ''),
|
||||
'class': 'q_'+q.identifier + (is_3d ? ' is_3d' : ''),
|
||||
'title': ''
|
||||
}).inject(self.quality);
|
||||
|
||||
@@ -252,9 +240,9 @@ var Movie = new Class({
|
||||
var self = this;
|
||||
|
||||
if(self.data.title)
|
||||
return self.getUnprefixedTitle(self.data.title)
|
||||
return self.getUnprefixedTitle(self.data.title);
|
||||
else if(self.data.info.titles.length > 0)
|
||||
return self.getUnprefixedTitle(self.data.info.titles[0])
|
||||
return self.getUnprefixedTitle(self.data.info.titles[0]);
|
||||
|
||||
return 'Unknown movie'
|
||||
},
|
||||
@@ -275,12 +263,12 @@ var Movie = new Class({
|
||||
self.el.addEvent('outerClick', function(){
|
||||
self.removeView();
|
||||
self.slide('out')
|
||||
})
|
||||
});
|
||||
el.show();
|
||||
self.data_container.addClass('hide_right');
|
||||
}
|
||||
else {
|
||||
self.el.removeEvents('outerClick')
|
||||
self.el.removeEvents('outerClick');
|
||||
|
||||
setTimeout(function(){
|
||||
if(self.el)
|
||||
@@ -297,7 +285,7 @@ var Movie = new Class({
|
||||
if(self.el)
|
||||
self.el
|
||||
.removeClass(self.view+'_view')
|
||||
.addClass(new_view+'_view')
|
||||
.addClass(new_view+'_view');
|
||||
|
||||
self.view = new_view;
|
||||
},
|
||||
|
||||
@@ -41,7 +41,7 @@ Block.Search.MovieItem = new Class({
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
if(info.titles)
|
||||
info.titles.each(function(title){
|
||||
@@ -132,19 +132,19 @@ Block.Search.MovieItem = new Class({
|
||||
|
||||
if(!self.options_el.hasClass('set')){
|
||||
|
||||
if(self.info.in_library){
|
||||
if(info.in_library){
|
||||
var in_library = [];
|
||||
(self.info.in_library.releases || []).each(function(release){
|
||||
(info.in_library.releases || []).each(function(release){
|
||||
in_library.include(release.quality)
|
||||
});
|
||||
}
|
||||
|
||||
self.options_el.grab(
|
||||
new Element('div', {
|
||||
'class': self.info.in_wanted && self.info.in_wanted.profile_id || in_library ? 'in_library_wanted' : ''
|
||||
'class': info.in_wanted && info.in_wanted.profile_id || in_library ? 'in_library_wanted' : ''
|
||||
}).adopt(
|
||||
self.info.in_wanted && self.info.in_wanted.profile_id ? new Element('span.in_wanted', {
|
||||
'text': 'Already in wanted list: ' + Quality.getProfile(self.info.in_wanted.profile_id).get('label')
|
||||
info.in_wanted && info.in_wanted.profile_id ? new Element('span.in_wanted', {
|
||||
'text': 'Already in wanted list: ' + Quality.getProfile(info.in_wanted.profile_id).get('label')
|
||||
}) : (in_library ? new Element('span.in_library', {
|
||||
'text': 'Already in library: ' + in_library.join(', ')
|
||||
}) : null),
|
||||
@@ -172,7 +172,7 @@ Block.Search.MovieItem = new Class({
|
||||
new Element('option', {
|
||||
'text': alt.title
|
||||
}).inject(self.title_select)
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
// Fill categories
|
||||
@@ -215,9 +215,9 @@ Block.Search.MovieItem = new Class({
|
||||
loadingMask: function(){
|
||||
var self = this;
|
||||
|
||||
self.mask = new Element('div.mask').inject(self.el).fade('hide')
|
||||
self.mask = new Element('div.mask').inject(self.el).fade('hide');
|
||||
|
||||
createSpinner(self.mask)
|
||||
createSpinner(self.mask);
|
||||
self.mask.fade('in')
|
||||
|
||||
},
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
from .main import Charts
|
||||
|
||||
|
||||
def autoload():
|
||||
return Charts()
|
||||
|
||||
|
||||
config = [{
|
||||
'name': 'charts',
|
||||
'groups': [
|
||||
{
|
||||
'label': 'Charts',
|
||||
'description': 'Displays selected charts on the home page',
|
||||
'type': 'list',
|
||||
'name': 'charts_providers',
|
||||
'tab': 'display',
|
||||
'options': [
|
||||
{
|
||||
'name': 'max_items',
|
||||
'default': 5,
|
||||
'type': 'int',
|
||||
'description': 'Maximum number of items displayed from each chart.',
|
||||
},
|
||||
{
|
||||
'name': 'update_interval',
|
||||
'default': 12,
|
||||
'type': 'int',
|
||||
'advanced': True,
|
||||
'description': '(hours)',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}]
|
||||
@@ -0,0 +1,58 @@
|
||||
import time
|
||||
|
||||
from couchpotato import tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent,fireEvent
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Charts(Plugin):
|
||||
|
||||
update_in_progress = False
|
||||
|
||||
def __init__(self):
|
||||
addApiView('charts.view', self.automationView)
|
||||
addEvent('app.load', self.setCrons)
|
||||
|
||||
def setCrons(self):
|
||||
fireEvent('schedule.interval', 'charts.update_cache', self.updateViewCache, hours = self.conf('update_interval', default = 12))
|
||||
|
||||
def automationView(self, force_update = False, **kwargs):
|
||||
|
||||
if force_update:
|
||||
charts = self.updateViewCache()
|
||||
else:
|
||||
charts = self.getCache('charts_cached')
|
||||
if not charts:
|
||||
charts = self.updateViewCache()
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'count': len(charts),
|
||||
'charts': charts
|
||||
}
|
||||
|
||||
|
||||
def updateViewCache(self):
|
||||
|
||||
if self.update_in_progress:
|
||||
while self.update_in_progress:
|
||||
time.sleep(1)
|
||||
catched_charts = self.getCache('charts_cached')
|
||||
if catched_charts:
|
||||
return catched_charts
|
||||
|
||||
try:
|
||||
self.update_in_progress = True
|
||||
charts = fireEvent('automation.get_chart_list', merge = True)
|
||||
self.setCache('charts_cached', charts, timeout = 7200 * tryInt(self.conf('update_interval', default = 12)))
|
||||
except:
|
||||
log.error('Failed refreshing charts')
|
||||
|
||||
self.update_in_progress = False
|
||||
|
||||
return charts
|
||||
@@ -0,0 +1,261 @@
|
||||
.charts {
|
||||
clear: both;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.charts > h2 {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.charts .chart {
|
||||
display: inline-block;
|
||||
width: 50%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.charts .refresh {
|
||||
clear:both;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.charts .refresh .refreshing {
|
||||
display: block;
|
||||
padding: 20px;
|
||||
font-size: 20px;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.charts .refresh a {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
display: none;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: -40px;
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.charts .refresh a:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.charts p.no_charts_enabled {
|
||||
padding: 0.7em 1em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.charts .chart h3 a {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
|
||||
.charts .chart .media_result {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 150px;
|
||||
}
|
||||
|
||||
@media all and (max-width: 960px) {
|
||||
.charts .chart {
|
||||
width: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 600px) {
|
||||
.charts .chart {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data {
|
||||
left: 150px;
|
||||
background: #4e5969;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data .info {
|
||||
top: 10px;
|
||||
left: 15px;
|
||||
right: 15px;
|
||||
bottom: 10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data .info h2 {
|
||||
white-space: normal;
|
||||
max-height: 120px;
|
||||
font-size: 18px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data .info .rating,
|
||||
.charts .chart .media_result .data .info .genres,
|
||||
.charts .chart .media_result .data .info .year {
|
||||
position: static;
|
||||
display: block;
|
||||
padding: 0;
|
||||
opacity: .6;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data .info .year {
|
||||
margin: 10px 0 0;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data .info .rating {
|
||||
font-size: 20px;
|
||||
float: right;
|
||||
margin-top: -20px;
|
||||
}
|
||||
.charts .chart .media_result .data .info .rating:before {
|
||||
content: "\e031";
|
||||
font-family: 'Elusive-Icons';
|
||||
font-size: 14px;
|
||||
margin: 0 5px 0 0;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data .info .genres {
|
||||
font-size: 11px;
|
||||
font-style: italic;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data .info .plot {
|
||||
display: block;
|
||||
font-size: 11px;
|
||||
overflow: hidden;
|
||||
text-align: justify;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
top: 64px;
|
||||
position: absolute;
|
||||
background: #4e5969;
|
||||
cursor: pointer;
|
||||
transition: all .4s ease-in-out;
|
||||
padding: 0 3px 10px 0;
|
||||
}
|
||||
.charts .chart .media_result .data:before {
|
||||
bottom: 0;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 10px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 10px;
|
||||
position: absolute;
|
||||
background: linear-gradient(
|
||||
0deg,
|
||||
rgba(78, 89, 105, 1) 0%,
|
||||
rgba(78, 89, 105, 0) 100%
|
||||
);
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data .info .plot.full {
|
||||
top: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .data {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .options {
|
||||
left: 150px;
|
||||
}
|
||||
.charts .chart .media_result .options select[name=title] { width: 100%; }
|
||||
.charts .chart .media_result .options select[name=profile] { width: 100%; }
|
||||
.charts .chart .media_result .options select[name=category] { width: 100%; }
|
||||
|
||||
.charts .chart .media_result .button {
|
||||
position: absolute;
|
||||
margin: 2px 0 0 0;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
}
|
||||
|
||||
|
||||
.charts .chart .media_result .thumbnail {
|
||||
width: 100px;
|
||||
position: absolute;
|
||||
left: 50px;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .chart_number {
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
padding: 10px;
|
||||
font: bold 2em/1em Helvetica, Sans-Serif;
|
||||
width: 50px;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
border-left: 8px solid transparent;
|
||||
}
|
||||
|
||||
.charts .chart .media_result.chart_in_wanted .chart_number {
|
||||
border-color: rgba(0, 255, 40, 0.3);
|
||||
}
|
||||
|
||||
.charts .chart .media_result.chart_in_library .chart_number {
|
||||
border-color: rgba(0, 202, 32, 0.3);
|
||||
}
|
||||
|
||||
|
||||
.charts .chart .media_result .actions {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
display: none;
|
||||
width: 90px;
|
||||
}
|
||||
.charts .chart .media_result:hover .actions {
|
||||
display: block;
|
||||
}
|
||||
.charts .chart .media_result:hover h2 .title {
|
||||
opacity: 0;
|
||||
}
|
||||
.charts .chart .media_result .data.open .actions {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.charts .chart .media_result .actions a {
|
||||
margin-left: 10px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
|
||||
.toggle_menu {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.toggle_menu a {
|
||||
display: block;
|
||||
width: 50%;
|
||||
float: left;
|
||||
color: rgba(255,255,255,.6);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.0666667);
|
||||
}
|
||||
|
||||
.toggle_menu a:hover {
|
||||
border-color: #047792;
|
||||
border-width: 4px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.toggle_menu a.active {
|
||||
border-bottom: 4px solid #04bce6;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.toggle_menu a:last-child {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.toggle_menu h2 {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
var Charts = new Class({
|
||||
|
||||
Implements: [Options, Events],
|
||||
|
||||
initialize: function(options){
|
||||
var self = this;
|
||||
self.setOptions(options);
|
||||
|
||||
self.create();
|
||||
},
|
||||
|
||||
create: function(){
|
||||
var self = this;
|
||||
|
||||
self.el = new Element('div.charts').adopt(
|
||||
self.el_no_charts_enabled = new Element('p.no_charts_enabled', {
|
||||
'html': 'Hey, it looks like you have no charts enabled at the moment. If you\'d like some great movie suggestions you can go to <a href="' + App.createUrl('settings/display') + '">settings</a> and turn on some charts of your choice.'
|
||||
}),
|
||||
self.el_refresh_container = new Element('div.refresh').adopt(
|
||||
self.el_refresh_link = new Element('a.refresh.icon2', {
|
||||
'href': '#',
|
||||
'events': {
|
||||
'click': function(e) {
|
||||
e.preventDefault();
|
||||
self.el.getChildren('div.chart').destroy();
|
||||
self.el_refreshing_text.show();
|
||||
self.el_refresh_link.hide();
|
||||
self.api_request = Api.request('charts.view', {
|
||||
'data': { 'force_update': 1 },
|
||||
'onComplete': self.fill.bind(self)
|
||||
});
|
||||
}
|
||||
}
|
||||
}),
|
||||
self.el_refreshing_text = new Element('span.refreshing', {
|
||||
'text': 'Refreshing charts...'
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
if( Cookie.read('suggestions_charts_menu_selected') === 'charts')
|
||||
self.el.show();
|
||||
else
|
||||
self.el.hide();
|
||||
|
||||
self.api_request = Api.request('charts.view', {
|
||||
'onComplete': self.fill.bind(self)
|
||||
});
|
||||
|
||||
self.fireEvent.delay(0, self, 'created');
|
||||
|
||||
},
|
||||
|
||||
fill: function(json){
|
||||
|
||||
var self = this;
|
||||
|
||||
self.el_refreshing_text.hide();
|
||||
self.el_refresh_link.show();
|
||||
|
||||
if(!json || json.count == 0){
|
||||
self.el_no_charts_enabled.show();
|
||||
self.el_refresh_link.show();
|
||||
self.el_refreshing_text.hide();
|
||||
}
|
||||
else {
|
||||
self.el_no_charts_enabled.hide();
|
||||
|
||||
json.charts.sort(function(a, b) {
|
||||
return a.order - b.order;
|
||||
});
|
||||
|
||||
Object.each(json.charts, function(chart){
|
||||
|
||||
var c = new Element('div.chart').grab(
|
||||
new Element('h3').grab( new Element('a', {
|
||||
'text': chart.name,
|
||||
'href': chart.url
|
||||
}))
|
||||
);
|
||||
|
||||
var it = 1;
|
||||
|
||||
Object.each(chart.list, function(movie){
|
||||
|
||||
var m = new Block.Search.MovieItem(movie, {
|
||||
'onAdded': function(){
|
||||
self.afterAdded(m, movie)
|
||||
}
|
||||
});
|
||||
|
||||
var in_database_class = movie.in_wanted ? 'chart_in_wanted' : (movie.in_library ? 'chart_in_library' : ''),
|
||||
in_database_title = movie.in_wanted ? 'Movie in wanted list' : (movie.in_library ? 'Movie in library' : '');
|
||||
|
||||
m.el
|
||||
.addClass(in_database_class)
|
||||
.grab(
|
||||
new Element('div.chart_number', {
|
||||
'text': it++,
|
||||
'title': in_database_title
|
||||
})
|
||||
);
|
||||
|
||||
m.data_container.grab(
|
||||
new Element('div.actions').adopt(
|
||||
new Element('a.add.icon2', {
|
||||
'title': 'Add movie with your default quality',
|
||||
'data-add': movie.imdb,
|
||||
'events': {
|
||||
'click': m.showOptions.bind(m)
|
||||
}
|
||||
}),
|
||||
$(new MA.IMDB(m)),
|
||||
$(new MA.Trailer(m, {
|
||||
'height': 150
|
||||
}))
|
||||
)
|
||||
);
|
||||
m.data_container.removeEvents('click');
|
||||
|
||||
var plot = false;
|
||||
if(m.info.plot && m.info.plot.length > 0)
|
||||
plot = m.info.plot;
|
||||
|
||||
// Add rating
|
||||
m.info_container.adopt(
|
||||
m.rating = m.info.rating && m.info.rating.imdb && m.info.rating.imdb.length == 2 && parseFloat(m.info.rating.imdb[0]) > 0 ? new Element('span.rating', {
|
||||
'text': parseFloat(m.info.rating.imdb[0]),
|
||||
'title': parseInt(m.info.rating.imdb[1]) + ' votes'
|
||||
}) : null,
|
||||
m.genre = m.info.genres && m.info.genres.length > 0 ? new Element('span.genres', {
|
||||
'text': m.info.genres.slice(0, 3).join(', ')
|
||||
}) : null,
|
||||
m.plot = plot ? new Element('span.plot', {
|
||||
'text': plot,
|
||||
'events': {
|
||||
'click': function(){
|
||||
this.toggleClass('full')
|
||||
}
|
||||
}
|
||||
}) : null
|
||||
);
|
||||
|
||||
$(m).inject(c);
|
||||
|
||||
});
|
||||
|
||||
c.inject(self.el);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
self.fireEvent('loaded');
|
||||
|
||||
},
|
||||
|
||||
afterAdded: function(m){
|
||||
|
||||
$(m).getElement('div.chart_number')
|
||||
.addClass('chart_in_wanted')
|
||||
.set('title', 'Movie in wanted list');
|
||||
|
||||
},
|
||||
|
||||
toElement: function(){
|
||||
return this.el;
|
||||
}
|
||||
|
||||
});
|
||||
@@ -13,6 +13,7 @@ log = CPLog(__name__)
|
||||
class Automation(AutomationBase):
|
||||
|
||||
enabled_option = 'automation_enabled'
|
||||
chart_enabled_option = 'chart_display_enabled'
|
||||
http_time_between_calls = 2
|
||||
|
||||
interval = 1800
|
||||
@@ -20,6 +21,7 @@ class Automation(AutomationBase):
|
||||
|
||||
def __init__(self):
|
||||
addEvent('automation.get_movies', self._getMovies)
|
||||
addEvent('automation.get_chart_list', self._getChartList)
|
||||
|
||||
def _getMovies(self):
|
||||
|
||||
@@ -34,6 +36,13 @@ class Automation(AutomationBase):
|
||||
|
||||
return self.getIMDBids()
|
||||
|
||||
def _getChartList(self):
|
||||
|
||||
if not (self.conf(self.chart_enabled_option) or self.conf(self.chart_enabled_option) is None):
|
||||
return
|
||||
|
||||
return self.getChartList()
|
||||
|
||||
def search(self, name, year = None, imdb_only = False):
|
||||
|
||||
prop_name = 'automation.cached.%s.%s' % (name, year)
|
||||
@@ -94,5 +103,9 @@ class Automation(AutomationBase):
|
||||
def getIMDBids(self):
|
||||
return []
|
||||
|
||||
def getChartList(self):
|
||||
# Example return: [ {'name': 'Display name of list', 'url': 'http://example.com/', 'order': 1, 'list': []} ]
|
||||
return
|
||||
|
||||
def canCheck(self):
|
||||
return time.time() > self.last_checked + self.interval
|
||||
|
||||
@@ -14,6 +14,8 @@ class Bluray(Automation, RSS):
|
||||
interval = 1800
|
||||
rss_url = 'http://www.blu-ray.com/rss/newreleasesfeed.xml'
|
||||
backlog_url = 'http://www.blu-ray.com/movies/movies.php?show=newreleases&page=%s'
|
||||
display_url = 'http://www.blu-ray.com/movies/movies.php?show=newreleases'
|
||||
chart_order = 1
|
||||
|
||||
def getIMDBids(self):
|
||||
|
||||
@@ -62,7 +64,7 @@ class Bluray(Automation, RSS):
|
||||
name = self.getTextElement(movie, 'title').lower().split('blu-ray')[0].strip('(').rstrip()
|
||||
year = self.getTextElement(movie, 'description').split('|')[1].strip('(').strip()
|
||||
|
||||
if not name.find('/') == -1: # make sure it is not a double movie release
|
||||
if not name.find('/') == -1: # make sure it is not a double movie release
|
||||
continue
|
||||
|
||||
if tryInt(year) < self.getMinimal('year'):
|
||||
@@ -77,6 +79,32 @@ class Bluray(Automation, RSS):
|
||||
return movies
|
||||
|
||||
|
||||
def getChartList(self):
|
||||
# Nearly identical to 'getIMDBids', but we don't care about minimalMovie and return all movie data (not just id)
|
||||
movie_list = {'name': 'Blu-ray.com - New Releases', 'url': self.display_url, 'order': self.chart_order, 'list': []}
|
||||
max_items = int(self.conf('max_items', section='charts', default=5))
|
||||
rss_movies = self.getRSSData(self.rss_url)
|
||||
|
||||
for movie in rss_movies:
|
||||
name = self.getTextElement(movie, 'title').lower().split('blu-ray')[0].strip('(').rstrip()
|
||||
year = self.getTextElement(movie, 'description').split('|')[1].strip('(').strip()
|
||||
|
||||
if not name.find('/') == -1: # make sure it is not a double movie release
|
||||
continue
|
||||
|
||||
movie = self.search(name, year)
|
||||
|
||||
if movie:
|
||||
movie_list['list'].append( movie )
|
||||
if len(movie_list['list']) >= max_items:
|
||||
break
|
||||
|
||||
if not movie_list['list']:
|
||||
return
|
||||
|
||||
return [ movie_list ]
|
||||
|
||||
|
||||
config = [{
|
||||
'name': 'bluray',
|
||||
'groups': [
|
||||
@@ -101,5 +129,19 @@ config = [{
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'tab': 'display',
|
||||
'list': 'charts_providers',
|
||||
'name': 'bluray_charts_display',
|
||||
'label': 'Blu-ray.com',
|
||||
'description': 'Display <a href="http://www.blu-ray.com/movies/movies.php?show=newreleases">new releases</a> from Blu-ray.com',
|
||||
'options': [
|
||||
{
|
||||
'name': 'chart_display_enabled',
|
||||
'default': True,
|
||||
'type': 'enabler',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}]
|
||||
|
||||
@@ -46,7 +46,10 @@ class Goodfilms(Automation):
|
||||
break
|
||||
|
||||
for movie in this_watch_list:
|
||||
movies.append({ 'title': movie['data-film-title'], 'year': movie['data-film-year'] })
|
||||
movies.append({
|
||||
'title': movie['data-film-title'],
|
||||
'year': movie['data-film-year']
|
||||
})
|
||||
|
||||
if not 'next page' in data.lower():
|
||||
break
|
||||
|
||||
@@ -105,6 +105,16 @@ class IMDBAutomation(IMDBBase):
|
||||
'top250': 'http://www.imdb.com/chart/top',
|
||||
'boxoffice': 'http://www.imdb.com/chart/',
|
||||
}
|
||||
chart_names = {
|
||||
'theater': 'IMDB - Movies in Theaters',
|
||||
'top250': 'IMDB - Top 250 Movies',
|
||||
'boxoffice': 'IMDB - Box Office',
|
||||
}
|
||||
chart_order = {
|
||||
'theater': 2,
|
||||
'top250': 4,
|
||||
'boxoffice': 3,
|
||||
}
|
||||
|
||||
first_table = ['boxoffice']
|
||||
|
||||
@@ -144,6 +154,46 @@ class IMDBAutomation(IMDBBase):
|
||||
return movies
|
||||
|
||||
|
||||
def getChartList(self):
|
||||
# Nearly identical to 'getIMDBids', but we don't care about minimalMovie and return all movie data (not just id)
|
||||
movie_lists = []
|
||||
max_items = int(self.conf('max_items', section='charts', default=5))
|
||||
|
||||
for url in self.chart_urls:
|
||||
if self.conf('chart_display_%s' % url):
|
||||
movie_list = {'name': self.chart_names[url], 'url': self.chart_urls[url], 'order': self.chart_order[url], 'list': []}
|
||||
data = self.getHTMLData(self.chart_urls[url])
|
||||
if data:
|
||||
html = BeautifulSoup(data)
|
||||
|
||||
try:
|
||||
result_div = html.find('div', attrs = {'id': 'main'})
|
||||
|
||||
try:
|
||||
if url in self.first_table:
|
||||
table = result_div.find('table')
|
||||
result_div = table if table else result_div
|
||||
except:
|
||||
pass
|
||||
|
||||
imdb_ids = getImdb(str(result_div), multiple = True)
|
||||
|
||||
for imdb_id in imdb_ids[0:max_items]:
|
||||
info = self.getInfo(imdb_id)
|
||||
movie_list['list'].append(info)
|
||||
|
||||
if self.shuttingDown():
|
||||
break
|
||||
except:
|
||||
log.error('Failed loading IMDB chart results from %s: %s', (url, traceback.format_exc()))
|
||||
|
||||
if movie_list['list']:
|
||||
movie_lists.append(movie_list)
|
||||
|
||||
|
||||
return movie_lists
|
||||
|
||||
|
||||
config = [{
|
||||
'name': 'imdb',
|
||||
'groups': [
|
||||
@@ -206,5 +256,40 @@ config = [{
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'tab': 'display',
|
||||
'list': 'charts_providers',
|
||||
'name': 'imdb_charts_display',
|
||||
'label': 'IMDB',
|
||||
'description': 'Display movies from IMDB Charts',
|
||||
'options': [
|
||||
{
|
||||
'name': 'chart_display_enabled',
|
||||
'default': True,
|
||||
'type': 'enabler',
|
||||
},
|
||||
{
|
||||
'name': 'chart_display_theater',
|
||||
'type': 'bool',
|
||||
'label': 'In Theaters',
|
||||
'description': 'New Movies <a href="http://www.imdb.com/movies-in-theaters/">In-Theaters</a> chart',
|
||||
'default': False,
|
||||
},
|
||||
{
|
||||
'name': 'chart_display_top250',
|
||||
'type': 'bool',
|
||||
'label': 'TOP 250',
|
||||
'description': 'IMDB <a href="http://www.imdb.com/chart/top/">TOP 250</a> chart',
|
||||
'default': False,
|
||||
},
|
||||
{
|
||||
'name': 'chart_display_boxoffice',
|
||||
'type': 'bool',
|
||||
'label': 'Box office TOP 10',
|
||||
'description': 'IMDB Box office <a href="http://www.imdb.com/chart/">TOP 10</a> chart',
|
||||
'default': True,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
}]
|
||||
|
||||
@@ -50,7 +50,10 @@ class Letterboxd(Automation):
|
||||
|
||||
for movie in soup.find_all('a', attrs = {'class': 'frame'}):
|
||||
match = removeEmpty(self.pattern.split(movie['title']))
|
||||
movies.append({'title': match[0], 'year': match[1] })
|
||||
movies.append({
|
||||
'title': match[0],
|
||||
'year': match[1]
|
||||
})
|
||||
|
||||
return movies
|
||||
|
||||
|
||||
@@ -104,6 +104,9 @@ class MovieResultModifier(Plugin):
|
||||
if media.get('status') == 'active':
|
||||
temp['in_wanted'] = media
|
||||
|
||||
try: temp['in_wanted']['profile'] = db.get('id', media['profile_id'])
|
||||
except: temp['in_wanted']['profile'] = {'label': ''}
|
||||
|
||||
for release in fireEvent('release.for_media', media['_id'], single = True):
|
||||
if release.get('status') == 'done':
|
||||
if not temp['in_library']:
|
||||
|
||||
@@ -74,6 +74,7 @@ class TheMovieDb(MovieProvider):
|
||||
if not result:
|
||||
try:
|
||||
log.debug('Getting info: %s', cache_key)
|
||||
# noinspection PyArgumentList
|
||||
movie = tmdb3.Movie(identifier)
|
||||
try: exists = movie.title is not None
|
||||
except: exists = False
|
||||
|
||||
@@ -14,6 +14,6 @@ class BiTHDTV(MovieProvider, Base):
|
||||
def buildUrl(self, media):
|
||||
query = tryUrlencode({
|
||||
'search': fireEvent('library.query', media, single = True),
|
||||
'cat': 7 # Movie cat
|
||||
'cat': 7 # Movie cat
|
||||
})
|
||||
return query
|
||||
|
||||
@@ -11,6 +11,7 @@ autoload = 'Bitsoup'
|
||||
|
||||
class Bitsoup(MovieProvider, Base):
|
||||
cat_ids = [
|
||||
([17], ['3d']),
|
||||
([41], ['720p', '1080p']),
|
||||
([20], ['dvdr']),
|
||||
([19], ['brrip', 'dvdrip']),
|
||||
@@ -23,6 +24,6 @@ class Bitsoup(MovieProvider, Base):
|
||||
fireEvent('library.query', media, include_year = False, single = True),
|
||||
media['info']['year']
|
||||
),
|
||||
'cat': self.getCatId(quality['identifier'])[0],
|
||||
'cat': self.getCatId(quality)[0],
|
||||
})
|
||||
return query
|
||||
|
||||
@@ -10,6 +10,7 @@ autoload = 'IPTorrents'
|
||||
class IPTorrents(MovieProvider, Base):
|
||||
|
||||
cat_ids = [
|
||||
([87], ['3d']),
|
||||
([48], ['720p', '1080p', 'bd50']),
|
||||
([72], ['cam', 'ts', 'tc', 'r5', 'scr']),
|
||||
([7], ['dvdrip', 'brrip']),
|
||||
@@ -19,4 +20,4 @@ class IPTorrents(MovieProvider, Base):
|
||||
def buildUrl(self, title, media, quality):
|
||||
query = '%s %s' % (title.replace(':', ''), media['info']['year'])
|
||||
|
||||
return self._buildUrl(query, quality['identifier'])
|
||||
return self._buildUrl(query, quality)
|
||||
|
||||
@@ -10,29 +10,29 @@ autoload = 'PassThePopcorn'
|
||||
class PassThePopcorn(MovieProvider, Base):
|
||||
|
||||
quality_search_params = {
|
||||
'bd50': {'media': 'Blu-ray', 'format': 'BD50'},
|
||||
'1080p': {'resolution': '1080p'},
|
||||
'720p': {'resolution': '720p'},
|
||||
'brrip': {'media': 'Blu-ray'},
|
||||
'dvdr': {'resolution': 'anysd'},
|
||||
'dvdrip': {'media': 'DVD'},
|
||||
'scr': {'media': 'DVD-Screener'},
|
||||
'r5': {'media': 'R5'},
|
||||
'tc': {'media': 'TC'},
|
||||
'ts': {'media': 'TS'},
|
||||
'cam': {'media': 'CAM'}
|
||||
'bd50': {'media': 'Blu-ray', 'format': 'BD50'},
|
||||
'1080p': {'resolution': '1080p'},
|
||||
'720p': {'resolution': '720p'},
|
||||
'brrip': {'media': 'Blu-ray'},
|
||||
'dvdr': {'resolution': 'anysd'},
|
||||
'dvdrip': {'media': 'DVD'},
|
||||
'scr': {'media': 'DVD-Screener'},
|
||||
'r5': {'media': 'R5'},
|
||||
'tc': {'media': 'TC'},
|
||||
'ts': {'media': 'TS'},
|
||||
'cam': {'media': 'CAM'}
|
||||
}
|
||||
|
||||
post_search_filters = {
|
||||
'bd50': {'Codec': ['BD50']},
|
||||
'1080p': {'Resolution': ['1080p']},
|
||||
'720p': {'Resolution': ['720p']},
|
||||
'brrip': {'Source': ['Blu-ray'], 'Quality': ['High Definition'], 'Container': ['!ISO']},
|
||||
'dvdr': {'Codec': ['DVD5', 'DVD9']},
|
||||
'dvdrip': {'Source': ['DVD'], 'Codec': ['!DVD5', '!DVD9']},
|
||||
'scr': {'Source': ['DVD-Screener']},
|
||||
'r5': {'Source': ['R5']},
|
||||
'tc': {'Source': ['TC']},
|
||||
'ts': {'Source': ['TS']},
|
||||
'cam': {'Source': ['CAM']}
|
||||
'bd50': {'Codec': ['BD50']},
|
||||
'1080p': {'Resolution': ['1080p']},
|
||||
'720p': {'Resolution': ['720p']},
|
||||
'brrip': {'Source': ['Blu-ray'], 'Quality': ['High Definition'], 'Container': ['!ISO']},
|
||||
'dvdr': {'Codec': ['DVD5', 'DVD9']},
|
||||
'dvdrip': {'Source': ['DVD'], 'Codec': ['!DVD5', '!DVD9']},
|
||||
'scr': {'Source': ['DVD-Screener']},
|
||||
'r5': {'Source': ['R5']},
|
||||
'tc': {'Source': ['TC']},
|
||||
'ts': {'Source': ['TS']},
|
||||
'cam': {'Source': ['CAM']}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,8 @@ class SceneAccess(MovieProvider, Base):
|
||||
]
|
||||
|
||||
def buildUrl(self, media, quality):
|
||||
url = self.urls['search'] % (
|
||||
self.getCatId(quality['identifier'])[0],
|
||||
self.getCatId(quality['identifier'])[0]
|
||||
)
|
||||
cat_id = self.getCatId(quality)[0]
|
||||
url = self.urls['search'] % (cat_id, cat_id)
|
||||
|
||||
arguments = tryUrlencode({
|
||||
'search': fireEvent('library.query', media, single = True),
|
||||
|
||||
@@ -12,10 +12,11 @@ autoload = 'ThePirateBay'
|
||||
class ThePirateBay(MovieProvider, Base):
|
||||
|
||||
cat_ids = [
|
||||
([207], ['720p', '1080p']),
|
||||
([201], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']),
|
||||
([201, 207], ['brrip']),
|
||||
([202], ['dvdr'])
|
||||
([209], ['3d']),
|
||||
([207], ['720p', '1080p']),
|
||||
([201], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']),
|
||||
([201, 207], ['brrip']),
|
||||
([202], ['dvdr'])
|
||||
]
|
||||
|
||||
def buildUrl(self, media, page, cats):
|
||||
|
||||
@@ -16,5 +16,6 @@ class TorrentDay(MovieProvider, Base):
|
||||
([3], ['dvdr']),
|
||||
([5], ['bd50']),
|
||||
]
|
||||
|
||||
def buildUrl(self, media):
|
||||
return fireEvent('library.query', media, single = True)
|
||||
|
||||
@@ -24,5 +24,5 @@ class TorrentLeech(MovieProvider, Base):
|
||||
def buildUrl(self, media, quality):
|
||||
return (
|
||||
tryUrlencode(fireEvent('library.query', media, single = True)),
|
||||
self.getCatId(quality['identifier'])[0]
|
||||
self.getCatId(quality)[0]
|
||||
)
|
||||
|
||||
@@ -31,6 +31,6 @@ class TorrentShack(MovieProvider, Base):
|
||||
|
||||
def buildUrl(self, media, quality):
|
||||
query = (tryUrlencode(fireEvent('library.query', media, single = True)),
|
||||
self.getCatId(quality['identifier'])[0],
|
||||
self.getCatId(quality)[0],
|
||||
self.getSceneOnly())
|
||||
return query
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.media._base.providers.torrent.torrentz import Base
|
||||
from couchpotato.core.media.movie.providers.base import MovieProvider
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'Torrentz'
|
||||
|
||||
|
||||
class Torrentz(MovieProvider, Base):
|
||||
|
||||
def buildUrl(self, media):
|
||||
return tryUrlencode('"%s"' % fireEvent('library.query', media, single = True))
|
||||
@@ -52,7 +52,7 @@ class HDTrailers(TrailerProvider):
|
||||
return result_data
|
||||
|
||||
def findViaAlternative(self, group):
|
||||
results = {'480p':[], '720p':[], '1080p':[]}
|
||||
results = {'480p': [], '720p': [], '1080p': []}
|
||||
|
||||
movie_name = getTitle(group)
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
from couchpotato.core.media._base.providers.userscript.base import UserscriptBase
|
||||
|
||||
autoload = 'FilmCentrum'
|
||||
|
||||
|
||||
class FilmCentrum(UserscriptBase):
|
||||
|
||||
includes = ['*://filmcentrum.nl/films/*']
|
||||
@@ -143,14 +143,17 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
|
||||
ret = False
|
||||
|
||||
index = 0
|
||||
for q_identifier in profile.get('qualities'):
|
||||
index = profile['qualities'].index(q_identifier)
|
||||
quality_custom = {
|
||||
'quality': q_identifier,
|
||||
'finish': profile['finish'][index],
|
||||
'wait_for': profile['wait_for'][index]
|
||||
'wait_for': profile['wait_for'][index],
|
||||
'3d': profile['3d'][index] if profile.get('3d') else False
|
||||
}
|
||||
|
||||
index += 1
|
||||
|
||||
if not self.conf('always_search') and not self.couldBeReleased(q_identifier in pre_releases, release_dates, movie['info']['year']):
|
||||
too_early_to_search.append(q_identifier)
|
||||
continue
|
||||
@@ -168,6 +171,9 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
quality = fireEvent('quality.single', identifier = q_identifier, single = True)
|
||||
log.info('Search for %s in %s', (default_title, quality['label']))
|
||||
|
||||
# Extend quality with profile customs
|
||||
quality['custom'] = quality_custom
|
||||
|
||||
results = fireEvent('searcher.search', search_protocols, movie, quality, single = True) or []
|
||||
if len(results) == 0:
|
||||
log.debug('Nothing found for %s in %s', (default_title, quality['label']))
|
||||
@@ -221,13 +227,17 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
if not fireEvent('searcher.correct_words', nzb['name'], media, single = True):
|
||||
return False
|
||||
|
||||
preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True)
|
||||
preferred_quality = quality if quality else fireEvent('quality.single', identifier = quality['identifier'], single = True)
|
||||
|
||||
# Contains lower quality string
|
||||
if fireEvent('searcher.contains_other_quality', nzb, movie_year = media['info']['year'], preferred_quality = preferred_quality, single = True):
|
||||
log.info2('Wrong: %s, looking for %s', (nzb['name'], quality['label']))
|
||||
return False
|
||||
|
||||
# Contains lower quality string
|
||||
if not fireEvent('searcher.correct_3d', nzb, preferred_quality = preferred_quality, single = True):
|
||||
log.info2('Wrong: %s, %slooking for %s in 3D', (nzb['name'], ('' if preferred_quality['custom'].get('3d') else 'NOT '), quality['label']))
|
||||
return False
|
||||
|
||||
# File to small
|
||||
if nzb['size'] and tryInt(preferred_quality['size_min']) > tryInt(nzb['size']):
|
||||
@@ -239,7 +249,6 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
log.info2('Wrong: "%s" is too large to be %s. %sMB instead of the maximum of %sMB.', (nzb['name'], preferred_quality['label'], nzb['size'], preferred_quality['size_max']))
|
||||
return False
|
||||
|
||||
|
||||
# Provider specific functions
|
||||
get_more = nzb.get('get_more_info')
|
||||
if get_more:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
.suggestions {
|
||||
clear: both;
|
||||
padding-top: 10px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.suggestions > h2 {
|
||||
@@ -91,7 +94,6 @@
|
||||
padding: 0 3px 10px 0;
|
||||
}
|
||||
.suggestions .media_result .data:before {
|
||||
bottom: 0;
|
||||
content: '';
|
||||
display: block;
|
||||
height: 10px;
|
||||
@@ -107,7 +109,7 @@
|
||||
z-index: 3;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
|
||||
.suggestions .media_result .data .info .plot.full {
|
||||
top: 0;
|
||||
overflow: auto;
|
||||
@@ -123,14 +125,14 @@
|
||||
.suggestions .media_result .options select[name=title] { width: 100%; }
|
||||
.suggestions .media_result .options select[name=profile] { width: 100%; }
|
||||
.suggestions .media_result .options select[name=category] { width: 100%; }
|
||||
|
||||
.suggestions .media_result .button {
|
||||
|
||||
.suggestions .media_result .button {
|
||||
position: absolute;
|
||||
margin: 2px 0 0 0;
|
||||
right: 15px;
|
||||
bottom: 15px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.suggestions .media_result .thumbnail {
|
||||
width: 100px;
|
||||
|
||||
@@ -42,11 +42,10 @@ var SuggestList = new Class({
|
||||
|
||||
}
|
||||
}
|
||||
}).grab(
|
||||
new Element('h2', {
|
||||
'text': 'You might like these'
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
var cookie_menu_select = Cookie.read('suggestions_charts_menu_selected');
|
||||
if( cookie_menu_select === 'suggestions' || cookie_menu_select === null ) self.el.show(); else self.el.hide();
|
||||
|
||||
self.api_request = Api.request('suggestion.view', {
|
||||
'onComplete': self.fill.bind(self)
|
||||
@@ -116,7 +115,7 @@ var SuggestList = new Class({
|
||||
}
|
||||
}
|
||||
}) : null
|
||||
)
|
||||
);
|
||||
|
||||
$(m).inject(self.el);
|
||||
|
||||
@@ -150,4 +149,4 @@ var SuggestList = new Class({
|
||||
return this.el;
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
@@ -131,7 +131,7 @@ class CoreNotifier(Notification):
|
||||
|
||||
for message in messages:
|
||||
if message.get('time') > last_check:
|
||||
message['sticky'] = True # Always sticky core messages
|
||||
message['sticky'] = True # Always sticky core messages
|
||||
|
||||
message_type = 'core.message.important' if message.get('important') else 'core.message'
|
||||
fireEvent(message_type, message = message.get('message'), data = message)
|
||||
|
||||
@@ -17,14 +17,14 @@ var NotificationBase = new Class({
|
||||
App.addEvent('load', self.addTestButtons.bind(self));
|
||||
|
||||
// Notification bar
|
||||
self.notifications = []
|
||||
self.notifications = [];
|
||||
App.addEvent('load', function(){
|
||||
|
||||
App.block.notification = new Block.Menu(self, {
|
||||
'button_class': 'icon2.eye-open',
|
||||
'class': 'notification_menu',
|
||||
'onOpen': self.markAsRead.bind(self)
|
||||
})
|
||||
});
|
||||
$(App.block.notification).inject(App.getBlock('search'), 'after');
|
||||
self.badge = new Element('div.badge').inject(App.block.notification, 'top').hide();
|
||||
|
||||
@@ -40,7 +40,7 @@ var NotificationBase = new Class({
|
||||
var self = this;
|
||||
|
||||
var added = new Date();
|
||||
added.setTime(result.added*1000)
|
||||
added.setTime(result.added*1000);
|
||||
|
||||
result.el = App.getBlock('notification').addLink(
|
||||
new Element('span.'+(result.read ? 'read' : '' )).adopt(
|
||||
@@ -51,7 +51,7 @@ var NotificationBase = new Class({
|
||||
self.notifications.include(result);
|
||||
|
||||
if((result.data.important !== undefined || result.data.sticky !== undefined) && !result.read){
|
||||
var sticky = true
|
||||
var sticky = true;
|
||||
App.trigger('message', [result.message, sticky, result])
|
||||
}
|
||||
else if(!result.read){
|
||||
@@ -62,7 +62,7 @@ var NotificationBase = new Class({
|
||||
|
||||
setBadge: function(value){
|
||||
var self = this;
|
||||
self.badge.set('text', value)
|
||||
self.badge.set('text', value);
|
||||
self.badge[value ? 'show' : 'hide']()
|
||||
},
|
||||
|
||||
@@ -73,9 +73,9 @@ var NotificationBase = new Class({
|
||||
if(!force_ids) {
|
||||
var rn = self.notifications.filter(function(n){
|
||||
return !n.read && n.data.important === undefined
|
||||
})
|
||||
});
|
||||
|
||||
var ids = []
|
||||
var ids = [];
|
||||
rn.each(function(n){
|
||||
ids.include(n._id)
|
||||
})
|
||||
@@ -106,7 +106,7 @@ var NotificationBase = new Class({
|
||||
'onSuccess': function(json){
|
||||
self.processData(json, true)
|
||||
}
|
||||
}).send()
|
||||
}).send();
|
||||
|
||||
setInterval(function(){
|
||||
|
||||
@@ -141,7 +141,7 @@ var NotificationBase = new Class({
|
||||
|
||||
stopPoll: function(){
|
||||
if(this.request)
|
||||
this.request.cancel()
|
||||
this.request.cancel();
|
||||
this.stopped = true;
|
||||
},
|
||||
|
||||
@@ -154,7 +154,7 @@ var NotificationBase = new Class({
|
||||
App.trigger(result._t || result.type, [result]);
|
||||
if(result.message && result.read === undefined && !init)
|
||||
self.showMessage(result.message);
|
||||
})
|
||||
});
|
||||
|
||||
if(json.result.length > 0)
|
||||
self.last_id = json.result.getLast().message_id
|
||||
@@ -180,11 +180,11 @@ var NotificationBase = new Class({
|
||||
}, 10);
|
||||
|
||||
var hide_message = function(){
|
||||
new_message.addClass('hide')
|
||||
new_message.addClass('hide');
|
||||
setTimeout(function(){
|
||||
new_message.destroy();
|
||||
}, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
if(sticky)
|
||||
new_message.grab(
|
||||
@@ -206,7 +206,7 @@ var NotificationBase = new Class({
|
||||
addTestButtons: function(){
|
||||
var self = this;
|
||||
|
||||
var setting_page = App.getPage('Settings')
|
||||
var setting_page = App.getPage('Settings');
|
||||
setting_page.addEvent('create', function(){
|
||||
Object.each(setting_page.tabs.notifications.groups, self.addTestButton.bind(self))
|
||||
})
|
||||
|
||||
@@ -97,7 +97,8 @@ config = [{
|
||||
'name': 'smtp_server',
|
||||
'label': 'SMTP server',
|
||||
},
|
||||
{ 'name': 'smtp_port',
|
||||
{
|
||||
'name': 'smtp_port',
|
||||
'label': 'SMTP server port',
|
||||
'default': '25',
|
||||
'type': 'int',
|
||||
|
||||
@@ -19,6 +19,8 @@ class Growl(Notification):
|
||||
def __init__(self):
|
||||
super(Growl, self).__init__()
|
||||
|
||||
self.growl = None
|
||||
|
||||
if self.isEnabled():
|
||||
addEvent('app.load', self.register)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ autoload = 'NMJ'
|
||||
|
||||
class NMJ(Notification):
|
||||
|
||||
# noinspection PyMissingConstructor
|
||||
def __init__(self):
|
||||
addEvent('renamer.after', self.addToLibrary)
|
||||
addApiView(self.testNotifyName(), self.test)
|
||||
|
||||
@@ -30,7 +30,7 @@ class Pushalot(Notification):
|
||||
}
|
||||
|
||||
headers = {
|
||||
'Content-type': 'application/x-www-form-urlencoded'
|
||||
'Content-type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
|
||||
try:
|
||||
|
||||
@@ -25,6 +25,7 @@ class Pushover(Notification):
|
||||
'token': self.app_token,
|
||||
'message': toUnicode(message),
|
||||
'priority': self.conf('priority'),
|
||||
'sound': self.conf('sound'),
|
||||
}
|
||||
|
||||
if data and data.get('identifier'):
|
||||
@@ -33,10 +34,9 @@ class Pushover(Notification):
|
||||
'url_title': toUnicode('%s on IMDb' % getTitle(data)),
|
||||
})
|
||||
|
||||
http_handler.request('POST',
|
||||
"/1/messages.json",
|
||||
headers = {'Content-type': 'application/x-www-form-urlencoded'},
|
||||
body = tryUrlencode(api_data)
|
||||
http_handler.request('POST', '/1/messages.json',
|
||||
headers = {'Content-type': 'application/x-www-form-urlencoded'},
|
||||
body = tryUrlencode(api_data)
|
||||
)
|
||||
|
||||
response = http_handler.getresponse()
|
||||
@@ -83,6 +83,11 @@ config = [{
|
||||
'advanced': True,
|
||||
'description': 'Also send message when movie is snatched.',
|
||||
},
|
||||
{
|
||||
'name': 'sound',
|
||||
'advanced': True,
|
||||
'description': 'Define <a href="https://pushover.net/api%23sounds" target="_blank">custom sound</a> for Pushover alert.'
|
||||
},
|
||||
],
|
||||
}
|
||||
],
|
||||
|
||||
@@ -59,7 +59,7 @@ var TwitterNotification = new Class({
|
||||
).inject(fieldset.getElement('.test_button'), 'before');
|
||||
})
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@ class XBMC(Notification):
|
||||
self.use_json_notifications[host] = False
|
||||
|
||||
# send the text message
|
||||
resp = self.notifyXBMCnoJSON(host, {'title':self.default_title, 'message':message})
|
||||
resp = self.notifyXBMCnoJSON(host, {'title': self.default_title, 'message': message})
|
||||
for r in resp:
|
||||
if r.get('result') and r['result'] == 'OK':
|
||||
log.debug('Message delivered successfully!')
|
||||
|
||||
@@ -35,9 +35,9 @@ class Automation(Plugin):
|
||||
prop_name = 'automation.added.%s' % imdb_id
|
||||
added = Env.prop(prop_name, default = False)
|
||||
if not added:
|
||||
added_movie = fireEvent('movie.add', params = {'identifier': imdb_id}, force_readd = False, search_after = False, update_library = True, single = True)
|
||||
added_movie = fireEvent('movie.add', params = {'identifier': imdb_id}, force_readd = False, search_after = False, update_after = True, single = True)
|
||||
if added_movie:
|
||||
movie_ids.append(added_movie['id'])
|
||||
movie_ids.append(added_movie['_id'])
|
||||
Env.prop(prop_name, True)
|
||||
|
||||
for movie_id in movie_ids:
|
||||
|
||||
@@ -43,8 +43,8 @@ class Plugin(object):
|
||||
http_failed_disabled = {}
|
||||
http_opener = requests.Session()
|
||||
|
||||
def __new__(typ, *args, **kwargs):
|
||||
new_plugin = super(Plugin, typ).__new__(typ)
|
||||
def __new__(cls, *args, **kwargs):
|
||||
new_plugin = super(Plugin, cls).__new__(cls)
|
||||
new_plugin.registerPlugin()
|
||||
|
||||
return new_plugin
|
||||
@@ -329,7 +329,7 @@ class Plugin(object):
|
||||
elif data.get('password'):
|
||||
tag += '{{%s}}' % data.get('password')
|
||||
|
||||
max_length = 127 - len(tag) # Some filesystems don't support 128+ long filenames
|
||||
max_length = 127 - len(tag) # Some filesystems don't support 128+ long filenames
|
||||
return '%s%s' % (toSafeString(toUnicode(release_name)[:max_length]), tag)
|
||||
|
||||
def createFileName(self, data, filedata, media):
|
||||
@@ -349,6 +349,7 @@ class Plugin(object):
|
||||
now = time.time()
|
||||
file_too_new = False
|
||||
|
||||
file_time = []
|
||||
for cur_file in files:
|
||||
|
||||
# File got removed while checking
|
||||
|
||||
@@ -17,7 +17,8 @@ if os.name == 'nt':
|
||||
raise ImportError("Missing the win32file module, which is a part of the prerequisite \
|
||||
pywin32 package. You can get it from http://sourceforge.net/projects/pywin32/files/pywin32/")
|
||||
else:
|
||||
import win32file #@UnresolvedImport
|
||||
# noinspection PyUnresolvedReferences
|
||||
import win32file
|
||||
|
||||
autoload = 'FileBrowser'
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
.category > .delete:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
.category .ctrlHolder:hover {
|
||||
background: none;
|
||||
}
|
||||
@@ -69,7 +69,7 @@
|
||||
}
|
||||
|
||||
#category_ordering li .handle {
|
||||
background: url('../../static/profile_plugin/handle.png') center;
|
||||
background: url('../../images/handle.png') center;
|
||||
width: 20px;
|
||||
float: right;
|
||||
}
|
||||
@@ -79,4 +79,4 @@
|
||||
float: right;
|
||||
width: 250px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ var CategoryListBase = new Class({
|
||||
setup: function(categories){
|
||||
var self = this;
|
||||
|
||||
self.categories = []
|
||||
self.categories = [];
|
||||
Array.each(categories, self.createCategory.bind(self));
|
||||
|
||||
},
|
||||
@@ -17,7 +17,7 @@ var CategoryListBase = new Class({
|
||||
addSettings: function(){
|
||||
var self = this;
|
||||
|
||||
self.settings = App.getPage('Settings')
|
||||
self.settings = App.getPage('Settings');
|
||||
self.settings.addEvent('create', function(){
|
||||
var tab = self.settings.createSubTab('category', {
|
||||
'label': 'Categories',
|
||||
@@ -31,7 +31,7 @@ var CategoryListBase = new Class({
|
||||
self.createList();
|
||||
self.createOrdering();
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
// Add categories in renamer
|
||||
self.settings.addEvent('create', function(){
|
||||
@@ -97,9 +97,9 @@ var CategoryListBase = new Class({
|
||||
createCategory: function(data){
|
||||
var self = this;
|
||||
|
||||
var data = data || {'id': randomString()}
|
||||
var category = new Category(data)
|
||||
self.categories.include(category)
|
||||
var data = data || {'id': randomString()};
|
||||
var category = new Category(data);
|
||||
self.categories.include(category);
|
||||
|
||||
return category;
|
||||
},
|
||||
@@ -108,7 +108,7 @@ var CategoryListBase = new Class({
|
||||
var self = this;
|
||||
|
||||
var category_list;
|
||||
var group = self.settings.createGroup({
|
||||
self.settings.createGroup({
|
||||
'label': 'Category ordering'
|
||||
}).adopt(
|
||||
new Element('.ctrlHolder#category_ordering').adopt(
|
||||
@@ -118,7 +118,7 @@ var CategoryListBase = new Class({
|
||||
'html': 'Change the order the categories are in the dropdown list.<br />First one will be default.'
|
||||
})
|
||||
)
|
||||
).inject(self.content)
|
||||
).inject(self.content);
|
||||
|
||||
Array.each(self.categories, function(category){
|
||||
new Element('li', {'data-id': category.data._id}).adopt(
|
||||
@@ -145,7 +145,7 @@ var CategoryListBase = new Class({
|
||||
|
||||
var ids = [];
|
||||
|
||||
self.category_sortable.list.getElements('li').each(function(el, nr){
|
||||
self.category_sortable.list.getElements('li').each(function(el){
|
||||
ids.include(el.get('data-id'));
|
||||
});
|
||||
|
||||
@@ -157,7 +157,7 @@ var CategoryListBase = new Class({
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
window.CategoryList = new CategoryListBase();
|
||||
|
||||
@@ -235,8 +235,6 @@ var Category = new Class({
|
||||
if(self.save_timer) clearTimeout(self.save_timer);
|
||||
self.save_timer = (function(){
|
||||
|
||||
var data = self.getData();
|
||||
|
||||
Api.request('category.save', {
|
||||
'data': self.getData(),
|
||||
'useSpinner': true,
|
||||
@@ -257,7 +255,7 @@ var Category = new Class({
|
||||
getData: function(){
|
||||
var self = this;
|
||||
|
||||
var data = {
|
||||
return {
|
||||
'id' : self.data._id,
|
||||
'label' : self.el.getElement('.category_label input').get('value'),
|
||||
'required' : self.el.getElement('.category_required input').get('value'),
|
||||
@@ -265,8 +263,6 @@ var Category = new Class({
|
||||
'ignored' : self.el.getElement('.category_ignored input').get('value'),
|
||||
'destination': self.data.destination
|
||||
}
|
||||
|
||||
return data
|
||||
},
|
||||
|
||||
del: function(){
|
||||
|
||||
@@ -77,8 +77,8 @@ class Dashboard(Plugin):
|
||||
if coming_soon:
|
||||
|
||||
# Don't list older movies
|
||||
if ((not late and (media['info']['year'] >= now_year-1) and (not eta.get('dvd') and not eta.get('theater') or eta.get('dvd') and eta.get('dvd') > (now - 2419200))) or
|
||||
(late and ((media['info']['year'] < now_year-1) or ((eta.get('dvd', 0) > 0 or eta.get('theater')) and eta.get('dvd') < (now - 2419200))))):
|
||||
if ((not late and (media['info']['year'] >= now_year - 1) and (not eta.get('dvd') and not eta.get('theater') or eta.get('dvd') and eta.get('dvd') > (now - 2419200))) or
|
||||
(late and (media['info']['year'] < now_year - 1 or (eta.get('dvd', 0) > 0 or eta.get('theater')) and eta.get('dvd') < (now - 2419200)))):
|
||||
medias.append(media)
|
||||
|
||||
if len(medias) >= limit:
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import os.path
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from couchpotato import get_db
|
||||
@@ -64,7 +63,7 @@ class FileManager(Plugin):
|
||||
def download(self, url = '', dest = None, overwrite = False, urlopen_kwargs = None):
|
||||
if not urlopen_kwargs: urlopen_kwargs = {}
|
||||
|
||||
if not dest: # to Cache
|
||||
if not dest: # to Cache
|
||||
dest = os.path.join(Env.get('cache_dir'), '%s.%s' % (md5(url), getExt(url)))
|
||||
|
||||
if not overwrite and os.path.isfile(dest):
|
||||
|
||||
@@ -67,14 +67,14 @@ class Logging(Plugin):
|
||||
if x is nr:
|
||||
current_path = path
|
||||
|
||||
log = ''
|
||||
log_content = ''
|
||||
if current_path:
|
||||
f = open(current_path, 'r')
|
||||
log = f.read()
|
||||
log_content = f.read()
|
||||
|
||||
return {
|
||||
'success': True,
|
||||
'log': toUnicode(log),
|
||||
'log': toUnicode(log_content),
|
||||
'total': total,
|
||||
}
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
overflow: hidden;
|
||||
line-height: 150%;
|
||||
font-size: 11px;
|
||||
font-family: Lucida Console, Monaco, Nimbus Mono L;
|
||||
font-family: Lucida Console, Monaco, Nimbus Mono L, monospace, serif;
|
||||
}
|
||||
|
||||
.page.log .container .error {
|
||||
|
||||
@@ -42,7 +42,7 @@ Page.Log = new Class({
|
||||
}
|
||||
}
|
||||
}).inject(nav);
|
||||
};
|
||||
}
|
||||
|
||||
new Element('li', {
|
||||
'text': 'clear',
|
||||
@@ -63,7 +63,6 @@ Page.Log = new Class({
|
||||
},
|
||||
|
||||
addColors: function(text){
|
||||
var self = this;
|
||||
|
||||
text = text
|
||||
.replace(/&/g, '&')
|
||||
@@ -74,9 +73,9 @@ Page.Log = new Class({
|
||||
.replace(/\u001b\[36m/gi, '</span><span class="debug">')
|
||||
.replace(/\u001b\[33m/gi, '</span><span class="debug">')
|
||||
.replace(/\u001b\[0m\n/gi, '</div><div class="time">')
|
||||
.replace(/\u001b\[0m/gi, '</span><span>')
|
||||
.replace(/\u001b\[0m/gi, '</span><span>');
|
||||
|
||||
return '<div class="time">' + text + '</div>';
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
@@ -160,7 +160,7 @@ class Manage(Plugin):
|
||||
except:
|
||||
log.error('Failed updating library: %s', (traceback.format_exc()))
|
||||
|
||||
while True and not self.shuttingDown():
|
||||
while self.in_progress and len(self.in_progress) > 0 and not self.shuttingDown():
|
||||
|
||||
delete_me = {}
|
||||
|
||||
@@ -171,14 +171,12 @@ class Manage(Plugin):
|
||||
for delete in delete_me:
|
||||
del self.in_progress[delete]
|
||||
|
||||
if len(self.in_progress) == 0:
|
||||
break
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
fireEvent('notify.frontend', type = 'manage.updating', data = False)
|
||||
self.in_progress = False
|
||||
|
||||
# noinspection PyDefaultArgument
|
||||
def createAddToLibrary(self, folder, added_identifiers = []):
|
||||
|
||||
def addToLibrary(group, total_found, to_go):
|
||||
@@ -219,7 +217,7 @@ class Manage(Plugin):
|
||||
pr = self.in_progress[folder]
|
||||
pr['to_go'] -= 1
|
||||
|
||||
avg = (time.time() - pr['started'])/(pr['total'] - pr['to_go'])
|
||||
avg = (time.time() - pr['started']) / (pr['total'] - pr['to_go'])
|
||||
pr['eta'] = tryInt(avg * pr['to_go'])
|
||||
|
||||
|
||||
|
||||
@@ -79,7 +79,8 @@ class ProfilePlugin(Plugin):
|
||||
'core': kwargs.get('core', False),
|
||||
'qualities': [],
|
||||
'wait_for': [],
|
||||
'finish': []
|
||||
'finish': [],
|
||||
'3d': []
|
||||
}
|
||||
|
||||
# Update types
|
||||
@@ -88,6 +89,7 @@ class ProfilePlugin(Plugin):
|
||||
profile['qualities'].append(type.get('quality'))
|
||||
profile['wait_for'].append(tryInt(type.get('wait_for')))
|
||||
profile['finish'].append((tryInt(type.get('finish')) == 1) if order > 0 else True)
|
||||
profile['3d'].append(tryInt(type.get('3d')))
|
||||
order += 1
|
||||
|
||||
id = kwargs.get('id')
|
||||
@@ -184,6 +186,14 @@ class ProfilePlugin(Plugin):
|
||||
}, {
|
||||
'label': 'SD',
|
||||
'qualities': ['dvdrip', 'dvdr']
|
||||
}, {
|
||||
'label': 'Prefer 3D HD',
|
||||
'qualities': ['1080p', '720p', '720p', '1080p'],
|
||||
'3d': [True, True]
|
||||
}, {
|
||||
'label': '3D HD',
|
||||
'qualities': ['1080p', '720p'],
|
||||
'3d': [True, True]
|
||||
}]
|
||||
|
||||
# Create default quality profile
|
||||
@@ -197,12 +207,15 @@ class ProfilePlugin(Plugin):
|
||||
'order': order,
|
||||
'qualities': profile.get('qualities'),
|
||||
'finish': [],
|
||||
'wait_for': []
|
||||
'wait_for': [],
|
||||
'3d': []
|
||||
}
|
||||
|
||||
threed = profile.get('3d', [])
|
||||
for q in profile.get('qualities'):
|
||||
pro['finish'].append(True)
|
||||
pro['wait_for'].append(0)
|
||||
pro['3d'].append(threed.pop() if threed else False)
|
||||
|
||||
db.insert(pro)
|
||||
order += 1
|
||||
|
||||
@@ -22,18 +22,17 @@
|
||||
.profile > .delete:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
.profile .ctrlHolder:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.profile .qualities {
|
||||
min-height: 80px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.profile .formHint {
|
||||
width: 250px !important;
|
||||
width: 210px !important;
|
||||
vertical-align: top !important;
|
||||
margin: 0 !important;
|
||||
padding-left: 3px !important;
|
||||
@@ -78,21 +77,55 @@
|
||||
}
|
||||
|
||||
.profile .quality_type select {
|
||||
width: 186px;
|
||||
width: 120px;
|
||||
margin-left: -1px;
|
||||
}
|
||||
|
||||
.profile .types li.is_empty .check, .profile .types li.is_empty .delete, .profile .types li.is_empty .handle {
|
||||
.profile .types li.is_empty .check,
|
||||
.profile .types li.is_empty .delete,
|
||||
.profile .types li.is_empty .handle,
|
||||
.profile .types li.is_empty .check_label {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.profile .types .type label {
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
float: none;
|
||||
text-transform: uppercase;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
margin-right: 20px;
|
||||
text-shadow: none;
|
||||
vertical-align: bottom;
|
||||
padding: 0;
|
||||
height: 17px;
|
||||
}
|
||||
.profile .types .type label .check {
|
||||
margin-right: 5px;
|
||||
}
|
||||
.profile .types .type label .check_label {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
height: 16px;
|
||||
line-height: 13px;
|
||||
}
|
||||
|
||||
.profile .types .type .threed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.profile .types .type.allow_3d .threed {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.profile .types .type .handle {
|
||||
background: url('../../static/profile_plugin/handle.png') center;
|
||||
background: url('../../images/handle.png') center;
|
||||
display: inline-block;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
cursor: -webkit-grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
cursor: grab;
|
||||
margin: 0;
|
||||
}
|
||||
@@ -106,6 +139,9 @@
|
||||
font-size: 13px;
|
||||
color: #fd5353;
|
||||
}
|
||||
.profile .types .type:not(.allow_3d) .delete {
|
||||
margin-left: 55px;
|
||||
}
|
||||
|
||||
.profile .types .type:hover:not(.is_empty) .delete {
|
||||
visibility: visible;
|
||||
@@ -144,7 +180,7 @@
|
||||
}
|
||||
|
||||
#profile_ordering li .handle {
|
||||
background: url('../../static/profile_plugin/handle.png') center;
|
||||
background: url('../../images/handle.png') center;
|
||||
width: 20px;
|
||||
float: right;
|
||||
}
|
||||
@@ -154,4 +190,4 @@
|
||||
float: right;
|
||||
width: 250px;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,6 +63,7 @@ var Profile = new Class({
|
||||
data.types.include({
|
||||
'quality': quality,
|
||||
'finish': data.finish[nr] || false,
|
||||
'3d': data['3d'] ? data['3d'][nr] || false : false,
|
||||
'wait_for': data.wait_for[nr] || 0
|
||||
})
|
||||
});
|
||||
@@ -99,7 +100,7 @@ var Profile = new Class({
|
||||
'onComplete': function(json){
|
||||
if(json.success){
|
||||
self.data = json.profile;
|
||||
self.type_container.getElement('li:first-child input[type=checkbox]')
|
||||
self.type_container.getElement('li:first-child input.finish[type=checkbox]')
|
||||
.set('checked', true)
|
||||
.getParent().addClass('checked');
|
||||
}
|
||||
@@ -118,16 +119,17 @@ var Profile = new Class({
|
||||
'label' : self.el.getElement('.quality_label input').get('value'),
|
||||
'wait_for' : self.el.getElement('.wait_for input').get('value'),
|
||||
'types': []
|
||||
}
|
||||
};
|
||||
|
||||
Array.each(self.type_container.getElements('.type'), function(type){
|
||||
if(!type.hasClass('deleted') && type.getElement('select').get('value') != -1)
|
||||
data.types.include({
|
||||
'quality': type.getElement('select').get('value'),
|
||||
'finish': +type.getElement('input[type=checkbox]').checked,
|
||||
'finish': +type.getElement('input.finish[type=checkbox]').checked,
|
||||
'3d': +type.getElement('input.3d[type=checkbox]').checked,
|
||||
'wait_for': 0
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
return data
|
||||
},
|
||||
@@ -240,6 +242,7 @@ Profile.Type = new Class({
|
||||
|
||||
self.addEvent('change', function(){
|
||||
self.el[self.qualities.get('value') == '-1' ? 'addClass' : 'removeClass']('is_empty');
|
||||
self.el[Quality.getQuality(self.qualities.get('value')).allow_3d ? 'addClass': 'removeClass']('allow_3d');
|
||||
self.deleted = self.qualities.get('value') == '-1';
|
||||
});
|
||||
|
||||
@@ -250,24 +253,40 @@ Profile.Type = new Class({
|
||||
var data = self.data;
|
||||
|
||||
self.el = new Element('li.type').adopt(
|
||||
new Element('span.quality_type').adopt(
|
||||
new Element('span.quality_type').grab(
|
||||
self.fillQualities()
|
||||
),
|
||||
new Element('span.finish').adopt(
|
||||
self.finish = new Element('input.inlay.finish[type=checkbox]', {
|
||||
'checked': data.finish !== undefined ? data.finish : 1,
|
||||
'events': {
|
||||
'change': function(e){
|
||||
if(self.el == self.el.getParent().getElement(':first-child')){
|
||||
self.finish_class.check();
|
||||
alert('Top quality always finishes the search')
|
||||
return;
|
||||
}
|
||||
self.finish_container = new Element('label.finish').adopt(
|
||||
new Element('span.finish').grab(
|
||||
self.finish = new Element('input.inlay.finish[type=checkbox]', {
|
||||
'checked': data.finish !== undefined ? data.finish : 1,
|
||||
'events': {
|
||||
'change': function(){
|
||||
if(self.el == self.el.getParent().getElement(':first-child')){
|
||||
self.finish_class.check();
|
||||
alert('Top quality always finishes the search');
|
||||
return;
|
||||
}
|
||||
|
||||
self.fireEvent('change');
|
||||
self.fireEvent('change');
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
),
|
||||
new Element('span.check_label[text=finish]')
|
||||
),
|
||||
self['3d_container'] = new Element('label.threed').adopt(
|
||||
new Element('span.3d').grab(
|
||||
self['3d'] = new Element('input.inlay.3d[type=checkbox]', {
|
||||
'checked': data['3d'] !== undefined ? data['3d'] : 0,
|
||||
'events': {
|
||||
'change': function(){
|
||||
self.fireEvent('change');
|
||||
}
|
||||
}
|
||||
})
|
||||
),
|
||||
new Element('span.check_label[text=3D]')
|
||||
),
|
||||
new Element('span.delete.icon2', {
|
||||
'events': {
|
||||
@@ -279,7 +298,11 @@ Profile.Type = new Class({
|
||||
|
||||
self.el[self.data.quality ? 'removeClass' : 'addClass']('is_empty');
|
||||
|
||||
if(self.data.quality && Quality.getQuality(self.data.quality).allow_3d)
|
||||
self.el.addClass('allow_3d');
|
||||
|
||||
self.finish_class = new Form.Check(self.finish);
|
||||
self['3d_class'] = new Form.Check(self['3d']);
|
||||
|
||||
},
|
||||
|
||||
@@ -290,7 +313,7 @@ Profile.Type = new Class({
|
||||
'events': {
|
||||
'change': self.fireEvent.bind(self, 'change')
|
||||
}
|
||||
}).adopt(
|
||||
}).grab(
|
||||
new Element('option', {
|
||||
'text': '+ Add another quality',
|
||||
'value': -1
|
||||
@@ -300,7 +323,8 @@ Profile.Type = new Class({
|
||||
Object.each(Quality.qualities, function(q){
|
||||
new Element('option', {
|
||||
'text': q.label,
|
||||
'value': q.identifier
|
||||
'value': q.identifier,
|
||||
'data-allow_3d': q.allow_3d
|
||||
}).inject(self.qualities)
|
||||
});
|
||||
|
||||
@@ -316,6 +340,7 @@ Profile.Type = new Class({
|
||||
return {
|
||||
'quality': self.qualities.get('value'),
|
||||
'finish': +self.finish.checked,
|
||||
'3d': +self['3d'].checked,
|
||||
'wait_for': 0
|
||||
}
|
||||
},
|
||||
@@ -338,4 +363,4 @@ Profile.Type = new Class({
|
||||
return this.el;
|
||||
}
|
||||
|
||||
})
|
||||
});
|
||||
|
||||
@@ -21,9 +21,9 @@ class QualityPlugin(Plugin):
|
||||
}
|
||||
|
||||
qualities = [
|
||||
{'identifier': 'bd50', 'hd': True, 'size': (15000, 60000), 'label': 'BR-Disk', 'alternative': ['bd25'], 'allow': ['1080p'], 'ext':[], 'tags': ['bdmv', 'certificate', ('complete', 'bluray')]},
|
||||
{'identifier': '1080p', 'hd': True, 'size': (4000, 20000), 'label': '1080p', 'width': 1920, 'height': 1080, 'alternative': [], 'allow': [], 'ext':['mkv', 'm2ts'], 'tags': ['m2ts', 'x264', 'h264']},
|
||||
{'identifier': '720p', 'hd': True, 'size': (3000, 10000), 'label': '720p', 'width': 1280, 'height': 720, 'alternative': [], 'allow': [], 'ext':['mkv', 'ts'], 'tags': ['x264', 'h264']},
|
||||
{'identifier': 'bd50', 'hd': True, 'allow_3d': True, 'size': (15000, 60000), 'label': 'BR-Disk', 'alternative': ['bd25'], 'allow': ['1080p'], 'ext':[], 'tags': ['bdmv', 'certificate', ('complete', 'bluray')]},
|
||||
{'identifier': '1080p', 'hd': True, 'allow_3d': True, 'size': (4000, 20000), 'label': '1080p', 'width': 1920, 'height': 1080, 'alternative': [], 'allow': [], 'ext':['mkv', 'm2ts'], 'tags': ['m2ts', 'x264', 'h264']},
|
||||
{'identifier': '720p', 'hd': True, 'allow_3d': True, 'size': (3000, 10000), 'label': '720p', 'width': 1280, 'height': 720, 'alternative': [], 'allow': [], 'ext':['mkv', 'ts'], 'tags': ['x264', 'h264']},
|
||||
{'identifier': 'brrip', 'hd': True, 'size': (700, 7000), 'label': 'BR-Rip', 'alternative': ['bdrip'], 'allow': ['720p', '1080p'], 'ext':[], 'tags': ['hdtv', 'hdrip', 'webdl', ('web', 'dl')]},
|
||||
{'identifier': 'dvdr', 'size': (3000, 10000), 'label': 'DVD-R', 'alternative': ['br2dvd'], 'allow': [], 'ext':['iso', 'img', 'vob'], 'tags': ['pal', 'ntsc', 'video_ts', 'audio_ts', ('dvd', 'r')]},
|
||||
{'identifier': 'dvdrip', 'size': (600, 2400), 'label': 'DVD-Rip', 'width': 720, 'alternative': [], 'allow': [], 'ext':[], 'tags': [('dvd', 'rip'), ('dvd', 'xvid'), ('dvd', 'divx')]},
|
||||
@@ -34,6 +34,11 @@ class QualityPlugin(Plugin):
|
||||
{'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': [], 'ext':[]}
|
||||
]
|
||||
pre_releases = ['cam', 'ts', 'tc', 'r5', 'scr']
|
||||
threed_tags = {
|
||||
'hsbs': [('half', 'sbs')],
|
||||
'fsbs': [('full', 'sbs')],
|
||||
'3d': [],
|
||||
}
|
||||
|
||||
cached_qualities = None
|
||||
cached_order = None
|
||||
@@ -58,6 +63,7 @@ class QualityPlugin(Plugin):
|
||||
|
||||
addEvent('app.test', self.doTest)
|
||||
|
||||
self.order = []
|
||||
self.addOrder()
|
||||
|
||||
def addOrder(self):
|
||||
@@ -185,14 +191,19 @@ class QualityPlugin(Plugin):
|
||||
# Start with 0
|
||||
score = {}
|
||||
for quality in qualities:
|
||||
score[quality.get('identifier')] = 0
|
||||
score[quality.get('identifier')] = {
|
||||
'score': 0,
|
||||
'3d': {}
|
||||
}
|
||||
|
||||
for cur_file in files:
|
||||
words = re.split('\W+', cur_file.lower())
|
||||
|
||||
for quality in qualities:
|
||||
contains_score = self.containsTagScore(quality, words, cur_file)
|
||||
self.calcScore(score, quality, contains_score)
|
||||
threedscore = self.contains3D(quality, words, cur_file) if quality.get('allow_3d') else (0, None)
|
||||
|
||||
self.calcScore(score, quality, contains_score, threedscore)
|
||||
|
||||
# Try again with loose testing
|
||||
for quality in qualities:
|
||||
@@ -208,10 +219,13 @@ class QualityPlugin(Plugin):
|
||||
if not has_non_zero:
|
||||
return None
|
||||
|
||||
heighest_quality = max(score, key = score.get)
|
||||
heighest_quality = max(score, key = lambda p: score[p]['score'])
|
||||
if heighest_quality:
|
||||
for quality in qualities:
|
||||
if quality.get('identifier') == heighest_quality:
|
||||
quality['is_3d'] = False
|
||||
if score[heighest_quality].get('3d'):
|
||||
quality['is_3d'] = True
|
||||
return self.setCache(cache_key, quality)
|
||||
|
||||
return None
|
||||
@@ -234,12 +248,12 @@ class QualityPlugin(Plugin):
|
||||
qualities = [qualities] if isinstance(qualities, (str, unicode)) else qualities
|
||||
|
||||
for alt in qualities:
|
||||
if (isinstance(alt, tuple)):
|
||||
if isinstance(alt, tuple):
|
||||
if len(set(words) & set(alt)) == len(alt):
|
||||
log.debug('Found %s via %s %s in %s', (quality['identifier'], tag_type, quality.get(tag_type), cur_file))
|
||||
score += points.get(tag_type)
|
||||
|
||||
if (isinstance(alt, (str, unicode)) and ss(alt.lower()) in cur_file.lower()):
|
||||
if isinstance(alt, (str, unicode)) and ss(alt.lower()) in cur_file.lower():
|
||||
log.debug('Found %s via %s %s in %s', (quality['identifier'], tag_type, quality.get(tag_type), cur_file))
|
||||
score += points.get(tag_type) / 2
|
||||
|
||||
@@ -255,6 +269,23 @@ class QualityPlugin(Plugin):
|
||||
|
||||
return score
|
||||
|
||||
def contains3D(self, quality, words, cur_file = ''):
|
||||
cur_file = ss(cur_file)
|
||||
|
||||
for key in self.threed_tags:
|
||||
tags = self.threed_tags.get(key, [])
|
||||
|
||||
for tag in tags:
|
||||
if (isinstance(tag, tuple) and '.'.join(tag) in '.'.join(words)) or (isinstance(tag, (str, unicode)) and ss(tag.lower()) in cur_file.lower()):
|
||||
log.debug('Found %s in %s', (tag, cur_file))
|
||||
return 1, key
|
||||
|
||||
if list(set([key]) & set(words)):
|
||||
log.debug('Found %s in %s', (tag, cur_file))
|
||||
return 1, key
|
||||
|
||||
return 0, None
|
||||
|
||||
def guessLooseScore(self, quality, extra = None):
|
||||
|
||||
score = 0
|
||||
@@ -277,9 +308,16 @@ class QualityPlugin(Plugin):
|
||||
|
||||
return score
|
||||
|
||||
def calcScore(self, score, quality, add_score):
|
||||
def calcScore(self, score, quality, add_score, threedscore = (0, None)):
|
||||
|
||||
score[quality['identifier']] += add_score
|
||||
score[quality['identifier']]['score'] += add_score
|
||||
|
||||
threedscore, threedtag = threedscore
|
||||
if threedscore and threedtag:
|
||||
if threedscore not in score[quality['identifier']]['3d']:
|
||||
score[quality['identifier']]['3d'][threedtag] = 0
|
||||
|
||||
score[quality['identifier']]['3d'][threedtag] += threedscore
|
||||
|
||||
# Set order for allow calculation (and cache)
|
||||
if not self.cached_order:
|
||||
@@ -289,7 +327,7 @@ class QualityPlugin(Plugin):
|
||||
|
||||
if add_score != 0:
|
||||
for allow in quality.get('allow', []):
|
||||
score[allow] -= 40 if self.cached_order[allow] < self.cached_order[quality['identifier']] else 5
|
||||
score[allow]['score'] -= 40 if self.cached_order[allow] < self.cached_order[quality['identifier']] else 5
|
||||
|
||||
def doTest(self):
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ var QualityBase = new Class({
|
||||
|
||||
self.qualities = data.qualities;
|
||||
|
||||
self.profiles = []
|
||||
self.profiles = [];
|
||||
Array.each(data.profiles, self.createProfilesClass.bind(self));
|
||||
|
||||
App.addEvent('load', self.addSettings.bind(self))
|
||||
@@ -37,7 +37,7 @@ var QualityBase = new Class({
|
||||
addSettings: function(){
|
||||
var self = this;
|
||||
|
||||
self.settings = App.getPage('Settings')
|
||||
self.settings = App.getPage('Settings');
|
||||
self.settings.addEvent('create', function(){
|
||||
var tab = self.settings.createSubTab('profile', {
|
||||
'label': 'Quality',
|
||||
@@ -91,9 +91,9 @@ var QualityBase = new Class({
|
||||
createProfilesClass: function(data){
|
||||
var self = this;
|
||||
|
||||
var data = data || {'id': randomString()}
|
||||
var profile = new Profile(data)
|
||||
self.profiles.include(profile)
|
||||
var data = data || {'id': randomString()};
|
||||
var profile = new Profile(data);
|
||||
self.profiles.include(profile);
|
||||
|
||||
return profile;
|
||||
},
|
||||
@@ -102,7 +102,7 @@ var QualityBase = new Class({
|
||||
var self = this;
|
||||
|
||||
var profile_list;
|
||||
var group = self.settings.createGroup({
|
||||
self.settings.createGroup({
|
||||
'label': 'Profile Defaults',
|
||||
'description': '(Needs refresh \'' +(App.isMac() ? 'CMD+R' : 'F5')+ '\' after editing)'
|
||||
}).adopt(
|
||||
@@ -113,7 +113,7 @@ var QualityBase = new Class({
|
||||
'html': 'Change the order the profiles are in the dropdown list. Uncheck to hide it completely.<br />First one will be default.'
|
||||
})
|
||||
)
|
||||
).inject(self.content)
|
||||
).inject(self.content);
|
||||
|
||||
Array.each(self.profiles, function(profile){
|
||||
var check;
|
||||
@@ -175,14 +175,14 @@ var QualityBase = new Class({
|
||||
'description': 'Edit the minimal and maximum sizes (in MB) for each quality.',
|
||||
'advanced': true,
|
||||
'name': 'sizes'
|
||||
}).inject(self.content)
|
||||
}).inject(self.content);
|
||||
|
||||
|
||||
new Element('div.item.head.ctrlHolder').adopt(
|
||||
new Element('span.label', {'text': 'Quality'}),
|
||||
new Element('span.min', {'text': 'Min'}),
|
||||
new Element('span.max', {'text': 'Max'})
|
||||
).inject(group)
|
||||
).inject(group);
|
||||
|
||||
Array.each(self.qualities, function(quality){
|
||||
new Element('div.ctrlHolder.item').adopt(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user