From 5a6486189e6c1e02ceb3d03f5f8dbc6ce77e605b Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 17 Oct 2018 19:59:43 +0200 Subject: [PATCH 1/5] Merged to updated logger name. --- src/cli.py | 4 ++++ src/core.py | 10 +++++++--- src/video.py | 24 ++++++++++++------------ 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/cli.py b/src/cli.py index 074eb68..05c817e 100755 --- a/src/cli.py +++ b/src/cli.py @@ -27,6 +27,10 @@ def listPath(path): else: print('Path does not exists') +def guessFromInput(video): + print('Insufficient info for {}'.format(video.name)) + video_name = input('Input + @click.command() @click.argument('path') @click.option('--greeting', '-g') diff --git a/src/core.py b/src/core.py index d721319..480e49d 100755 --- a/src/core.py +++ b/src/core.py @@ -25,7 +25,7 @@ from subtitle import SUBTITLE_EXTENSIONS, Subtitle, get_subtitle_path from utils import sanitize, refine logging.basicConfig(filename=env.logfile, level=logging.DEBUG) -logger = logging.getLogger('seasonedParser_core') +logger = logging.getLogger('seasonedParser') fh = logging.FileHandler(env.logfile) fh.setLevel(logging.DEBUG) sh = logging.StreamHandler() @@ -70,6 +70,9 @@ def search_external_subtitles(path, directory=None): return subtitles +def find_file_size(video): + return os.path.getsize(video.name) + def scan_video(path): """Scan a video from a `path`. @@ -92,8 +95,8 @@ def scan_video(path): # guess video = Video.fromguess(path, guessit(path)) - # size - video.size = os.path.getsize(path) + if video.sufficientInfo(): + video.setMoveLocation() # hash of name # if isinstance(video, Movie): @@ -296,6 +299,7 @@ def scan_folder(path): v.subtitles |= set(search_external_subtitles(v.name)) refine(v) videos.append(v) + video.size = find_file_size() click.echo('%s video%s collected / %s error%s' % ( click.style(str(len(videos)), bold=True, fg='green' if videos else None), diff --git a/src/video.py b/src/video.py index baf2348..9790788 100644 --- a/src/video.py +++ b/src/video.py @@ -13,7 +13,7 @@ import hashlib, tvdb_api import env_variables as env -logger = logging.getLogger('seasonedParser_core') +logger = logging.getLogger('seasonedParser') #: Video extensions VIDEO_EXTENSIONS = ('.3g2', '.3gp', '.3gp2', '.3gpp', '.60d', '.ajp', '.asf', '.asx', '.avchd', '.avi', '.bik', @@ -33,13 +33,13 @@ class Video(object): :param str resolution: resolution of the video stream (480p, 720p, 1080p or 1080i, 4K). :param str video_codec: codec of the video stream. :param str audio_codec: codec of the main audio stream. - :param str home: optimal parent folder. + :param str move_location: location to move file to. :param dict name_hash: hashes of the video file by provider names. :param int size: size of the video file in bytes. :param set subtitles: existing subtitle languages. """ def __init__(self, name, hash=None, size=None, format=None, release_group=None, resolution=None, video_codec=None, audio_codec=None, - home=None, subtitles=None, embeded_subtitles=None): + move_location=None, subtitles=None, embeded_subtitles=None): #: Name or path of the video self.name = name @@ -64,8 +64,8 @@ class Video(object): #: Codec of the main audio stream self.audio_codec = audio_codec - #: optimal home path; parent folder. - self.home = home + #: optimal move_location path; parent folder. + self.move_location = move_location #: Existing subtitle languages self.subtitles = subtitles or set() @@ -159,7 +159,7 @@ class Episode(Video): if guess['type'] != 'episode': raise ValueError('The guess must be an episode guess') - if 'title' not in guess or 'episode' not in guess: + if 'title' not in guess or 'season' not in guess or 'episode' not in guess: raise ValueError('Insufficient data to process the guess') return cls(name, guess['title'], guess.get('season', 1), guess['episode'], title=guess.get('episode_title'), @@ -186,11 +186,11 @@ class Episode(Video): return True - def moveLocation(self): + def setMoveLocation(self): series = titlecase(self.series) grandParent = '{}/{} Season {:02d}'.format(series, series, self.season) parent = '{} S{:02d}E{:02d}'.format(series, self.season, self.episode) - self.home = os.path.join(env.SHOWBASE, grandParent, parent, os.path.basename(self.name)) + self.move_location = os.path.join(env.SHOWBASE, grandParent, parent, os.path.basename(self.name)) def __repr__(self): if self.year is None: @@ -220,7 +220,7 @@ class Movie(Video): if guess['type'] != 'movie': raise ValueError('The guess must be a movie guess') - if 'title' not in guess: + if 'title' not in guess or 'year' not in guess: raise ValueError('Insufficient data to process the guess') return cls(name, guess['title'], format=guess.get('format'), release_group=guess.get('release_group'), @@ -228,7 +228,7 @@ class Movie(Video): audio_codec=guess.get('audio_codec'), year=guess.get('year')) @classmethod - def fromname(cls, name): + def fromname(cls, name, year): return cls.fromguess(name, guessit(name, {'type': 'movie'})) def sufficientInfo(self): @@ -244,10 +244,10 @@ class Movie(Video): return True - def moveLocation(self): + def setMoveLocation(self): title = titlecase(self.title) parent = '{} ({})'.format(title, self.year) - self.home = os.path.join(env.MOVIEBASE, parent, os.path.basename(self.name)) + self.move_location = os.path.join(env.MOVIEBASE, parent, os.path.basename(self.name)) def __repr__(self): if self.year is None: From 86d17c5e22a41a44a24df0107d9677070e343054 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 17 Oct 2018 22:51:56 +0200 Subject: [PATCH 2/5] Recatored the cli program. --- requirements.txt | 1 + src/cli.py | 52 ++++++++++++------------------------------------ 2 files changed, 14 insertions(+), 39 deletions(-) diff --git a/requirements.txt b/requirements.txt index 01e0530..0a9c345 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ hashids==1.2.0 enzyme>=0.4.1 click>=6.7 langdetect>=1.0.7 +titlecase>=0.12.0 diff --git a/src/cli.py b/src/cli.py index 05c817e..9060dfe 100755 --- a/src/cli.py +++ b/src/cli.py @@ -1,43 +1,17 @@ -#/usr/local/bin/python3 -import click -import os -import logging +#!usr/bin/env python3.6 -import env_variables as env +from core import scan_folder, moveHome +from video import Video +from guessit import guessit -logging.basicConfig(filename=env.logfile, level=logging.INFO) -logger = logging.getLogger('seasonedParser') -fh = logging.FileHandler(env.logfile) -fh.setLevel(logging.INFO) +videos, insufficient_info = scan_folder('Spider.Man') +print('Sweet lemonade: {} {}'.format(videos, insufficient_info)) -formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') -fh.setFormatter(formatter) +for video in videos: + moveHome(video) -logger.addHandler(fh) - -def listPath(path): - if (os.path.isdir(path)): - print('Contents of path:') - print(os.listdir(path)) - - elif os.path.isfile(path): - print('File to parse:') - print(path) - - else: - print('Path does not exists') - -def guessFromInput(video): - print('Insufficient info for {}'.format(video.name)) - video_name = input('Input - -@click.command() -@click.argument('path') -@click.option('--greeting', '-g') -def main(path, greeting): - logger.info('Received cli variables: \n\t path: {}'.format(path)) - listPath(path) - - -if __name__ == '__main__': - main() +for file in insufficient_info: + supplementary_info = input("Insufficient info for match file: '{}'\nSupplementary info: ".format(file)) + print(supplementary_info) + video = Video.fromguess(file, guessit(supplementary_info)) + moveHome(video) From 50e6e4a2596659a1311ee6901ce6901c93a309a3 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 17 Oct 2018 22:58:22 +0200 Subject: [PATCH 3/5] Insufficient exception is thrown when not enough info is needed to move file to correct location. All insufficient items are returned along with the found videos. The wanted path we want to move the file is no longer class vairable, but gets the string by function. --- src/core.py | 46 +++++++++++++++++++++++----------------------- src/video.py | 28 +++++++--------------------- 2 files changed, 30 insertions(+), 44 deletions(-) diff --git a/src/core.py b/src/core.py index 480e49d..29ca813 100755 --- a/src/core.py +++ b/src/core.py @@ -19,6 +19,7 @@ from titlecase import titlecase import langdetect import env_variables as env +from exceptions import InsufficientInfoError from video import VIDEO_EXTENSIONS, Episode, Movie, Video from subtitle import SUBTITLE_EXTENSIONS, Subtitle, get_subtitle_path @@ -95,8 +96,8 @@ def scan_video(path): # guess video = Video.fromguess(path, guessit(path)) - if video.sufficientInfo(): - video.setMoveLocation() + video.subtitles |= set(search_external_subtitles(video.name)) + refine(video) # hash of name # if isinstance(video, Movie): @@ -260,7 +261,7 @@ def save_subtitles(files, single=False, directory=None, encoding=None): def scan_folder(path): videos = [] - ignored_videos = [] + insufficient_info = [] errored_paths = [] logger.debug('Collecting path %s', path) @@ -274,41 +275,39 @@ def scan_folder(path): # if path is a file if os.path.isfile(path): logger.info('Path is a file') + try: video = scan_video(path) - except: - logger.exception('Unexpected error while collection file with path {}'.format(path)) - - video.subtitles |= set(search_external_subtitles(video.name)) + videos.append(video) - refine(video) - videos.append(video) + except InsufficientInfoError as e: + logger.error(e) + insufficient_info.append(path) # directories if os.path.isdir(path): logger.info('Path is a directory') + + scanned_videos = [] try: scanned_videos = scan_videos(path) + except InsufficientInfoError as e: + logger.error(e) + insufficient_info.append(path) except: logger.exception('Unexpected error while collecting directory path %s', path) errored_paths.append(path) - # Iterates over our scanned videos - with click.progressbar(scanned_videos, label='Parsing videos') as bar: - for v in bar: - v.subtitles |= set(search_external_subtitles(v.name)) - refine(v) - videos.append(v) - video.size = find_file_size() - - click.echo('%s video%s collected / %s error%s' % ( + click.echo('%s video%s collected / %s file%s with insufficient info / %s error%s' % ( click.style(str(len(videos)), bold=True, fg='green' if videos else None), 's' if len(videos) > 1 else '', + click.style(str(len(insufficient_info)), bold=True, fg='yellow' if insufficient_info else None), + 's' if len(insufficient_info) > 1 else '', click.style(str(len(errored_paths)), bold=True, fg='red' if errored_paths else None), 's' if len(errored_paths) > 1 else '', )) - return videos + return videos, insufficient_info def pickforgirlscouts(video): if video.sufficientInfo(): @@ -318,19 +317,20 @@ def pickforgirlscouts(video): return False def moveHome(video): - dir = os.path.dirname(video.home) + wantedFilePath = video.wantedFilePath() + dir = os.path.dirname(wantedFilePath) if not os.path.exists(dir): logger.info('Creating directory {}'.format(dir)) os.makedirs(dir) - logger.info("Moving video file from: '{}' to: '{}'".format(video.name, video.home)) - shutil.move(video.name, video.home) + logger.info("Moving video file from: '{}' to: '{}'".format(video.name, wantedFilePath)) + shutil.move(video.name, wantedFilePath) for sub in video.subtitles: if not os.path.isfile(sub): continue oldpath = sub - newpath = subtitle_path(video.home, sub) + newpath = subtitle_path(wantedFilePath, sub) logger.info("Moving subtitle file from: '{}' to: '{}'".format(oldpath, newpath)) shutil.move(oldpath, newpath) diff --git a/src/video.py b/src/video.py index 9790788..78af278 100644 --- a/src/video.py +++ b/src/video.py @@ -12,6 +12,7 @@ from titlecase import titlecase import hashlib, tvdb_api import env_variables as env +from exceptions import InsufficientInfoError logger = logging.getLogger('seasonedParser') @@ -160,7 +161,7 @@ class Episode(Video): raise ValueError('The guess must be an episode guess') if 'title' not in guess or 'season' not in guess or 'episode' not in guess: - raise ValueError('Insufficient data to process the guess') + raise InsufficientInfoError('Insufficient data to process the guess') return cls(name, guess['title'], guess.get('season', 1), guess['episode'], title=guess.get('episode_title'), year=guess.get('year'), format=guess.get('format'), original_series='year' not in guess, @@ -171,26 +172,11 @@ class Episode(Video): def fromname(cls, name): return cls.fromguess(name, guessit(name, {'type': 'episode'})) - def sufficientInfo(self): - ser = hasattr(self, 'series') - sea = hasattr(self, 'season') - ep = hasattr(self, 'episode') - - if False in [ser, sea, ep]: - logger.error('{}, {} or {} found to have none value, manual correction required'.format(self.series, self.season, self.episode)) - return False - - if list in [type(self.series), type(self.season), type(self.episode)]: - logger.error('{}, {} or {} found to have list values, manual correction required'.format(self.series, self.season, self.episode)) - return False - - return True - - def setMoveLocation(self): + def wantedFilePath(self): series = titlecase(self.series) grandParent = '{}/{} Season {:02d}'.format(series, series, self.season) parent = '{} S{:02d}E{:02d}'.format(series, self.season, self.episode) - self.move_location = os.path.join(env.SHOWBASE, grandParent, parent, os.path.basename(self.name)) + return os.path.join(env.SHOWBASE, grandParent, parent, os.path.basename(self.name)) def __repr__(self): if self.year is None: @@ -221,7 +207,7 @@ class Movie(Video): raise ValueError('The guess must be a movie guess') if 'title' not in guess or 'year' not in guess: - raise ValueError('Insufficient data to process the guess') + raise InsufficientInfoError('Insufficient data to process the guess') return cls(name, guess['title'], format=guess.get('format'), release_group=guess.get('release_group'), resolution=guess.get('screen_size'), video_codec=guess.get('video_codec'), @@ -244,10 +230,10 @@ class Movie(Video): return True - def setMoveLocation(self): + def wantedFilePath(self): title = titlecase(self.title) parent = '{} ({})'.format(title, self.year) - self.move_location = os.path.join(env.MOVIEBASE, parent, os.path.basename(self.name)) + return os.path.join(env.MOVIEBASE, parent, os.path.basename(self.name)) def __repr__(self): if self.year is None: From 7e7eebd462949032e1024484557791ab86f9bbac Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 17 Oct 2018 23:00:06 +0200 Subject: [PATCH 4/5] Removed main function from core. --- src/core.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/core.py b/src/core.py index 29ca813..79c83a4 100755 --- a/src/core.py +++ b/src/core.py @@ -334,31 +334,3 @@ def moveHome(video): logger.info("Moving subtitle file from: '{}' to: '{}'".format(oldpath, newpath)) shutil.move(oldpath, newpath) -def main(): - path = '/mnt/mainframe/' - - videos = scan_folder(path) - - scout = [] - civilian = [] - for video in videos: - if pickforgirlscouts(video): - scout.append(video) - else: - civilian.append(video) - - click.echo('%s scout%s collected / %s civilan%s / %s candidate%s' % ( - click.style(str(len(scout)), bold=True, fg='green' if scout else None), - 's' if len(scout) > 1 else '', - click.style(str(len(civilian)), bold=True, fg='red' if civilian else None), - 's' if len(civilian) > 1 else '', - click.style(str(len(videos)), bold=True, fg='blue' if videos else None), - 's' if len(videos) > 1 else '' - )) - - for video in scout: - moveHome(video) - -if __name__ == '__main__': - main() - From 37a0c6f62bc4b5d04d602c370473c69ce13794e6 Mon Sep 17 00:00:00 2001 From: KevinMidboe Date: Wed, 17 Oct 2018 23:29:09 +0200 Subject: [PATCH 5/5] Will iterate over all files until all have a files have sufficient info. --- src/cli.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/cli.py b/src/cli.py index 9060dfe..0abf09f 100755 --- a/src/cli.py +++ b/src/cli.py @@ -4,14 +4,22 @@ from core import scan_folder, moveHome from video import Video from guessit import guessit +from exceptions import InsufficientInfoError + videos, insufficient_info = scan_folder('Spider.Man') print('Sweet lemonade: {} {}'.format(videos, insufficient_info)) for video in videos: moveHome(video) -for file in insufficient_info: - supplementary_info = input("Insufficient info for match file: '{}'\nSupplementary info: ".format(file)) - print(supplementary_info) - video = Video.fromguess(file, guessit(supplementary_info)) - moveHome(video) +while len(insufficient_info) > 1: + for file in insufficient_info: + supplementary_info = input("Insufficient info for match file: '{}'\nSupplementary info: ".format(file)) + print(supplementary_info) + try: + video = Video.fromguess(file, guessit(supplementary_info)) + insufficient_info.pop() + except InsufficientInfoError: + pass + + moveHome(video)