46
src/cli.py
46
src/cli.py
@@ -1,25 +1,37 @@
|
|||||||
#!usr/bin/env python3.6
|
#!usr/bin/env python3.6
|
||||||
|
from guessit import guessit
|
||||||
|
import click
|
||||||
|
|
||||||
from core import scan_folder, moveHome
|
from core import scan_folder, moveHome
|
||||||
from video import Video
|
from video import Video
|
||||||
from guessit import guessit
|
|
||||||
|
|
||||||
from exceptions import InsufficientInfoError
|
from exceptions import InsufficientInfoError
|
||||||
|
|
||||||
videos, insufficient_info = scan_folder('Spider.Man')
|
@click.command()
|
||||||
print('Sweet lemonade: {} {}'.format(videos, insufficient_info))
|
@click.argument('path')
|
||||||
|
def main(path):
|
||||||
|
videos, insufficient_info = scan_folder(path)
|
||||||
|
# print('Sweet lemonade: {} {}'.format(videos, insufficient_info))
|
||||||
|
|
||||||
for video in videos:
|
for video in videos:
|
||||||
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)
|
moveHome(video)
|
||||||
|
|
||||||
|
while len(insufficient_info) >= 1:
|
||||||
|
for file in insufficient_info:
|
||||||
|
supplementary_info = input("Insufficient info for match file: '{}'\nSupplementary info: ".format(file))
|
||||||
|
|
||||||
|
if supplementary_info is 'q':
|
||||||
|
exit(0)
|
||||||
|
if supplementary_info is 's':
|
||||||
|
insufficient_info.pop()
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
video = Video.fromguess(file, guessit(supplementary_info))
|
||||||
|
print(video)
|
||||||
|
moveHome(video)
|
||||||
|
insufficient_info.pop()
|
||||||
|
except InsufficientInfoError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|||||||
39
src/core.py
39
src/core.py
@@ -19,7 +19,7 @@ from titlecase import titlecase
|
|||||||
import langdetect
|
import langdetect
|
||||||
|
|
||||||
import env_variables as env
|
import env_variables as env
|
||||||
from exceptions import InsufficientInfoError
|
from exceptions import InsufficientNameError
|
||||||
|
|
||||||
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
|
||||||
@@ -28,9 +28,9 @@ 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')
|
logger = logging.getLogger('seasonedParser')
|
||||||
fh = logging.FileHandler(env.logfile)
|
fh = logging.FileHandler(env.logfile)
|
||||||
fh.setLevel(logging.DEBUG)
|
fh.setLevel(logging.INFO)
|
||||||
sh = logging.StreamHandler()
|
sh = logging.StreamHandler()
|
||||||
sh.setLevel(logging.DEBUG)
|
sh.setLevel(logging.ERROR)
|
||||||
|
|
||||||
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
|
||||||
fh.setFormatter(formatter)
|
fh.setFormatter(formatter)
|
||||||
@@ -158,6 +158,8 @@ def scan_videos(path):
|
|||||||
|
|
||||||
# walk the path
|
# walk the path
|
||||||
videos = []
|
videos = []
|
||||||
|
insufficient_name = []
|
||||||
|
errors_path = []
|
||||||
for dirpath, dirnames, filenames in os.walk(path):
|
for dirpath, dirnames, filenames in os.walk(path):
|
||||||
logger.debug('Walking directory %r', dirpath)
|
logger.debug('Walking directory %r', dirpath)
|
||||||
|
|
||||||
@@ -189,8 +191,13 @@ def scan_videos(path):
|
|||||||
if filename.endswith(VIDEO_EXTENSIONS): # video
|
if filename.endswith(VIDEO_EXTENSIONS): # video
|
||||||
try:
|
try:
|
||||||
video = scan_video(filepath)
|
video = scan_video(filepath)
|
||||||
|
except InsufficientInfoError as e:
|
||||||
|
logger.info(e)
|
||||||
|
insufficient_name.append(filepath)
|
||||||
|
continue
|
||||||
except ValueError: # pragma: no cover
|
except ValueError: # pragma: no cover
|
||||||
logger.exception('Error scanning video')
|
logger.exception('Error scanning video')
|
||||||
|
errors_path.append(filepath)
|
||||||
continue
|
continue
|
||||||
else: # pragma: no cover
|
else: # pragma: no cover
|
||||||
raise ValueError('Unsupported file %r' % filename)
|
raise ValueError('Unsupported file %r' % filename)
|
||||||
@@ -199,7 +206,7 @@ def scan_videos(path):
|
|||||||
|
|
||||||
bar.update(1)
|
bar.update(1)
|
||||||
|
|
||||||
return videos
|
return videos, insufficient_name, errors_path
|
||||||
|
|
||||||
|
|
||||||
def organize_files(path):
|
def organize_files(path):
|
||||||
@@ -261,7 +268,7 @@ def save_subtitles(files, single=False, directory=None, encoding=None):
|
|||||||
|
|
||||||
def scan_folder(path):
|
def scan_folder(path):
|
||||||
videos = []
|
videos = []
|
||||||
insufficient_info = []
|
insufficient_name = []
|
||||||
errored_paths = []
|
errored_paths = []
|
||||||
logger.debug('Collecting path %s', path)
|
logger.debug('Collecting path %s', path)
|
||||||
|
|
||||||
@@ -280,9 +287,9 @@ def scan_folder(path):
|
|||||||
video = scan_video(path)
|
video = scan_video(path)
|
||||||
videos.append(video)
|
videos.append(video)
|
||||||
|
|
||||||
except InsufficientInfoError as e:
|
except InsufficientNameError as e:
|
||||||
logger.error(e)
|
logger.info(e)
|
||||||
insufficient_info.append(path)
|
insufficient_name.append(path)
|
||||||
|
|
||||||
# directories
|
# directories
|
||||||
if os.path.isdir(path):
|
if os.path.isdir(path):
|
||||||
@@ -290,24 +297,21 @@ def scan_folder(path):
|
|||||||
|
|
||||||
scanned_videos = []
|
scanned_videos = []
|
||||||
try:
|
try:
|
||||||
scanned_videos = scan_videos(path)
|
videos, insufficient_name, errored_paths = 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)
|
||||||
|
|
||||||
click.echo('%s video%s collected / %s file%s with insufficient info / %s error%s' % (
|
click.echo('%s video%s collected / %s file%s with insufficient name / %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),
|
click.style(str(len(insufficient_name)), bold=True, fg='yellow' if insufficient_name else None),
|
||||||
's' if len(insufficient_info) > 1 else '',
|
's' if len(insufficient_name) > 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, insufficient_info
|
return videos, insufficient_name
|
||||||
|
|
||||||
def pickforgirlscouts(video):
|
def pickforgirlscouts(video):
|
||||||
if video.sufficientInfo():
|
if video.sufficientInfo():
|
||||||
@@ -334,3 +338,6 @@ def moveHome(video):
|
|||||||
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)
|
||||||
|
|
||||||
|
# Give feedback before delete ?
|
||||||
|
def empthDirectory(paths):
|
||||||
|
pass
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3.6
|
#!/usr/bin/env python3.6
|
||||||
|
|
||||||
class InsufficientInfoError(Exception):
|
class InsufficientNameError(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|||||||
22
src/video.py
22
src/video.py
@@ -12,7 +12,7 @@ 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
|
from exceptions import InsufficientNameError
|
||||||
|
|
||||||
logger = logging.getLogger('seasonedParser')
|
logger = logging.getLogger('seasonedParser')
|
||||||
|
|
||||||
@@ -34,13 +34,11 @@ 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 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 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,
|
||||||
move_location=None, subtitles=None, embeded_subtitles=None):
|
subtitles=None, embeded_subtitles=None):
|
||||||
#: Name or path of the video
|
#: Name or path of the video
|
||||||
self.name = name
|
self.name = name
|
||||||
|
|
||||||
@@ -65,9 +63,6 @@ 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 move_location path; parent folder.
|
|
||||||
self.move_location = move_location
|
|
||||||
|
|
||||||
#: Existing subtitle languages
|
#: Existing subtitle languages
|
||||||
self.subtitles = subtitles or set()
|
self.subtitles = subtitles or set()
|
||||||
|
|
||||||
@@ -157,11 +152,15 @@ class Episode(Video):
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def fromguess(cls, name, guess):
|
def fromguess(cls, name, guess):
|
||||||
|
logger.info('Guess: {}'.format(guess))
|
||||||
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 'season' not in guess or 'episode' not in guess:
|
if 'title' not in guess or 'season' not in guess or 'episode' not in guess:
|
||||||
raise InsufficientInfoError('Insufficient data to process the guess')
|
raise InsufficientNameError('Guess failed to have sufficient data from query: {}'.format(name))
|
||||||
|
|
||||||
|
if any([isinstance(x, list) for x in [guess['title'], guess['season'], guess['episode']]]):
|
||||||
|
raise InsufficientNameError('Guess could not be parsed, list values found.')
|
||||||
|
|
||||||
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,
|
||||||
@@ -179,12 +178,9 @@ class Episode(Video):
|
|||||||
return 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:
|
|
||||||
return '<%s [%r, %dx%s]>' % (self.__class__.__name__, self.series, self.season, self.episode)
|
|
||||||
if self.subtitles is not None and len(self.subtitles) > 0:
|
if self.subtitles is not None and len(self.subtitles) > 0:
|
||||||
return '<%s [%r, %dx%s] %s>' % (self.__class__.__name__, self.series, self.season, self.episode, self.subtitles)
|
return '<%s [%r, %dx%s] %s>' % (self.__class__.__name__, self.series, self.season, self.episode, self.subtitles)
|
||||||
|
return '<%s [%r, %dx%d]>' % (self.__class__.__name__, self.series, self.season, self.episode)
|
||||||
return '<%s [%r, %d, %dx%d]>' % (self.__class__.__name__, self.series, self.year, self.season, self.episode)
|
|
||||||
|
|
||||||
class Movie(Video):
|
class Movie(Video):
|
||||||
"""Movie :class:`Video`.
|
"""Movie :class:`Video`.
|
||||||
@@ -207,7 +203,7 @@ class Movie(Video):
|
|||||||
raise ValueError('The guess must be a movie guess')
|
raise ValueError('The guess must be a movie guess')
|
||||||
|
|
||||||
if 'title' not in guess or 'year' not in guess:
|
if 'title' not in guess or 'year' not in guess:
|
||||||
raise InsufficientInfoError('Insufficient data to process the guess')
|
raise InsufficientNameError('Guess failed to have sufficient data from query: {}'.format(name))
|
||||||
|
|
||||||
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'),
|
||||||
|
|||||||
Reference in New Issue
Block a user