@ -4,13 +4,13 @@ import re
import time
import traceback
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 , getIdentifier , tryInt
from couchpotato . core . logger import CPLog
from couchpotato . core . media . _base . searcher . base import SearcherBase
from couchpotato . core . media . _base . searcher . main import Searcher
from couchpotato . core . media . _base . searcher . main import SearchSetupError
from couchpotato . core . media . movie import MovieTypeBase
from couchpotato . environment import Env
@ -20,7 +20,7 @@ log = CPLog(__name__)
autoload = ' MovieSearcher '
class MovieSearcher ( SearcherBase , MovieTypeBase ) :
class MovieSearcher ( Searcher , MovieTypeBase ) :
in_progress = False
@ -110,153 +110,6 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
self . in_progress = False
def single ( self , movie , search_protocols = None , manual = False , force_download = False ) :
# Find out search type
try :
if not search_protocols :
search_protocols = fireEvent ( ' searcher.protocols ' , single = True )
except SearchSetupError :
return
if not movie [ ' profile_id ' ] or ( movie [ ' status ' ] == ' done ' and not manual ) :
log . debug ( ' Movie doesn \' t have a profile or already done, assuming in manage tab. ' )
fireEvent ( ' media.restatus ' , movie [ ' _id ' ] , single = True )
return
default_title = getTitle ( movie )
if not default_title :
log . error ( ' No proper info found for movie, removing it from library to stop it from causing more issues. ' )
fireEvent ( ' media.delete ' , movie [ ' _id ' ] , single = True )
return
# Update media status and check if it is still not done (due to the stop searching after feature
if fireEvent ( ' media.restatus ' , movie [ ' _id ' ] , single = True ) == ' done ' :
log . debug ( ' No better quality found, marking movie %s as done. ' , default_title )
pre_releases = fireEvent ( ' quality.pre_releases ' , single = True )
release_dates = fireEvent ( ' movie.update_release_dates ' , movie [ ' _id ' ] , merge = True )
found_releases = [ ]
previous_releases = movie . get ( ' releases ' , [ ] )
too_early_to_search = [ ]
outside_eta_results = 0
always_search = self . conf ( ' always_search ' )
ignore_eta = manual
total_result_count = 0
fireEvent ( ' notify.frontend ' , type = ' movie.searcher.started ' , data = { ' _id ' : movie [ ' _id ' ] } , message = ' Searching for " %s " ' % default_title )
# Ignore eta once every 7 days
if not always_search :
prop_name = ' last_ignored_eta. %s ' % movie [ ' _id ' ]
last_ignored_eta = float ( Env . prop ( prop_name , default = 0 ) )
if last_ignored_eta < time . time ( ) - 604800 :
ignore_eta = True
Env . prop ( prop_name , value = time . time ( ) )
db = get_db ( )
profile = db . get ( ' id ' , movie [ ' profile_id ' ] )
ret = False
for index , q_identifier in enumerate ( profile . get ( ' qualities ' , [ ] ) ) :
quality_custom = {
' index ' : index ,
' quality ' : q_identifier ,
' finish ' : profile [ ' finish ' ] [ index ] ,
' wait_for ' : tryInt ( profile [ ' wait_for ' ] [ index ] ) ,
' 3d ' : profile [ ' 3d ' ] [ index ] if profile . get ( ' 3d ' ) else False ,
' minimum_score ' : profile . get ( ' minimum_score ' , 1 ) ,
}
could_not_be_released = not self . couldBeReleased ( q_identifier in pre_releases , release_dates , movie [ ' info ' ] [ ' year ' ] )
if not always_search and could_not_be_released :
too_early_to_search . append ( q_identifier )
# Skip release, if ETA isn't ignored
if not ignore_eta :
continue
has_better_quality = 0
# See if better quality is available
for release in movie . get ( ' releases ' , [ ] ) :
if release [ ' status ' ] not in [ ' available ' , ' ignored ' , ' failed ' ] :
is_higher = fireEvent ( ' quality.ishigher ' , \
{ ' identifier ' : q_identifier , ' is_3d ' : quality_custom . get ( ' 3d ' , 0 ) } , \
{ ' identifier ' : release [ ' quality ' ] , ' is_3d ' : release . get ( ' is_3d ' , 0 ) } , \
profile , single = True )
if is_higher != ' higher ' :
has_better_quality + = 1
# Don't search for quality lower then already available.
if has_better_quality > 0 :
log . info ( ' Better quality ( %s ) already available or snatched for %s ' , ( q_identifier , default_title ) )
fireEvent ( ' media.restatus ' , movie [ ' _id ' ] , single = True )
break
quality = fireEvent ( ' quality.single ' , identifier = q_identifier , single = True )
log . info ( ' Search for %s in %s %s ' , ( default_title , quality [ ' label ' ] , ' ignoring ETA ' if always_search or ignore_eta else ' ' ) )
# Extend quality with profile customs
quality [ ' custom ' ] = quality_custom
results = fireEvent ( ' searcher.search ' , search_protocols , movie , quality , single = True ) or [ ]
# Check if movie isn't deleted while searching
if not fireEvent ( ' media.get ' , movie . get ( ' _id ' ) , single = True ) :
break
# Add them to this movie releases list
found_releases + = fireEvent ( ' release.create_from_search ' , results , movie , quality , single = True )
results_count = len ( found_releases )
total_result_count + = results_count
if results_count == 0 :
log . debug ( ' Nothing found for %s in %s ' , ( default_title , quality [ ' label ' ] ) )
# Keep track of releases found outside ETA window
outside_eta_results + = results_count if could_not_be_released else 0
# Don't trigger download, but notify user of available releases
if could_not_be_released and results_count > 0 :
log . debug ( ' Found %s releases for " %s " , but ETA isn \' t correct yet. ' , ( results_count , default_title ) )
# Try find a valid result and download it
if ( force_download or not could_not_be_released or always_search ) and fireEvent ( ' release.try_download_result ' , results , movie , quality_custom , single = True ) :
ret = True
# Remove releases that aren't found anymore
temp_previous_releases = [ ]
for release in previous_releases :
if release . get ( ' status ' ) == ' available ' and release . get ( ' identifier ' ) not in found_releases :
fireEvent ( ' release.delete ' , release . get ( ' _id ' ) , single = True )
else :
temp_previous_releases . append ( release )
previous_releases = temp_previous_releases
del temp_previous_releases
# Break if CP wants to shut down
if self . shuttingDown ( ) or ret :
break
if total_result_count > 0 :
fireEvent ( ' media.tag ' , movie [ ' _id ' ] , ' recent ' , update_edited = True , single = True )
if len ( too_early_to_search ) > 0 :
log . info2 ( ' Too early to search for %s , %s ' , ( too_early_to_search , default_title ) )
if outside_eta_results > 0 :
message = ' Found %s releases for " %s " before ETA. Select and download via the dashboard. ' % ( outside_eta_results , default_title )
log . info ( message )
if not manual :
fireEvent ( ' media.available ' , message = message , data = { } )
fireEvent ( ' notify.frontend ' , type = ' movie.searcher.ended ' , data = { ' _id ' : movie [ ' _id ' ] } )
return ret
def correctRelease ( self , nzb = None , media = None , quality = None , * * kwargs ) :
if media . get ( ' type ' ) != ' movie ' : return
@ -271,19 +124,23 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
return False
# Check for required and ignored words
if not fireEvent ( ' searcher.correct_words ' , nzb [ ' name ' ] , media , single = True ) :
if not self . correctWords ( nzb [ ' name ' ] , media ) :
return False
preferred_quality = quality if quality else fireEvent ( ' quality.single ' , identifier = quality [ ' identifier ' ] , single = True )
# Contains lower quality string
contains_other = fireEvent ( ' searcher.contains_other_quality ' , nzb , movie_year = media [ ' info ' ] [ ' year ' ] , preferred_quality = preferred_quality , types = [ self . _type ] , single = True )
contains_other = self . containsOtherQuality (
nzb , movie_year = media [ ' info ' ] [ ' year ' ] ,
preferred_quality = preferred_quality ,
types = [ self . _type ] )
if contains_other != False :
log . info2 ( ' Wrong: %s , looking for %s , found %s ' , ( nzb [ ' name ' ] , quality [ ' label ' ] , [ x for x in contains_other ] if contains_other else ' no quality ' ) )
return False
# Contains lower quality string
if not fireEvent ( ' searcher.correct_3d ' , nzb , preferred_quality = preferred_quality , types = [ self . _type ] , single = True ) :
# FIXME: media was passed instead of nzb here before
if not self . correct3D ( nzb , preferred_quality = preferred_quality , types = [ self . _type ] ) :
log . info2 ( ' Wrong: %s , %s looking for %s in 3D ' , ( nzb [ ' name ' ] , ( ' ' if preferred_quality [ ' custom ' ] . get ( ' 3d ' ) else ' NOT ' ) , quality [ ' label ' ] ) )
return False
@ -318,23 +175,24 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
for movie_title in possibleTitles ( raw_title ) :
movie_words = re . split ( ' \ W+ ' , simplifyString ( movie_title ) )
if fireEvent ( ' searcher.correct_name ' , nzb [ ' name ' ] , movie_title , single = Tru e ) :
if self . correctName ( nzb [ ' name ' ] , movie_title ) :
# if no IMDB link, at least check year range 1
if len ( movie_words ) > 2 and fireEvent ( ' searcher.correct_year ' , nzb [ ' name ' ] , media [ ' info ' ] [ ' year ' ] , 1 , single = True ) :
if len ( movie_words ) > 2 and self . correctYear ( nzb [ ' name ' ] , media [ ' info ' ] [ ' year ' ] , 1 ) :
return True
# if no IMDB link, at least check year
if len ( movie_words ) < = 2 and fireEvent ( ' searcher.correct_year ' , nzb [ ' name ' ] , media [ ' info ' ] [ ' year ' ] , 0 , single = True ) :
if len ( movie_words ) < = 2 and self . correctYear ( nzb [ ' name ' ] , media [ ' info ' ] [ ' year ' ] , 0 ) :
return True
log . info ( " Wrong: %s , undetermined naming. Looking for ' %s ( %s ) ' " , ( nzb [ ' name ' ] , media_title , media [ ' info ' ] [ ' year ' ] ) )
return False
def couldBeReleased ( self , is_pre_release , dates , year = None ) :
def couldBeReleased ( self , is_pre_release , dates , media ) :
now = int ( time . time ( ) )
now_year = date . today ( ) . year
now_month = date . today ( ) . month
year = media [ ' info ' ] [ ' year ' ]
if ( year is None or year < now_year - 1 or ( year < = now_year - 1 and now_month > 4 ) ) and ( not dates or ( dates . get ( ' theater ' , 0 ) == 0 and dates . get ( ' dvd ' , 0 ) == 0 ) ) :
return True
@ -405,9 +263,10 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
if media [ ' type ' ] == ' movie ' :
return getTitle ( media )
class SearchSetupError ( Exception ) :
pass
def getProfileId ( self , media ) :
assert media [ ' type ' ] == ' movie '
return media . get ( ' profile_id ' )
config = [ {
' name ' : ' moviesearcher ' ,