diff --git a/couchpotato/core/media/movie/_base/main.py b/couchpotato/core/media/movie/_base/main.py index bc049a57..819e2edc 100644 --- a/couchpotato/core/media/movie/_base/main.py +++ b/couchpotato/core/media/movie/_base/main.py @@ -2,17 +2,11 @@ from couchpotato import get_session 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 getImdb, splitString, tryInt, \ - mergeDicts +from couchpotato.core.helpers.variable import splitString, tryInt from couchpotato.core.logger import CPLog from couchpotato.core.media.movie import MovieTypeBase -from couchpotato.core.settings.model import Library, LibraryTitle, Media, \ - Release -from sqlalchemy.orm import joinedload_all -from sqlalchemy.sql.expression import or_, asc, not_, desc -from string import ascii_lowercase +from couchpotato.core.settings.model import Media import time -import os log = CPLog(__name__) @@ -27,28 +21,6 @@ class MovieBase(MovieTypeBase): super(MovieBase, self).__init__() self.initType() - addApiView('movie.list', self.listView, docs = { - 'desc': 'List movies in wanted list', - 'params': { - 'status': {'type': 'array or csv', 'desc': 'Filter movie by status. Example:"active,done"'}, - 'release_status': {'type': 'array or csv', 'desc': 'Filter movie by status of its releases. Example:"snatched,available"'}, - 'limit_offset': {'desc': 'Limit and offset the movie list. Examples: "50" or "50,30"'}, - 'starts_with': {'desc': 'Starts with these characters. Example: "a" returns all movies starting with the letter "a"'}, - 'search': {'desc': 'Search movie title'}, - }, - 'return': {'type': 'object', 'example': """{ - 'success': True, - 'empty': bool, any movies returned or not, - 'movies': array, movies found, -}"""} - }) - addApiView('movie.get', self.getView, docs = { - 'desc': 'Get a movie by id', - 'params': { - 'id': {'desc': 'The id of the movie'}, - } - }) - addApiView('movie.available_chars', self.charView) addApiView('movie.add', self.addView, docs = { 'desc': 'Add new movie to the wanted list', 'params': { @@ -66,256 +38,8 @@ class MovieBase(MovieTypeBase): 'default_title': {'desc': 'Movie title to use for searches. Has to be one of the titles returned by movie.search.'}, } }) - addApiView('movie.delete', self.deleteView, docs = { - 'desc': 'Delete a movie from the wanted list', - 'params': { - 'id': {'desc': 'Movie ID(s) you want to delete.', 'type': 'int (comma separated)'}, - 'delete_from': {'desc': 'Delete movie from this page', 'type': 'string: all (default), wanted, manage'}, - 'with_files': {'desc': 'Delete the files as well', 'type': 'bool (true or false)'}, - } - }) addEvent('movie.add', self.add) - addEvent('movie.delete', self.delete) - addEvent('movie.get', self.get) - addEvent('movie.list', self.list) - addEvent('movie.restatus', self.restatus) - - def getView(self, id = None, **kwargs): - - movie = self.get(id) if id else None - - return { - 'success': movie is not None, - 'movie': movie, - } - - def get(self, movie_id): - - db = get_session() - - imdb_id = getImdb(str(movie_id)) - - if imdb_id: - m = db.query(Media).filter(Media.library.has(identifier = imdb_id)).first() - else: - m = db.query(Media).filter_by(id = movie_id).first() - - results = None - if m: - results = m.to_dict(self.default_dict) - - db.expire_all() - return results - - def list(self, status = None, release_status = None, limit_offset = None, starts_with = None, search = None, order = None): - - db = get_session() - - # Make a list from string - if status and not isinstance(status, (list, tuple)): - status = [status] - if release_status and not isinstance(release_status, (list, tuple)): - release_status = [release_status] - - # query movie ids - q = db.query(Media) \ - .with_entities(Media.id) \ - .group_by(Media.id) - - # Filter on movie status - if status and len(status) > 0: - statuses = fireEvent('status.get', status, single = len(status) > 1) - statuses = [s.get('id') for s in statuses] - - q = q.filter(Media.status_id.in_(statuses)) - - # Filter on release status - if release_status and len(release_status) > 0: - q = q.join(Media.releases) - - statuses = fireEvent('status.get', release_status, single = len(release_status) > 1) - statuses = [s.get('id') for s in statuses] - - q = q.filter(Release.status_id.in_(statuses)) - - # Only join when searching / ordering - if starts_with or search or order != 'release_order': - q = q.join(Media.library, Library.titles) \ - .filter(LibraryTitle.default == True) - - # Add search filters - filter_or = [] - if starts_with: - starts_with = toUnicode(starts_with.lower()) - if starts_with in ascii_lowercase: - filter_or.append(LibraryTitle.simple_title.startswith(starts_with)) - else: - ignore = [] - for letter in ascii_lowercase: - ignore.append(LibraryTitle.simple_title.startswith(toUnicode(letter))) - filter_or.append(not_(or_(*ignore))) - - if search: - filter_or.append(LibraryTitle.simple_title.like('%%' + search + '%%')) - - if len(filter_or) > 0: - q = q.filter(or_(*filter_or)) - - total_count = q.count() - if total_count == 0: - return 0, [] - - if order == 'release_order': - q = q.order_by(desc(Release.last_edit)) - else: - q = q.order_by(asc(LibraryTitle.simple_title)) - - if limit_offset: - splt = splitString(limit_offset) if isinstance(limit_offset, (str, unicode)) else limit_offset - limit = splt[0] - offset = 0 if len(splt) is 1 else splt[1] - q = q.limit(limit).offset(offset) - - # Get all movie_ids in sorted order - movie_ids = [m.id for m in q.all()] - - # List release statuses - releases = db.query(Release) \ - .filter(Release.movie_id.in_(movie_ids)) \ - .all() - - release_statuses = dict((m, set()) for m in movie_ids) - releases_count = dict((m, 0) for m in movie_ids) - for release in releases: - release_statuses[release.movie_id].add('%d,%d' % (release.status_id, release.quality_id)) - releases_count[release.movie_id] += 1 - - # Get main movie data - q2 = db.query(Media) \ - .options(joinedload_all('library.titles')) \ - .options(joinedload_all('library.files')) \ - .options(joinedload_all('status')) \ - .options(joinedload_all('files')) - - q2 = q2.filter(Media.id.in_(movie_ids)) - - results = q2.all() - - # Create dict by movie id - movie_dict = {} - for movie in results: - movie_dict[movie.id] = movie - - # List movies based on movie_ids order - movies = [] - for movie_id in movie_ids: - - releases = [] - for r in release_statuses.get(movie_id): - x = splitString(r) - releases.append({'status_id': x[0], 'quality_id': x[1]}) - - # Merge releases with movie dict - movies.append(mergeDicts(movie_dict[movie_id].to_dict({ - 'library': {'titles': {}, 'files':{}}, - 'files': {}, - }), { - 'releases': releases, - 'releases_count': releases_count.get(movie_id), - })) - - db.expire_all() - return total_count, movies - - def availableChars(self, status = None, release_status = None): - - status = status or [] - release_status = release_status or [] - - db = get_session() - - # Make a list from string - if not isinstance(status, (list, tuple)): - status = [status] - if release_status and not isinstance(release_status, (list, tuple)): - release_status = [release_status] - - q = db.query(Media) - - # Filter on movie status - if status and len(status) > 0: - statuses = fireEvent('status.get', status, single = len(release_status) > 1) - statuses = [s.get('id') for s in statuses] - - q = q.filter(Media.status_id.in_(statuses)) - - # Filter on release status - if release_status and len(release_status) > 0: - - statuses = fireEvent('status.get', release_status, single = len(release_status) > 1) - statuses = [s.get('id') for s in statuses] - - q = q.join(Media.releases) \ - .filter(Release.status_id.in_(statuses)) - - q = q.join(Library, LibraryTitle) \ - .with_entities(LibraryTitle.simple_title) \ - .filter(LibraryTitle.default == True) - - titles = q.all() - - chars = set() - for title in titles: - try: - char = title[0][0] - char = char if char in ascii_lowercase else '#' - chars.add(str(char)) - except: - log.error('Failed getting title for %s', title.libraries_id) - - if len(chars) == 25: - break - - db.expire_all() - return ''.join(sorted(chars)) - - def listView(self, **kwargs): - - status = splitString(kwargs.get('status')) - release_status = splitString(kwargs.get('release_status')) - limit_offset = kwargs.get('limit_offset') - starts_with = kwargs.get('starts_with') - search = kwargs.get('search') - order = kwargs.get('order') - - total_movies, movies = self.list( - status = status, - release_status = release_status, - limit_offset = limit_offset, - starts_with = starts_with, - search = search, - order = order - ) - - return { - 'success': True, - 'empty': len(movies) == 0, - 'total': total_movies, - 'movies': movies, - } - - def charView(self, **kwargs): - - status = splitString(kwargs.get('status', None)) - release_status = splitString(kwargs.get('release_status', None)) - chars = self.availableChars(status, release_status) - - return { - 'success': True, - 'empty': len(chars) == 0, - 'chars': chars, - } def add(self, params = None, force_readd = True, search_after = True, update_library = False, status_id = None): if not params: params = {} @@ -451,145 +175,10 @@ class MovieBase(MovieTypeBase): fireEvent('media.restatus', m.id) - movie_dict = m.to_dict(self.default_dict) + movie_dict = m.to_dict(self.search_dict) fireEventAsync('movie.searcher.single', movie_dict, on_complete = self.createNotifyFront(media_id)) db.expire_all() return { 'success': True, } - - def deleteView(self, id = '', **kwargs): - - ids = splitString(id) - for movie_id in ids: - self.delete(movie_id, delete_from = kwargs.get('delete_from', 'all'), with_files = kwargs.get('with_files')) - - return { - 'success': True, - } - - def deleteFiles(self, instance): - directories = dict() - - # Walk through all files in the Couch database - for file_ in instance.files: - # Add the directories and filename prefixes to a list so we can - # remove the directories and related files as well - directory = os.path.dirname(file_.path) - if directory not in directories: - directories[directory] = set() - directories[directory].add(os.path.splitext(file_.path)[0]) - - if os.path.isfile(file_.path): - try: - os.remove(file_.path) - log.info('Removed %s', file_.path) - except: - log.error('Unable to remove %s', file_.path) - - # Walk through the directories and file prefixes for removal if - # possible - for directory, prefixes in directories.iteritems(): - if os.path.isdir(directory): - - # If the files in the directory have the same name as the - # expected files (except for extensions and stuff), remove them - files = os.listdir(directory) - for file_ in files: - for prefix in prefixes: - if file_.startswith(prefix): - try: - os.remove(file_) - print 'rmoeving', file_ - log.info('Removed %s', file_) - except: - log.error('Unable to remove %s', file_) - - try: - os.rmdir(directory) - log.info('Removed %s', directory) - except: - log.error('Unable to remove %s', directory) - - def delete(self, movie_id, delete_from = None, with_files = False): - - db = get_session() - - movie = db.query(Media).filter_by(id = movie_id).first() - if movie: - if with_files: - self.deleteFiles(movie) - - deleted = False - if delete_from == 'all': - db.delete(movie) - db.commit() - deleted = True - else: - done_status = fireEvent('status.get', 'done', single = True) - - total_releases = len(movie.releases) - total_deleted = 0 - new_movie_status = None - for release in movie.releases: - if with_files: - self.deleteFiles(release) - - if delete_from in ['wanted', 'snatched', 'late']: - if release.status_id != done_status.get('id'): - db.delete(release) - total_deleted += 1 - new_movie_status = 'done' - elif delete_from == 'manage': - if release.status_id == done_status.get('id'): - db.delete(release) - total_deleted += 1 - new_movie_status = 'active' - db.commit() - - if total_releases == total_deleted: - db.delete(movie) - db.commit() - deleted = True - elif new_movie_status: - new_status = fireEvent('status.get', new_movie_status, single = True) - movie.profile_id = None - movie.status_id = new_status.get('id') - db.commit() - else: - fireEvent('movie.restatus', movie.id, single = True) - - if deleted: - fireEvent('notify.frontend', type = 'movie.deleted', data = movie.to_dict()) - - db.expire_all() - return True - - def restatus(self, movie_id): - - active_status, done_status = fireEvent('status.get', ['active', 'done'], single = True) - - db = get_session() - - m = db.query(Media).filter_by(id = movie_id).first() - if not m or len(m.library.titles) == 0: - log.debug('Can\'t restatus movie, doesn\'t seem to exist.') - return False - - log.debug('Changing status for %s', m.library.titles[0].title) - if not m.profile: - m.status_id = done_status.get('id') - else: - move_to_wanted = True - - for t in m.profile.types: - for release in m.releases: - if t.quality.identifier is release.quality.identifier and (release.status_id is done_status.get('id') and t.finish): - move_to_wanted = False - - m.status_id = active_status.get('id') if move_to_wanted else done_status.get('id') - - db.commit() - - return True diff --git a/couchpotato/core/media/movie/_base/static/movie.actions.js b/couchpotato/core/media/movie/_base/static/movie.actions.js index b81ab24f..66c84c68 100644 --- a/couchpotato/core/media/movie/_base/static/movie.actions.js +++ b/couchpotato/core/media/movie/_base/static/movie.actions.js @@ -431,7 +431,7 @@ MA.Release = new Class({ markMovieDone: function(){ var self = this; - Api.request('movie.delete', { + Api.request('media.delete', { 'data': { 'id': self.movie.get('id'), 'delete_from': 'wanted' @@ -450,7 +450,7 @@ MA.Release = new Class({ }, - tryNextRelease: function(movie_id){ + tryNextRelease: function(){ var self = this; Api.request('movie.searcher.try_next', { @@ -792,27 +792,10 @@ MA.Delete = new Class({ new Element('a.button.delete', { 'text': 'Delete ' + self.movie.title.get('text'), 'events': { - 'click': self.del.bind(self, false) + 'click': self.del.bind(self) } }) - ); - - /* Deleting files is only useful if it's already downloaded */ - if(self.movie.list.options.identifier == 'manage'){ - self.delete_container.adopt( - new Element('span', { - 'text': ' ' - }), - new Element('a.button.delete', { - 'text': '+ Files', - 'events': { - 'click': self.del.bind(self, true) - } - }) - ); - } - - self.delete_container.inject(self.movie, 'top'); + ).inject(self.movie, 'top'); } self.movie.slide('in', self.delete_container); @@ -827,7 +810,7 @@ MA.Delete = new Class({ self.movie.slide('out'); }, - del: function(withFiles, e){ + del: function(e){ (e).preventDefault(); var self = this; @@ -838,11 +821,10 @@ MA.Delete = new Class({ self.callChain(); }, function(){ - Api.request('movie.delete', { + Api.request('media.delete', { 'data': { 'id': self.movie.get('id'), - 'delete_from': self.movie.list.options.identifier, - 'with_files': !!withFiles + 'delete_from': self.movie.list.options.identifier }, 'onComplete': function(){ movie.set('tween', { @@ -858,6 +840,7 @@ MA.Delete = new Class({ ); self.callChain(); + } }); @@ -941,4 +924,4 @@ MA.Files = new Class({ self.movie.slide('in', self.options_container); }, -}); +}); \ No newline at end of file diff --git a/couchpotato/core/plugins/score/main.py b/couchpotato/core/plugins/score/main.py index 17448eab..54b6ca31 100644 --- a/couchpotato/core/plugins/score/main.py +++ b/couchpotato/core/plugins/score/main.py @@ -35,8 +35,8 @@ class Score(Plugin): # Torrents only if nzb.get('seeders'): try: - score += nzb.get('seeders') * 100 / 15 - score += nzb.get('leechers') * 100 / 30 + score += nzb.get('seeders') / 5 + score += nzb.get('leechers') / 10 except: pass diff --git a/couchpotato/core/providers/torrent/yify/__init__.py b/couchpotato/core/providers/torrent/yify/__init__.py index 99c1162e..775ecdbe 100644 --- a/couchpotato/core/providers/torrent/yify/__init__.py +++ b/couchpotato/core/providers/torrent/yify/__init__.py @@ -18,12 +18,6 @@ config = [{ 'type': 'enabler', 'default': 0 }, - { - 'name': 'domain', - 'advanced': True, - 'label': 'Proxy server', - 'description': 'Domain for requests, keep empty to let CouchPotato pick.', - }, { 'name': 'seed_ratio', 'label': 'Seed ratio', diff --git a/couchpotato/core/providers/torrent/yify/main.py b/couchpotato/core/providers/torrent/yify/main.py index e70a6399..da254fc0 100644 --- a/couchpotato/core/providers/torrent/yify/main.py +++ b/couchpotato/core/providers/torrent/yify/main.py @@ -1,8 +1,8 @@ from couchpotato.core.helpers.variable import tryInt from couchpotato.core.logger import CPLog -from couchpotato.core.providers.torrent.base import TorrentMagnetProvider from couchpotato.core.providers.base import MultiProvider from couchpotato.core.providers.info.base import MovieProvider +from couchpotato.core.providers.torrent.base import TorrentProvider import traceback log = CPLog(__name__) @@ -12,22 +12,15 @@ class Yify(MultiProvider): def getTypes(self): return [Movie] -class Base(TorrentMagnetProvider): +class Base(TorrentProvider): urls = { - 'test' : '%s/api', - 'search' : '%s/api/list.json?keywords=%s&quality=%s', - 'detail': '%s/api/movie.json?id=%s' + 'test' : 'https://yify-torrents.com/api', + 'search' : 'https://yify-torrents.com/api/list.json?keywords=%s&quality=%s', + 'detail': 'https://yify-torrents.com/api/movie.json?id=%s' } http_time_between_calls = 1 #seconds - - proxy_list = [ - 'https://yify-torrents.im', - 'http://yify.unlocktorrent.com', - 'http://yify.ftwnet.co.uk', - 'http://yify-torrents.com.come.in', - ] def search(self, movie, quality): @@ -38,9 +31,7 @@ class Base(TorrentMagnetProvider): def _search(self, movie, quality, results): - search_url = self.urls['search'] % (self.getDomain(), movie['library']['identifier'], quality['identifier']) - - data = self.getJsonData(search_url) + data = self.getJsonData(self.urls['search'] % (movie['library']['identifier'], quality['identifier'])) if data and data.get('MovieList'): try: @@ -56,8 +47,8 @@ class Base(TorrentMagnetProvider): results.append({ 'id': result['MovieID'], 'name': title, - 'url': result['TorrentMagnetUrl'], - 'detail_url': self.urls['detail'] % (self.getDomain(),result['MovieID']), + 'url': result['TorrentUrl'], + 'detail_url': self.urls['detail'] % result['MovieID'], 'size': self.parseSize(result['Size']), 'seeders': tryInt(result['TorrentSeeds']), 'leechers': tryInt(result['TorrentPeers']) @@ -66,8 +57,5 @@ class Base(TorrentMagnetProvider): except: log.error('Failed getting results from %s: %s', (self.getName(), traceback.format_exc())) - def correctProxy(self, data): - return 'title="YIFY-Torrents RSS feed"' in data - class Movie(MovieProvider, Base): pass \ No newline at end of file