video_utils.utils package
Submodules
video_utils.utils.check_cli module
Utility to check if CLI is installed
- video_utils.utils.check_cli.check_cli(cli)
Check if CLI is installed
Determine if given command line interface (CLI) program is installed on the machine.
- Parameters:
cli (str) – Name of CLI to check is installed.
- Returns:
Path to the CLI executable
- Return type:
str
video_utils.utils.ffmpeg_utils module
FFMpeg utilities
- class video_utils.utils.ffmpeg_utils.Chapter(chapter=None)
Bases:
objectRepresents a chapter in a video file
- add_offset(offset: float, flag: int = 2) None
To adjust the start, end, or both times
- Parameters:
offset (float) – Offset time in seconds
- Keyword Arguments:
flag (int) – Set to: 0 - to add offset to start time, 1 - to add offset to end time, 2 - (default) add offset to start and end
- Returns:
updates internal attributes
- Return type:
None
- base2seconds(val)
Method that converts value in time_base units to seconds
- property den: int
- property end: int
End time of chapter, in time base units
- property end_time: float
End time of chapter in seconds
- get_end(time_base=None) tuple
Method to return chapter end time in time_base and seconds units
- get_start(time_base=None)
Get chapter start time in time_base and seconds units
- property num: int
- seconds2base(val)
Method that converts value in seconds to time_base units
- property start: int
Start time of chapter, in time base units
- property start_time: float
Start time of chapter in seconds
- property time_base
Time base for start/end values
- property title
Chapter title
- to_ffmetadata()
Method that returns information in format for FFMETADATA file
- class video_utils.utils.ffmpeg_utils.FFMetaData(version=1)
Bases:
objectFor creating a FFMetaData file for input into ffmpeg CLI
- add_chapter(*args, **kwargs)
Method to add chapter marker to FFMetaData file
- Parameters:
one (If) – Must be Chapter instance
three (If) – start : Start time of chapter (float or datetime.timedelta) end : End time of chapter (float or datetime.timedelta) title : Chapter title (str)
- Keyword Arguments:
time_base – String of form ‘num/den’, where num and den are integers. If the time_base is missing then start/end times are assumed to be in nanosecond. Ignored if NOT three (3) inputs.
- Returns:
None
- add_metadata(**kwargs)
Method to add new metadata tags to FFMetaData file
- Parameters:
None –
- Keyword Arguments:
**kwargs – Any key/value pair where key is a valid metadata tag and value is the value for the tag.
- Returns:
None
- save(fpath)
Method to write ffmetadata to file
- Parameters:
fpath (str) – Full path of file to write to
- Keyword Arguments:
None –
- Returns:
Returns the fpath input
- Return type:
str
- class video_utils.utils.ffmpeg_utils.FFmpegProgress(interval: float = 60.0, nintervals: int | None = None)
Bases:
objectMonitor FFMpeg progress
Class for monitoring output from ffmpeg to determine how much time remains in the conversion.
- progress(in_val)
Get progress of FFMpeg transcode
- video_utils.utils.ffmpeg_utils.check_integrity(fpath: str) bool
Test the integrity of a video file.
Runs ffmpeg with null output, checking errors for ‘overread’. If overread found, then return False, else True
- Parameters:
fpath (str) – Full path of file to check
- Keyword Arguments:
None –
- Returns:
True if no overread errors, False otherwise
- Return type:
bool
- video_utils.utils.ffmpeg_utils.combine_mp4_files(out_file: str, *args: str) None
Combine multiple (2+) mp4 files into a single mp4 file
- Parameters:
out_file (str) – Output (combined) file path
*args (str) – Any number of input file paths
- Keyword Arguments:
None –
- Returns:
None
- video_utils.utils.ffmpeg_utils.cropdetect(infile: str, video_res: tuple[int] | None, seg_len: int | float = 20, threads: int | None = None) str | None
Use FFmpeg to to detect a cropping region for video files
- Parameters:
infile (str) – Path to input file for crop detection
video_res (tuple) – Tuple of ints specifying the video width/height
- Keyword Arguments:
seg_len – Length of video, in seconds, starting from beginning to use for crop detection, default is 20 seconds
threads (int) – Number of threads to use when running ffmpeg to detect crop
- Returns:
- FFmpeg video filter in the format
crop=w:h:x:yor None if no cropping detected
- FFmpeg video filter in the format
- video_utils.utils.ffmpeg_utils.cropdetect_cmd(infile: str, start_time: timedelta, seg_len: timedelta, threads: int | None) list[str]
Generate ffmpeg command list for crop detection
- Parameters:
infile (str) – File to read from
start_time (timedelta) – Start time for segment
seg_len (timedelta) – Length of segment for crop detection
threads (int) – number of threads to let ffmpeg use
- Keyword Arguments:
None. –
- Returns:
Command to be run using subprocess.Popen
- Return type:
list
- video_utils.utils.ffmpeg_utils.extract_hevc(in_file, out_file: str | None = None) str | Popen
Extract HEVC stream from file
- Parameters:
in_file (str) – Input file to extract stream from
- Keyword Arguments:
out_file (str) – File to write stream to. If no file specified, then a Popen object is returned with stdout=subprocess.PIPE
- Returns:
- Based on the out_file keyword argument, either the path
to the output file or a Popen object is returned.
- Return type:
str, Popen
- video_utils.utils.ffmpeg_utils.get_chapters(fpath)
Function for extract chapter information from video
- Parameters:
fpath (str) – Path of file to extract chapter information from
- Keyword Arguments:
None –
- Returns:
List of Chapter objects if chapters exist, None otherwise
- video_utils.utils.ffmpeg_utils.get_content_light_level_metadata(side_data)
Extract content light level metadata
- Parameters:
side_data (list) – Dicts containing HDR metadata
- Returns:
dict
- video_utils.utils.ffmpeg_utils.get_hdr_opts(fpath)
Function to get HDR x265 options
- Parameters:
fpath (str) – Path of file to get HDR options for
- Returns:
None, tuple
- video_utils.utils.ffmpeg_utils.get_mastering_display_metadata(side_data)
Extract mastering display metadata
- Parameters:
side_data (list) – Dicts containing HDR metadata
- Returns:
dict
- video_utils.utils.ffmpeg_utils.get_video_length(in_file: str) float
Returns float length of video, in seconds
- video_utils.utils.ffmpeg_utils.partial_extract(in_file: str, out_file: str, start_offset: float | timedelta, duration: float | timedelta, chapter_file: str | None = None) bool
Function for extracting video segement
- Parameters:
in_file (str) – Path of file to extract segment from
out_file (str) – Path of file to extract segment to
start_offset (float,timedelta) – Segment start time in input file. If float, units must be seconds.
duration (float,timedelta) – Segment duration If float, units must be seconds.
- Keyword Arguments:
chapter_file (str) – Path to ffmetadata file specifying chapters for new segment
- Returns:
True on successful extraction, False otherwise
- Return type:
bool
- video_utils.utils.ffmpeg_utils.progress(proc: Popen, interval: float = 60.0, nintervals: int | None = None) None
Determine how much time remains in the conversion.
- Parameters:
proc (Popen) – A subprocess.Popen instance. The stdout of Popen must be set to subprocess.PIPE and the stderr must be set to subprocess.STDOUT so that all information runs through stdout. The universal_newlines keyword must be set to True as well
- Keyword Arguments:
interval (float) – The update interval, in seconds, to log time remaining info. Default is sixty (60) seconds, or 1 minute.
nintervals (int) – Set to number of updates you would like to be logged about progress. Default is to log as many updates as it takes at the interval requested. Setting this keyword will override the value set in the interval keyword. Note that the value of interval will be used until the first log, after which point the interval will be updated based on the remaing conversion time and the requested number of updates
- Returns:
- Returns nothing. Does NOT wait for the process to finish so MUST
handle that in calling function
- video_utils.utils.ffmpeg_utils.split_on_chapter(in_file: str, n_chapters: int | list[int], preroll: int | float | None = None, postroll: int | float | None = None) None
Split a video file based on chapters.
The idea is that if a TV show is ripped with multiple episodes in one file, assuming all episodes have the same number of chapters, one can split the file into individual episode files.
- Parameters:
in_file (str) – Full path to the file that is to be split.
n_chapters (int,list) – The number of chapters in each episode.
- Keyword Arguments:
None –
- Returns:
- Outputs n files, where n is equal to the total number
of chapters in the input file divided by n_chaps.
- video_utils.utils.ffmpeg_utils.total_seconds(*args) list
Convert time strings to the total number of seconds represented by the time
- Parameters:
*args – One or more time strings of format HH:MM:SS
- Keyword Arguments:
None. –
- Returns:
Returns a numpy array of total number of seconds in time
video_utils.utils.handlers module
Specialized logging handlers
- class video_utils.utils.handlers.EMailHandler(*args, **kwargs)
Bases:
HandlerLogging handler for sending email
Send an email with some logging information
- emit(record)
Overload emit to send email
- parse_send_to(send_to)
Parse list of send-to emails
Parse the list of send-to emails from the config yaml file
- Parameters:
send_to (list,tuple,str) – Emails address to send logs to
- Returns:
- String of comma separated emails if list could be
parsed, None otherwise
- Return type:
str,None
- send(subject=None)
Actually send the email
- Keyword Arguments:
subject (str) – Custom subject line for the email
- Returns:
True on send attempted, False on failure
- Return type:
bool
- set_send_level(level)
Set the level at which logs are automatically emailed. For example, if the SendLevel is set to logging.WARNING, if a log with level WARNING or higher is encountered, an email will be sent. Note that if this level is set lower than the logger level, the email my be empty.
- Parameters:
level (int) – The logging level
- Keyword Arguments:
None –
- Returns:
None
- class video_utils.utils.handlers.RotatingFile(*args, **kwargs)
Bases:
ThreadA class that acts like the logging.handlers.RotatingFileHander; writing data out to a file until file grows too large, then rolling over.
This class is intended to be used for stdout and stderr for calls to subprocess module routines, such as the Popen constructor. However, there are many other applications for this class.
- close()
Method to clean up the pipe and logging file
- fileno()
Method to get underlying file pointer; in this case pipe
- run()
Overloads the run method; this method will run in own thread.
Reads data from pipe and passes to self.log.emit
- Parameters:
None –
- Keyword Arguments:
None –
- Returns:
None
- setFormatter(*args)
Set the formatter for the log handler
- start()
Overload thread start method
- video_utils.utils.handlers.init_log_file(format_dict: dict) None
Initialize new handlers given options
Given a dictionary of options for a handler, attempted to define a new handler for the package logger
- Argumenst:
- format_dict (dict)Information for setting up a new handler for
the package logger
Returns:
- video_utils.utils.handlers.send_email(func)
Function to act as decorator to send email using EMailHandler
- Parameters:
func – Function to wrap
- Keyword Arguments:
None. –
- Returns:
Wrapped function
video_utils.utils.pid_check module
Check for already running process
Utilities for checking if a process with the same name/pid is currently running so that multiple instances of code are not running at the same time, namely the file watchdogs
- video_utils.utils.pid_check.pid_running(fpath)
Check if process is already running
Check if current process is already running based on data in JSON formatted file created by pid_store()
- Parameters:
fpath (str) – Full path of file to check pid information against
- Keyword Arguments:
None –
- Returns:
True if process is already running, False otherwise
- Return type:
bool
- video_utils.utils.pid_check.pid_store(fpath)
Store information about the current process in a JSON formatted file
- Parameters:
fpath (str) – Full path of file to write data to
- Keyword Arguments:
None –
- Returns:
True if file created, False otherwise
- Return type:
bool
video_utils.utils.subproc_pool module
Scheduler for multiple subprocesses
Classes to support scheduling of multiple subprocesses so that not too many threads/processes are consumed on machine.
- class video_utils.utils.subproc_pool.PopenPool(*args, threads=None, cpulimit=None, queueDepth=None, **kwargs)
Bases:
ThreadMimic multiprocessing.Pool class, but for subprocess.Popen objects
- close()
Closes the PopenPool, similar to multiprocessing.Pool.close()
Running this method will disable adding processes to the queue.
- property cpulimit
Percentage of CPU allowed for each process
- popen_async(*args, **kwargs)
A method to asynconously run subprocess.Popen calls
- Parameters:
*args – All inputs for subprocess.Popen
- Keyword Arguments:
threads (int) – Specify the number of threads the process will use. Default is one (1)
subprocess.Popen (**kwargs All keywords for) –
- Returns:
- A PopenPool.PopenThread instance; very similar to subprocess.Popen
instance
Note
- If too many processes are already queued, this method will
block until some finish.
- run()
Handles dequeuing and starting Popen processes.
- property threads
Number of threads to all to run at one time
- wait(timeout: int | float | None = None) bool
Method to wait for all processes in queue to finish
- Parameters:
None –
- Keyword Arguments:
timeout (float) – Timeout in seconds
- Returns:
- True if queue is empty, False otherwise;
will be False on timeout
- Return type:
bool
- class video_utils.utils.subproc_pool.PopenThread(*args, **kwargs)
Bases:
ThreadWrapper class for subprocess.Popen that allows starting process in future
This class is designed to allow for starting a subprocess.Popen instance in the future by initializing the Popen instance within the Thread.run method. Thus, the subprocess does not start until the Thread.start method is called.
Note
The thread will run until the subprocess finishes, fails, or is killed
- apply_func(func, *args, **kwargs)
Method to apply function to Popen process
- Parameters:
func – Function to apply to process; Must accept subprocess.Popen instance as first argument
*args – Any other arguments to pass the func
- Keyword Arguments:
**kwargs – Any keywords to pass to func
- Returns:
True if function applied, False otherwise.
- Return type:
bool
- kill()
Kill the subprocess; see subprocess.Popen()
- poll()
Poll subprocess to see if finished; see subprocess.Popen()
- property returncode
Return code of subprocess; see subprocess.Popen()
- run()
Overload run method
- start_wait(timeout=None)
Wait for subprocess to start
Waits for global NLOCK to be acquried
- Keyword Arguments:
timeout (float) – Time (in seconds) to wait for process to start. If None, will block forever.
- Returns:
True if process started, False if failed or timed out
- Return type:
bool
- property threads
Number of threads this process requries
- wait(timeout=None)
Wait for subprocess to finish; see subprocess.Popen()
- video_utils.utils.subproc_pool.make_dirs(path)
Try to make all directories in input path
- Parameters:
path (str) – Path to file
- Keyword Arguments:
None –
- Returns:
True if directory(ies) created, False otherwise
- Return type:
bool
video_utils.utils.update_file_names module
To update file naming convention
- video_utils.utils.update_file_names.gen_hard_links(topdir, rootdir=None, **kwargs)
Function to generate hard links to existing files using new file nameing convention. Will walk through given directory, finding all files with IMDBid in name. Will then get TVDb or TMDb information for renaming.
- video_utils.utils.update_file_names.movie_rename(topdir, path, metadata, imdbID, rootdir=None)
To rename movies to new convention
- video_utils.utils.update_file_names.tv_rename(topdir, path, imdbID, seriesID, rootdir=None, **kwargs)
To rename TV episodes to new convention
- video_utils.utils.update_file_names.update_file_names(*args, rootdir=None, dbID=None, **kwargs)
Function to iterate over Plex Library directories to rename
- Parameters:
*args – A bunch of comma separated paths. These can either be top level Library directories OR individual directories within a library. If they are individual directories within a library, then must set the rootdir keyword for things to work correctly.
- Keyword Arguments:
rootdir (str) – Set if scanning a subset of directories in a library directory. For example, if renaming files in Westworld and the args path is /path/to/TV Shows/Westworld, then should set rootdir=/path/to/TV Shows
dbID (str) – Force a given TV series ID
**kwargs – Various other keywords, including the dvdOrder key
- Returns:
None
- video_utils.utils.update_file_names.update_specials(season00, dbID, rootdir, **kwargs)
Update special episodes?
Module contents
Various utilities to aid in processing
Tons of different utilities to aid in the conversion/manipulation of video files.
- class video_utils.utils.NLock(threads=None)
Bases:
objectSemaphore-like class that allows acquire to decrement by arbitrary number
This class is designed to mimic a semaphore object, with the aquried and release methods decrementing and incrementing, respectively, an internal counter. The difference is that the acquire and release methods can be passed a ‘threads’ value to increment/decrement by a number larget than one (1). This allows locking for processes that require more than one thread to run.
Note
This is not perfect and, because of how events are ‘queued’, more threads than specified could start. For example, say 2 threads are allowed in the NLock object: code-block:
LOCK = NLock(2)
Now, image two (2) processes, each requiring one (1) thread, acquire the lock code-block:
if LOCK.acquire( threads = 1 ): ...process1... if LOCK.acquire( threads = 1 ): ...process2...
While those are running, say a third (3rd) process that requries two (2) threads tries to get the lock code-block:
if LOCK.acquire( threads = 2 ): ...process3...
Now, this call to acquire() for process3 will block, but only until one (1) of the single threaded processes finish (process1 or process2). Say process1 is very long running and process2 is fairly short. In this case, process2 will finish, releasing the lock, allowing process3 to start.
The issue in this case is that the NLock object has now allowed three (3) threads to run instead of only allowing two (2).
This issue will only be encountered if acquire is called with varying thread counts.
- acquire(*args, **kwargs)
Allows for n grabs to lock
Alows for n grabs to lock before it will block. For example, say n = 10, then the acquire() can be called 10 times before it will block. Just as with threading.Lock.acquire(), it will block forever unless keywords are passed
- Parameters:
*args – Accepts all inputs from threading.Lock.aquire()
- Keyword Arguments:
threads (int) – Specifies the number of ‘locks’ to acquire. default 1. When using this class to block number of processes, this is used when a process will use more than one thread.
**kwargs – All other keywords for threading.Lock.aquire()
- Returns:
True if lock acquired, False otherwise
- Return type:
bool
- locked()
Returns True if locked, False otherwise
- property n
Count of threads trying to acquire lock
- release(threads=None)
This method acts the same as a normal threading.Lock.release()
- Parameters:
None –
- Keyword Arguments:
threads (int) – Specifies the number of ‘locks’ to release. default 1. When using this class to block number of processes, this is used when a process will use more than one thread.
- Returns:
None
- property threads
Number of thread acquires allowed
- video_utils.utils.isRunning(**kwargs)
Check for SIGINT or SIGTERM encountered
- video_utils.utils.thread_check(val: int | None) tuple[int | None]
Check requested number of threads
Check that requested number of threads is integer type and is within allowable range
- Returns:
- First value is clipped number of threads to use for a proccess
and second number is maximum number of threads
- Return type:
tuple