small fix and encoder update to db

main
oscar 2 months ago
parent c79ec8e179
commit 4596d9168f

1
.gitignore vendored

@ -181,3 +181,4 @@ cython_debug/
.cursorindexingignore .cursorindexingignore
/failed /failed
/static/thumbnails /static/thumbnails
/.temp

@ -45,7 +45,7 @@ def mark_missing_videos(cursor, conn):
conn.commit() conn.commit()
def fill_missing_filepaths(cursor, conn): def fill_missing_filepaths(cursor, conn):
cursor.execute("SELECT id, filepath, status FROM videos") cursor.execute("SELECT id, filepath, status, video_id FROM videos")
videos = cursor.fetchall() videos = cursor.fetchall()
with tqdm(videos, desc="Updating filepaths...") as pbar: with tqdm(videos, desc="Updating filepaths...") as pbar:
@ -54,8 +54,10 @@ def fill_missing_filepaths(cursor, conn):
filepath = vid['filepath'] filepath = vid['filepath']
if not filepath: if not filepath:
continue filename = f'{vid["video_id"]}.mp4'
filename = os.path.basename(filepath) else:
filename = os.path.basename(filepath)
status = vid['status'] status = vid['status']
path = find_video_path(filename) path = find_video_path(filename)

@ -1,11 +1,8 @@
import os, shutil import os, shutil, config
import ffmpeg import ffmpeg
from tqdm import tqdm from tqdm import tqdm
def is_av1(filepath): def is_av1(filepath):
"""
Check if a video file is already AV1-encoded.
"""
try: try:
probe = ffmpeg.probe(filepath) probe = ffmpeg.probe(filepath)
for stream in probe['streams']: for stream in probe['streams']:
@ -17,10 +14,6 @@ def is_av1(filepath):
return False return False
def get_video_info(filepath): def get_video_info(filepath):
"""
Returns (bitrate_in_kbps, (width, height)) for the specified video file.
If probing fails, returns (None, (None, None)).
"""
try: try:
probe = ffmpeg.probe(filepath) probe = ffmpeg.probe(filepath)
format_info = probe['format'] format_info = probe['format']
@ -39,10 +32,6 @@ def get_video_info(filepath):
return None, (None, None) return None, (None, None)
def get_files(folder): def get_files(folder):
"""
Recursively gather all .mp4 files in `folder`.
Sort them by file size (smallest to largest) just as an example sorting.
"""
all_files = [] all_files = []
for root, _, filenames in os.walk(folder): for root, _, filenames in os.walk(folder):
for filename in filenames: for filename in filenames:
@ -51,16 +40,7 @@ def get_files(folder):
all_files.append(os.path.join(root, filename)) all_files.append(os.path.join(root, filename))
return sorted(all_files, key=os.path.getsize) return sorted(all_files, key=os.path.getsize)
def parse_text_for_print(text):
"""
If string is longer than 100 characters, only print the first 100 characters.
"""
return text[:100] + '...' if len(text) > 100 else text
def get_target_bitrate(width, height): def get_target_bitrate(width, height):
"""
Your existing function to choose a bitrate based on resolution.
"""
resolutions = { resolutions = {
(854, 480): 1000, (854, 480): 1000,
(1280, 720): 1500, (1280, 720): 1500,
@ -76,7 +56,6 @@ def get_target_bitrate(width, height):
return 2500 return 2500
def get_fps(filepath): def get_fps(filepath):
"""Get the frames per second (FPS) of the input video."""
try: try:
probe = ffmpeg.probe(filepath) probe = ffmpeg.probe(filepath)
video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None) video_stream = next((stream for stream in probe['streams'] if stream['codec_type'] == 'video'), None)
@ -90,16 +69,12 @@ def get_fps(filepath):
return None return None
def encode_video(filepath, output_path, target_bitrate): def encode_video(filepath, output_path, target_bitrate):
"""
Encode video using ffmpeg with a target bitrate (in kbps).
Using NVIDIA AV1 hardware encoder (av1_nvenc) for RTX 40-series.
"""
try: try:
fps = get_fps(filepath) fps = get_fps(filepath)
if fps is None: if fps is None:
print(f"Could not determine FPS for {filepath}, using default keyframe interval.") print(f"Could not determine FPS for {filepath}.")
fps = 30 # Default fallback if FPS can't be determined return False
keyframe_interval = int(fps) # Set the keyframe interval to match 1 second keyframe_interval = int(fps) # Set the keyframe interval to match 1 second
# Calculate 1.5x for max bitrate # Calculate 1.5x for max bitrate
@ -123,18 +98,12 @@ def encode_video(filepath, output_path, target_bitrate):
overwrite_output=True, overwrite_output=True,
) )
) )
filepath_print = parse_text_for_print(filepath) print(f" Finished encoding {os.path.basename(filepath)} to AV1 at {target_bitrate} kbps "
print(f" Finished encoding {filepath_print} to AV1 at {target_bitrate} kbps "
f"(maxrate={max_bitrate} kbps).") f"(maxrate={max_bitrate} kbps).")
except ffmpeg.Error as e: except ffmpeg.Error as e:
filepath_print = parse_text_for_print(filepath) print(f" Error encoding {os.path.basename(filepath)} to AV1: {e}")
print(f" Error encoding {filepath_print} to AV1: {e}")
def check_and_replace_if_smaller(original_path, temp_output_path): def check_and_replace_if_smaller(original_path, temp_output_path):
"""
Compare file sizes and replace the original if the new one is smaller.
Otherwise, delete the temporary file.
"""
if not os.path.exists(temp_output_path): if not os.path.exists(temp_output_path):
print(f"[ERROR] Temp file {temp_output_path} not found. Skipping replacement...") print(f"[ERROR] Temp file {temp_output_path} not found. Skipping replacement...")
return return
@ -162,26 +131,34 @@ def check_and_replace_if_smaller(original_path, temp_output_path):
shutil.move(temp_output_path, original_path) shutil.move(temp_output_path, original_path)
return True return True
def reencode_videos_av1(input_folder): def update_codec_db(video_id, codec):
""" conn, cursor = config.get_local_db_connection()
Main loop: cursor.execute("UPDATE videos SET codec = %s WHERE id = %s", (codec, video_id))
1. Gather .mp4 files conn.commit()
2. For each file, check if it's AV1 or if it needs re-encoding based on target bitrate. conn.close()
3. Re-encode if needed.
4. Compare file sizes and replace if smaller. def reencode_videos_av1():
""" # get videos
files = get_files(input_folder) conn, cursor = config.get_local_db_connection()
for input_path in tqdm(files, desc="Processing videos", unit="file"): cursor.execute("SELECT * FROM videos WHERE codec IS NULL AND status != 'missing' AND filepath IS NOT NULL ORDER BY size ASC;")
short_name = parse_text_for_print(input_path) videos = cursor.fetchall()
for video in tqdm(videos, desc="Processing videos", unit="file"):
input_path = video['filepath']
if not os.path.exists(input_path):
print(f"🚫 File not found: {input_path}")
continue
file_size_in_mb = os.path.getsize(input_path) / (1024 * 1024) file_size_in_mb = os.path.getsize(input_path) / (1024 * 1024)
print(f"\nProcessing {short_name} ({file_size_in_mb:.2f} MB)...") print(f"\nProcessing {os.path.basename(input_path)} ({file_size_in_mb:.2f} MB)...")
# 2) Get current bitrate & resolution # 2) Get current bitrate & resolution
current_bitrate, (width, height) = get_video_info(input_path) current_bitrate, (width, height) = get_video_info(input_path)
if not current_bitrate: if not current_bitrate:
print("Video's bitrate is not available. Skipping") print("Video's bitrate is not available. Skipping")
continue continue
target_bitrate = get_target_bitrate(width, height) target_bitrate = get_target_bitrate(width, height)
# If current bitrate <= target, it's not worth it to re-encode # If current bitrate <= target, it's not worth it to re-encode
@ -190,39 +167,21 @@ def reencode_videos_av1(input_folder):
if is_av1(input_path): if is_av1(input_path):
print("Video is already encoded in AV1. Skipping") print("Video is already encoded in AV1. Skipping")
update_codec_db(video['id'], 'av1')
# move to 'encoded' folder inside the input folder
new_file_path = os.path.join(input_folder, "encoded", os.path.basename(input_path))
os.makedirs(os.path.dirname(new_file_path), exist_ok=True)
shutil.move(input_path, new_file_path)
continue continue
# 3) Re-encode # 3) Re-encode
output_path = os.path.join('temp', os.path.basename(input_path)) output_path = os.path.join('.temp', os.path.basename(input_path))
encode_video(input_path, output_path, target_bitrate) os.makedirs(os.path.dirname(output_path), exist_ok=True)
encoded = encode_video(input_path, output_path, target_bitrate)
if not encoded:
print("Encoding failed. Skipping.")
continue
# 4) Compare file sizes and replace if smaller # 4) Compare file sizes and replace if smaller
check_and_replace_if_smaller(input_path, output_path) check_and_replace_if_smaller(input_path, output_path)
update_codec_db(video['id'], 'av1')
# move to 'encoded' folder inside the input folder
new_file_path = os.path.join(input_folder, "encoded", os.path.basename(input_path))
os.makedirs(os.path.dirname(new_file_path), exist_ok=True)
shutil.move(input_path, new_file_path)
# ---------------------- Main Script Entry ---------------------- #
if __name__ == "__main__": if __name__ == "__main__":
import sys reencode_videos_av1()
if len(sys.argv) > 1:
input_folder = sys.argv[1]
else:
input_folder = input("Enter the input folder path: ")
if not os.path.isdir(input_folder):
print(f"Input folder '{input_folder}' does not exist.")
sys.exit(1)
print("Re-encoding videos to AV1 (only if bitrate is above our resolution-based presets)...")
reencode_videos_av1(input_folder)
print("All done!")
Loading…
Cancel
Save