Working TV correctRelease function with quality, identifier and show title checking.
This commit is contained in:
@@ -146,9 +146,9 @@ def getImdb(txt, check_inside = False, multiple = False):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def tryInt(s):
|
def tryInt(s, default=0):
|
||||||
try: return int(s)
|
try: return int(s)
|
||||||
except: return 0
|
except: return default
|
||||||
|
|
||||||
def tryFloat(s):
|
def tryFloat(s):
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
import pprint
|
import pprint
|
||||||
|
import re
|
||||||
from couchpotato import get_session, Env
|
from couchpotato import get_session, Env
|
||||||
from couchpotato.core.event import addEvent, fireEvent
|
from couchpotato.core.event import addEvent, fireEvent
|
||||||
from couchpotato.core.helpers.variable import getTitle
|
from couchpotato.core.helpers.encoding import simplifyString
|
||||||
|
from couchpotato.core.helpers.variable import getTitle, tryInt, possibleTitles
|
||||||
from couchpotato.core.logger import CPLog
|
from couchpotato.core.logger import CPLog
|
||||||
from couchpotato.core.media._base.searcher.main import SearchSetupError
|
from couchpotato.core.media._base.searcher.main import SearchSetupError
|
||||||
from couchpotato.core.plugins.base import Plugin
|
from couchpotato.core.plugins.base import Plugin
|
||||||
from couchpotato.core.settings.model import Media
|
from couchpotato.core.settings.model import Media, Library
|
||||||
|
from caper import Caper
|
||||||
|
|
||||||
log = CPLog(__name__)
|
log = CPLog(__name__)
|
||||||
|
|
||||||
@@ -14,6 +17,15 @@ class ShowSearcher(Plugin):
|
|||||||
|
|
||||||
in_progress = False
|
in_progress = False
|
||||||
|
|
||||||
|
# TODO come back to this later, think this could be handled better
|
||||||
|
quality_map = {
|
||||||
|
'webdl_1080p': {'resolution': ['1080p'], 'source': ['webdl']},
|
||||||
|
'webdl_720p': {'resolution': ['720p'], 'source': ['webdl']},
|
||||||
|
|
||||||
|
'hdtv_720p': {'resolution': ['720p'], 'source': ['hdtv']},
|
||||||
|
'hdtv_sd': {'resolution': ['480p', None], 'source': ['hdtv']},
|
||||||
|
}
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(ShowSearcher, self).__init__()
|
super(ShowSearcher, self).__init__()
|
||||||
|
|
||||||
@@ -21,10 +33,12 @@ class ShowSearcher(Plugin):
|
|||||||
addEvent('searcher.correct_release', self.correctRelease)
|
addEvent('searcher.correct_release', self.correctRelease)
|
||||||
addEvent('searcher.get_search_title', self.getSearchTitle)
|
addEvent('searcher.get_search_title', self.getSearchTitle)
|
||||||
|
|
||||||
|
self.caper = Caper()
|
||||||
|
|
||||||
def _lookupMedia(self, media):
|
def _lookupMedia(self, media):
|
||||||
db = get_session()
|
db = get_session()
|
||||||
|
|
||||||
media_library = db.query(Media).filter_by(id = media['id']).first().library
|
media_library = db.query(Library).filter_by(id = media['library_id']).first()
|
||||||
|
|
||||||
show = None
|
show = None
|
||||||
season = None
|
season = None
|
||||||
@@ -45,6 +59,8 @@ class ShowSearcher(Plugin):
|
|||||||
return show, season, episode
|
return show, season, episode
|
||||||
|
|
||||||
def single(self, media, search_protocols = None):
|
def single(self, media, search_protocols = None):
|
||||||
|
pprint.pprint(media)
|
||||||
|
|
||||||
if media['type'] == 'show':
|
if media['type'] == 'show':
|
||||||
# TODO handle show searches (scan all seasons)
|
# TODO handle show searches (scan all seasons)
|
||||||
return
|
return
|
||||||
@@ -70,7 +86,7 @@ class ShowSearcher(Plugin):
|
|||||||
found_releases = []
|
found_releases = []
|
||||||
too_early_to_search = []
|
too_early_to_search = []
|
||||||
|
|
||||||
default_title = getTitle(media['library'])
|
default_title = self.getSearchTitle(media['library'])
|
||||||
if not default_title:
|
if not default_title:
|
||||||
log.error('No proper info found for episode, removing it from library to cause it from having more issues.')
|
log.error('No proper info found for episode, removing it from library to cause it from having more issues.')
|
||||||
#fireEvent('episode.delete', episode['id'], single = True)
|
#fireEvent('episode.delete', episode['id'], single = True)
|
||||||
@@ -126,14 +142,118 @@ class ShowSearcher(Plugin):
|
|||||||
if not fireEvent('searcher.correct_words', release['name'], media, single = True):
|
if not fireEvent('searcher.correct_words', release['name'], media, single = True):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
preferred_quality = fireEvent('quality.single', identifier = quality['identifier'], single = True)
|
#pprint.pprint(release)
|
||||||
|
|
||||||
# Contains lower quality string
|
show, season, episode = self._lookupMedia(media)
|
||||||
if fireEvent('searcher.contains_other_quality', release, preferred_quality = preferred_quality, single = True):
|
if show is None or season is None:
|
||||||
log.info2('Wrong: %s, looking for %s', (release['name'], quality['label']))
|
log.error('Unable to find show or season library in database, missing required data for searching')
|
||||||
|
return
|
||||||
|
|
||||||
|
release_info = self.caper.parse(release['name'])
|
||||||
|
if len(release_info.chains) < 1:
|
||||||
|
log.info2('Wrong: %s, unable to parse release name (no chains)', release['name'])
|
||||||
return False
|
return False
|
||||||
|
|
||||||
pprint.pprint(release)
|
# TODO look at all chains
|
||||||
|
chain = release_info.chains[0]
|
||||||
|
|
||||||
|
if not self.correctQuality(chain, quality['identifier']):
|
||||||
|
log.info('Wrong: %s, quality does not match', release['name'])
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not self.correctIdentifier(chain, media):
|
||||||
|
log.info('Wrong: %s, identifier does not match', release['name'])
|
||||||
|
return False
|
||||||
|
|
||||||
|
#print chain.weight
|
||||||
|
#pprint.pprint(chain.info)
|
||||||
|
|
||||||
|
if 'show_name' not in chain.info or not len(chain.info['show_name']):
|
||||||
|
log.info('Wrong: %s, missing show name in parsed result', release['name'])
|
||||||
|
return False
|
||||||
|
|
||||||
|
chain_words = [x.lower() for x in chain.info['show_name']]
|
||||||
|
chain_title = ' '.join(chain_words)
|
||||||
|
|
||||||
|
library_title = None
|
||||||
|
|
||||||
|
# Check show titles match
|
||||||
|
for raw_title in show.titles:
|
||||||
|
for valid_words in [x.split(' ') for x in possibleTitles(raw_title.title)]:
|
||||||
|
if not library_title:
|
||||||
|
library_title = ' '.join(valid_words)
|
||||||
|
|
||||||
|
if valid_words == chain_words:
|
||||||
|
return True
|
||||||
|
|
||||||
|
log.info("Wrong: title '%s', undetermined show naming. Looking for '%s (%s)'", (chain_title, library_title, media['library']['year']))
|
||||||
|
return False
|
||||||
|
|
||||||
|
def correctQuality(self, chain, quality_identifier):
|
||||||
|
if quality_identifier not in self.quality_map:
|
||||||
|
log.info2('Wrong: unknown preferred quality %s for TV searching', quality_identifier)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if 'video' not in chain.info:
|
||||||
|
log.info2('Wrong: no video tags found')
|
||||||
|
return False
|
||||||
|
|
||||||
|
video_tags = self.quality_map[quality_identifier]
|
||||||
|
|
||||||
|
if not self.chainMatches(chain, 'video', video_tags):
|
||||||
|
log.info2('Wrong: %s tags not in chain', video_tags)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def correctIdentifier(self, chain, media):
|
||||||
|
required_id = self.getIdentifier(media['library'], 'season_number', 'episode_number')
|
||||||
|
|
||||||
|
if 'identifier' not in chain.info:
|
||||||
|
return False
|
||||||
|
|
||||||
|
# TODO could be handled better?
|
||||||
|
if len(chain.info['identifier']) != 1:
|
||||||
|
return False
|
||||||
|
identifier = chain.info['identifier'][0]
|
||||||
|
|
||||||
|
# TODO air by date episodes
|
||||||
|
release_id = self.getIdentifier(identifier, 'season', 'episode')
|
||||||
|
|
||||||
|
if required_id != release_id:
|
||||||
|
log.info2('Wrong: required identifier %s does not match release identifier %s', (str(required_id), str(release_id)))
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getIdentifier(self, d, episode_key, season_key):
|
||||||
|
return (
|
||||||
|
tryInt(d.get(season_key), None) if season_key in d else None,
|
||||||
|
tryInt(d.get(episode_key), None) if episode_key in d else None
|
||||||
|
)
|
||||||
|
|
||||||
|
def chainMatches(self, chain, group, tags):
|
||||||
|
found_tags = []
|
||||||
|
|
||||||
|
for match in chain.info[group]:
|
||||||
|
for ck, cv in match.items():
|
||||||
|
if ck in tags and self.cleanMatchValue(cv) in tags[ck]:
|
||||||
|
found_tags.append(ck)
|
||||||
|
|
||||||
|
|
||||||
|
if set(tags.keys()) == set(found_tags):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return set([key for key, value in tags.items() if value]) == set(found_tags)
|
||||||
|
|
||||||
|
def cleanMatchValue(self, value):
|
||||||
|
value = value.lower()
|
||||||
|
value = value.strip()
|
||||||
|
|
||||||
|
for ch in [' ', '-', '.']:
|
||||||
|
value = value.replace(ch, '')
|
||||||
|
|
||||||
|
return value
|
||||||
|
|
||||||
def getSearchTitle(self, media):
|
def getSearchTitle(self, media):
|
||||||
show, season, episode = self._lookupMedia(media)
|
show, season, episode = self._lookupMedia(media)
|
||||||
|
|||||||
@@ -26,7 +26,15 @@ class QualityPlugin(Plugin):
|
|||||||
{'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr'], 'ext':['avi', 'mpg', 'mpeg']},
|
{'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr'], 'ext':['avi', 'mpg', 'mpeg']},
|
||||||
{'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
|
{'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
|
||||||
{'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
|
{'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
|
||||||
{'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']}
|
{'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': [], 'ext':['avi', 'mpg', 'mpeg']},
|
||||||
|
|
||||||
|
# TODO come back to this later, think this could be handled better
|
||||||
|
# WEB-DL
|
||||||
|
{'identifier': 'webdl_1080p', 'hd': True, 'size': (800, 5000), 'label': 'WEB-DL - 1080p', 'width': 1920, 'height': 1080, 'alternative': [], 'allow': [], 'ext':['mkv']},
|
||||||
|
{'identifier': 'webdl_720p', 'hd': True, 'size': (800, 5000), 'label': 'WEB-DL - 720p', 'width': 1280, 'height': 720, 'alternative': [], 'allow': [], 'ext':['mkv']},
|
||||||
|
# HDTV
|
||||||
|
{'identifier': 'hdtv_720p', 'hd': True, 'size': (800, 5000), 'label': 'HDTV - 720p', 'width': 1280, 'height': 720, 'alternative': [], 'allow': [], 'ext':['mkv']},
|
||||||
|
{'identifier': 'hdtv_sd', 'hd': False, 'size': (100, 1000), 'label': 'HDTV - SD', 'width': 720, 'alternative': [], 'allow': [], 'ext':['mkv', 'mp4', 'avi']},
|
||||||
]
|
]
|
||||||
pre_releases = ['cam', 'ts', 'tc', 'r5', 'scr']
|
pre_releases = ['cam', 'ts', 'tc', 'r5', 'scr']
|
||||||
|
|
||||||
|
|||||||
@@ -36,21 +36,22 @@ class Base(TorrentProvider):
|
|||||||
log.warning('Unable to find category for quality %s', quality_identifier)
|
log.warning('Unable to find category for quality %s', quality_identifier)
|
||||||
return
|
return
|
||||||
|
|
||||||
return self.urls['search'] % (cat_id, tryUrlencode(query))
|
return self.urls['search'] % (cat_id, tryUrlencode(query).replace('%', '%%'))
|
||||||
|
|
||||||
def _searchOnTitle(self, title, media, quality, results):
|
def _searchOnTitle(self, title, media, quality, results):
|
||||||
|
|
||||||
freeleech = '' if not self.conf('freeleech') else '&free=on'
|
freeleech = '' if not self.conf('freeleech') else '&free=on'
|
||||||
|
|
||||||
|
base_url = self.buildUrl(title, media, quality)
|
||||||
|
if not base_url: return
|
||||||
|
|
||||||
pages = 1
|
pages = 1
|
||||||
current_page = 1
|
current_page = 1
|
||||||
while current_page <= pages and not self.shuttingDown():
|
while current_page <= pages and not self.shuttingDown():
|
||||||
url = self.buildUrl(title, media, quality)
|
data = self.getHTMLData(
|
||||||
if not url: return
|
base_url % (freeleech, current_page),
|
||||||
|
opener = self.login_opener
|
||||||
url = url % (freeleech, current_page)
|
)
|
||||||
|
|
||||||
data = self.getHTMLData(url, opener = self.login_opener)
|
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
html = BeautifulSoup(data)
|
html = BeautifulSoup(data)
|
||||||
@@ -135,12 +136,12 @@ class Show(ShowProvider, Base):
|
|||||||
|
|
||||||
cat_ids = [
|
cat_ids = [
|
||||||
('season', [
|
('season', [
|
||||||
([65], ['hdtv', '480p', '720p', '1080p']),
|
([65], ['hdtv_sd', 'hdtv_720p', 'webdl_720p', 'webdl_1080p']),
|
||||||
]),
|
]),
|
||||||
('episode', [
|
('episode', [
|
||||||
([5], ['720p', '1080p']),
|
([5], ['hdtv_720p', 'webdl_720p', 'webdl_1080p']),
|
||||||
([78], ['480p']),
|
([78], ['hdtv_sd']),
|
||||||
([4, 79], ['hdtv'])
|
([4, 79], ['hdtv_sd'])
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user