* Rectified a bug where MediaLibrary would encounter an error due to a missing directory

* Enhanced clarity by refining comments
* Improved code readability through cleanup and organization
* Ensured consistency and enhanced readability by adjusting variable names
This commit is contained in:
Fabrice Quenneville 2024-04-24 01:18:24 -04:00
parent 44bf4fef04
commit 3ec56f4a66
4 changed files with 237 additions and 271 deletions

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python3
''' This is the library object who holds the information about the workspace and all the videos in it.'''
''' This module defines the MediaLibrary class, which manages information about the workspace and all videos in it.'''
from pathlib import Path
@ -9,184 +9,159 @@ from .tools import deletefile
import colorama
colorama.init()
class MediaLibrary():
''' This is the library object who holds the information about the workspace and all the videos in it. '''
cgreen = colorama.Fore.GREEN
creset = colorama.Fore.RESET
def __init__(self, files = False, directories = False, inputs = ["any"], filters = [], verbose = False):
class MediaLibrary():
'''This class manages information about the workspace and all videos in it.'''
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.
Initializes a MediaLibrary instance with provided parameters.
Args:
files = False : A list of video files
directories = False : A list of directories containing videos directly or in the subdirectories
inputs = ["any"] : A list of filters to keep when browsing the directories
filters = [] : A list of filters to apply to the videos
verbose = False : A list of print options
Returns:
files (list or False): A list of video files.
directories (list or False): A list of directories containing videos directly or in subdirectories.
inputs (list): A list of filters to keep when browsing the directories.
filters (list): A list of filters to apply to the videos.
verbose (bool): A flag to enable verbose output.
'''
if not files and not directories:
return
self.directories = None
self.inputs = inputs
self.filters = filters
self.videos = dict()
self.directories = None
self.inputs = inputs
self.filters = filters
self.videos = dict()
if files:
for filepath in files:
self.videos[filepath] = Video(filepath, verbose = verbose)
self.videos[filepath] = Video(filepath, verbose=verbose)
if directories:
self.directories = directories
self.load_directories(verbose = verbose)
self.directories = directories
self.load_directories(verbose=verbose)
self.filter_videos(verbose = verbose)
self.filter_videos(verbose=verbose)
def __str__(self):
''' Returns a sting representations about the current instance of the MediaLibrary object
Args:
'''
Returns a string representation of the MediaLibrary instance.
Returns:
String : Information about what the MediaLibrary is tracking
str: Information about the MediaLibrary instance.
'''
text = ""
if self.directories:
text = f"MediaCurator is watching the following directories: "
text += f"MediaCurator is watching the following directories: "
text += '\n '.join(map(str, self.directories)) + '\n'
text += f"MediaCurator is tracking {len(self.videos)} video files"
return text
def load_directories(self, verbose = False):
def load_directories(self, verbose=False):
'''
Scan folders for video files respecting the inputs requested by the user
Save them to the videos dictionary
Scans folders for video files respecting the inputs requested by the user and saves them to the videos dictionary.
Args:
verbose = False : A list of print options
Returns:
verbose (bool): A flag to enable verbose output.
'''
print(f"{colorama.Fore.GREEN}Scanning files in {', '.join(map(str, self.directories))} for videos{colorama.Fore.RESET}")
print(f"{cgreen}Scanning files in {', '.join(map(str, self.directories))} for videos{creset}")
videolist = []
for directory in self.directories:
path = Path(directory)
# get all video filetypes
if "wmv" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[wW][mM][vV]"))
if "avi" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[aA][vV][iI]"))
if "mkv" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[mM][kK][vV]"))
if "mp4" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[mM][pP]4"))
if "m4v" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[mM]4[vV]"))
if "flv" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[fF][lL][vV]"))
if "mpg" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[mM][pP][gG]"))
if "mpeg" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[mM][pP][eE][gG]"))
if "vid" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[vV][iI][dD]"))
if "vob" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[vV][oO][bB]"))
if "divx" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[dD][iI][vV][xX]"))
if "ogm" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[oO][gG][mM]"))
if "webm" in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob("*.[wW][eE][bB][mM]"))
# Get all video filetypes
for ext in ["wmv", "avi", "mkv", "mp4", "m4v", "flv", "mpg", "mpeg", "vid", "vob", "divx", "ogm", "webm"]:
if ext in self.inputs or "any" in self.inputs or len(self.inputs) < 1:
videolist += list(path.rglob(f"*.[{ext.upper()}{ext.lower()}]"))
# Remove folders
videolist_tmp = videolist
videolist = [video for video in videolist_tmp if video.is_file()]
# Map it all to the videos dictionary as initiated Video objects
print(f"{colorama.Fore.GREEN}Analazing {len(videolist)} videos in {', '.join(map(str, self.directories))}{colorama.Fore.RESET}")
print(f"{cgreen}Analyzing {len(videolist)} videos in {', '.join(map(str, self.directories))}{creset}")
iteration = 0
for video in videolist:
if verbose:
iteration += 1
print(f'{int((iteration / len(videolist )* 100))}% complete', end='\r')
print(f'{int((iteration / len(videolist) * 100))}% complete', end='\r')
self.videos[video] = Video(video, verbose = verbose)
self.videos[video] = Video(video, verbose=verbose)
def filter_videos(self, verbose = False):
''' Mark useless videos in the videos dictionary (default is useful)
def filter_videos(self, verbose=False):
'''
Marks videos for operation in the videos dictionary (default is not to operate).
Args:
verbose = False : A list of print options
Returns:
verbose (bool): A flag to enable verbose output.
'''
print(f"{colorama.Fore.GREEN}Filtering {len(self.videos)} videos for the requested parameters{colorama.Fore.RESET}")
print(f"{cgreen}Filtering {len(self.videos)} videos for the requested parameters{creset}")
for filepath in self.videos:
# filter for filetypes
# Filter for filetypes
if len([filtr for filtr in self.filters if filtr in ["old", "mpeg4", "mpeg", "wmv3", "wmv", "h264", "hevc", "x265", "av1"]]) > 0:
useful = False
operate = False
if "old" in self.filters and self.videos[filepath].codec not in ["hevc", "av1"]:
useful = True
operate = True
if ("mpeg4" in self.filters or "mpeg" in self.filters) and self.videos[filepath].codec in ["mpeg4", "msmpeg4v3"]:
useful = True
operate = True
if "mpeg" in self.filters and self.videos[filepath].codec in ["mpeg1video"]:
useful = True
operate = True
if ("wmv3" in self.filters or "wmv" in self.filters) and self.videos[filepath].codec in ["wmv3"]:
useful = True
operate = True
if "h264" in self.filters and self.videos[filepath].codec in ["h264"]:
useful = True
operate = True
if ("hevc" in self.filters or "x265" in self.filters) and self.videos[filepath].codec in ["hevc"]:
useful = True
operate = True
if "av1" in self.filters and self.videos[filepath].codec in ["av1"]:
useful = True
self.videos[filepath].useful = useful
operate = True
self.videos[filepath].operate = operate
# keep video if useful and user wants to also filter by selected resolutions
if self.videos[filepath].useful and len([filtr for filtr in self.filters if filtr in ["lowres", "hd", "subsd", "sd", "720p", "1080p", "uhd"]]) > 0:
useful = False
# Keep video for operation if specified resolution
if self.videos[filepath].operate and len([filtr for filtr in self.filters if filtr in ["lowres", "hd", "subsd", "sd", "720p", "1080p", "uhd"]]) > 0:
operate = False
if "subsd" in self.filters and self.videos[filepath].definition in ["subsd"]:
useful = True
operate = True
if "sd" in self.filters and self.videos[filepath].definition in ["sd"]:
useful = True
operate = True
if "720p" in self.filters and self.videos[filepath].definition in ["720p"]:
useful = True
operate = True
if "1080p" in self.filters and self.videos[filepath].definition in ["1080p"]:
useful = True
operate = True
if "uhd" in self.filters and self.videos[filepath].definition in ["uhd"]:
useful = True
operate = True
if "lowres" in self.filters and self.videos[filepath].definition in ["subsd", "sd"]:
useful = True
operate = True
if "hd" in self.filters and self.videos[filepath].definition in ["720p", "1080p", "uhd"]:
useful = True
self.videos[filepath].useful = useful
operate = True
self.videos[filepath].operate = operate
# keep video if useful and user wants to also filter when there is an ffmpeg errors
if self.videos[filepath].useful and len([filtr for filtr in self.filters if filtr in ["fferror"]]) > 0:
useful = False
# Keep video for operation if ffmpeg error exists
if self.videos[filepath].operate and len([filtr for filtr in self.filters if filtr in ["fferror"]]) > 0:
operate = False
if self.videos[filepath].error:
useful = True
self.videos[filepath].useful = useful
operate = True
self.videos[filepath].operate = operate
print(f"{cgreen}Found {len([filepath for filepath in self.videos if self.videos[filepath].operate])} videos for the requested parameters{creset}")
print(f"{colorama.Fore.GREEN}Found {len([filepath for filepath in self.videos if self.videos[filepath].useful])} videos for the requested parameters{colorama.Fore.RESET}")
def unwatch(self, filepath, delete = False):
''' remove a video from the index and delete it if requested
def unwatch(self, filepath, delete=False):
'''
Removes a video from the index and deletes it if requested.
Args:
filepath : A string containing the full filepath
delete = False : Delete the file as well as removing it from the library
filepath (str): The full filepath of the video.
delete (bool): If True, delete the file as well as removing it from the library.
Returns:
boolean : Operation success
bool: True if operation successful, False otherwise.
'''
if delete:

View File

@ -10,14 +10,16 @@ import sys
import colorama
colorama.init()
cgreen = colorama.Fore.GREEN
cyellow = colorama.Fore.YELLOW
cred = colorama.Fore.RED
creset = colorama.Fore.RESET
def load_arguments():
'''Get/load command parameters
Args:
Returns:
arguments: A dictionary of lists of the options passed by the user
dict: A dictionary containing lists of options passed by the user
'''
arguments = {
"directories": list(),
@ -29,12 +31,12 @@ def load_arguments():
}
for arg in sys.argv:
# Confirm with the user that he selected to delete found files
# Confirm with the user that they selected to delete found files
if "-del" in arg:
print(
f"{colorama.Fore.YELLOW}WARNING: Delete option selected!{colorama.Fore.RESET}")
if not user_confirm(f"Are you sure you wish to delete all found results after selected operations are succesfull ? [Y/N] ?", color="yellow"):
print(f"{colorama.Fore.GREEN}Exiting!{colorama.Fore.RESET}")
f"{cyellow}WARNING: Delete option selected!{creset}")
if not user_confirm(f"Are you sure you wish to delete all found results after selected operations are successful? [Y/N] ?", color="yellow"):
print(f"{cgreen}Exiting!{creset}")
exit()
elif "-in:" in arg:
arguments["inputs"] += arg[4:].split(",")
@ -53,19 +55,17 @@ def load_arguments():
def detect_ffmpeg():
'''Returns the version of ffmpeg that is installed or false
Args:
'''Returns the version of ffmpeg that is installed or False
Returns:
String : The version number of the installed FFMPEG
False : The failure of retreiving the version number
str: The version number of the installed FFMPEG
False: If version retrieval failed
'''
try:
txt = subprocess.check_output(
['ffmpeg', '-version'], stderr=subprocess.STDOUT).decode()
if "ffmpeg version" in txt:
# Strip the useless text and
# Strip the useless text
return txt.split(' ')[2]
except:
pass
@ -73,19 +73,19 @@ def detect_ffmpeg():
def user_confirm(question, color=False):
'''Returns the user answer to a yes or no question
'''Returns the user's answer to a yes or no question
Args:
question : A String containing the user question
color : A String containing the prefered color for a question (reg/yellow)
question (str): The user question
color (str, optional): The preferred color for a question (red/yellow)
Returns:
Bool : Positive or negative return to the user question
bool: True for a positive response, False otherwise
'''
if color == "yellow":
print(f"{colorama.Fore.YELLOW}{question} {colorama.Fore.RESET}", end='')
print(f"{cyellow}{question} {creset}", end='')
answer = input()
elif color == "red":
print(f"{colorama.Fore.RED}{question} {colorama.Fore.RESET}", end='')
print(f"{cred}{question} {creset}", end='')
answer = input()
else:
answer = input(f"{question} ")
@ -98,32 +98,31 @@ def user_confirm(question, color=False):
def deletefile(filepath):
'''Delete a file, Returns a boolean
'''Delete a file and return a boolean indicating success
Args:
filepath : A string containing the full filepath
filepath (str): The full filepath
Returns:
Bool : The success of the operation
bool: True if successful, False otherwise
'''
try:
os.remove(filepath)
except OSError:
print(f"{colorama.Fore.RED}Error deleting {filepath}{colorama.Fore.RESET}")
print(f"{cred}Error deleting {filepath}{creset}")
return False
print(f"{colorama.Fore.GREEN}Successfully deleted {filepath}{colorama.Fore.RESET}")
print(f"{cgreen}Successfully deleted {filepath}{creset}")
return True
def findfreename(filepath, attempt=0):
''' Given a filepath it will try to find a free filename by appending to the name.
First trying as passed in argument, then adding [HEVC] to the end and if all fail [HEVC](#).
'''Find a free filename by appending [HEVC] or [HEVC](#) to the name if necessary
Args:
filepath : A string containing the full filepath
attempt : The number of times we have already tryed
filepath (str): The full filepath
attempt (int, optional): The number of attempts made
Returns:
filepath : The first free filepath we found
str: The first free filepath found
'''
attempt += 1
filename = str(filepath)[:str(filepath).rindex(".")]

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python3
'''Its a video!'''
'''This module defines the Video class, which contains information and methods for video files.'''
from .tools import deletefile, findfreename
import subprocess
@ -8,71 +8,73 @@ import os
import colorama
colorama.init()
cyellow = colorama.Fore.YELLOW
cred = colorama.Fore.RED
creset = colorama.Fore.RESET
class Video():
'''Contains the information and methods of a video file.'''
def __init__(self, filepath, useful = True, verbose = False):
'''Contains the information and methods of a video file.
def __init__(self, filepath, operate=True, verbose=False):
'''Initializes a Video instance with provided parameters.
Args:
filepath : A string containing the full filepath
useful : A boolean marking if the video is to be operated on
verbose = False : A list of print options
Returns:
filepath (str): The full filepath of the video.
operate (bool): A flag indicating if the video is to be operated on.
verbose (bool): A flag indicating whether to enable verbose output.
'''
# Initialize attributes
self.path = None
self.filename_origin = None
self.filesize = None
self.filename_new = None
self.filename_tmp = None
self.useful = None
self.operate = None
self.codec = None
self.error = None
self.definition = None
self.width = None
self.height = None
#Breaking down the full path in its components
# Break down the full path into its components
if os.name == 'nt':
self.path = str(filepath)[:str(filepath).rindex("\\") + 1]
self.filename_origin = str(filepath)[str(filepath).rindex("\\") + 1:]
self.path = str(filepath)[:str(filepath).rindex("\\") + 1]
self.filename_origin = str(filepath)[str(filepath).rindex("\\") + 1:]
else:
self.path = str(filepath)[:str(filepath).rindex("/") + 1]
self.filename_origin = str(filepath)[str(filepath).rindex("/") + 1:]
self.path = str(filepath)[:str(filepath).rindex("/") + 1]
self.filename_origin = str(filepath)[str(filepath).rindex("/") + 1:]
if not os.path.exists(filepath):
self.error = f"FileNotFoundError: [Errno 2] No such file or directory: '{filepath}'"
self.useful = useful
self.error = f"FileNotFoundError: [Errno 2] No such file or directory: '{filepath}'"
self.operate = operate
else:
# Marking useful is user manually set it.
self.useful = useful
# Mark the video for operation if specified manually by the user
self.operate = operate
#Gathering information on the video
self.filesize = self.detect_filesize(filepath)
self.error = self.detect_fferror(filepath)
self.codec = self.detect_codec(filepath)
# Gather 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 )
self.definition = self.detect_definition(
width=self.width,
height=self.height
)
except:
self.width, self.height = False, False
self.definition = False
self.definition = False
if self.error and verbose:
print(f"{colorama.Fore.RED}There seams to be an error with \"{filepath}\"{colorama.Fore.RESET}")
print(f"{colorama.Fore.RED} {self.error}{colorama.Fore.RESET}")
print(f"{cred}There seems to be an error with \"{filepath}\"{creset}")
print(f"{cred} {self.error}{creset}")
def __str__(self):
'''Returns a short formated string about the video
Args:
'''Returns a short formatted string about the video.
Returns:
String : A short formated string about the video
str: A short formatted string about the video.
'''
if type(self.error) is str and "FileNotFoundError" in self.error:
@ -80,49 +82,41 @@ class Video():
text = f"{self.codec} - "
# If the first character of the definition is not a number (ie UHD and not 720p) upper it
# If the first character of the definition is not a number (e.g., UHD and not 720p), upper it
if self.definition and 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
# Return the size in MB or GB if more than 1024 MB
if self.filesize >= 1024:
text += f"{self.filesize / 1024 :.2f} gb - "
text += f"{self.filesize / 1024 :.2f} GB - "
else:
text += f"{self.filesize} mb - "
text += f"{self.filesize} MB - "
text += f"'{self.path + self.filename_origin}'"
if self.error:
text += f"{colorama.Fore.RED}\nErrors:{colorama.Fore.RESET}"
text += f"{cred}\nErrors:{creset}"
for err in self.error.splitlines():
text += f"{colorama.Fore.RED}\n {err}{colorama.Fore.RESET}"
text += f"{cred}\n {err}{creset}"
return text
__repr__ = __str__
def fprint(self):
'''Returns a long formated string about the video
Args:
'''Returns a long formatted string about the video.
Returns:
String : A long formated string about the video
str: A long formatted string about the video.
'''
if type(self.error) is str and "FileNotFoundError" in self.error:
return self.error
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 and self.definition[0] and not self.definition[0].isnumeric():
text += f" Definition: {self.definition.upper()}: ({self.width}x{self.height})\n"
else:
@ -130,34 +124,31 @@ class Video():
text += f" Codec: {self.codec}\n"
# Return the size in mb or gb if more than 1024 mb
# Return the size in MB or GB if more than 1024 MB
if self.filesize >= 1024:
text += f" size: {self.filesize / 1024 :.2f} gb"
text += f" Size: {self.filesize / 1024 :.2f} GB"
else:
text += f" size: {self.filesize} mb"
text += f" Size: {self.filesize} MB"
if self.error:
text += f"{colorama.Fore.RED}\n Errors:{colorama.Fore.RESET}"
text += f"{cred}\n Errors:{creset}"
for err in self.error.splitlines():
text += f"{colorama.Fore.RED}\n {err}{colorama.Fore.RESET}"
text += f"{cred}\n {err}{creset}"
return text
def convert(self, vcodec = "x265", acodec = False, extension = "mkv", verbose = False):
def convert(self, vcodec="x265", acodec=False, extension="mkv", verbose=False):
'''
Convert to original file to the requested format / codec
verbose will print ffmpeg's output
Converts the original file to the requested format / codec.
Args:
vcodec = "x265" : The new video codec, supports av1 or x265
acodec = False : Currently not enabled, will be for audio codecs
extension = "mkv" : A string containing the new video container format and file extention
verbose = False : A boolean enabling verbosity
vcodec (str): The new video codec, supports av1 or x265.
acodec (bool): Currently not enabled, will be for audio codecs.
extension (str): The new video container format and file extension.
verbose (bool): A flag enabling verbosity.
Returns:
boolean : Operation success
bool: True if operation successful, False otherwise.
'''
# Setting new filename
@ -178,18 +169,18 @@ class Video():
else:
newfilename = str(newfilename)[str(newfilename).rindex("/") + 1:]
self.filename_tmp = newfilename
self.filename_tmp = newfilename
# Settting ffmpeg
# Setting ffmpeg
args = ['ffmpeg', '-i', self.path + self.filename_origin]
# conversion options
# Conversion options
if vcodec == "av1":
args += ['-c:v', 'libaom-av1', '-strict', 'experimental']
elif vcodec == "x265" or vcodec == "hevc":
args += ['-c:v', 'libx265']
args += ['-max_muxing_queue_size', '1000']
# conversion output
# Conversion output
args += [self.path + self.filename_tmp]
try:
@ -200,10 +191,10 @@ class Video():
except subprocess.CalledProcessError as e:
deletefile(self.path + self.filename_tmp)
self.filename_tmp = ""
print(f"{colorama.Fore.RED}Conversion failed {e}{colorama.Fore.RESET}")
print(f"{cred}Conversion failed {e}{creset}")
return False
except KeyboardInterrupt:
print(f"{colorama.Fore.YELLOW}Conversion cancelled, cleaning up...{colorama.Fore.RESET}")
print(f"{cyellow}Conversion cancelled, cleaning up...{creset}")
deletefile(self.path + self.filename_tmp)
self.filename_tmp = ""
exit()
@ -211,89 +202,89 @@ class Video():
try:
os.chmod(f"{self.path}{self.filename_tmp}", 0o777)
except PermissionError:
print(f"{colorama.Fore.RED}PermissionError on: '{self.path}{self.filename_tmp}'{colorama.Fore.RESET}")
print(f"{cred}PermissionError on: '{self.path}{self.filename_tmp}'{creset}")
self.filename_new = self.filename_tmp
self.filename_tmp = ""
return True
@staticmethod
def detect_fferror(filepath):
'''Returns a string with the detected errors
'''Returns a string with the detected errors.
Args:
filepath : A string containing the full filepath
filepath (str): The full filepath of the video.
Returns:
String : The errors that have been found / happened
False : The lack of errors
str: The errors that have been found/happened.
False: The lack of errors.
'''
try:
args = ["ffprobe","-v","error",str(filepath)]
args = ["ffprobe", "-v", "error", str(filepath)]
output = subprocess.check_output(args, stderr=subprocess.STDOUT)
output = output.decode().strip()
if len(output) > 0:
return output
except (subprocess.CalledProcessError, IndexError):
return f'{colorama.Fore.RED}There seams to be a "subprocess.CalledProcessError" error with \"{filepath}\"{colorama.Fore.RESET}'
return f'{cred}There seems to be a "subprocess.CalledProcessError" error with \"{filepath}\"{creset}'
return False
@staticmethod
def detect_codec(filepath):
'''Returns a string with the detected codec
'''Returns a string with the detected codec.
Args:
filepath : A string containing the full filepath
filepath (str): The full filepath of the video.
Returns:
String : The codec that has been detected
False : An error in the codec fetching process
str: The codec that has been detected.
False: An error in the codec fetching process.
'''
output = False
try:
args = ["ffprobe", "-v", "quiet", "-select_streams", "v:0", "-show_entries", "stream=codec_name", "-of", "default=noprint_wrappers=1:nokey=1", str(filepath)]
output = subprocess.check_output(args, stderr=subprocess.STDOUT)
# decoding from binary, stripping whitespace, keep only last line
# in case ffmprobe added error messages over the requested information
# Decoding from binary, stripping whitespace, keep only last line
# in case ffprobe added error messages over the requested information
output = output.decode().strip()
except (subprocess.CalledProcessError, IndexError):
return False
return output
@staticmethod
def detect_resolution(filepath):
'''Returns a list with the detected width(0) and height(1)
'''Returns a list with the detected width(0) and height(1).
Args:
filepath : A string containing the full filepath
filepath (str): The full filepath of the video.
Returns:
List : the detected width(0) and height(1)
False : An error in the codec fetching process
List: The detected width(0) and height(1).
False: An error in the resolution fetching process.
'''
try:
args = ["ffprobe","-v","quiet","-select_streams","v:0", "-show_entries","stream=width,height","-of","csv=s=x:p=0",str(filepath)]
args = ["ffprobe", "-v", "quiet", "-select_streams", "v:0", "-show_entries", "stream=width,height", "-of", "csv=s=x:p=0", str(filepath)]
output = subprocess.check_output(args, stderr=subprocess.STDOUT)
# decoding from binary, stripping whitespace, keep only last line
# in case ffmprobe added error messages over the requested information
# Decoding from binary, stripping whitespace, keep only last line
# in case ffprobe added error messages over the requested information
output = output.decode().strip()
# See if we got convertable data
# See if we got convertible data
output = [int(output.split("x")[0]), int(output.split("x")[1])]
except (subprocess.CalledProcessError, IndexError):
return False
return output[0], output[1]
@staticmethod
def detect_definition(filepath = False, width = False, height = False):
'''Returns a string with the detected definition corrected for dead space
def detect_definition(filepath=False, width=False, height=False):
'''Returns a string with the detected definition corrected for dead space.
Args:
filepath : A string containing the full filepath
filepath (str): A string containing the full filepath.
width (int): The width of the video.
height (int): The height of the video.
Returns:
List : The classified definition in width(0) and height(1)
False : An error in the process
str: The classified definition in width(0) and height(1).
False: An error in the process.
'''
if filepath:
width, height = Video.detect_resolution(filepath)
@ -312,13 +303,14 @@ class Video():
@staticmethod
def detect_filesize(filepath):
'''Returns an integer with size in mb
'''Returns an integer with size in MB.
Args:
filepath : A string containing the full filepath
filepath (str): A string containing the full filepath.
Returns:
Integer : The filesize in mb
False : An error in the process
int: The filesize in MB.
False: An error in the process.
'''
try:
size = int(os.path.getsize(filepath) / 1024 / 1024)

View File

@ -22,13 +22,20 @@ except ModuleNotFoundError:
from library.medialibrary import MediaLibrary
from library.tools import detect_ffmpeg, user_confirm, load_arguments
# Import colorama for colored output
import colorama
colorama.init()
# Define color codes for colored output
ccyan = colorama.Fore.CYAN
cblue = colorama.Fore.BLUE
cgreen = colorama.Fore.GREEN
cred = colorama.Fore.RED
creset = colorama.Fore.RESET
def main():
'''
MediaCurator's main function
MediaCurator's main function
Args:
@ -39,14 +46,14 @@ def main():
# confirm that the command has enough parameters
if len(sys.argv) < 2:
print(f"{colorama.Fore.RED}ERROR: Command not understood, please see documentation.{colorama.Fore.RESET}")
print(f"{cred}ERROR: Command not understood, please see documentation.{creset}")
# confirm that ffmpeg in indeed installed
ffmpeg_version = detect_ffmpeg()
if not ffmpeg_version:
print(f"{colorama.Fore.RED}No ffmpeg version detected{colorama.Fore.RESET}")
print(f"{cred}No ffmpeg version detected{creset}")
exit()
print(f"{colorama.Fore.BLUE}ffmpeg version detected: {ffmpeg_version}{colorama.Fore.RESET}")
print(f"{cblue}ffmpeg version detected: {ffmpeg_version}{creset}")
# Get/load command parameters
arguments = load_arguments()
@ -57,27 +64,26 @@ def main():
elif len(arguments["directories"]) > 0:
medialibrary = MediaLibrary(directories = arguments["directories"], inputs = arguments["inputs"], filters = arguments["filters"])
else:
print(f"{colorama.Fore.RED}ERROR: No files or directories selected.{colorama.Fore.RESET}")
print(f"{cred}ERROR: No files or directories selected.{creset}")
return
# Actions
if sys.argv[1] == "list":
# Pulling list of marked videos / original keys for the medialibrary.videos dictionary
keylist = [filepath for filepath in medialibrary.videos if medialibrary.videos[filepath].useful]
keylist = [filepath for filepath in medialibrary.videos if medialibrary.videos[filepath].operate]
keylist.sort()
for filepath in keylist:
if medialibrary.videos[filepath].useful:
if medialibrary.videos[filepath].operate:
if "formated" in arguments["printop"] or "verbose" in arguments["printop"]:
if medialibrary.videos[filepath].error:
print(f"{colorama.Fore.RED}{medialibrary.videos[filepath].fprint()}{colorama.Fore.RESET}")
print(f"{cred}{medialibrary.videos[filepath].fprint()}{creset}")
else:
print(medialibrary.videos[filepath].fprint())
else:
if medialibrary.videos[filepath].error:
print(f"{colorama.Fore.RED}{medialibrary.videos[filepath]}{colorama.Fore.RESET}")
print(f"{cred}{medialibrary.videos[filepath]}{creset}")
else:
print(medialibrary.videos[filepath])
@ -85,21 +91,20 @@ def main():
if "-del" in sys.argv:
medialibrary.unwatch(filepath, delete = True)
elif sys.argv[1] == "test":
# Pulling list of marked videos / original keys for the medialibrary.videos dictionary
keylist = [filepath for filepath in medialibrary.videos if medialibrary.videos[filepath].useful]
keylist = [filepath for filepath in medialibrary.videos if medialibrary.videos[filepath].operate]
keylist.sort()
for filepath in keylist:
if medialibrary.videos[filepath].useful:
if medialibrary.videos[filepath].operate:
if "formated" in arguments["printop"] or "verbose" in arguments["printop"]:
if medialibrary.videos[filepath].error:
print(f"{colorama.Fore.RED}{medialibrary.videos[filepath].fprint()}{colorama.Fore.RESET}")
print(f"{cred}{medialibrary.videos[filepath].fprint()}{creset}")
else:
print(medialibrary.videos[filepath].fprint())
else:
if medialibrary.videos[filepath].error:
print(f"{colorama.Fore.RED}{medialibrary.videos[filepath]}{colorama.Fore.RESET}")
print(f"{cred}{medialibrary.videos[filepath]}{creset}")
else:
print(medialibrary.videos[filepath])
@ -111,7 +116,7 @@ def main():
counter = 0
# Pulling list of marked videos / original keys for the medialibrary.videos dictionary
keylist = [filepath for filepath in medialibrary.videos if medialibrary.videos[filepath].useful]
keylist = [filepath for filepath in medialibrary.videos if medialibrary.videos[filepath].operate]
keylist.sort()
for filepath in keylist:
@ -123,19 +128,19 @@ def main():
vcodec = "x265"
# Verbosing
print(f"{colorama.Fore.GREEN}****** Starting conversion {counter} of {len(keylist)}: '{colorama.Fore.CYAN}{medialibrary.videos[filepath].filename_origin}{colorama.Fore.GREEN}' from {colorama.Fore.CYAN}{medialibrary.videos[filepath].codec}{colorama.Fore.GREEN} to {colorama.Fore.CYAN}{vcodec}{colorama.Fore.GREEN}...{colorama.Fore.RESET}")
print(f"{colorama.Fore.CYAN}Original file:{colorama.Fore.RESET}")
print(f"{cgreen}****** Starting conversion {counter} of {len(keylist)}: '{ccyan}{medialibrary.videos[filepath].filename_origin}{cgreen}' from {ccyan}{medialibrary.videos[filepath].codec}{cgreen} to {ccyan}{vcodec}{cgreen}...{creset}")
print(f"{ccyan}Original file:{creset}")
if "formated" in arguments["printop"] or "verbose" in arguments["printop"]:
print(medialibrary.videos[filepath].fprint())
else:
print(medialibrary.videos[filepath])
print(f"{colorama.Fore.GREEN}Converting please wait...{colorama.Fore.RESET}", end="\r")
print(f"{cgreen}Converting please wait...{creset}", end="\r")
# Converting
if medialibrary.videos[filepath].convert(verbose = "verbose" in arguments["printop"]):
# Mark the job as done
medialibrary.videos[filepath].useful = False
medialibrary.videos[filepath].operate = False
# Scan the new video
newfpath = medialibrary.videos[filepath].path + medialibrary.videos[filepath].filename_new
@ -143,17 +148,16 @@ def main():
medialibrary.videos[newfpath] = Video(newfpath, verbose = "verbose" in arguments["printop"])
# Verbose
print(f"{colorama.Fore.GREEN}Successfully converted '{medialibrary.videos[filepath].filename_origin}'{colorama.Fore.CYAN}({medialibrary.videos[filepath].filesize}mb){colorama.Fore.GREEN} to '{medialibrary.videos[newfpath].filename_origin}'{colorama.Fore.CYAN}({medialibrary.videos[newfpath].filesize}mb){colorama.Fore.GREEN}, {colorama.Fore.CYAN}new file:{colorama.Fore.RESET}")
print(f"{cgreen}Successfully converted '{medialibrary.videos[filepath].filename_origin}'{ccyan}({medialibrary.videos[filepath].filesize}mb){cgreen} to '{medialibrary.videos[newfpath].filename_origin}'{ccyan}({medialibrary.videos[newfpath].filesize}mb){cgreen}, {ccyan}new file:{creset}")
if "formated" in arguments["printop"] or "verbose" in arguments["printop"]:
if medialibrary.videos[newfpath].error:
print(f"{colorama.Fore.RED}{medialibrary.videos[newfpath].fprint()}{colorama.Fore.RESET}")
print(f"{cred}{medialibrary.videos[newfpath].fprint()}{creset}")
else:
print(medialibrary.videos[newfpath].fprint())
else:
if medialibrary.videos[newfpath].error:
print(f"{colorama.Fore.RED}{medialibrary.videos[newfpath]}{colorama.Fore.RESET}")
print(f"{cred}{medialibrary.videos[newfpath]}{creset}")
else:
print(medialibrary.videos[newfpath])
@ -161,9 +165,5 @@ def main():
if "-del" in sys.argv:
medialibrary.unwatch(filepath, delete = True)
if __name__ == '__main__':
main()