Improved error detection and verbosity handling
This commit is contained in:
parent
2e3bddf12b
commit
8df660019d
@ -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/
|
||||
```
|
||||
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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):
|
||||
'''
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user