Improved error detection and verbosity handling

This commit is contained in:
Fabrice Quenneville 2020-11-19 23:00:48 -05:00
parent 2e3bddf12b
commit 8df660019d
4 changed files with 147 additions and 84 deletions

View File

@ -17,7 +17,7 @@ pip install -r requirements.txt
```
## Usage
./curator.py [list,convert] [-del] [-verbose] [-in:any,avi,mkv,wmv,mpg,mp4,m4v,flv,vid] [-filters:fferror,old,lowres,hd,720p,1080p,uhd,mpeg,mpeg4,x264,wmv3,wmv] [-out:mkv/mp4,x265/av1] [-dir/-files:"/mnt/media/",,"/mnt/media2/"]
./curator.py [list,convert] [-del] [-in:any,avi,mkv,wmv,mpg,mp4,m4v,flv,vid] [-filters:fferror,old,lowres,hd,720p,1080p,uhd,mpeg,mpeg4,x264,wmv3,wmv] [-out:mkv/mp4,x265/av1] [-print:list,formated,verbose] [-dir/-files:"/mnt/media/",,"/mnt/media2/"]
> for multiple files or filenames use double comma separated values ",,"
@ -25,12 +25,13 @@ default options are:
-in:any
-filters:
-out:mkv,x265
-print:list
Examples:
```bash
./curator.py list -filters:old -dir:/mnt/media/ >> ../medlist.txt
./curator.py convert -del -filters:mpeg4 -out:x265,mkv -dir:"/mnt/media/Movies/"
./curator.py convert -del -verbose -in:avi,mpg -dir:/mnt/media/
./curator.py list -filters:old -print:formated -dir:/mnt/media/ >> ../medlist.txt
./curator.py convert -del -filters:mpeg4 -out:av1,mp4 -dir:"/mnt/media/Movies/"
./curator.py convert -del -in:avi,mpg -print:formated,verbose -dir:/mnt/media/
```

View File

@ -6,7 +6,7 @@
ex:
./converter.py list -in:any -filters:old -dir:/mnt/media/ >> ../medlist.txt
./converter.py convert -del -in:any -filters:mpeg4 -out:x265,mkv -dir:"/mnt/media/Movies/"
./converter.py convert -del -verbose -in:avi,mpg -dir:/mnt/media/
./converter.py convert -del -in:avi,mpg -dir:/mnt/media/
'''
import sys
@ -38,6 +38,7 @@ def main():
inputs = []
filters = []
outputs = []
printop = []
for arg in sys.argv:
# Confirm with the user that he selected to delete found files
@ -52,6 +53,8 @@ def main():
filters += arg[9:].split(",")
elif "-out:" in arg:
outputs += arg[5:].split(",")
elif "-print:" in arg:
printop += arg[7:].split(",")
elif "-files:" in arg:
files += arg[7:].split(",,")
elif "-dir:" in arg:
@ -63,13 +66,22 @@ def main():
elif len(directories) > 0:
medialibrary = MediaLibrary(directories = directories, inputs = inputs, filters = filters)
else:
print(f"{BColors.FAIL}ERROR: No files or directories selected.{BColors.FAIL}")
print(f"{BColors.FAIL}ERROR: No files or directories selected.{BColors.ENDC}")
# Actions
if sys.argv[1] == "list":
for filepath in medialibrary.videos:
if medialibrary.videos[filepath].useful:
if "formated" in printop or "verbose" in printop:
if medialibrary.videos[filepath].error:
print(f"{BColors.FAIL}{medialibrary.videos[filepath].fprint()}{BColors.ENDC}")
else:
print(medialibrary.videos[filepath].fprint())
else:
if medialibrary.videos[filepath].error:
print(f"{BColors.FAIL}{medialibrary.videos[filepath]}{BColors.ENDC}")
else:
print(medialibrary.videos[filepath])
# TODO delete file when -del
elif sys.argv[1] == "test":
@ -94,20 +106,36 @@ def main():
# Verbosing
print(f"{BColors.OKGREEN}****** Starting conversion {counter} of {len(keylist)}: '{BColors.OKCYAN}{medialibrary.videos[filepath].filename_origin}{BColors.OKGREEN}' from {BColors.OKCYAN}{medialibrary.videos[filepath].codec}{BColors.OKGREEN} to {BColors.OKCYAN}{vcodec}{BColors.OKGREEN}...{BColors.ENDC}")
print(f"{BColors.OKCYAN}Original file:{BColors.ENDC}")
if "formated" in printop or "verbose" in printop:
print(medialibrary.videos[filepath].fprint())
else:
print(medialibrary.videos[filepath])
print(f"{BColors.OKGREEN}Converting please wait...{BColors.ENDC}")
print(f"{BColors.OKGREEN}Converting please wait...{BColors.ENDC}", end="\r")
# Converting
if medialibrary.videos[filepath].convert():
if medialibrary.videos[filepath].convert(verbose = "verbose" in printop):
# Mark the job as done
medialibrary.videos[filepath].useful = False
# Scan the new video
newfpath = medialibrary.videos[filepath].path + medialibrary.videos[filepath].filename_new
medialibrary.videos[newfpath] = Video(newfpath)
# Vervose
medialibrary.videos[newfpath] = Video(newfpath, verbose = "verbose" in printop)
# Verbose
print(f"{BColors.OKGREEN}Successfully converted '{medialibrary.videos[filepath].filename_origin}'{BColors.OKCYAN}({medialibrary.videos[filepath].filesize}mb){BColors.OKGREEN} to '{medialibrary.videos[newfpath].filename_origin}'{BColors.OKCYAN}({medialibrary.videos[newfpath].filesize}mb){BColors.OKGREEN}, {BColors.OKCYAN}new file:{BColors.ENDC}")
if "formated" in printop or "verbose" in printop:
if medialibrary.videos[newfpath].error:
print(f"{BColors.FAIL}{medialibrary.videos[newfpath].fprint()}{BColors.ENDC}")
else:
print(medialibrary.videos[newfpath].fprint())
else:
if medialibrary.videos[newfpath].error:
print(f"{BColors.FAIL}{medialibrary.videos[newfpath]}{BColors.ENDC}")
else:
print(medialibrary.videos[newfpath])
# if marked for deletion delete and unwatch the video

View File

@ -21,13 +21,13 @@ class MediaLibrary():
inputs = []
filters = []
def __init__(self, files = False, directories = False, inputs = ["any"], filters = []):
def __init__(self, files = False, directories = False, inputs = ["any"], filters = [], verbose = False):
'''
This is the library object who holds the information about the workspace and all the videos in it.
'''
if files:
for filepath in files:
self.videos[filepath] = Video(filepath)
self.videos[filepath] = Video(filepath, verbose = verbose)
elif directories:
self.directories = directories
@ -37,7 +37,7 @@ class MediaLibrary():
self.inputs = inputs
self.filters = filters
self.load_videos()
self.load_videos(verbose = verbose)
self.filter_videos()
@ -54,7 +54,7 @@ class MediaLibrary():
return text
def load_videos(self):
def load_videos(self, verbose = False):
'''
Scan folders for video files respecting the inputs requested by the user
Save them to the videos dictionary
@ -93,7 +93,8 @@ class MediaLibrary():
if "-verbose" in sys.argv:
iteration += 1
print(f'{int((iteration / len(videolist )* 100))}% complete', end='\r')
self.videos[video] = Video(video)
self.videos[video] = Video(video, verbose = verbose)
def filter_videos(self):
'''

View File

@ -24,7 +24,100 @@ class Video():
width = int()
height = int()
def convert(self, vcodec = "x265", acodec = False, extension = "mkv"):
def __init__(self, filepath, useful = True, verbose = False):
'''
'''
#Breaking down the full path in its components
self.path = str(filepath)[:str(filepath).rindex("/") + 1]
self.filename_origin = str(filepath)[str(filepath).rindex("/") + 1:]
# Marking useful is user manually set it.
self.useful = useful
#Gathering information on the video
self.filesize = self.detect_filesize(filepath)
self.error = self.detect_fferror(filepath)
self.codec = self.detect_codec(filepath)
try:
self.width, self.height = self.detect_resolution(filepath)
self.definition = self.detect_definition(
width = self.width,
height = self.height )
except:
self.width, self.height = False, False
self.definition = False
if self.error and verbose:
print(f"{BColors.FAIL}There seams to be an error with \"{filepath}\"{BColors.ENDC}")
print(f"{BColors.FAIL} {self.error}{BColors.ENDC}")
def __str__(self):
'''
Building and returning formated information about the video file
'''
text = f"{self.codec} - "
# If the first character of the definition is not a number (ie UHD and not 720p) upper it
if self.definition[0] and not self.definition[0].isnumeric():
text += f"{self.definition.upper()}: ({self.width}x{self.height}) - "
else:
text += f"{self.definition}: ({self.width}x{self.height}) - "
# Return the size in mb or gb if more than 1024 mb
if self.filesize >= 1024:
text += f"{self.filesize / 1024 :.2f} gb - "
else:
text += f"{self.filesize} mb - "
text += f"'{self.path + self.filename_origin}'"
if self.error:
text += f"{BColors.FAIL}\nErrors:{BColors.ENDC}"
for err in self.error.splitlines():
text += f"{BColors.FAIL}\n {err}{BColors.ENDC}"
return text
__repr__ = __str__
def fprint(self):
'''
Building and returning formated information about the video file
'''
text = f"{self.path + self.filename_origin}\n"
#text += f" Useful: {self.useful}\n"
# If the first character of the definition is not a number (ie UHD and not 720p) upper it
if self.definition[0] and not self.definition[0].isnumeric():
text += f" Definition: {self.definition.upper()}: ({self.width}x{self.height})\n"
else:
text += f" Definition: {self.definition}: ({self.width}x{self.height})\n"
text += f" Codec: {self.codec}\n"
# Return the size in mb or gb if more than 1024 mb
if self.filesize >= 1024:
text += f" size: {self.filesize / 1024 :.2f} gb"
else:
text += f" size: {self.filesize} mb"
if self.error:
text += f"{BColors.FAIL}\n Errors:{BColors.ENDC}"
for err in self.error.splitlines():
text += f"{BColors.FAIL}\n {err}{BColors.ENDC}"
return text
def convert(self, vcodec = "x265", acodec = False, extension = "mkv", verbose = False):
'''
Convert to original file to the requested format / codec
'''
@ -58,7 +151,7 @@ class Video():
try:
if "-verbose" in sys.argv:
if verbose:
subprocess.call(args)
else:
txt = subprocess.check_output(args, stderr=subprocess.STDOUT)
@ -79,66 +172,6 @@ class Video():
return True
def __init__(self, filepath, useful = True):
'''
'''
#Breaking down the full path in its components
self.path = str(filepath)[:str(filepath).rindex("/") + 1]
self.filename_origin = str(filepath)[str(filepath).rindex("/") + 1:]
# Marking useful is user manually set it.
self.useful = useful
#Gathering information on the video
self.filesize = self.detect_filesize(filepath)
self.error = self.detect_fferror(filepath)
self.codec = self.detect_codec(filepath)
try:
self.width, self.height = self.detect_resolution(filepath)
self.definition = self.detect_definition(
width = self.width,
height = self.height )
except:
self.width, self.height = False, False
self.definition = False
if self.error and "-verbose" in sys.argv:
print(f"{BColors.FAIL}There seams to be an error with \"{filepath}\"{BColors.ENDC}")
print(f"{BColors.FAIL} {self.error}{BColors.ENDC}")
def __str__(self):
'''
Building and returning formated information about the video file
'''
text = f"{self.path + self.filename_origin}\n"
text += f" Useful: {self.useful}\n"
# If the first character of the definition is not a number (ie UHD and not 720p) upper it
if self.definition[0] and not self.definition[0].isnumeric():
text += f" Definition: {self.definition.upper()}: ({self.width}x{self.height})\n"
else:
text += f" Definition: {self.definition}: ({self.width}x{self.height})\n"
text += f" Codec: {self.codec}\n"
# Return the size in mb or gb if more than 1024 mb
if self.filesize >= 1024:
text += f" size: {self.filesize / 1024 :.2f} gb"
else:
text += f" size: {self.filesize} mb"
if self.error:
text += f"{BColors.FAIL}\n Errors: {', '.join(map(str, self.error))}{BColors.ENDC}"
return text
__repr__ = __str__
@ -161,11 +194,11 @@ class Video():
@staticmethod
def detect_fferror(filepath):
try:
args = ["ffprobe","-v","error","-select_streams","v:0", "-show_entries","stream=width,height","-of","csv=s=x:p=0",str(filepath)]
args = ["ffprobe","-v","error",str(filepath)]
output = subprocess.check_output(args, stderr=subprocess.STDOUT)
output = output.decode().strip().splitlines()
if len(output) > 1:
return output[0:-1]
output = output.decode().strip()
if len(output) > 0:
return output
except (subprocess.CalledProcessError, IndexError):
return f'{BColors.FAIL}There seams to be a "subprocess.CalledProcessError" error with \"{filepath}\"{BColors.ENDC}'
return False