Merge branch 'refs/heads/develop' into tv
Conflicts: couchpotato/core/media/__init__.py couchpotato/core/media/_base/searcher/main.py couchpotato/core/media/movie/_base/main.py couchpotato/core/media/movie/searcher/__init__.py couchpotato/core/media/movie/searcher/main.py
This commit is contained in:
@@ -2,16 +2,13 @@ from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class MediaBase(Plugin):
|
||||
|
||||
identifier = None
|
||||
|
||||
def __init__(self):
|
||||
_type = None
|
||||
|
||||
def initType(self):
|
||||
addEvent('media.types', self.getType)
|
||||
|
||||
def getType(self):
|
||||
return self.identifier
|
||||
return self._type
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class SearcherBase(Plugin):
|
||||
|
||||
in_progress = False
|
||||
|
||||
def __init__(self):
|
||||
super(SearcherBase, self).__init__()
|
||||
|
||||
|
||||
addEvent('searcher.progress', self.getProgress)
|
||||
addEvent('%s.searcher.progress' % self.getType(), self.getProgress)
|
||||
|
||||
self.initCron()
|
||||
|
||||
|
||||
""" Set the searcher cronjob
|
||||
Make sure to reset cronjob after setting has changed
|
||||
|
||||
"""
|
||||
def initCron(self):
|
||||
|
||||
_type = self.getType()
|
||||
|
||||
def setCrons():
|
||||
|
||||
fireEvent('schedule.cron', '%s.searcher.all' % _type, self.searchAll,
|
||||
day = self.conf('cron_day'), hour = self.conf('cron_hour'), minute = self.conf('cron_minute'))
|
||||
|
||||
addEvent('app.load', setCrons)
|
||||
addEvent('setting.save.%s_searcher.cron_day.after' % _type, setCrons)
|
||||
addEvent('setting.save.%s_searcher.cron_hour.after' % _type, setCrons)
|
||||
addEvent('setting.save.%s_searcher.cron_minute.after' % _type, setCrons)
|
||||
|
||||
|
||||
""" Return progress of current searcher
|
||||
|
||||
"""
|
||||
def getProgress(self, **kwargs):
|
||||
|
||||
progress = {}
|
||||
progress[self.getType()] = self.in_progress
|
||||
|
||||
return progress
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import simplifyString, toUnicode
|
||||
from couchpotato.core.helpers.variable import md5, getTitle
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.media._base.searcher.base import SearcherBase
|
||||
from couchpotato.core.settings.model import Movie, Release, ReleaseInfo
|
||||
from couchpotato.environment import Env
|
||||
from inspect import ismethod, isfunction
|
||||
@@ -15,7 +16,7 @@ import traceback
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class Searcher(Plugin):
|
||||
class Searcher(SearcherBase):
|
||||
|
||||
def __init__(self):
|
||||
addEvent('searcher.get_types', self.getSearchTypes)
|
||||
@@ -24,6 +25,30 @@ class Searcher(Plugin):
|
||||
addEvent('searcher.correct_name', self.correctName)
|
||||
addEvent('searcher.download', self.download)
|
||||
|
||||
addApiView('searcher.full_search', self.searchAllView, docs = {
|
||||
'desc': 'Starts a full search for all media',
|
||||
})
|
||||
|
||||
addApiView('searcher.progress', self.getProgressForAll, docs = {
|
||||
'desc': 'Get the progress of all media searches',
|
||||
'return': {'type': 'object', 'example': """{
|
||||
'movie': False || object, total & to_go,
|
||||
'show': False || object, total & to_go,
|
||||
}"""},
|
||||
})
|
||||
|
||||
def searchAllView(self):
|
||||
|
||||
results = {}
|
||||
for _type in fireEvent('media.types'):
|
||||
results[_type] = fireEvent('%s.searcher.all_view' % _type)
|
||||
|
||||
return results
|
||||
|
||||
def getProgressForAll(self):
|
||||
progress = fireEvent('searcher.progress', merge = True)
|
||||
return progress
|
||||
|
||||
def download(self, data, movie, manual = False):
|
||||
|
||||
# Test to see if any downloaders are enabled for this type
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
from couchpotato.core.media import MediaBase
|
||||
|
||||
|
||||
class MovieTypeBase(MediaBase):
|
||||
|
||||
_type = 'movie'
|
||||
|
||||
@@ -4,7 +4,7 @@ from couchpotato.core.event import fireEvent, fireEventAsync, addEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode, simplifyString
|
||||
from couchpotato.core.helpers.variable import getImdb, splitString, tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.media import MediaBase
|
||||
from couchpotato.core.media.movie import MovieTypeBase
|
||||
from couchpotato.core.settings.model import Library, LibraryTitle, Movie, \
|
||||
Release
|
||||
from couchpotato.environment import Env
|
||||
@@ -16,9 +16,7 @@ import time
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class MovieBase(MediaBase):
|
||||
|
||||
identifier = 'movie'
|
||||
class MovieBase(MovieTypeBase):
|
||||
|
||||
default_dict = {
|
||||
'profile': {'types': {'quality': {}}},
|
||||
@@ -29,7 +27,10 @@ class MovieBase(MediaBase):
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# Initialize this type
|
||||
super(MovieBase, self).__init__()
|
||||
self.initType()
|
||||
|
||||
addApiView('movie.search', self.search, docs = {
|
||||
'desc': 'Search the movie providers for a movie',
|
||||
|
||||
@@ -5,7 +5,7 @@ def start():
|
||||
return MovieSearcher()
|
||||
|
||||
config = [{
|
||||
'name': 'searcher',
|
||||
'name': 'moviesearcher',
|
||||
'order': 20,
|
||||
'groups': [
|
||||
{
|
||||
@@ -18,12 +18,14 @@ config = [{
|
||||
{
|
||||
'name': 'always_search',
|
||||
'default': False,
|
||||
'migrate_from': 'searcher',
|
||||
'type': 'bool',
|
||||
'label': 'Always search',
|
||||
'description': 'Search for movies even before there is a ETA. Enabling this will probably get you a lot of fakes.',
|
||||
},
|
||||
{
|
||||
'name': 'run_on_launch',
|
||||
'migrate_from': 'searcher',
|
||||
'label': 'Run on launch',
|
||||
'advanced': True,
|
||||
'default': 0,
|
||||
@@ -32,6 +34,7 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'cron_day',
|
||||
'migrate_from': 'searcher',
|
||||
'label': 'Day',
|
||||
'advanced': True,
|
||||
'default': '*',
|
||||
@@ -40,6 +43,7 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'cron_hour',
|
||||
'migrate_from': 'searcher',
|
||||
'label': 'Hour',
|
||||
'advanced': True,
|
||||
'default': random.randint(0, 23),
|
||||
@@ -48,6 +52,7 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'cron_minute',
|
||||
'migrate_from': 'searcher',
|
||||
'label': 'Minute',
|
||||
'advanced': True,
|
||||
'default': random.randint(0, 59),
|
||||
|
||||
@@ -5,12 +5,12 @@ from couchpotato.core.helpers.encoding import simplifyString, toUnicode
|
||||
from couchpotato.core.helpers.variable import md5, getTitle, splitString, \
|
||||
possibleTitles, getImdb
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.media._base.searcher.base import SearcherBase
|
||||
from couchpotato.core.media.movie import MovieTypeBase
|
||||
from couchpotato.core.settings.model import Movie, Release, ReleaseInfo
|
||||
from couchpotato.environment import Env
|
||||
from datetime import date
|
||||
from sqlalchemy.exc import InterfaceError
|
||||
import datetime
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
@@ -19,12 +19,15 @@ import traceback
|
||||
log = CPLog(__name__)
|
||||
|
||||
|
||||
class MovieSearcher(Plugin):
|
||||
class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
|
||||
in_progress = False
|
||||
|
||||
def __init__(self):
|
||||
super(MovieSearcher, self).__init__()
|
||||
|
||||
addEvent('movie.searcher.all', self.searchAll)
|
||||
addEvent('movie.searcher.all_view', self.searchAllView)
|
||||
addEvent('movie.searcher.single', self.single)
|
||||
addEvent('movie.searcher.correct_movie', self.correctMovie)
|
||||
addEvent('movie.searcher.try_next_release', self.tryNextRelease)
|
||||
@@ -48,45 +51,26 @@ class MovieSearcher(Plugin):
|
||||
}"""},
|
||||
})
|
||||
|
||||
if self.conf('run_on_launch', section = 'searcher'):
|
||||
if self.conf('run_on_launch'):
|
||||
addEvent('app.load', self.searchAll)
|
||||
|
||||
addEvent('app.load', self.setCrons)
|
||||
addEvent('setting.save.searcher.cron_day.after', self.setCrons)
|
||||
addEvent('setting.save.searcher.cron_hour.after', self.setCrons)
|
||||
addEvent('setting.save.searcher.cron_minute.after', self.setCrons)
|
||||
|
||||
def setCrons(self):
|
||||
|
||||
fireEvent('schedule.cron', 'movie.searcher.all', self.searchAll,
|
||||
day = self.conf('cron_day', section = 'searcher'), hour = self.conf('cron_hour', section = 'searcher'), minute = self.conf('cron_minute', section = 'searcher'))
|
||||
|
||||
def searchAllView(self, **kwargs):
|
||||
|
||||
in_progress = self.in_progress
|
||||
if not in_progress:
|
||||
fireEventAsync('movie.searcher.all')
|
||||
fireEvent('notify.frontend', type = 'movie.searcher.started', data = True, message = 'Full search started')
|
||||
else:
|
||||
fireEvent('notify.frontend', type = 'movie.searcher.already_started', data = True, message = 'Full search already in progress')
|
||||
fireEventAsync('movie.searcher.all')
|
||||
|
||||
return {
|
||||
'success': not in_progress
|
||||
}
|
||||
|
||||
def getProgress(self, **kwargs):
|
||||
|
||||
return {
|
||||
'progress': self.in_progress
|
||||
'success': not self.in_progress
|
||||
}
|
||||
|
||||
def searchAll(self):
|
||||
|
||||
if self.in_progress:
|
||||
log.info('Search already in progress')
|
||||
fireEvent('notify.frontend', type = 'movie.searcher.already_started', data = True, message = 'Full search already in progress')
|
||||
return
|
||||
|
||||
self.in_progress = True
|
||||
fireEvent('notify.frontend', type = 'movie.searcher.started', data = True, message = 'Full search started')
|
||||
|
||||
db = get_session()
|
||||
|
||||
@@ -166,7 +150,7 @@ class MovieSearcher(Plugin):
|
||||
|
||||
ret = False
|
||||
for quality_type in movie['profile']['types']:
|
||||
if not self.conf('always_search', section = 'searcher') and not self.couldBeReleased(quality_type['quality']['identifier'] in pre_releases, release_dates, movie['library']['year']):
|
||||
if not self.conf('always_search') and not self.couldBeReleased(quality_type['quality']['identifier'] in pre_releases, release_dates, movie['library']['year']):
|
||||
too_early_to_search.append(quality_type['quality']['identifier'])
|
||||
continue
|
||||
|
||||
@@ -382,54 +366,6 @@ class MovieSearcher(Plugin):
|
||||
log.info("Wrong: %s, undetermined naming. Looking for '%s (%s)'", (nzb['name'], movie_name, movie['library']['year']))
|
||||
return False
|
||||
|
||||
def containsOtherQuality(self, nzb, movie_year = None, preferred_quality = {}):
|
||||
|
||||
name = nzb['name']
|
||||
size = nzb.get('size', 0)
|
||||
nzb_words = re.split('\W+', simplifyString(name))
|
||||
|
||||
qualities = fireEvent('quality.all', single = True)
|
||||
|
||||
found = {}
|
||||
for quality in qualities:
|
||||
# Main in words
|
||||
if quality['identifier'] in nzb_words:
|
||||
found[quality['identifier']] = True
|
||||
|
||||
# Alt in words
|
||||
if list(set(nzb_words) & set(quality['alternative'])):
|
||||
found[quality['identifier']] = True
|
||||
|
||||
# Try guessing via quality tags
|
||||
guess = fireEvent('quality.guess', [nzb.get('name')], single = True)
|
||||
if guess:
|
||||
found[guess['identifier']] = True
|
||||
|
||||
# Hack for older movies that don't contain quality tag
|
||||
year_name = fireEvent('scanner.name_year', name, single = True)
|
||||
if len(found) == 0 and movie_year < datetime.datetime.now().year - 3 and not year_name.get('year', None):
|
||||
if size > 3000: # Assume dvdr
|
||||
log.info('Quality was missing in name, assuming it\'s a DVD-R based on the size: %s', (size))
|
||||
found['dvdr'] = True
|
||||
else: # Assume dvdrip
|
||||
log.info('Quality was missing in name, assuming it\'s a DVD-Rip based on the size: %s', (size))
|
||||
found['dvdrip'] = True
|
||||
|
||||
# Allow other qualities
|
||||
for allowed in preferred_quality.get('allow'):
|
||||
if found.get(allowed):
|
||||
del found[allowed]
|
||||
|
||||
return not (found.get(preferred_quality['identifier']) and len(found) == 1)
|
||||
|
||||
def checkIMDB(self, haystack, imdbId):
|
||||
|
||||
for string in haystack:
|
||||
if 'imdb.com/title/' + imdbId in string:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def couldBeReleased(self, is_pre_release, dates, year = None):
|
||||
|
||||
now = int(time.time())
|
||||
|
||||
@@ -15,12 +15,13 @@ class ThePirateBay(TorrentMagnetProvider):
|
||||
|
||||
urls = {
|
||||
'detail': '%s/torrent/%s',
|
||||
'search': '%s/search/%s/%s/7/%d'
|
||||
'search': '%s/search/%s/%s/7/%s'
|
||||
}
|
||||
|
||||
cat_ids = [
|
||||
([207], ['720p', '1080p']),
|
||||
([201], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr', 'brrip']),
|
||||
([201], ['cam', 'ts', 'dvdrip', 'tc', 'r5', 'scr']),
|
||||
([201, 207], ['brrip']),
|
||||
([202], ['dvdr'])
|
||||
]
|
||||
|
||||
@@ -50,10 +51,11 @@ class ThePirateBay(TorrentMagnetProvider):
|
||||
|
||||
page = 0
|
||||
total_pages = 1
|
||||
cats = self.getCatId(quality['identifier'])
|
||||
|
||||
while page < total_pages:
|
||||
|
||||
search_url = self.urls['search'] % (self.getDomain(), tryUrlencode('"%s" %s' % (title, movie['library']['year'])), page, self.getCatId(quality['identifier'])[0])
|
||||
search_url = self.urls['search'] % (self.getDomain(), tryUrlencode('"%s" %s' % (title, movie['library']['year'])), page, ','.join(str(x) for x in cats))
|
||||
page += 1
|
||||
|
||||
data = self.getHTMLData(search_url)
|
||||
|
||||
@@ -2,7 +2,7 @@ from __future__ import with_statement
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import isInt, toUnicode
|
||||
from couchpotato.core.helpers.variable import mergeDicts, tryInt
|
||||
from couchpotato.core.helpers.variable import mergeDicts, tryInt, tryFloat
|
||||
from couchpotato.core.settings.model import Properties
|
||||
import ConfigParser
|
||||
import os.path
|
||||
@@ -77,9 +77,17 @@ class Settings(object):
|
||||
|
||||
def registerDefaults(self, section_name, options = {}, save = True):
|
||||
self.addSection(section_name)
|
||||
|
||||
for option_name, option in options.iteritems():
|
||||
self.setDefault(section_name, option_name, option.get('default', ''))
|
||||
|
||||
# Migrate old settings from old location to the new location
|
||||
if option.get('migrate_from'):
|
||||
if self.p.has_option(option.get('migrate_from'), option_name):
|
||||
previous_value = self.p.get(option.get('migrate_from'), option_name)
|
||||
self.p.set(section_name, option_name, previous_value)
|
||||
self.p.remove_option(option.get('migrate_from'), option_name)
|
||||
|
||||
if option.get('type'):
|
||||
self.setType(section_name, option_name, option.get('type'))
|
||||
|
||||
@@ -122,7 +130,7 @@ class Settings(object):
|
||||
try:
|
||||
return self.p.getfloat(section, option)
|
||||
except:
|
||||
return tryInt(self.p.get(section, option))
|
||||
return tryFloat(self.p.get(section, option))
|
||||
|
||||
def getUnicode(self, section, option):
|
||||
value = self.p.get(section, option).decode('unicode_escape')
|
||||
|
||||
@@ -56,13 +56,13 @@ Page.Wanted = new Class({
|
||||
self.search_progress = Api.request('movie.searcher.progress', {
|
||||
'onComplete': function(json){
|
||||
self.search_in_progress = true;
|
||||
if(!json.progress){
|
||||
if(!json.movie){
|
||||
clearInterval(self.progress_interval);
|
||||
self.search_in_progress = false;
|
||||
self.manual_search.set('text', start_text);
|
||||
}
|
||||
else {
|
||||
var progress = json.progress;
|
||||
var progress = json.movie;
|
||||
self.manual_search.set('text', 'Searching.. (' + (((progress.total-progress.to_go)/progress.total)*100).round() + '%)');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user