Skip to content

Instantly share code, notes, and snippets.

@adde88
Last active September 11, 2024 12:30
Show Gist options
  • Save adde88/2f61e245b197675a02d18fccd14fe523 to your computer and use it in GitHub Desktop.
Save adde88/2f61e245b197675a02d18fccd14fe523 to your computer and use it in GitHub Desktop.
Script to analyze movie files, to output important information for me. Like filename, filesize, codec, bit-rate, dimensions. More to cum
{
"PresetList": [
{
"AlignAVStart": true,
"AudioCopyMask": [
"copy:aac"
],
"AudioEncoderFallback": "av_aac",
"AudioLanguageList": [],
"AudioList": [
{
"AudioBitrate": 160,
"AudioCompressionLevel": -1.0,
"AudioDitherMethod": "auto",
"AudioEncoder": "av_aac",
"AudioMixdown": "stereo",
"AudioNormalizeMixLevel": false,
"AudioSamplerate": "auto",
"AudioTrackDRCSlider": 0.0,
"AudioTrackGainSlider": 0.0,
"AudioTrackQuality": 1.0,
"AudioTrackQualityEnable": false
}
],
"AudioSecondaryEncoderMode": true,
"AudioTrackSelectionBehavior": "first",
"ChapterMarkers": true,
"ChildrenArray": [],
"Default": false,
"FileFormat": "av_mp4",
"Folder": false,
"FolderOpen": false,
"InlineParameterSets": false,
"MetadataPassthrough": true,
"Mp4HttpOptimize": true,
"Mp4iPodCompatible": false,
"PictureAllowUpscaling": false,
"PictureAutoCrop": true,
"PictureBottomCrop": 0,
"PictureChromaSmoothCustom": "",
"PictureChromaSmoothPreset": "off",
"PictureChromaSmoothTune": "none",
"PictureColorspaceCustom": "",
"PictureColorspacePreset": "off",
"PictureCombDetectCustom": "",
"PictureCombDetectPreset": "off",
"PictureDARWidth": 1920,
"PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
"PictureDeblockPreset": "off",
"PictureDeblockTune": "medium",
"PictureDeinterlaceCustom": "",
"PictureDeinterlaceFilter": "off",
"PictureDeinterlacePreset": "",
"PictureDenoiseCustom": "",
"PictureDenoiseFilter": "off",
"PictureDenoisePreset": "",
"PictureDenoiseTune": "none",
"PictureDetelecine": "off",
"PictureDetelecineCustom": "",
"PictureForceHeight": 0,
"PictureForceWidth": 0,
"PictureHeight": 1080,
"PictureItuPAR": false,
"PictureKeepRatio": true,
"PictureLeftCrop": 0,
"PictureLooseCrop": false,
"PictureModulus": 2,
"PicturePAR": "auto",
"PicturePARHeight": 1,
"PicturePARWidth": 1,
"PicturePadBottom": 0,
"PicturePadColor": "black",
"PicturePadLeft": 0,
"PicturePadMode": "none",
"PicturePadRight": 0,
"PicturePadTop": 0,
"PictureRightCrop": 0,
"PictureRotate": "angle=0:hflip=0",
"PictureSharpenCustom": "",
"PictureSharpenFilter": "off",
"PictureSharpenPreset": "",
"PictureSharpenTune": "",
"PictureTopCrop": 0,
"PictureUseMaximumSize": true,
"PictureWidth": 1920,
"PresetDescription": "Small H.264 video (up to 1080p30) and AAC stereo audio, in an MP4 container.",
"PresetDisabled": false,
"PresetName": "Very Fast 1080p30 (modified)",
"SubtitleAddCC": false,
"SubtitleAddForeignAudioSearch": true,
"SubtitleAddForeignAudioSubtitle": false,
"SubtitleBurnBDSub": true,
"SubtitleBurnBehavior": "foreign",
"SubtitleBurnDVDSub": true,
"SubtitleLanguageList": [],
"SubtitleTrackSelectionBehavior": "none",
"Type": 1,
"UsesPictureFilters": true,
"VideoAvgBitrate": 2000,
"VideoColorMatrixCodeOverride": 0,
"VideoEncoder": "nvenc_h264",
"VideoFramerate": "30",
"VideoFramerateMode": "cfr",
"VideoGrayScale": false,
"VideoLevel": "auto",
"VideoOptionExtra": "",
"VideoPreset": "medium",
"VideoProfile": "auto",
"VideoQSVDecode": false,
"VideoQualitySlider": 15.0,
"VideoQualityType": 1,
"VideoScaler": "swscale",
"VideoTune": "",
"VideoTurboTwoPass": true,
"VideoTwoPass": false,
"x264Option": "",
"x264UseAdvancedOptions": false
}
],
"VersionMajor": 47,
"VersionMicro": 0,
"VersionMinor": 0
}
{
"PresetList": [
{
"AlignAVStart": true,
"AudioCopyMask": [
"copy:aac"
],
"AudioEncoderFallback": "av_aac",
"AudioLanguageList": [],
"AudioList": [
{
"AudioBitrate": 160,
"AudioCompressionLevel": -1.0,
"AudioDitherMethod": "auto",
"AudioEncoder": "av_aac",
"AudioMixdown": "stereo",
"AudioNormalizeMixLevel": false,
"AudioSamplerate": "auto",
"AudioTrackDRCSlider": 0.0,
"AudioTrackGainSlider": 0.0,
"AudioTrackQuality": 1.0,
"AudioTrackQualityEnable": false
}
],
"AudioSecondaryEncoderMode": true,
"AudioTrackSelectionBehavior": "first",
"ChapterMarkers": true,
"ChildrenArray": [],
"Default": true,
"FileFormat": "av_mp4",
"Folder": false,
"FolderOpen": false,
"InlineParameterSets": false,
"MetadataPassthrough": true,
"Mp4HttpOptimize": true,
"Mp4iPodCompatible": false,
"PictureAllowUpscaling": false,
"PictureAutoCrop": true,
"PictureBottomCrop": 0,
"PictureChromaSmoothCustom": "",
"PictureChromaSmoothPreset": "off",
"PictureChromaSmoothTune": "none",
"PictureColorspaceCustom": "",
"PictureColorspacePreset": "off",
"PictureCombDetectCustom": "",
"PictureCombDetectPreset": "off",
"PictureDARWidth": 1920,
"PictureDeblockCustom": "strength=strong:thresh=20:blocksize=8",
"PictureDeblockPreset": "off",
"PictureDeblockTune": "medium",
"PictureDeinterlaceCustom": "",
"PictureDeinterlaceFilter": "off",
"PictureDeinterlacePreset": "",
"PictureDenoiseCustom": "",
"PictureDenoiseFilter": "off",
"PictureDenoisePreset": "",
"PictureDenoiseTune": "none",
"PictureDetelecine": "off",
"PictureDetelecineCustom": "",
"PictureForceHeight": 0,
"PictureForceWidth": 0,
"PictureHeight": 1080,
"PictureItuPAR": false,
"PictureKeepRatio": true,
"PictureLeftCrop": 0,
"PictureLooseCrop": false,
"PictureModulus": 2,
"PicturePAR": "auto",
"PicturePARHeight": 1,
"PicturePARWidth": 1,
"PicturePadBottom": 0,
"PicturePadColor": "black",
"PicturePadLeft": 0,
"PicturePadMode": "none",
"PicturePadRight": 0,
"PicturePadTop": 0,
"PictureRightCrop": 0,
"PictureRotate": "angle=0:hflip=0",
"PictureSharpenCustom": "",
"PictureSharpenFilter": "off",
"PictureSharpenPreset": "",
"PictureSharpenTune": "",
"PictureTopCrop": 0,
"PictureUseMaximumSize": true,
"PictureWidth": 1920,
"PresetDescription": "Small H.264 video (up to 1080p30) and AAC stereo audio, in an MP4 container.",
"PresetDisabled": false,
"PresetName": "Very Fast 1080p30",
"SubtitleAddCC": false,
"SubtitleAddForeignAudioSearch": true,
"SubtitleAddForeignAudioSubtitle": false,
"SubtitleBurnBDSub": true,
"SubtitleBurnBehavior": "foreign",
"SubtitleBurnDVDSub": true,
"SubtitleLanguageList": [],
"SubtitleTrackSelectionBehavior": "none",
"Type": 1,
"UsesPictureFilters": true,
"VideoAvgBitrate": 2000,
"VideoColorMatrixCodeOverride": 0,
"VideoEncoder": "nvenc_h265",
"VideoFramerate": "30",
"VideoFramerateMode": "cfr",
"VideoGrayScale": false,
"VideoLevel": "auto",
"VideoOptionExtra": "",
"VideoPreset": "medium",
"VideoProfile": "auto",
"VideoQSVDecode": false,
"VideoQualitySlider": 15.0,
"VideoQualityType": 1,
"VideoScaler": "swscale",
"VideoTune": "",
"VideoTurboTwoPass": true,
"VideoTwoPass": false,
"x264Option": "",
"x264UseAdvancedOptions": false
}
],
"VersionMajor": 47,
"VersionMicro": 0,
"VersionMinor": 0
}
#!/usr/bin/env python3
#
# Made by: Andreas Nilsen - @adde88 - <[email protected]>
# Script to analyze movie files, and print out some details about them
# It can also be used to compress files that has not been compressed by this script earlier
# It uses HandBrakeCLI to compress the files, so make sure you have installed that as well as the python libraries being used, such as tqdm.
# I attempted using ffmpeg-python to compress the files, but it was more user friendly to use this option.
#
# PLEASE REMEMBER TO EDIT LINE 72 (inside the code-block where the 'compress_file' function is defined)!!!
import os
import sys
import subprocess
import json
import math
import time
import threading
from datetime import datetime
from tqdm import tqdm
import re
def get_file_size(file_path):
return os.path.getsize(file_path)
def convert_size(size_bytes):
if size_bytes == 0:
return "0B"
size_name = ("B", "KB", "MB", "GB", "TB")
i = int(math.floor(math.log(size_bytes, 1024)))
p = math.pow(1024, i)
s = round(size_bytes / p, 2)
return f"{s} {size_name[i]}"
def convert_bitrate(bitrate_bps):
if bitrate_bps == "N/A":
return "N/A"
bitrate_bps = int(bitrate_bps)
if bitrate_bps == 0:
return "0 bps"
size_name = ("bps", "Kbps", "Mbps", "Gbps", "Tbps")
i = int(math.floor(math.log(bitrate_bps, 1000)))
p = math.pow(1000, i)
s = round(bitrate_bps / p, 2)
return f"{s} {size_name[i]}"
def get_movie_details(file_path):
command = [
'ffprobe', '-v', 'error', '-select_streams', 'v:0', '-show_entries',
'stream=codec_name,bit_rate,width,height', '-of', 'json', file_path
]
result = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode != 0:
raise Exception(f"ffprobe error: {result.stderr}")
details = json.loads(result.stdout)
stream = details['streams'][0]
return {
'codec': stream['codec_name'],
'bit_rate': stream.get('bit_rate', 'N/A'),
'width': stream['width'],
'height': stream['height']
}
def get_file_dates(file_path):
creation_time = os.path.getctime(file_path)
modification_time = os.path.getmtime(file_path)
creation_date = datetime.fromtimestamp(creation_time).strftime('%d. %B %Y @ %H:%M:%S')
modification_date = datetime.fromtimestamp(modification_time).strftime('%d. %B %Y @ %H:%M:%S')
return creation_date, modification_date
def compress_file(file_path, codec_choice):
# Please edit this line below to point to the correct location you donloaded the preset files i added
preset_path = "/home/andreas/hb-imports-265.json" if codec_choice == 'h265_nvenc' else "/home/andreas/hb-imports-264.json"
output_path = os.path.join(os.path.dirname(file_path), f"Compressed-{os.path.basename(file_path)}")
# Check if the compressed file already exists
if os.path.exists(output_path):
print(f"The file '{output_path}' already exists. Skipping compression.")
return output_path
print("Please wait and be patient, compressing file, this will take a little while...")
start_time = time.time()
def print_elapsed_time():
while not stop_timer:
elapsed_time = time.time() - start_time
print(f"Elapsed time: {int(elapsed_time)} seconds", end='\r')
time.sleep(1)
stop_timer = False
timer_thread = threading.Thread(target=print_elapsed_time)
timer_thread.start()
command = [
'HandBrakeCLI', '-i', file_path, '-o', output_path, '--preset-import-file', preset_path
]
process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
process.wait()
stop_timer = True
timer_thread.join()
total_time = time.time() - start_time
if process.returncode != 0:
print(f"HandBrakeCLI error: {process.stderr.read()}")
else:
print(f"\nFile compressed successfully: {output_path}")
print(f"Total compression time: {int(total_time)} seconds")
return output_path
def analyze_file(file_path):
file_size = get_file_size(file_path)
movie_details = get_movie_details(file_path)
creation_date, modification_date = get_file_dates(file_path)
print(f"Filename: {file_path}")
print(f"Filesize: {convert_size(file_size)}")
print(f"Codec: {movie_details['codec']}")
print(f"Bit-rate: {convert_bitrate(movie_details['bit_rate'])}")
print(f"Dimensions: {movie_details['width']}x{movie_details['height']}")
print(f"Created: {creation_date}")
print(f"Modified: {modification_date}")
def main():
if len(sys.argv) != 2:
print("Usage: python3 movie_analyze.py <movie_name>")
sys.exit(1)
file_path = sys.argv[1]
if not os.path.isfile(file_path):
print(f"File was not found: {file_path}")
sys.exit(1)
analyze_file(file_path)
compress = input("\nDo you want to compress the file? (yes/no): ").strip().lower()
if compress == 'yes':
codec_choice = input("Choose codec (1: h264_nvenc, 2: h265_nvenc): ").strip()
if codec_choice == '1':
codec_choice = 'h264_nvenc'
elif codec_choice == '2' or codec_choice == '':
codec_choice = 'h265_nvenc'
else:
print("Invalid codec choice. Exiting.")
sys.exit(1)
compressed_file_path = compress_file(file_path, codec_choice)
if compressed_file_path:
analyze_new = input("\nDo you want to analyze the compressed file? (yes/no): ").strip().lower()
if analyze_new == 'yes':
analyze_file(compressed_file_path)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment