Merge branch 'refs/heads/develop' into tv
This commit is contained in:
@@ -1,25 +1,36 @@
|
||||
## Got a issue/feature request or submitting a pull request?
|
||||
# Contributing to CouchPotatoServer
|
||||
|
||||
Make sure you think of the following things:
|
||||
1. [Contributing](#contributing)
|
||||
2. [Submitting an Issue](#issues)
|
||||
3. [Submitting a Pull Request](#pull-requests)
|
||||
|
||||
## Issue
|
||||
* Search through the existing (and closed) issues first, see if you can get your answer there.
|
||||
* Double check the result manually, because it could be an external issue.
|
||||
* Post logs! Without seeing what is going on, I can't reproduce the error.
|
||||
* Also check the logs before submitting, obvious errors like permission or http errors are often not related to CP.
|
||||
* What is the movie + quality you are searching for?
|
||||
* What are you're settings for the specific problem?
|
||||
* What providers are you using? (While you're logs include these, scanning through hundred of lines of log isn't our hobby)
|
||||
* Post the logs from config directory, please do not copy paste the UI. Use pastebin to store these logs!
|
||||
## Contributing
|
||||
Thank you for your interest in contributing to CouchPotato. There are several ways to help out, even if you've never worked on an open source project before.
|
||||
If you've found a bug or want to request a feature, you can report it by [posting an issue](https://github.com/RuudBurger/CouchPotatoServer/issues/new) - be sure to read the [guidelines](#issues) first!
|
||||
If you want to contribute your own work, please read the [guidelines](#pull-requests) for submitting a pull request.
|
||||
Lastly, for anything related to CouchPotato, feel free to stop by the [forum](http://couchpota.to/forum/) or the [#couchpotato](http://webchat.freenode.net/?channels=couchpotato) IRC channel at irc.freenode.net.
|
||||
|
||||
## Issues
|
||||
Issues are intended for reporting bugs and weird behaviour or suggesting improvements to CouchPotatoServer.
|
||||
Before you submit an issue, please go through the following checklist:
|
||||
* Search through existing issues (*including closed issues!*) first: you might be able to get your answer there.
|
||||
* Double check your issue manually, because it could be an external issue.
|
||||
* Post logs with your issue: Without seeing what is going on, the developers can't reproduce the error.
|
||||
* Check the logs yourself before submitting them. Obvious errors like permission or HTTP errors are often not related to CouchPotato.
|
||||
* What movie and quality are you searching for?
|
||||
* What are your settings for the specific problem?
|
||||
* What providers are you using? (While your logs include these, scanning through hundreds of lines of logs isn't our hobby)
|
||||
* Post the logs from the *config* directory, please do not copy paste the UI. Use pastebin to store these logs!
|
||||
* Give a short step by step of how to reproduce the error.
|
||||
* What hardware / OS are you using and what are the limits? NAS can be slow and maybe have a different python installed then when you use CP on OSX or Windows for example.
|
||||
* I will mark issues with the "can't reproduce" tag. Don't go asking "why closed" if it clearly says the issue in the tag ;)
|
||||
* If you're running on a NAS (QNAP, Austor etc..) with pre-made packages, make sure these are setup to use our source repo (RuudBurger/CouchPotatoServer) and nothing else!!
|
||||
* What hardware / OS are you using and what are its limitations? For example: NAS can be slow and maybe have a different version of python installed then when you use CP on OSX or Windows.
|
||||
* Your issue might be marked with the "can't reproduce" tag. Don't ask why your issue was closed if it says so in the tag.
|
||||
* If you're running on a NAS (QNAP, Austor etc..) with pre-made packages, make sure these are set up to use our source repository (RuudBurger/CouchPotatoServer) and nothing else!!
|
||||
|
||||
The more relevant information you can provide, the more likely it is the issue will be resolved rather than closed.
|
||||
|
||||
## Pull Request
|
||||
* Make sure you're pull request is made for develop branch (or relevant feature branch)
|
||||
## Pull Requests
|
||||
Pull requests are intended for contributing code or documentation to the project. Before you submit a pull request, consider the following:
|
||||
* Make sure your pull request is made for the *develop* branch (or relevant feature branch).
|
||||
* Have you tested your PR? If not, why?
|
||||
* Are there any limitations of your PR we should know of?
|
||||
* Make sure to keep you're PR up-to-date with the branch you're trying to push into.
|
||||
|
||||
**If we don't get enough info, the chance of the issue getting closed is a lot bigger ;)**
|
||||
* Does your PR have any limitations we should know of?
|
||||
* Is your PR up-to-date with the branch you're trying to push into?
|
||||
|
||||
@@ -48,10 +48,6 @@ def addView(route, func, static = False):
|
||||
views[route] = func
|
||||
|
||||
|
||||
def get_session():
|
||||
return None
|
||||
|
||||
|
||||
def get_db():
|
||||
return Env.get('db')
|
||||
|
||||
|
||||
@@ -279,8 +279,8 @@ class Database(object):
|
||||
|
||||
quality = db.get('id', q_id)
|
||||
quality['order'] = q.get('order')
|
||||
quality['size_min'] = q.get('size_min')
|
||||
quality['size_max'] = q.get('size_max')
|
||||
quality['size_min'] = tryInt(q.get('size_min'))
|
||||
quality['size_max'] = tryInt(q.get('size_max'))
|
||||
db.update(quality)
|
||||
|
||||
quality_link[x] = quality
|
||||
|
||||
@@ -210,7 +210,7 @@ class ReleaseDownloadList(list):
|
||||
'status': 'busy',
|
||||
'downloader': self.provider.getName(),
|
||||
'folder': '',
|
||||
'files': '',
|
||||
'files': [],
|
||||
}
|
||||
|
||||
return mergeDicts(defaults, result)
|
||||
|
||||
@@ -147,7 +147,7 @@ class Deluge(Downloader):
|
||||
'seed_ratio': torrent['ratio'],
|
||||
'timeleft': str(timedelta(seconds = torrent['eta'])),
|
||||
'folder': sp(download_dir if len(torrent_files) == 1 else os.path.join(download_dir, torrent['name'])),
|
||||
'files': '|'.join(torrent_files),
|
||||
'files': torrent_files,
|
||||
})
|
||||
|
||||
return release_downloads
|
||||
|
||||
@@ -135,7 +135,7 @@ class qBittorrent(Downloader):
|
||||
'original_status': torrent.state,
|
||||
'timeleft': torrent.progress * 100 if torrent.progress else -1, # percentage
|
||||
'folder': sp(torrent.save_path),
|
||||
'files': '|'.join(torrent_files)
|
||||
'files': torrent_files
|
||||
})
|
||||
|
||||
return release_downloads
|
||||
|
||||
@@ -59,20 +59,22 @@ class rTorrent(Downloader):
|
||||
return self.rt
|
||||
|
||||
url = cleanHost(self.conf('host'), protocol = True, ssl = self.conf('ssl'))
|
||||
|
||||
# Automatically add '+https' to 'httprpc' protocol if SSL is enabled
|
||||
if self.conf('ssl') and url.startswith('httprpc://'):
|
||||
url = url.replace('httprpc://', 'httprpc+https://')
|
||||
|
||||
parsed = urlparse(url)
|
||||
|
||||
# rpc_url is only used on http/https scgi pass-through
|
||||
if parsed.scheme in ['http', 'https']:
|
||||
url += self.conf('rpc_url')
|
||||
|
||||
if self.conf('username') and self.conf('password'):
|
||||
self.rt = RTorrent(
|
||||
url,
|
||||
self.conf('username'),
|
||||
self.conf('password')
|
||||
)
|
||||
else:
|
||||
self.rt = RTorrent(url)
|
||||
self.rt = RTorrent(
|
||||
url,
|
||||
self.conf('username'),
|
||||
self.conf('password')
|
||||
)
|
||||
|
||||
self.error_msg = ''
|
||||
try:
|
||||
@@ -243,7 +245,7 @@ class rTorrent(Downloader):
|
||||
'original_status': torrent.state,
|
||||
'timeleft': str(timedelta(seconds = float(torrent.left_bytes) / torrent.down_rate)) if torrent.down_rate > 0 else -1,
|
||||
'folder': sp(torrent.directory),
|
||||
'files': '|'.join(torrent_files)
|
||||
'files': torrent_files
|
||||
})
|
||||
|
||||
return release_downloads
|
||||
|
||||
@@ -33,7 +33,7 @@ class Synology(Downloader):
|
||||
|
||||
try:
|
||||
# Send request to Synology
|
||||
srpc = SynologyRPC(host[0], host[1], self.conf('username'), self.conf('password'))
|
||||
srpc = SynologyRPC(host[0], host[1], self.conf('username'), self.conf('password'), self.conf('destination'))
|
||||
if data['protocol'] == 'torrent_magnet':
|
||||
log.info('Adding torrent URL %s', data['url'])
|
||||
response = srpc.create_task(url = data['url'])
|
||||
@@ -84,7 +84,7 @@ class SynologyRPC(object):
|
||||
|
||||
"""SynologyRPC lite library"""
|
||||
|
||||
def __init__(self, host = 'localhost', port = 5000, username = None, password = None):
|
||||
def __init__(self, host = 'localhost', port = 5000, username = None, password = None, destination = None):
|
||||
|
||||
super(SynologyRPC, self).__init__()
|
||||
|
||||
@@ -92,6 +92,7 @@ class SynologyRPC(object):
|
||||
self.auth_url = 'http://%s:%s/webapi/auth.cgi' % (host, port)
|
||||
self.username = username
|
||||
self.password = password
|
||||
self.destination = destination
|
||||
self.session_name = 'DownloadStation'
|
||||
|
||||
def _login(self):
|
||||
@@ -144,6 +145,10 @@ class SynologyRPC(object):
|
||||
'version': '1',
|
||||
'method': 'create',
|
||||
'_sid': self.sid}
|
||||
|
||||
if self.destination and len(self.destination) > 0:
|
||||
args['destination'] = self.destination
|
||||
|
||||
if url:
|
||||
log.info('Login success, adding torrent URI')
|
||||
args['uri'] = url
|
||||
@@ -195,6 +200,11 @@ config = [{
|
||||
'name': 'password',
|
||||
'type': 'password',
|
||||
},
|
||||
{
|
||||
'name': 'destination',
|
||||
'description': 'Specify <strong>existing</strong> destination share to where your files will be downloaded, usually <strong>Downloads</strong>',
|
||||
'advanced': True,
|
||||
},
|
||||
{
|
||||
'name': 'use_for',
|
||||
'label': 'Use for',
|
||||
|
||||
@@ -141,7 +141,7 @@ class Transmission(Downloader):
|
||||
'seed_ratio': torrent['uploadRatio'],
|
||||
'timeleft': str(timedelta(seconds = torrent['eta'])),
|
||||
'folder': sp(torrent_folder if len(torrent_files) == 1 else os.path.join(torrent_folder, torrent['name'])),
|
||||
'files': '|'.join(torrent_files)
|
||||
'files': torrent_files
|
||||
})
|
||||
|
||||
return release_downloads
|
||||
|
||||
@@ -184,7 +184,7 @@ class uTorrent(Downloader):
|
||||
'original_status': torrent[1],
|
||||
'timeleft': str(timedelta(seconds = torrent[10])),
|
||||
'folder': sp(torrent[26]),
|
||||
'files': '|'.join(torrent_files)
|
||||
'files': torrent_files
|
||||
})
|
||||
|
||||
return release_downloads
|
||||
|
||||
@@ -227,6 +227,10 @@ def toIterable(value):
|
||||
return [value]
|
||||
|
||||
|
||||
def getIdentifier(media):
|
||||
return media.get('identifier') or media.get('identifiers', {}).get('imdb')
|
||||
|
||||
|
||||
def getTitle(media_dict):
|
||||
try:
|
||||
try:
|
||||
@@ -241,10 +245,10 @@ def getTitle(media_dict):
|
||||
try:
|
||||
return media_dict['media']['info']['titles'][0]
|
||||
except:
|
||||
log.error('Could not get title for %s', media_dict.get('identifier'))
|
||||
log.error('Could not get title for %s', getIdentifier(media_dict))
|
||||
return None
|
||||
|
||||
log.error('Could not get title for %s', media_dict['identifier'])
|
||||
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)
|
||||
@@ -293,7 +297,7 @@ def isSubFolder(sub_folder, 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'(.+){{([^{}]+)}}$'), re.compile(r'(.+)\s+password\s*=\s*(.+)$', re.I)]
|
||||
re_password = [re.compile(r'(.+){{([^{}]+)}}$'), re.compile(r'(.+)\s+password\s*=\s*(.+)$', re.I)]
|
||||
def scanForPassword(name):
|
||||
m = None
|
||||
for reg in re_password:
|
||||
|
||||
@@ -25,7 +25,7 @@ class MediaBase(Plugin):
|
||||
def onComplete():
|
||||
try:
|
||||
db = get_db()
|
||||
media = db.get('id', media_id)
|
||||
media = fireEvent('media.get', media_id, single = True)
|
||||
event_name = '%s.searcher.single' % media.get('type')
|
||||
|
||||
fireEventAsync(event_name, media, on_complete = self.createNotifyFront(media_id))
|
||||
@@ -38,8 +38,7 @@ class MediaBase(Plugin):
|
||||
|
||||
def notifyFront():
|
||||
try:
|
||||
db = get_db()
|
||||
media = db.get('id', media_id)
|
||||
media = fireEvent('media.get', media_id, single = True)
|
||||
event_name = '%s.update' % media.get('type')
|
||||
|
||||
fireEvent('notify.frontend', type = event_name, data = media)
|
||||
|
||||
@@ -6,7 +6,7 @@ from couchpotato.core.helpers.encoding import toUnicode, simplifyString
|
||||
|
||||
|
||||
class MediaIndex(MultiTreeBasedIndex):
|
||||
_version = 2
|
||||
_version = 3
|
||||
|
||||
custom_header = """from CodernityDB.tree_index import MultiTreeBasedIndex"""
|
||||
|
||||
@@ -26,31 +26,10 @@ class MediaIndex(MultiTreeBasedIndex):
|
||||
|
||||
ids = []
|
||||
for x in identifiers:
|
||||
ids.append(md5('%s-%s' % (x, data['identifiers'][x])).hexdigest())
|
||||
ids.append(md5('%s-%s' % (x, identifiers[x])).hexdigest())
|
||||
|
||||
return ids, None
|
||||
|
||||
def run_to_dict(self, db, media_id, dict_dept = None):
|
||||
if not dict_dept: dict_dept = {}
|
||||
|
||||
return db.get('id', media_id)
|
||||
|
||||
def run_identifiers(self, db, identifiers, with_doc = False):
|
||||
for x in identifiers:
|
||||
try:
|
||||
media = db.get('media', '%s-%s' % (x, identifiers[x]), with_doc = with_doc)
|
||||
return media
|
||||
except:
|
||||
pass
|
||||
|
||||
def run_with_status(self, db, status = [], with_doc = True):
|
||||
|
||||
status = list(status if isinstance(status, (list, tuple)) else [status])
|
||||
|
||||
for s in status:
|
||||
for ms in db.get_many('media_status', s, with_doc = with_doc):
|
||||
yield ms['doc'] if with_doc else ms
|
||||
|
||||
|
||||
class MediaStatusIndex(TreeBasedIndex):
|
||||
_version = 1
|
||||
|
||||
@@ -74,6 +74,8 @@ class MediaPlugin(MediaBase):
|
||||
addEvent('app.load', self.addSingleDeleteView, priority = 100)
|
||||
|
||||
addEvent('media.get', self.get)
|
||||
addEvent('media.with_status', self.withStatus)
|
||||
addEvent('media.with_identifiers', self.withIdentifiers)
|
||||
addEvent('media.list', self.list)
|
||||
addEvent('media.delete', self.delete)
|
||||
addEvent('media.restatus', self.restatus)
|
||||
@@ -99,13 +101,10 @@ class MediaPlugin(MediaBase):
|
||||
|
||||
try:
|
||||
media = get_db().get('id', media_id)
|
||||
|
||||
|
||||
default_title = getTitle(media)
|
||||
event = '%s.update_info' % media.get('type')
|
||||
|
||||
def handler():
|
||||
fireEvent(event, identifier = media.get('identifier'), default_title = default_title, on_complete = self.createOnComplete(media_id))
|
||||
fireEvent(event, media_id = media_id, on_complete = self.createOnComplete(media_id))
|
||||
|
||||
if handler:
|
||||
return handler
|
||||
@@ -124,16 +123,21 @@ class MediaPlugin(MediaBase):
|
||||
|
||||
imdb_id = getImdb(str(media_id))
|
||||
|
||||
media = None
|
||||
if imdb_id:
|
||||
m = db.get('media', 'imdb-%s' % imdb_id, with_doc = True)['doc']
|
||||
media = db.get('media', 'imdb-%s' % imdb_id, with_doc = True)['doc']
|
||||
else:
|
||||
m = db.get('id', media_id)
|
||||
media = db.get('id', media_id)
|
||||
|
||||
results = None
|
||||
if m:
|
||||
results = db.run('media', 'to_dict', m['_id'])
|
||||
if media:
|
||||
|
||||
return results
|
||||
# Attach category
|
||||
if media.get('category_id'):
|
||||
media['category'] = db.get('id', media.get('category_id'))
|
||||
|
||||
media['releases'] = list(fireEvent('release.for_media', media['_id'], single = True))
|
||||
|
||||
return media
|
||||
|
||||
def getView(self, id = None, **kwargs):
|
||||
|
||||
@@ -144,6 +148,29 @@ class MediaPlugin(MediaBase):
|
||||
'media': media,
|
||||
}
|
||||
|
||||
def withStatus(self, status, with_doc = True):
|
||||
|
||||
db = get_db()
|
||||
|
||||
status = list(status if isinstance(status, (list, tuple)) else [status])
|
||||
|
||||
for s in status:
|
||||
for ms in db.get_many('media_status', s, with_doc = with_doc):
|
||||
yield ms['doc'] if with_doc else ms
|
||||
|
||||
def withIdentifiers(self, identifiers, with_doc = False):
|
||||
|
||||
db = get_db()
|
||||
|
||||
for x in identifiers:
|
||||
try:
|
||||
media = db.get('media', '%s-%s' % (x, identifiers[x]), with_doc = with_doc)
|
||||
return media
|
||||
except:
|
||||
pass
|
||||
|
||||
log.error('No media found with identifiers: %s', identifiers)
|
||||
|
||||
def list(self, types = None, status = None, release_status = None, status_or = False, limit_offset = None, starts_with = None, search = None):
|
||||
|
||||
db = get_db()
|
||||
@@ -170,13 +197,13 @@ class MediaPlugin(MediaBase):
|
||||
# Filter on movie status
|
||||
if status and len(status) > 0:
|
||||
filter_by['media_status'] = set()
|
||||
for media_status in db.run('media', 'with_status', status, with_doc = False):
|
||||
for media_status in fireEvent('media.with_status', status, with_doc = False, single = True):
|
||||
filter_by['media_status'].add(media_status.get('_id'))
|
||||
|
||||
# Filter on release status
|
||||
if release_status and len(release_status) > 0:
|
||||
filter_by['release_status'] = set()
|
||||
for release_status in db.run('release', 'with_status', release_status, with_doc = False):
|
||||
for release_status in fireEvent('release.with_status', release_status, with_doc = False, single = True):
|
||||
filter_by['release_status'].add(release_status.get('media_id'))
|
||||
|
||||
# Add search filters
|
||||
@@ -220,9 +247,7 @@ class MediaPlugin(MediaBase):
|
||||
offset -= 1
|
||||
continue
|
||||
|
||||
media = db.run('media', 'to_dict', media_id)
|
||||
|
||||
media['releases'] = list(db.run('release', 'for_media', media_id))
|
||||
media = fireEvent('media.get', media_id, single = True)
|
||||
|
||||
# Merge releases with movie dict
|
||||
medias.append(media)
|
||||
@@ -285,13 +310,13 @@ class MediaPlugin(MediaBase):
|
||||
# Filter on movie status
|
||||
if status and len(status) > 0:
|
||||
filter_by['media_status'] = set()
|
||||
for media_status in db.run('media', 'with_status', status, with_doc = False):
|
||||
for media_status in fireEvent('media.with_status', status, with_doc = False, single = True):
|
||||
filter_by['media_status'].add(media_status.get('_id'))
|
||||
|
||||
# Filter on release status
|
||||
if release_status and len(release_status) > 0:
|
||||
filter_by['release_status'] = set()
|
||||
for release_status in db.run('release', 'with_status', release_status, with_doc = False):
|
||||
for release_status in fireEvent('release.with_status', release_status, with_doc = False, single = True):
|
||||
filter_by['release_status'].add(release_status.get('media_id'))
|
||||
|
||||
# Filter by combining ids
|
||||
@@ -341,7 +366,7 @@ class MediaPlugin(MediaBase):
|
||||
deleted = True
|
||||
else:
|
||||
|
||||
media_releases = list(db.run('release', 'for_media', media['_id']))
|
||||
media_releases = list(fireEvent('release.for_media', media['_id'], single = True))
|
||||
|
||||
total_releases = len(media_releases)
|
||||
total_deleted = 0
|
||||
@@ -407,7 +432,7 @@ class MediaPlugin(MediaBase):
|
||||
move_to_wanted = True
|
||||
|
||||
profile = db.get('id', m['profile_id'])
|
||||
media_releases = list(db.run('release', 'for_media', m['_id']))
|
||||
media_releases = list(fireEvent('release.for_media', m['_id'], single = True))
|
||||
|
||||
for q_identifier in profile['qualities']:
|
||||
index = profile['qualities'].index(q_identifier)
|
||||
|
||||
@@ -2,7 +2,7 @@ import re
|
||||
import traceback
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from couchpotato.core.helpers.variable import tryInt
|
||||
from couchpotato.core.helpers.variable import tryInt, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.providers.torrent.base import TorrentProvider
|
||||
|
||||
@@ -22,7 +22,7 @@ class Base(TorrentProvider):
|
||||
|
||||
def _search(self, movie, quality, results):
|
||||
|
||||
data = self.getHTMLData(self.urls['search'] % (self.conf('passkey'), movie['identifier'], self.conf('only_internal')))
|
||||
data = self.getHTMLData(self.urls['search'] % (self.conf('passkey'), getIdentifier(movie), self.conf('only_internal')))
|
||||
|
||||
if data:
|
||||
try:
|
||||
|
||||
@@ -2,7 +2,7 @@ import re
|
||||
import json
|
||||
import traceback
|
||||
|
||||
from couchpotato.core.helpers.variable import tryInt
|
||||
from couchpotato.core.helpers.variable import tryInt, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.providers.torrent.base import TorrentProvider
|
||||
|
||||
@@ -44,7 +44,7 @@ class Base(TorrentProvider):
|
||||
|
||||
def _search(self, movie, quality, results):
|
||||
|
||||
match = re.match(r'tt(\d{7})', movie['identifier'])
|
||||
match = re.match(r'tt(\d{7})', getIdentifier(movie))
|
||||
|
||||
data = self._post_query(imdb = {'id': match.group(1)})
|
||||
|
||||
@@ -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': self.parseSize(result['size']),
|
||||
'size': tryInt(result['size'])/1024/1024,
|
||||
'seeders': tryInt(result['seeders']),
|
||||
'leechers': tryInt(result['leechers'])
|
||||
})
|
||||
|
||||
@@ -2,7 +2,7 @@ import re
|
||||
import traceback
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
from couchpotato.core.helpers.variable import tryInt
|
||||
from couchpotato.core.helpers.variable import tryInt, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.providers.torrent.base import TorrentMagnetProvider
|
||||
|
||||
@@ -38,7 +38,7 @@ class Base(TorrentMagnetProvider):
|
||||
|
||||
def _search(self, media, quality, results):
|
||||
|
||||
data = self.getHTMLData(self.urls['search'] % (self.getDomain(), 'm', media['identifier'].replace('tt', '')))
|
||||
data = self.getHTMLData(self.urls['search'] % (self.getDomain(), 'm', getIdentifier(media).replace('tt', '')))
|
||||
|
||||
if data:
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import time
|
||||
import traceback
|
||||
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.variable import getTitle, tryInt, mergeDicts
|
||||
from couchpotato.core.helpers.variable import getTitle, tryInt, mergeDicts, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.providers.torrent.base import TorrentProvider
|
||||
from dateutil.parser import parse
|
||||
@@ -36,7 +36,7 @@ class Base(TorrentProvider):
|
||||
params = mergeDicts(self.quality_search_params[quality_id].copy(), {
|
||||
'order_by': 'relevance',
|
||||
'order_way': 'descending',
|
||||
'searchstr': media['identifier']
|
||||
'searchstr': getIdentifier(media)
|
||||
})
|
||||
|
||||
url = '%s?json=noredirect&%s' % (self.urls['torrent'], tryUrlencode(params))
|
||||
|
||||
@@ -29,9 +29,9 @@ class Base(TorrentMagnetProvider):
|
||||
'http://pirateproxy.ca',
|
||||
'http://tpb.al',
|
||||
'http://www.tpb.gr',
|
||||
'http://nl.tpb.li',
|
||||
'http://bayproxy.me',
|
||||
'http://proxybay.eu',
|
||||
'https://www.getpirate.com',
|
||||
'http://www.getpirate.com',
|
||||
'http://piratebay.io',
|
||||
]
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import traceback
|
||||
|
||||
from couchpotato.core.helpers.variable import tryInt
|
||||
from couchpotato.core.helpers.variable import tryInt, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.providers.torrent.base import TorrentProvider
|
||||
|
||||
@@ -35,7 +35,7 @@ class Base(TorrentProvider):
|
||||
|
||||
def _search(self, movie, quality, results):
|
||||
|
||||
search_url = self.urls['search'] % (self.getDomain(), movie['identifier'], quality['identifier'])
|
||||
search_url = self.urls['search'] % (self.getDomain(), getIdentifier(movie), quality['identifier'])
|
||||
|
||||
data = self.getJsonData(search_url)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, fireEventAsync, addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.variable import splitString, getTitle, getImdb
|
||||
from couchpotato.core.helpers.variable import splitString, getTitle, getImdb, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media.movie import MovieTypeBase
|
||||
import six
|
||||
@@ -138,7 +138,7 @@ class MovieBase(MovieTypeBase):
|
||||
elif force_readd:
|
||||
|
||||
# Clean snatched history
|
||||
for release in db.run('release', 'for_media', m['_id']):
|
||||
for release in fireEvent('release.for_media', m['_id'], single = True):
|
||||
if release.get('status') in ['downloaded', 'snatched', 'done']:
|
||||
if params.get('ignore_previous', False):
|
||||
release['status'] = 'ignored'
|
||||
@@ -164,11 +164,11 @@ class MovieBase(MovieTypeBase):
|
||||
fireEventAsync('movie.update_info', m['_id'], default_title = params.get('title'), on_complete = onComplete)
|
||||
|
||||
# Remove releases
|
||||
for rel in db.run('release', 'for_media', m['_id']):
|
||||
for rel in fireEvent('release.for_media', m['_id'], single = True):
|
||||
if rel['status'] is 'available':
|
||||
db.delete(rel)
|
||||
|
||||
movie_dict = db.run('media', 'to_dict', m['_id'])
|
||||
movie_dict = fireEvent('media.get', m['_id'], single = True)
|
||||
|
||||
if do_search and search_after:
|
||||
onComplete = self.createOnComplete(m['_id'])
|
||||
@@ -215,7 +215,7 @@ class MovieBase(MovieTypeBase):
|
||||
m['category_id'] = cat_id if len(cat_id) > 0 else None
|
||||
|
||||
# Remove releases
|
||||
for rel in db.run('release', 'for_media', m['_id']):
|
||||
for rel in fireEvent('release.for_media', m['_id'], single = True):
|
||||
if rel['status'] is 'available':
|
||||
db.delete(rel)
|
||||
|
||||
@@ -229,7 +229,7 @@ class MovieBase(MovieTypeBase):
|
||||
|
||||
m = db.get('id', media_id)
|
||||
|
||||
movie_dict = db.run('media', 'to_dict', m['_id'])
|
||||
movie_dict = fireEvent('media.get', m['_id'], single = True)
|
||||
fireEventAsync('movie.searcher.single', movie_dict, on_complete = self.createNotifyFront(media_id))
|
||||
|
||||
except:
|
||||
@@ -266,7 +266,7 @@ class MovieBase(MovieTypeBase):
|
||||
else:
|
||||
media = db.get('media', 'imdb-%s' % identifier, with_doc = True)['doc']
|
||||
|
||||
info = fireEvent('movie.info', merge = True, extended = extended, identifier = media.get('identifier'))
|
||||
info = fireEvent('movie.info', merge = True, extended = extended, identifier = getIdentifier(media))
|
||||
|
||||
# Don't need those here
|
||||
try: del info['in_wanted']
|
||||
@@ -275,7 +275,7 @@ class MovieBase(MovieTypeBase):
|
||||
except: pass
|
||||
|
||||
if not info or len(info) == 0:
|
||||
log.error('Could not update, no movie info to work with: %s', media.get('identifier'))
|
||||
log.error('Could not update, no movie info to work with: %s', identifier)
|
||||
return False
|
||||
|
||||
# Update basic info
|
||||
@@ -285,19 +285,20 @@ class MovieBase(MovieTypeBase):
|
||||
log.debug('Adding titles: %s', titles)
|
||||
|
||||
# Define default title
|
||||
def_title = None
|
||||
if default_title:
|
||||
counter = 0
|
||||
for title in titles:
|
||||
if title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == six.u('') and toUnicode(titles[0]) == title):
|
||||
def_title = toUnicode(title)
|
||||
break
|
||||
counter += 1
|
||||
def_title = None
|
||||
if default_title:
|
||||
counter = 0
|
||||
for title in titles:
|
||||
if title.lower() == toUnicode(default_title.lower()) or (toUnicode(default_title) == six.u('') and toUnicode(titles[0]) == title):
|
||||
def_title = toUnicode(title)
|
||||
break
|
||||
counter += 1
|
||||
|
||||
if not def_title:
|
||||
def_title = toUnicode(titles[0])
|
||||
if not def_title:
|
||||
def_title = toUnicode(titles[0])
|
||||
|
||||
media['title'] = def_title
|
||||
media['title'] = def_title
|
||||
|
||||
# Files
|
||||
images = info.get('images', [])
|
||||
@@ -357,7 +358,7 @@ class MovieBase(MovieTypeBase):
|
||||
dates = media.get('info').get('release_date')
|
||||
|
||||
if dates and (dates.get('expires', 0) < time.time() or dates.get('expires', 0) > time.time() + (604800 * 4)) or not dates:
|
||||
dates = fireEvent('movie.info.release_date', identifier = media['identifier'], merge = True)
|
||||
dates = fireEvent('movie.info.release_date', identifier = getIdentifier(media), merge = True)
|
||||
media['info'].update({'release_date': dates})
|
||||
db.update(media)
|
||||
|
||||
|
||||
@@ -428,7 +428,7 @@ MA.Release = new Class({
|
||||
|
||||
Api.request('movie.searcher.try_next', {
|
||||
'data': {
|
||||
'id': self.movie.get('_id')
|
||||
'media_id': self.movie.get('_id')
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ class ITunes(Automation, RSS):
|
||||
urls = splitString(self.conf('automation_urls'))
|
||||
|
||||
namespace = 'http://www.w3.org/2005/Atom'
|
||||
namespace_im = 'https://rss.itunes.apple.com'
|
||||
namespace_im = 'http://itunes.apple.com/rss'
|
||||
|
||||
index = -1
|
||||
for url in urls:
|
||||
|
||||
@@ -3,7 +3,7 @@ import traceback
|
||||
|
||||
from CodernityDB.database import RecordNotFound
|
||||
from couchpotato import get_db
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.variable import mergeDicts, randomString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
@@ -104,7 +104,7 @@ class MovieResultModifier(Plugin):
|
||||
if media.get('status') == 'active':
|
||||
temp['in_wanted'] = media
|
||||
|
||||
for release in db.run('release', 'for_media', media.get('_id')):
|
||||
for release in fireEvent('release.for_media', media['_id'], single = True):
|
||||
if release.get('status') == 'done':
|
||||
if not temp['in_library']:
|
||||
temp['in_library'] = media
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.variable import getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.providers.nzb.binsearch import Base
|
||||
from couchpotato.core.media.movie.providers.base import MovieProvider
|
||||
@@ -13,7 +14,7 @@ class BinSearch(MovieProvider, Base):
|
||||
|
||||
def buildUrl(self, media, quality):
|
||||
query = tryUrlencode({
|
||||
'q': media['identifier'],
|
||||
'q': getIdentifier(media),
|
||||
'm': 'n',
|
||||
'max': 400,
|
||||
'adv_age': Env.setting('retention', 'nzb'),
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.variable import getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.providers.nzb.newznab import Base
|
||||
from couchpotato.core.media.movie.providers.base import MovieProvider
|
||||
@@ -13,7 +14,7 @@ class Newznab(MovieProvider, Base):
|
||||
def buildUrl(self, media, api_key):
|
||||
query = tryUrlencode({
|
||||
't': 'movie',
|
||||
'imdbid': media['identifier'].replace('tt', ''),
|
||||
'imdbid': getIdentifier(media).replace('tt', ''),
|
||||
'apikey': api_key,
|
||||
'extended': 1
|
||||
})
|
||||
|
||||
@@ -14,7 +14,7 @@ class NzbIndex(MovieProvider, Base):
|
||||
|
||||
def buildUrl(self, media, quality):
|
||||
title = fireEvent('library.query', media, include_year = False, single = True)
|
||||
year = media['year']
|
||||
year = media['info']['year']
|
||||
|
||||
query = tryUrlencode({
|
||||
'q': '"%s %s" | "%s (%s)"' % (title, year, title, year),
|
||||
|
||||
@@ -21,7 +21,7 @@ class Bitsoup(MovieProvider, Base):
|
||||
query = tryUrlencode({
|
||||
'search': '"%s" %s' % (
|
||||
fireEvent('library.query', media, include_year = False, single = True),
|
||||
media['year']
|
||||
media['info']['year']
|
||||
),
|
||||
'cat': self.getCatId(quality['identifier'])[0],
|
||||
})
|
||||
|
||||
@@ -17,6 +17,6 @@ class IPTorrents(MovieProvider, Base):
|
||||
]
|
||||
|
||||
def buildUrl(self, title, media, quality):
|
||||
query = '%s %s' % (title.replace(':', ''), media['year'])
|
||||
query = '%s %s' % (title.replace(':', ''), media['info']['year'])
|
||||
|
||||
return self._buildUrl(query, quality['identifier'])
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
from couchpotato.core.helpers.encoding import tryUrlencode
|
||||
from couchpotato.core.helpers.variable import getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.providers.torrent.torrentpotato import Base
|
||||
from couchpotato.core.media.movie.providers.base import MovieProvider
|
||||
@@ -14,6 +15,6 @@ class TorrentPotato(MovieProvider, Base):
|
||||
arguments = tryUrlencode({
|
||||
'user': host['name'],
|
||||
'passkey': host['pass_key'],
|
||||
'imdbid': media['identifier']
|
||||
'imdbid': getIdentifier(media),
|
||||
})
|
||||
return '%s?%s' % (host['host'], arguments)
|
||||
|
||||
@@ -8,7 +8,7 @@ from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import simplifyString
|
||||
from couchpotato.core.helpers.variable import getTitle, possibleTitles, getImdb
|
||||
from couchpotato.core.helpers.variable import getTitle, possibleTitles, getImdb, getIdentifier, tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media._base.searcher.base import SearcherBase
|
||||
from couchpotato.core.media.movie import MovieTypeBase
|
||||
@@ -38,7 +38,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
addApiView('movie.searcher.try_next', self.tryNextReleaseView, docs = {
|
||||
'desc': 'Marks the snatched results as ignored and try the next best release',
|
||||
'params': {
|
||||
'id': {'desc': 'The id of the movie'},
|
||||
'media_id': {'desc': 'The id of the media'},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -74,9 +74,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
self.in_progress = True
|
||||
fireEvent('notify.frontend', type = 'movie.searcher.started', data = True, message = 'Full search started')
|
||||
|
||||
db = get_db()
|
||||
|
||||
medias = [x['_id'] for x in db.run('media', 'with_status', 'active', with_doc = False)]
|
||||
medias = [x['_id'] for x in fireEvent('media.with_status', 'active', with_doc = False, single = True)]
|
||||
random.shuffle(medias)
|
||||
|
||||
total = len(medias)
|
||||
@@ -90,15 +88,15 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
|
||||
for media_id in medias:
|
||||
|
||||
media = db.run('media', 'to_dict', media_id)
|
||||
media = fireEvent('media.get', media_id, single = True)
|
||||
|
||||
try:
|
||||
self.single(media, search_protocols)
|
||||
except IndexError:
|
||||
log.error('Forcing library update for %s, if you see this often, please report: %s', (media['identifier'], traceback.format_exc()))
|
||||
log.error('Forcing library update for %s, if you see this often, please report: %s', (getIdentifier(media), traceback.format_exc()))
|
||||
fireEvent('movie.update_info', media_id)
|
||||
except:
|
||||
log.error('Search failed for %s: %s', (media['identifier'], traceback.format_exc()))
|
||||
log.error('Search failed for %s: %s', (getIdentifier(media), traceback.format_exc()))
|
||||
|
||||
self.in_progress['to_go'] -= 1
|
||||
|
||||
@@ -142,7 +140,6 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
|
||||
profile = db.get('id', movie['profile_id'])
|
||||
quality_order = fireEvent('quality.order', single = True)
|
||||
media_releases = db.run('release', 'for_media', movie['_id'])
|
||||
|
||||
ret = False
|
||||
|
||||
@@ -161,7 +158,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
has_better_quality = 0
|
||||
|
||||
# See if better quality is available
|
||||
for release in media_releases:
|
||||
for release in movie.get('releases', []):
|
||||
if quality_order.index(release['quality']) <= quality_order.index(q_identifier) and release['status'] not in ['available', 'ignored', 'failed']:
|
||||
has_better_quality += 1
|
||||
|
||||
@@ -187,7 +184,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
ret = True
|
||||
|
||||
# Remove releases that aren't found anymore
|
||||
for release in db.run('release', 'for_media', movie['_id']):
|
||||
for release in movie.get('releases', []):
|
||||
if release.get('status') == 'available' and release.get('identifier') not in found_releases:
|
||||
fireEvent('release.delete', release.get('_id'), single = True)
|
||||
|
||||
@@ -233,12 +230,12 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
|
||||
|
||||
# File to small
|
||||
if nzb['size'] and preferred_quality['size_min'] > nzb['size']:
|
||||
if nzb['size'] and tryInt(preferred_quality['size_min']) > tryInt(nzb['size']):
|
||||
log.info2('Wrong: "%s" is too small to be %s. %sMB instead of the minimal of %sMB.', (nzb['name'], preferred_quality['label'], nzb['size'], preferred_quality['size_min']))
|
||||
return False
|
||||
|
||||
# File to large
|
||||
if nzb['size'] and preferred_quality.get('size_max') < nzb['size']:
|
||||
if nzb['size'] and tryInt(preferred_quality['size_max']) < tryInt(nzb['size']):
|
||||
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
|
||||
|
||||
@@ -257,7 +254,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
return True
|
||||
|
||||
# Check if nzb contains imdb link
|
||||
if getImdb(nzb.get('description', '')) == media['identifier']:
|
||||
if getImdb(nzb.get('description', '')) == getIdentifier(media):
|
||||
return True
|
||||
|
||||
for raw_title in media['info']['titles']:
|
||||
@@ -317,9 +314,9 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
|
||||
return False
|
||||
|
||||
def tryNextReleaseView(self, id = None, **kwargs):
|
||||
def tryNextReleaseView(self, media_id = None, **kwargs):
|
||||
|
||||
trynext = self.tryNextRelease(id, manual = True)
|
||||
trynext = self.tryNextRelease(media_id, manual = True)
|
||||
|
||||
return {
|
||||
'success': trynext
|
||||
@@ -329,13 +326,13 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
|
||||
try:
|
||||
db = get_db()
|
||||
rels = db.run('media', 'with_status', media_id, status = ['snatched', 'done'])
|
||||
rels = fireEvent('media.with_status', ['snatched', 'done'], single = True)
|
||||
|
||||
for rel in rels:
|
||||
rel['status'] = 'ignored'
|
||||
db.update(rel)
|
||||
|
||||
movie_dict = db.run('media', 'to_dict', media_id)
|
||||
movie_dict = fireEvent('media.get', media_id, single = True)
|
||||
log.info('Trying next release for: %s', getTitle(movie_dict))
|
||||
self.single(movie_dict, manual = manual)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent
|
||||
from couchpotato.core.helpers.variable import splitString, removeDuplicate
|
||||
from couchpotato.core.helpers.variable import splitString, removeDuplicate, getIdentifier
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
|
||||
@@ -28,9 +28,8 @@ class Suggestion(Plugin):
|
||||
else:
|
||||
|
||||
if not movies or len(movies) == 0:
|
||||
db = get_db()
|
||||
active_movies = db.run('media', 'with_status', ['active', 'done'])
|
||||
movies = [x['identifier'] for x in active_movies]
|
||||
active_movies = fireEvent('media.with_status', ['active', 'done'], single = True)
|
||||
movies = [getIdentifier(x) for x in active_movies]
|
||||
|
||||
if not ignored or len(ignored) == 0:
|
||||
ignored = splitString(Env.prop('suggest_ignore', default = ''))
|
||||
@@ -86,8 +85,8 @@ class Suggestion(Plugin):
|
||||
# Get new results and add them
|
||||
if len(new_suggestions) - 1 < limit:
|
||||
db = get_db()
|
||||
active_movies = db.run('media', 'with_status', ['active', 'done'])
|
||||
movies = [x['identifier'] for x in active_movies]
|
||||
active_movies = fireEvent('media.with_status', ['active', 'done'], single = True)
|
||||
movies = [getIdentifier(x) for x in active_movies]
|
||||
movies.extend(seen)
|
||||
|
||||
ignored.extend([x.get('imdb') for x in cached_suggestion])
|
||||
|
||||
@@ -7,6 +7,8 @@ from couchpotato.core.notifications.base import Notification
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'Boxcar'
|
||||
|
||||
|
||||
class Boxcar(Notification):
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ class Boxcar2(Notification):
|
||||
|
||||
data = {
|
||||
'user_credentials': self.conf('token'),
|
||||
'notification[title]': toUnicode(message),
|
||||
'notification[title]': toUnicode('%s - %s' % (self.default_title, message)),
|
||||
'notification[long_message]': toUnicode(long_message),
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ from couchpotato.environment import Env
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'Email'
|
||||
|
||||
|
||||
class Email(Notification):
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ from gntp import notifier
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'Growl'
|
||||
|
||||
|
||||
class Growl(Notification):
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ except ImportError:
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'NMJ'
|
||||
|
||||
|
||||
class NMJ(Notification):
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import six
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'NotifyMyAndroid'
|
||||
|
||||
|
||||
class NotifyMyAndroid(Notification):
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import six
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'NotifyMyWP'
|
||||
|
||||
|
||||
class NotifyMyWP(Notification):
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ from couchpotato.core.notifications.base import Notification
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'Prowl'
|
||||
|
||||
|
||||
class Prowl(Notification):
|
||||
|
||||
|
||||
@@ -7,6 +7,8 @@ from couchpotato.core.notifications.base import Notification
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'Pushalot'
|
||||
|
||||
|
||||
class Pushalot(Notification):
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ from couchpotato.core.notifications.base import Notification
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
autoload = 'Pushbullet'
|
||||
|
||||
|
||||
class Pushbullet(Notification):
|
||||
|
||||
|
||||
@@ -7,11 +7,10 @@ import time
|
||||
import traceback
|
||||
import urllib2
|
||||
|
||||
from couchpotato import get_db
|
||||
from couchpotato.core.event import fireEvent, addEvent
|
||||
from couchpotato.core.helpers.encoding import ss, toSafeString, \
|
||||
toUnicode, sp
|
||||
from couchpotato.core.helpers.variable import getExt, md5, isLocalIP, scanForPassword, tryInt
|
||||
from couchpotato.core.helpers.variable import getExt, md5, isLocalIP, scanForPassword, tryInt, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.environment import Env
|
||||
import requests
|
||||
@@ -327,19 +326,22 @@ class Plugin(object):
|
||||
if name_password:
|
||||
release_name, password = name_password
|
||||
tag += '{{%s}}' % password
|
||||
elif data.get('password'):
|
||||
tag += '{{%s}}' % data.get('password')
|
||||
|
||||
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):
|
||||
name = sp(os.path.join(self.createNzbName(data, media)))
|
||||
name = self.createNzbName(data, media)
|
||||
if data.get('protocol') == 'nzb' and 'DOCTYPE nzb' not in filedata and '</nzb>' not in filedata:
|
||||
return '%s.%s' % (name, 'rar')
|
||||
return '%s.%s' % (name, data.get('protocol'))
|
||||
|
||||
def cpTag(self, media):
|
||||
if Env.setting('enabled', 'renamer'):
|
||||
return '.cp(' + media.get('identifier') + ')' if media.get('identifier') else ''
|
||||
identifier = getIdentifier(media)
|
||||
return '.cp(' + identifier + ')' if identifier else ''
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ class Dashboard(Plugin):
|
||||
limit = tryInt(splt[0])
|
||||
|
||||
# Get all active medias
|
||||
active_ids = [x['_id'] for x in db.run('media', 'with_status', 'active', with_doc = False)]
|
||||
active_ids = [x['_id'] for x in fireEvent('media.with_status', 'active', with_doc = False, single = True)]
|
||||
|
||||
medias = []
|
||||
now_year = date.today().year
|
||||
|
||||
@@ -4,7 +4,7 @@ import traceback
|
||||
|
||||
from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.variable import md5, getExt
|
||||
from couchpotato.core.logger import CPLog
|
||||
@@ -32,12 +32,11 @@ class FileManager(Plugin):
|
||||
'return': {'type': 'file'}
|
||||
})
|
||||
|
||||
addEvent('app.load', self.cleanup)
|
||||
fireEvent('schedule.interval', 'file.cleanup', self.cleanup, hours = 24)
|
||||
|
||||
def cleanup(self):
|
||||
|
||||
# Wait a bit after starting before cleanup
|
||||
time.sleep(2)
|
||||
log.debug('Cleaning up unused files')
|
||||
|
||||
try:
|
||||
|
||||
@@ -8,7 +8,7 @@ from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, addEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import sp
|
||||
from couchpotato.core.helpers.variable import splitString, getTitle, tryInt
|
||||
from couchpotato.core.helpers.variable import splitString, getTitle, tryInt, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
@@ -123,12 +123,11 @@ class Manage(Plugin):
|
||||
total_movies, done_movies = fireEvent('media.list', types = 'movie', status = 'done', single = True)
|
||||
|
||||
for done_movie in done_movies:
|
||||
if done_movie['identifier'] not in added_identifiers:
|
||||
if getIdentifier(done_movie) not in added_identifiers:
|
||||
fireEvent('media.delete', media_id = done_movie['_id'], delete_from = 'all')
|
||||
else:
|
||||
|
||||
db = get_db()
|
||||
releases = list(db.run('release', 'for_media', done_movie.get('_id')))
|
||||
releases = done_movie.get('releases', [])
|
||||
|
||||
for release in releases:
|
||||
if release.get('files'):
|
||||
|
||||
@@ -2,7 +2,7 @@ import traceback
|
||||
|
||||
from couchpotato import get_db, tryInt
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
@@ -41,7 +41,7 @@ class ProfilePlugin(Plugin):
|
||||
# Get all active movies without profile
|
||||
try:
|
||||
db = get_db()
|
||||
medias = db.run('media', 'with_status', ['active'])
|
||||
medias = fireEvent('media.with_status', 'active', single = True)
|
||||
|
||||
profile_ids = [x.get('_id') for x in self.all()]
|
||||
default_id = profile_ids[0]
|
||||
|
||||
@@ -5,7 +5,7 @@ from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode, ss
|
||||
from couchpotato.core.helpers.variable import mergeDicts, getExt
|
||||
from couchpotato.core.helpers.variable import mergeDicts, getExt, tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.plugins.quality.index import QualityIndex
|
||||
@@ -121,7 +121,7 @@ class QualityPlugin(Plugin):
|
||||
quality = db.get('quality', kwargs.get('identifier'), with_doc = True)
|
||||
|
||||
if quality:
|
||||
quality['doc'][kwargs.get('value_type')] = kwargs.get('value')
|
||||
quality['doc'][kwargs.get('value_type')] = tryInt(kwargs.get('value'))
|
||||
db.update(quality['doc'])
|
||||
|
||||
self.cached_qualities = None
|
||||
@@ -148,8 +148,8 @@ class QualityPlugin(Plugin):
|
||||
'_t': 'quality',
|
||||
'order': order,
|
||||
'identifier': q.get('identifier'),
|
||||
'size_min': q.get('size')[0],
|
||||
'size_max': q.get('size')[1]
|
||||
'size_min': tryInt(q.get('size')[0]),
|
||||
'size_max': tryInt(q.get('size')[1]),
|
||||
})
|
||||
|
||||
log.info('Creating profile: %s', q.get('label'))
|
||||
|
||||
@@ -18,18 +18,6 @@ class ReleaseIndex(TreeBasedIndex):
|
||||
if data.get('_t') == 'release' and data.get('media_id'):
|
||||
return data['media_id'], None
|
||||
|
||||
def run_for_media(self, db, media_id):
|
||||
for release in db.get_many('release', media_id, with_doc = True):
|
||||
yield release['doc']
|
||||
|
||||
def run_with_status(self, db, status = [], with_doc = True):
|
||||
|
||||
status = list(status if isinstance(status, (list, tuple)) else [status])
|
||||
|
||||
for s in status:
|
||||
for ms in db.get_many('release_status', s, with_doc = with_doc):
|
||||
yield ms['doc'] if with_doc else ms
|
||||
|
||||
|
||||
class ReleaseStatusIndex(TreeBasedIndex):
|
||||
_version = 1
|
||||
@@ -62,15 +50,15 @@ class ReleaseIDIndex(HashIndex):
|
||||
|
||||
|
||||
class ReleaseDownloadIndex(HashIndex):
|
||||
_version = 1
|
||||
_version = 2
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['key_format'] = '32s'
|
||||
super(ReleaseDownloadIndex, self).__init__(*args, **kwargs)
|
||||
|
||||
def make_key(self, key):
|
||||
return md5(key).hexdigest()
|
||||
return md5(key.lower()).hexdigest()
|
||||
|
||||
def make_key_value(self, data):
|
||||
if data.get('_t') == 'release' and data.get('download_info') and data['download_info']['id'] and data['download_info']['downloader']:
|
||||
return md5('%s-%s' % (data['download_info']['downloader'], data['download_info']['id'])).hexdigest(), None
|
||||
return md5(('%s-%s' % (data['download_info']['downloader'], data['download_info']['id'])).lower()).hexdigest(), None
|
||||
|
||||
@@ -53,6 +53,8 @@ class Release(Plugin):
|
||||
addEvent('release.delete', self.delete)
|
||||
addEvent('release.clean', self.clean)
|
||||
addEvent('release.update_status', self.updateStatus)
|
||||
addEvent('release.with_status', self.withStatus)
|
||||
addEvent('release.for_media', self.forMedia)
|
||||
|
||||
# Clean releases that didn't have activity in the last week
|
||||
addEvent('app.load', self.cleanDone)
|
||||
@@ -67,13 +69,13 @@ class Release(Plugin):
|
||||
db = get_db()
|
||||
|
||||
# get movies last_edit more than a week ago
|
||||
medias = db.run('media', 'with_status', ['done'])
|
||||
medias = fireEvent('media.with_status', 'done', single = True)
|
||||
|
||||
for media in medias:
|
||||
if media.get('last_edit', 0) > (now - week):
|
||||
continue
|
||||
|
||||
for rel in db.run('release', 'for_media', media['_id']):
|
||||
for rel in fireEvent('release.for_media', media['_id'], single = True):
|
||||
|
||||
# Remove all available releases
|
||||
if rel['status'] in ['available']:
|
||||
@@ -422,3 +424,20 @@ class Release(Plugin):
|
||||
log.error('Failed: %s', traceback.format_exc())
|
||||
|
||||
return False
|
||||
|
||||
def withStatus(self, status, with_doc = True):
|
||||
|
||||
db = get_db()
|
||||
|
||||
status = list(status if isinstance(status, (list, tuple)) else [status])
|
||||
|
||||
for s in status:
|
||||
for ms in db.get_many('release_status', s, with_doc = with_doc):
|
||||
yield ms['doc'] if with_doc else ms
|
||||
|
||||
def forMedia(self, media_id):
|
||||
|
||||
db = get_db()
|
||||
|
||||
for release in db.get_many('release', media_id, with_doc = True):
|
||||
yield release['doc']
|
||||
|
||||
@@ -10,7 +10,7 @@ from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import toUnicode, ss, sp
|
||||
from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle, \
|
||||
getImdb, link, symlink, tryInt, splitString, fnEscape, isSubFolder
|
||||
getImdb, link, symlink, tryInt, splitString, fnEscape, isSubFolder, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
@@ -79,16 +79,22 @@ class Renamer(Plugin):
|
||||
|
||||
downloader = kwargs.get('downloader')
|
||||
download_id = kwargs.get('download_id')
|
||||
files = '|'.join([sp(filename) for filename in splitString(kwargs.get('files'), '|')])
|
||||
files = [sp(filename) for filename in splitString(kwargs.get('files'), '|')]
|
||||
status = kwargs.get('status', 'completed')
|
||||
|
||||
release_download = None
|
||||
if not base_folder and media_folder:
|
||||
release_download = {'folder': media_folder}
|
||||
release_download.update({'id': download_id, 'downloader': downloader, 'status': status, 'files': files} if download_id else {})
|
||||
|
||||
if download_id:
|
||||
release_download.update({
|
||||
'id': download_id,
|
||||
'downloader': downloader,
|
||||
'status': status,
|
||||
'files': files
|
||||
})
|
||||
|
||||
fire_handle = fireEvent if not async else fireEventAsync
|
||||
|
||||
fire_handle('renamer.scan', base_folder = base_folder, release_download = release_download)
|
||||
|
||||
return {
|
||||
@@ -142,7 +148,7 @@ class Renamer(Plugin):
|
||||
log.debug('The provided media folder %s does not exist. Trying to find it in the \'from\' folder.', media_folder)
|
||||
|
||||
# Update to the from folder
|
||||
if len(splitString(release_download.get('files'), '|')) == 1:
|
||||
if len(release_download.get('files'), []) == 1:
|
||||
new_media_folder = from_folder
|
||||
else:
|
||||
new_media_folder = os.path.join(from_folder, os.path.basename(media_folder))
|
||||
@@ -152,7 +158,7 @@ class Renamer(Plugin):
|
||||
return
|
||||
|
||||
# Update the files
|
||||
new_files = [os.path.join(new_media_folder, os.path.relpath(filename, media_folder)) for filename in splitString(release_download.get('files'), '|')]
|
||||
new_files = [os.path.join(new_media_folder, os.path.relpath(filename, media_folder)) for filename in release_download.get('files', [])]
|
||||
if new_files and not os.path.isfile(new_files[0]):
|
||||
log.error('The provided media folder %s does not exist and its files could also not be found in the \'from\' folder.', media_folder)
|
||||
return
|
||||
@@ -160,7 +166,7 @@ class Renamer(Plugin):
|
||||
# Update release_download info to the from folder
|
||||
log.debug('Release %s found in the \'from\' folder.', media_folder)
|
||||
release_download['folder'] = new_media_folder
|
||||
release_download['files'] = '|'.join(new_files)
|
||||
release_download['files'] = new_files
|
||||
media_folder = new_media_folder
|
||||
|
||||
if media_folder:
|
||||
@@ -182,11 +188,10 @@ class Renamer(Plugin):
|
||||
log.info('Scanning media folder %s...', media_folder)
|
||||
folder = os.path.dirname(media_folder)
|
||||
|
||||
if release_download.get('files', ''):
|
||||
files = splitString(release_download['files'], '|')
|
||||
|
||||
release_files = release_download.get('files', [])
|
||||
if release_files:
|
||||
# If there is only one file in the torrent, the downloader did not create a subfolder
|
||||
if len(files) == 1:
|
||||
if len(release_files) == 1:
|
||||
folder = media_folder
|
||||
else:
|
||||
# Get all files from the specified folder
|
||||
@@ -446,7 +451,7 @@ class Renamer(Plugin):
|
||||
log.error('Failed marking movie finished: %s', (traceback.format_exc()))
|
||||
|
||||
# Go over current movie releases
|
||||
for release in db.run('release', 'for_media', media['_id']):
|
||||
for release in fireEvent('release.for_media', media['_id'], single = True):
|
||||
|
||||
# When a release already exists
|
||||
if release.get('status') == 'done':
|
||||
@@ -643,8 +648,8 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
|
||||
elif isinstance(release_download, dict):
|
||||
# Tag download_files if they are known
|
||||
if release_download['files']:
|
||||
tag_files = splitString(release_download['files'], '|')
|
||||
if release_download.get('files', []):
|
||||
tag_files = release_download.get('files', [])
|
||||
|
||||
# Tag all files in release folder
|
||||
else:
|
||||
@@ -678,8 +683,8 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
|
||||
elif isinstance(release_download, dict):
|
||||
# Untag download_files if they are known
|
||||
if release_download['files']:
|
||||
tag_files = splitString(release_download['files'], '|')
|
||||
if release_download.get('files'):
|
||||
tag_files = release_download.get('files', [])
|
||||
|
||||
# Untag all files in release folder
|
||||
else:
|
||||
@@ -719,8 +724,8 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
ignore_files = []
|
||||
|
||||
# Find tag on download_files if they are known
|
||||
if release_download['files']:
|
||||
tag_files = splitString(release_download['files'], '|')
|
||||
if release_download.get('files'):
|
||||
tag_files = release_download.get('files', [])
|
||||
|
||||
# Find tag on all files in release folder
|
||||
else:
|
||||
@@ -834,7 +839,7 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
try:
|
||||
db = get_db()
|
||||
|
||||
rels = list(db.run('release', 'with_status', ['snatched', 'seeding', 'missing']))
|
||||
rels = list(fireEvent('release.with_status', ['snatched', 'seeding', 'missing'], single = True))
|
||||
|
||||
if not rels:
|
||||
#No releases found that need status checking
|
||||
@@ -906,7 +911,7 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
found_release = True
|
||||
break
|
||||
else:
|
||||
if release_download['name'] == nzbname or rel['info']['name'] in release_download['name'] or getImdb(release_download['name']) == movie_dict['identifier']:
|
||||
if release_download['name'] == nzbname or rel['info']['name'] in release_download['name'] or getImdb(release_download['name']) == getIdentifier(movie_dict):
|
||||
log.debug('Found release by release name or imdb ID: %s', release_download['name'])
|
||||
found_release = True
|
||||
break
|
||||
@@ -1043,7 +1048,7 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
if rls:
|
||||
media = db.get('id', rls['media_id'])
|
||||
release_download.update({
|
||||
'imdb_id': media['identifier'],
|
||||
'imdb_id': getIdentifier(media),
|
||||
'quality': rls['quality'],
|
||||
'protocol': rls.get('info', {}).get('protocol') or rls.get('info', {}).get('type'),
|
||||
'release_id': rls['_id'],
|
||||
|
||||
@@ -8,7 +8,7 @@ from couchpotato import get_db
|
||||
from couchpotato.core.event import fireEvent, addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode, simplifyString, sp
|
||||
from couchpotato.core.helpers.variable import getExt, getImdb, tryInt, \
|
||||
splitString
|
||||
splitString, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from enzyme.exceptions import NoParserError, ParseError
|
||||
@@ -403,7 +403,7 @@ class Scanner(Plugin):
|
||||
if not group['media']:
|
||||
log.error('Unable to determine media: %s', group['identifiers'])
|
||||
else:
|
||||
group['identifier'] = group['media'].get('identifier') or group['media']['info'].get('imdb')
|
||||
group['identifier'] = getIdentifier(group['media']) or group['media']['info'].get('imdb')
|
||||
|
||||
processed_movies[identifier] = group
|
||||
|
||||
|
||||
@@ -89,13 +89,16 @@ class RTorrent:
|
||||
|
||||
def _get_conn(self):
|
||||
"""Get ServerProxy instance"""
|
||||
if self.username is not None and self.password is not None:
|
||||
|
||||
if self.username and self.password:
|
||||
if self.scheme == 'scgi':
|
||||
raise NotImplementedError()
|
||||
|
||||
secure = self.scheme == 'https'
|
||||
|
||||
return self.sp(
|
||||
self.uri,
|
||||
transport=BasicAuthTransport(self.username, self.password),
|
||||
transport=BasicAuthTransport(secure, self.username, self.password),
|
||||
**self.sp_kwargs
|
||||
)
|
||||
|
||||
|
||||
@@ -20,24 +20,46 @@
|
||||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
from base64 import encodestring
|
||||
import string
|
||||
from base64 import b64encode
|
||||
import httplib
|
||||
import xmlrpclib
|
||||
|
||||
|
||||
class BasicAuthTransport(xmlrpclib.Transport):
|
||||
def __init__(self, username=None, password=None):
|
||||
def __init__(self, secure=False, username=None, password=None):
|
||||
xmlrpclib.Transport.__init__(self)
|
||||
|
||||
self.secure = secure
|
||||
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
def send_auth(self, h):
|
||||
if self.username is not None and self.password is not None:
|
||||
h.putheader('AUTHORIZATION', "Basic %s" % string.replace(
|
||||
encodestring("%s:%s" % (self.username, self.password)),
|
||||
"\012", ""
|
||||
))
|
||||
if not self.username or not self.password:
|
||||
return
|
||||
|
||||
auth = b64encode("%s:%s" % (self.username, self.password))
|
||||
|
||||
h.putheader('Authorization', "Basic %s" % auth)
|
||||
|
||||
def make_connection(self, host):
|
||||
if self._connection and host == self._connection[0]:
|
||||
return self._connection[1]
|
||||
|
||||
chost, self._extra_headers, x509 = self.get_host_info(host)
|
||||
|
||||
if self.secure:
|
||||
try:
|
||||
self._connection = host, httplib.HTTPSConnection(chost, None, **(x509 or {}))
|
||||
except AttributeError:
|
||||
raise NotImplementedError(
|
||||
"your version of httplib doesn't support HTTPS"
|
||||
)
|
||||
else:
|
||||
self._connection = host, httplib.HTTPConnection(chost)
|
||||
|
||||
return self._connection[1]
|
||||
|
||||
|
||||
def single_request(self, host, handler, request_body, verbose=0):
|
||||
# issue XML-RPC request
|
||||
|
||||
Reference in New Issue
Block a user