Add and make use of renamer.scanfolder in downloaders
This is the next step in closing the loop between the downloaders and CPS. The download_id and folder from the downloader are used to find the downloaded files and start the renamer. This is done by adding an additional API call: renamer.scanfolder. I tested this for SabNZBd only (!) and everything works as expected. I also added transmission with thanks @manusfreedom for setting this up in f1cf0d91da. @manusfreedom, please check if this works as expected. Note that transmission now has a feature which is not in the other torrent providers: it waits until the seed ratio is met and then removes the torrent. I opened a topic in the forum to discuss how we want to deal with torrents: https://couchpota.to/forum/thread-1704.html
This commit is contained in:
@@ -2,7 +2,7 @@ from couchpotato import get_session
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import toUnicode, ss
|
||||
from couchpotato.core.helpers.request import jsonified
|
||||
from couchpotato.core.helpers.request import getParams, jsonified, getParam
|
||||
from couchpotato.core.helpers.variable import getExt, mergeDicts, getTitle, \
|
||||
getImdb
|
||||
from couchpotato.core.logger import CPLog
|
||||
@@ -31,6 +31,17 @@ class Renamer(Plugin):
|
||||
})
|
||||
|
||||
addEvent('renamer.scan', self.scan)
|
||||
|
||||
addApiView('renamer.scanfolder', self.scanfolderView, docs = {
|
||||
'desc': 'For the renamer to check for new files to rename in a specified folder',
|
||||
'params': {
|
||||
'movie_folder': {'desc': 'The folder of the movie to scan'},
|
||||
'downloader' : {'desc': 'Optional: The downloader this movie has been downloaded with'},
|
||||
'download_id': {'desc': 'Optional: The downloader\'s nzb/torrent ID'},
|
||||
},
|
||||
})
|
||||
|
||||
addEvent('renamer.scanfolder', self.scanfolder)
|
||||
addEvent('renamer.check_snatched', self.checkSnatched)
|
||||
|
||||
addEvent('app.load', self.scan)
|
||||
@@ -51,6 +62,26 @@ class Renamer(Plugin):
|
||||
})
|
||||
|
||||
def scan(self):
|
||||
self.scanfolder()
|
||||
|
||||
def scanfolderView(self):
|
||||
|
||||
params = getParams()
|
||||
movie_folder = params.get('movie_folder', None)
|
||||
downloader = params.get('downloader', None)
|
||||
download_id = params.get('download_id', None)
|
||||
|
||||
fireEventAsync('renamer.scanfolder',
|
||||
movie_folder = movie_folder,
|
||||
downloader = downloader,
|
||||
download_id = download_id
|
||||
)
|
||||
|
||||
return jsonified({
|
||||
'success': True
|
||||
})
|
||||
|
||||
def scanfolder(self, movie_folder = None, downloader = None, download_id = None):
|
||||
|
||||
if self.isDisabled():
|
||||
return
|
||||
@@ -59,18 +90,43 @@ class Renamer(Plugin):
|
||||
log.info('Renamer is already running, if you see this often, check the logs above for errors.')
|
||||
return
|
||||
|
||||
self.renaming_started = True
|
||||
|
||||
# Check to see if the "to" folder is inside the "from" folder.
|
||||
if not os.path.isdir(self.conf('from')) or not os.path.isdir(self.conf('to')):
|
||||
if movie_folder and not os.path.isdir(movie_folder): # or not os.path.isdir(self.conf('from')) or not os.path.isdir(self.conf('to')):
|
||||
log.debug('"To" and "From" have to exist.')
|
||||
return
|
||||
elif self.conf('from') in self.conf('to'):
|
||||
log.error('The "to" can\'t be inside of the "from" folder. You\'ll get an infinite loop.')
|
||||
return
|
||||
elif (movie_folder and movie_folder in [self.conf('to'), self.conf('from')]):
|
||||
log.error('The "to" and "from" folders can\'t be inside of or the same as the provided movie folder.')
|
||||
return
|
||||
|
||||
groups = fireEvent('scanner.scan', folder = self.conf('from'), single = True)
|
||||
# make sure the movie folder name is included in the search
|
||||
folder = None
|
||||
movie_files = []
|
||||
if movie_folder:
|
||||
log.info('Scanning movie folder %s...', movie_folder)
|
||||
movie_folder = movie_folder.rstrip(os.path.sep)
|
||||
folder = os.path.dirname(movie_folder)
|
||||
|
||||
self.renaming_started = True
|
||||
# Get all files from the specified folder
|
||||
try:
|
||||
for root, folders, names in os.walk(movie_folder):
|
||||
movie_files.extend([os.path.join(root, name) for name in names])
|
||||
except:
|
||||
log.error('Failed getting files from %s: %s', (movie_folder, traceback.format_exc()))
|
||||
|
||||
groups = fireEvent('scanner.scan', folder = folder if folder else self.conf('from'), files = movie_files, downloader = downloader, download_id = download_id, single = True)
|
||||
|
||||
# Make sure only one movie was found if a download ID is provided
|
||||
if downloader and download_id and not len(groups) == 1:
|
||||
log.info('Download ID provided (%s), but more than one group found (%s). Ignoring Download ID...', (download_id, len(groups)))
|
||||
downloader = None
|
||||
download_id = None
|
||||
groups = fireEvent('scanner.scan', folder = folder if folder else self.conf('from'), files = movie_files, single = True)
|
||||
|
||||
destination = self.conf('to')
|
||||
folder_name = self.conf('folder_name')
|
||||
file_name = self.conf('file_name')
|
||||
@@ -597,7 +653,10 @@ class Renamer(Plugin):
|
||||
db.commit()
|
||||
elif item['status'] == 'completed':
|
||||
log.info('Download of %s completed!', item['name'])
|
||||
scan_required = True
|
||||
if item['id'] and item['downloader'] and item['folder']:
|
||||
fireEventAsync('renamer.scanfolder', movie_folder = item['folder'], downloader = item['downloader'], download_id = item['id'])
|
||||
else:
|
||||
scan_required = True
|
||||
|
||||
found = True
|
||||
break
|
||||
|
||||
@@ -4,7 +4,7 @@ from couchpotato.core.helpers.encoding import toUnicode, simplifyString, ss
|
||||
from couchpotato.core.helpers.variable import getExt, getImdb, tryInt
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.settings.model import File, Movie
|
||||
from couchpotato.core.settings.model import File, Movie, Release, ReleaseInfo
|
||||
from enzyme.exceptions import NoParserError, ParseError
|
||||
from guessit import guess_movie_info
|
||||
from subliminal.videos import Video
|
||||
@@ -101,7 +101,7 @@ class Scanner(Plugin):
|
||||
addEvent('scanner.name_year', self.getReleaseNameYear)
|
||||
addEvent('scanner.partnumber', self.getPartNumber)
|
||||
|
||||
def scan(self, folder = None, files = None, simple = False, newer_than = 0, on_found = None):
|
||||
def scan(self, folder = None, files = None, downloader = None, download_id = None, simple = False, newer_than = 0, on_found = None):
|
||||
|
||||
folder = ss(os.path.normpath(folder))
|
||||
|
||||
@@ -119,8 +119,7 @@ class Scanner(Plugin):
|
||||
try:
|
||||
files = []
|
||||
for root, dirs, walk_files in os.walk(folder):
|
||||
for filename in walk_files:
|
||||
files.append(os.path.join(root, filename))
|
||||
files.extend(os.path.join(root, filename) for filename in walk_files)
|
||||
except:
|
||||
log.error('Failed getting files from %s: %s', (folder, traceback.format_exc()))
|
||||
else:
|
||||
@@ -129,6 +128,24 @@ class Scanner(Plugin):
|
||||
|
||||
db = get_session()
|
||||
|
||||
# Get the release with the downloader ID that was downloded by the downloader
|
||||
download_quality = None
|
||||
download_imdb_id = None
|
||||
if downloader and download_id:
|
||||
# NOTE TO RUUD: Don't really know how to do this better... but there must be a way...?
|
||||
rlsnfo_dwnlds = db.query(ReleaseInfo).filter_by(identifier = 'download_downloader', value = downloader)
|
||||
rlsnfo_ids = db.query(ReleaseInfo).filter_by(identifier = 'download_id', value = download_id)
|
||||
for rlsnfo_dwnld in rlsnfo_dwnlds:
|
||||
for rlsnfo_id in rlsnfo_ids:
|
||||
if rlsnfo_id.release == rlsnfo_dwnld.release:
|
||||
rls = rlsnfo_id.release
|
||||
|
||||
if rls:
|
||||
download_imdb_id = rls.movie.library.identifier
|
||||
download_quality = rls.quality.identifier
|
||||
else:
|
||||
log.error('Download ID %s from downloader %s not found in releases', (download_id, downloader))
|
||||
|
||||
for file_path in files:
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
@@ -346,7 +363,7 @@ class Scanner(Plugin):
|
||||
continue
|
||||
|
||||
log.debug('Getting metadata for %s', identifier)
|
||||
group['meta_data'] = self.getMetaData(group, folder = folder)
|
||||
group['meta_data'] = self.getMetaData(group, folder = folder, download_quality = download_quality)
|
||||
|
||||
# Subtitle meta
|
||||
group['subtitle_language'] = self.getSubtitleLanguage(group) if not simple else {}
|
||||
@@ -376,7 +393,7 @@ class Scanner(Plugin):
|
||||
del group['unsorted_files']
|
||||
|
||||
# Determine movie
|
||||
group['library'] = self.determineMovie(group)
|
||||
group['library'] = self.determineMovie(group, download_imdb_id = download_imdb_id)
|
||||
if not group['library']:
|
||||
log.error('Unable to determine movie: %s', group['identifiers'])
|
||||
else:
|
||||
@@ -401,7 +418,7 @@ class Scanner(Plugin):
|
||||
|
||||
return processed_movies
|
||||
|
||||
def getMetaData(self, group, folder = ''):
|
||||
def getMetaData(self, group, folder = '', download_quality = None):
|
||||
|
||||
data = {}
|
||||
files = list(group['files']['movie'])
|
||||
@@ -423,10 +440,14 @@ class Scanner(Plugin):
|
||||
|
||||
if data.get('audio'): break
|
||||
|
||||
# Use the quality guess first, if that failes use the quality we wanted to download
|
||||
data['quality'] = fireEvent('quality.guess', files = files, extra = data, single = True)
|
||||
if not data['quality']:
|
||||
data['quality'] = fireEvent('quality.single', 'dvdr' if group['is_dvd'] else 'dvdrip', single = True)
|
||||
|
||||
if download_quality:
|
||||
data['quality'] = fireEvent('quality.single', download_quality, single = True)
|
||||
else:
|
||||
data['quality'] = fireEvent('quality.single', 'dvdr' if group['is_dvd'] else 'dvdrip', single = True)
|
||||
|
||||
data['quality_type'] = 'HD' if data.get('resolution_width', 0) >= 1280 or data['quality'].get('hd') else 'SD'
|
||||
|
||||
filename = re.sub('(.cp\(tt[0-9{7}]+\))', '', files[0])
|
||||
@@ -501,17 +522,19 @@ class Scanner(Plugin):
|
||||
|
||||
return detected_languages
|
||||
|
||||
def determineMovie(self, group):
|
||||
imdb_id = None
|
||||
def determineMovie(self, group, download_imdb_id = None):
|
||||
# Get imdb id from downloader
|
||||
imdb_id = download_imdb_id
|
||||
|
||||
files = group['files']
|
||||
|
||||
# Check for CP(imdb_id) string in the file paths
|
||||
for cur_file in files['movie']:
|
||||
imdb_id = self.getCPImdb(cur_file)
|
||||
if imdb_id:
|
||||
log.debug('Found movie via CP tag: %s', cur_file)
|
||||
break
|
||||
if not imdb_id:
|
||||
for cur_file in files['movie']:
|
||||
imdb_id = self.getCPImdb(cur_file)
|
||||
if imdb_id:
|
||||
log.debug('Found movie via CP tag: %s', cur_file)
|
||||
break
|
||||
|
||||
# Check and see if nfo contains the imdb-id
|
||||
if not imdb_id:
|
||||
|
||||
Reference in New Issue
Block a user