Browse Source

Merge branch 'refs/heads/develop' into tv

pull/3111/head
Ruud 11 years ago
parent
commit
0f555dbb85
  1. 51
      contributing.md
  2. 4
      couchpotato/__init__.py
  3. 4
      couchpotato/core/database.py
  4. 2
      couchpotato/core/downloaders/base.py
  5. 2
      couchpotato/core/downloaders/deluge.py
  6. 2
      couchpotato/core/downloaders/qbittorrent_.py
  7. 20
      couchpotato/core/downloaders/rtorrent_.py
  8. 14
      couchpotato/core/downloaders/synology.py
  9. 2
      couchpotato/core/downloaders/transmission.py
  10. 2
      couchpotato/core/downloaders/utorrent.py
  11. 10
      couchpotato/core/helpers/variable.py
  12. 5
      couchpotato/core/media/__init__.py
  13. 25
      couchpotato/core/media/_base/media/index.py
  14. 63
      couchpotato/core/media/_base/media/main.py
  15. 4
      couchpotato/core/media/_base/providers/torrent/awesomehd.py
  16. 6
      couchpotato/core/media/_base/providers/torrent/hdbits.py
  17. 4
      couchpotato/core/media/_base/providers/torrent/kickasstorrents.py
  18. 4
      couchpotato/core/media/_base/providers/torrent/passthepopcorn.py
  19. 4
      couchpotato/core/media/_base/providers/torrent/thepiratebay.py
  20. 4
      couchpotato/core/media/_base/providers/torrent/yify.py
  21. 39
      couchpotato/core/media/movie/_base/main.py
  22. 2
      couchpotato/core/media/movie/_base/static/movie.actions.js
  23. 2
      couchpotato/core/media/movie/providers/automation/itunes.py
  24. 4
      couchpotato/core/media/movie/providers/info/_modifier.py
  25. 3
      couchpotato/core/media/movie/providers/nzb/binsearch.py
  26. 3
      couchpotato/core/media/movie/providers/nzb/newznab.py
  27. 2
      couchpotato/core/media/movie/providers/nzb/nzbindex.py
  28. 2
      couchpotato/core/media/movie/providers/torrent/bitsoup.py
  29. 2
      couchpotato/core/media/movie/providers/torrent/iptorrents.py
  30. 3
      couchpotato/core/media/movie/providers/torrent/torrentpotato.py
  31. 33
      couchpotato/core/media/movie/searcher.py
  32. 11
      couchpotato/core/media/movie/suggestion/main.py
  33. 2
      couchpotato/core/notifications/boxcar.py
  34. 2
      couchpotato/core/notifications/boxcar2.py
  35. 2
      couchpotato/core/notifications/email_.py
  36. 2
      couchpotato/core/notifications/growl.py
  37. 2
      couchpotato/core/notifications/nmj.py
  38. 2
      couchpotato/core/notifications/notifymyandroid.py
  39. 2
      couchpotato/core/notifications/notifymywp.py
  40. 2
      couchpotato/core/notifications/prowl.py
  41. 2
      couchpotato/core/notifications/pushalot.py
  42. 2
      couchpotato/core/notifications/pushbullet.py
  43. 10
      couchpotato/core/plugins/base.py
  44. 2
      couchpotato/core/plugins/dashboard.py
  45. 5
      couchpotato/core/plugins/file.py
  46. 7
      couchpotato/core/plugins/manage.py
  47. 4
      couchpotato/core/plugins/profile/main.py
  48. 8
      couchpotato/core/plugins/quality/main.py
  49. 18
      couchpotato/core/plugins/release/index.py
  50. 23
      couchpotato/core/plugins/release/main.py
  51. 47
      couchpotato/core/plugins/renamer.py
  52. 4
      couchpotato/core/plugins/scanner.py
  53. 7
      libs/rtorrent/__init__.py
  54. 38
      libs/rtorrent/lib/xmlrpc/basic_auth.py

51
contributing.md

@ -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!!
## Pull Request
* Make sure you're pull request is made for 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.
The more relevant information you can provide, the more likely it is the issue will be resolved rather than closed.
**If we don't get enough info, the chance of the issue getting closed is a lot bigger ;)**
## 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?
* 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?

4
couchpotato/__init__.py

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

4
couchpotato/core/database.py

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

2
couchpotato/core/downloaders/base.py

@ -210,7 +210,7 @@ class ReleaseDownloadList(list):
'status': 'busy',
'downloader': self.provider.getName(),
'folder': '',
'files': '',
'files': [],
}
return mergeDicts(defaults, result)

2
couchpotato/core/downloaders/deluge.py

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

2
couchpotato/core/downloaders/qbittorrent_.py

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

20
couchpotato/core/downloaders/rtorrent_.py

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

14
couchpotato/core/downloaders/synology.py

@ -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
@ -196,6 +201,11 @@ config = [{
'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',
'default': 'both',

2
couchpotato/core/downloaders/transmission.py

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

2
couchpotato/core/downloaders/utorrent.py

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

10
couchpotato/core/helpers/variable.py

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

5
couchpotato/core/media/__init__.py

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

25
couchpotato/core/media/_base/media/index.py

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

63
couchpotato/core/media/_base/media/main.py

@ -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)
if media:
results = None
if m:
results = db.run('media', 'to_dict', m['_id'])
# Attach category
if media.get('category_id'):
media['category'] = db.get('id', media.get('category_id'))
return results
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)

4
couchpotato/core/media/_base/providers/torrent/awesomehd.py

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

6
couchpotato/core/media/_base/providers/torrent/hdbits.py

@ -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'])
})

4
couchpotato/core/media/_base/providers/torrent/kickasstorrents.py

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

4
couchpotato/core/media/_base/providers/torrent/passthepopcorn.py

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

4
couchpotato/core/media/_base/providers/torrent/thepiratebay.py

@ -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',
]

4
couchpotato/core/media/_base/providers/torrent/yify.py

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

39
couchpotato/core/media/movie/_base/main.py

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

2
couchpotato/core/media/movie/_base/static/movie.actions.js

@ -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')
}
});

2
couchpotato/core/media/movie/providers/automation/itunes.py

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

4
couchpotato/core/media/movie/providers/info/_modifier.py

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

3
couchpotato/core/media/movie/providers/nzb/binsearch.py

@ -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'),

3
couchpotato/core/media/movie/providers/nzb/newznab.py

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

2
couchpotato/core/media/movie/providers/nzb/nzbindex.py

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

2
couchpotato/core/media/movie/providers/torrent/bitsoup.py

@ -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],
})

2
couchpotato/core/media/movie/providers/torrent/iptorrents.py

@ -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'])

3
couchpotato/core/media/movie/providers/torrent/torrentpotato.py

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

33
couchpotato/core/media/movie/searcher.py

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

11
couchpotato/core/media/movie/suggestion/main.py

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

2
couchpotato/core/notifications/boxcar.py

@ -7,6 +7,8 @@ from couchpotato.core.notifications.base import Notification
log = CPLog(__name__)
autoload = 'Boxcar'
class Boxcar(Notification):

2
couchpotato/core/notifications/boxcar2.py

@ -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),
}

2
couchpotato/core/notifications/email_.py

@ -12,6 +12,8 @@ from couchpotato.environment import Env
log = CPLog(__name__)
autoload = 'Email'
class Email(Notification):

2
couchpotato/core/notifications/growl.py

@ -9,6 +9,8 @@ from gntp import notifier
log = CPLog(__name__)
autoload = 'Growl'
class Growl(Notification):

2
couchpotato/core/notifications/nmj.py

@ -15,6 +15,8 @@ except ImportError:
log = CPLog(__name__)
autoload = 'NMJ'
class NMJ(Notification):

2
couchpotato/core/notifications/notifymyandroid.py

@ -6,6 +6,8 @@ import six
log = CPLog(__name__)
autoload = 'NotifyMyAndroid'
class NotifyMyAndroid(Notification):

2
couchpotato/core/notifications/notifymywp.py

@ -6,6 +6,8 @@ import six
log = CPLog(__name__)
autoload = 'NotifyMyWP'
class NotifyMyWP(Notification):

2
couchpotato/core/notifications/prowl.py

@ -7,6 +7,8 @@ from couchpotato.core.notifications.base import Notification
log = CPLog(__name__)
autoload = 'Prowl'
class Prowl(Notification):

2
couchpotato/core/notifications/pushalot.py

@ -7,6 +7,8 @@ from couchpotato.core.notifications.base import Notification
log = CPLog(__name__)
autoload = 'Pushalot'
class Pushalot(Notification):

2
couchpotato/core/notifications/pushbullet.py

@ -9,6 +9,8 @@ from couchpotato.core.notifications.base import Notification
log = CPLog(__name__)
autoload = 'Pushbullet'
class Pushbullet(Notification):

10
couchpotato/core/plugins/base.py

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

2
couchpotato/core/plugins/dashboard.py

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

5
couchpotato/core/plugins/file.py

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

7
couchpotato/core/plugins/manage.py

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

4
couchpotato/core/plugins/profile/main.py

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

8
couchpotato/core/plugins/quality/main.py

@ -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
couchpotato/core/plugins/release/index.py

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

23
couchpotato/core/plugins/release/main.py

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

47
couchpotato/core/plugins/renamer.py

@ -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 {})
fire_handle = fireEvent if not async else fireEventAsync
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'],

4
couchpotato/core/plugins/scanner.py

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

7
libs/rtorrent/__init__.py

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

38
libs/rtorrent/lib/xmlrpc/basic_auth.py

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

Loading…
Cancel
Save