| @@ -4,3 +4,4 @@ hashids==1.2.0 | |||||||
| enzyme>=0.4.1 | enzyme>=0.4.1 | ||||||
| click>=6.7 | click>=6.7 | ||||||
| langdetect>=1.0.7 | langdetect>=1.0.7 | ||||||
|  | titlecase>=0.12.0 | ||||||
|   | |||||||
							
								
								
									
										52
									
								
								src/cli.py
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								src/cli.py
									
									
									
									
									
								
							| @@ -1,39 +1,25 @@ | |||||||
| #/usr/local/bin/python3 | #!usr/bin/env python3.6 | ||||||
| import click |  | ||||||
| import os |  | ||||||
| import logging |  | ||||||
|  |  | ||||||
| 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) | from exceptions import InsufficientInfoError | ||||||
| logger = logging.getLogger('seasonedParser') |  | ||||||
| fh = logging.FileHandler(env.logfile) |  | ||||||
| fh.setLevel(logging.INFO) |  | ||||||
|  |  | ||||||
| formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') | videos, insufficient_info = scan_folder('Spider.Man') | ||||||
| fh.setFormatter(formatter) | print('Sweet lemonade: {} {}'.format(videos, insufficient_info)) | ||||||
|  |  | ||||||
| logger.addHandler(fh) | for video in videos: | ||||||
|  |     moveHome(video) | ||||||
|  |  | ||||||
| def listPath(path): | while len(insufficient_info) > 1: | ||||||
|     if (os.path.isdir(path)): |     for file in insufficient_info: | ||||||
|         print('Contents of path:') |         supplementary_info = input("Insufficient info for match file: '{}'\nSupplementary info: ".format(file))  | ||||||
|         print(os.listdir(path)) |         print(supplementary_info) | ||||||
|  |         try: | ||||||
|  |             video = Video.fromguess(file, guessit(supplementary_info)) | ||||||
|  |             insufficient_info.pop() | ||||||
|  |         except InsufficientInfoError: | ||||||
|  |             pass | ||||||
|              |              | ||||||
|     elif os.path.isfile(path): |         moveHome(video) | ||||||
|         print('File to parse:') |  | ||||||
|         print(path) |  | ||||||
|     |  | ||||||
|     else: |  | ||||||
|         print('Path does not exists') |  | ||||||
|  |  | ||||||
| @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() |  | ||||||
|   | |||||||
							
								
								
									
										78
									
								
								src/core.py
									
									
									
									
									
								
							
							
						
						
									
										78
									
								
								src/core.py
									
									
									
									
									
								
							| @@ -19,13 +19,14 @@ from titlecase import titlecase | |||||||
| import langdetect | import langdetect | ||||||
|  |  | ||||||
| import env_variables as env | import env_variables as env | ||||||
|  | from exceptions import InsufficientInfoError | ||||||
|  |  | ||||||
| from video import VIDEO_EXTENSIONS, Episode, Movie, Video | from video import VIDEO_EXTENSIONS, Episode, Movie, Video | ||||||
| from subtitle import SUBTITLE_EXTENSIONS, Subtitle, get_subtitle_path | from subtitle import SUBTITLE_EXTENSIONS, Subtitle, get_subtitle_path | ||||||
| from utils import sanitize, refine | from utils import sanitize, refine | ||||||
|  |  | ||||||
| logging.basicConfig(filename=env.logfile, level=logging.DEBUG) | logging.basicConfig(filename=env.logfile, level=logging.DEBUG) | ||||||
| logger = logging.getLogger('seasonedParser_core') | logger = logging.getLogger('seasonedParser') | ||||||
| fh = logging.FileHandler(env.logfile) | fh = logging.FileHandler(env.logfile) | ||||||
| fh.setLevel(logging.DEBUG) | fh.setLevel(logging.DEBUG) | ||||||
| sh = logging.StreamHandler() | sh = logging.StreamHandler() | ||||||
| @@ -70,6 +71,9 @@ def search_external_subtitles(path, directory=None): | |||||||
|  |  | ||||||
|     return subtitles |     return subtitles | ||||||
|  |  | ||||||
|  | def find_file_size(video): | ||||||
|  |     return os.path.getsize(video.name) | ||||||
|  |  | ||||||
| def scan_video(path): | def scan_video(path): | ||||||
|     """Scan a video from a `path`. |     """Scan a video from a `path`. | ||||||
|  |  | ||||||
| @@ -92,8 +96,8 @@ def scan_video(path): | |||||||
|     # guess |     # guess | ||||||
|     video = Video.fromguess(path, guessit(path)) |     video = Video.fromguess(path, guessit(path)) | ||||||
|  |  | ||||||
|     # size |     video.subtitles |= set(search_external_subtitles(video.name)) | ||||||
|     video.size = os.path.getsize(path) |     refine(video) | ||||||
|  |  | ||||||
|     # hash of name |     # hash of name | ||||||
|     # if isinstance(video, Movie): |     # if isinstance(video, Movie): | ||||||
| @@ -257,7 +261,7 @@ def save_subtitles(files, single=False, directory=None, encoding=None): | |||||||
|  |  | ||||||
| def scan_folder(path): | def scan_folder(path): | ||||||
|     videos = [] |     videos = [] | ||||||
|     ignored_videos = [] |     insufficient_info = [] | ||||||
|     errored_paths = [] |     errored_paths = [] | ||||||
|     logger.debug('Collecting path %s', path) |     logger.debug('Collecting path %s', path) | ||||||
|  |  | ||||||
| @@ -271,40 +275,39 @@ def scan_folder(path): | |||||||
|     # if path is a file |     # if path is a file | ||||||
|     if os.path.isfile(path): |     if os.path.isfile(path): | ||||||
|         logger.info('Path is a file') |         logger.info('Path is a file') | ||||||
|  |  | ||||||
|         try: |         try: | ||||||
|             video = scan_video(path) |             video = scan_video(path) | ||||||
|         except: |  | ||||||
|             logger.exception('Unexpected error while collection file with path {}'.format(path)) |  | ||||||
|          |  | ||||||
|         video.subtitles |= set(search_external_subtitles(video.name)) |  | ||||||
|  |  | ||||||
|         refine(video) |  | ||||||
|             videos.append(video) |             videos.append(video) | ||||||
|  |  | ||||||
|  |         except InsufficientInfoError as e: | ||||||
|  |             logger.error(e) | ||||||
|  |             insufficient_info.append(path) | ||||||
|  |  | ||||||
|     # directories |     # directories | ||||||
|     if os.path.isdir(path): |     if os.path.isdir(path): | ||||||
|         logger.info('Path is a directory') |         logger.info('Path is a directory') | ||||||
|  |  | ||||||
|  |         scanned_videos = [] | ||||||
|         try: |         try: | ||||||
|             scanned_videos = scan_videos(path) |             scanned_videos = scan_videos(path) | ||||||
|  |         except InsufficientInfoError as e: | ||||||
|  |             logger.error(e) | ||||||
|  |             insufficient_info.append(path) | ||||||
|         except: |         except: | ||||||
|             logger.exception('Unexpected error while collecting directory path %s', path) |             logger.exception('Unexpected error while collecting directory path %s', path) | ||||||
|             errored_paths.append(path) |             errored_paths.append(path) | ||||||
|  |  | ||||||
|         # Iterates over our scanned videos |         click.echo('%s video%s collected / %s file%s with insufficient info / %s error%s' % ( | ||||||
|         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) |  | ||||||
|  |  | ||||||
|     click.echo('%s video%s collected / %s error%s' % ( |  | ||||||
|         click.style(str(len(videos)), bold=True, fg='green' if videos else None), |         click.style(str(len(videos)), bold=True, fg='green' if videos else None), | ||||||
|         's' if len(videos) > 1 else '', |         '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), |         click.style(str(len(errored_paths)), bold=True, fg='red' if errored_paths else None), | ||||||
|         's' if len(errored_paths) > 1 else '', |         's' if len(errored_paths) > 1 else '', | ||||||
|     )) |     )) | ||||||
|  |  | ||||||
|     return videos |     return videos, insufficient_info | ||||||
|  |  | ||||||
| def pickforgirlscouts(video): | def pickforgirlscouts(video): | ||||||
|     if video.sufficientInfo(): |     if video.sufficientInfo(): | ||||||
| @@ -314,47 +317,20 @@ def pickforgirlscouts(video): | |||||||
|     return False |     return False | ||||||
|  |  | ||||||
| def moveHome(video): | def moveHome(video): | ||||||
|     dir = os.path.dirname(video.home) |     wantedFilePath = video.wantedFilePath() | ||||||
|  |     dir = os.path.dirname(wantedFilePath) | ||||||
|  |  | ||||||
|     if not os.path.exists(dir): |     if not os.path.exists(dir): | ||||||
|         logger.info('Creating directory {}'.format(dir)) |         logger.info('Creating directory {}'.format(dir)) | ||||||
|         os.makedirs(dir) |         os.makedirs(dir) | ||||||
|  |  | ||||||
|     logger.info("Moving video file from: '{}' to: '{}'".format(video.name, video.home)) |     logger.info("Moving video file from: '{}' to: '{}'".format(video.name, wantedFilePath)) | ||||||
|     shutil.move(video.name, video.home) |     shutil.move(video.name, wantedFilePath) | ||||||
|     for sub in video.subtitles: |     for sub in video.subtitles: | ||||||
|         if not os.path.isfile(sub): |         if not os.path.isfile(sub): | ||||||
|             continue |             continue | ||||||
|         oldpath = sub |         oldpath = sub | ||||||
|         newpath = subtitle_path(video.home, sub)  |         newpath = subtitle_path(wantedFilePath, sub)  | ||||||
|         logger.info("Moving subtitle file from: '{}' to: '{}'".format(oldpath, newpath)) |         logger.info("Moving subtitle file from: '{}' to: '{}'".format(oldpath, newpath)) | ||||||
|         shutil.move(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() |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										44
									
								
								src/video.py
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								src/video.py
									
									
									
									
									
								
							| @@ -12,8 +12,9 @@ from titlecase import titlecase | |||||||
| import hashlib, tvdb_api | import hashlib, tvdb_api | ||||||
|  |  | ||||||
| import env_variables as env | import env_variables as env | ||||||
|  | from exceptions import InsufficientInfoError | ||||||
|  |  | ||||||
| logger = logging.getLogger('seasonedParser_core') | logger = logging.getLogger('seasonedParser') | ||||||
|  |  | ||||||
| #: Video extensions | #: Video extensions | ||||||
| VIDEO_EXTENSIONS = ('.3g2', '.3gp', '.3gp2', '.3gpp', '.60d', '.ajp', '.asf', '.asx', '.avchd', '.avi', '.bik', | VIDEO_EXTENSIONS = ('.3g2', '.3gp', '.3gp2', '.3gpp', '.60d', '.ajp', '.asf', '.asx', '.avchd', '.avi', '.bik', | ||||||
| @@ -33,13 +34,13 @@ class Video(object): | |||||||
|     :param str resolution: resolution of the video stream (480p, 720p, 1080p or 1080i, 4K). |     :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 video_codec: codec of the video stream. | ||||||
|     :param str audio_codec: codec of the main audio 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 dict name_hash: hashes of the video file by provider names. | ||||||
|     :param int size: size of the video file in bytes. |     :param int size: size of the video file in bytes. | ||||||
|     :param set subtitles: existing subtitle languages. |     :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, |     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 |         #: Name or path of the video | ||||||
|         self.name = name |         self.name = name | ||||||
|  |  | ||||||
| @@ -64,8 +65,8 @@ class Video(object): | |||||||
|         #: Codec of the main audio stream |         #: Codec of the main audio stream | ||||||
|         self.audio_codec = audio_codec |         self.audio_codec = audio_codec | ||||||
|  |  | ||||||
|         #: optimal home path; parent folder. |         #: optimal move_location path; parent folder. | ||||||
|         self.home = home |         self.move_location = move_location  | ||||||
|  |  | ||||||
|         #: Existing subtitle languages |         #: Existing subtitle languages | ||||||
|         self.subtitles = subtitles or set() |         self.subtitles = subtitles or set() | ||||||
| @@ -159,8 +160,8 @@ class Episode(Video): | |||||||
|         if guess['type'] != 'episode': |         if guess['type'] != 'episode': | ||||||
|             raise ValueError('The guess must be an episode guess') |             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') |             raise InsufficientInfoError('Insufficient data to process the guess') | ||||||
|  |  | ||||||
|         return cls(name, guess['title'], guess.get('season', 1), guess['episode'], title=guess.get('episode_title'), |         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, |                    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): |     def fromname(cls, name): | ||||||
|         return cls.fromguess(name, guessit(name, {'type': 'episode'})) |         return cls.fromguess(name, guessit(name, {'type': 'episode'})) | ||||||
|  |  | ||||||
|     def sufficientInfo(self): |     def wantedFilePath(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 moveLocation(self): |  | ||||||
|         series = titlecase(self.series) |         series = titlecase(self.series) | ||||||
|         grandParent = '{}/{} Season {:02d}'.format(series, series, self.season) |         grandParent = '{}/{} Season {:02d}'.format(series, series, self.season) | ||||||
|         parent = '{} S{:02d}E{:02d}'.format(series, self.season, self.episode) |         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)) |         return os.path.join(env.SHOWBASE, grandParent, parent, os.path.basename(self.name)) | ||||||
|  |  | ||||||
|     def __repr__(self): |     def __repr__(self): | ||||||
|         if self.year is None: |         if self.year is None: | ||||||
| @@ -220,15 +206,15 @@ class Movie(Video): | |||||||
|         if guess['type'] != 'movie': |         if guess['type'] != 'movie': | ||||||
|             raise ValueError('The guess must be a movie guess') |             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') |             raise InsufficientInfoError('Insufficient data to process the guess') | ||||||
|  |  | ||||||
|         return cls(name, guess['title'], format=guess.get('format'), release_group=guess.get('release_group'), |         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'), |                    resolution=guess.get('screen_size'), video_codec=guess.get('video_codec'), | ||||||
|                    audio_codec=guess.get('audio_codec'), year=guess.get('year')) |                    audio_codec=guess.get('audio_codec'), year=guess.get('year')) | ||||||
|  |  | ||||||
|     @classmethod |     @classmethod | ||||||
|     def fromname(cls, name): |     def fromname(cls, name, year): | ||||||
|         return cls.fromguess(name, guessit(name, {'type': 'movie'})) |         return cls.fromguess(name, guessit(name, {'type': 'movie'})) | ||||||
|  |  | ||||||
|     def sufficientInfo(self): |     def sufficientInfo(self): | ||||||
| @@ -244,10 +230,10 @@ class Movie(Video): | |||||||
|  |  | ||||||
|         return True |         return True | ||||||
|   |   | ||||||
|     def moveLocation(self): |     def wantedFilePath(self): | ||||||
|         title = titlecase(self.title) |         title = titlecase(self.title) | ||||||
|         parent = '{} ({})'.format(title, self.year) |         parent = '{} ({})'.format(title, self.year) | ||||||
|         self.home = 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): |     def __repr__(self): | ||||||
|         if self.year is None: |         if self.year is None: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user