new scripts and cleanup
parent
a69468f555
commit
2a7cd821c1
@ -0,0 +1,65 @@
|
|||||||
|
print("Importing modules...")
|
||||||
|
|
||||||
|
from funcs import group_videos, group_for_concatenation_simple
|
||||||
|
from video_funcs import concatenate_videos
|
||||||
|
import os, config, shutil
|
||||||
|
|
||||||
|
MOVE_FUCKED = False
|
||||||
|
sort_type = {"size": lambda x: sum([video['size'] for video in x]),"count": lambda x: len(x)}
|
||||||
|
|
||||||
|
def get_videos(cursor, username=None):
|
||||||
|
if username:
|
||||||
|
cursor.execute("SELECT * FROM videos WHERE username = %s AND status != 'missing' ORDER BY created_at DESC", (username,))
|
||||||
|
else:
|
||||||
|
cursor.execute("SELECT * FROM videos WHERE status != 'missing' ORDER BY created_at DESC")
|
||||||
|
return cursor.fetchall()
|
||||||
|
|
||||||
|
def organize_videos():
|
||||||
|
username = input("Enter username: ")
|
||||||
|
|
||||||
|
conn, cursor = config.get_local_db_connection()
|
||||||
|
videos = get_videos(cursor, username)
|
||||||
|
|
||||||
|
# process the videos
|
||||||
|
video_data = group_videos(videos, sort_by="size", order="asc")
|
||||||
|
|
||||||
|
# group all videos for concatation first.
|
||||||
|
grouped_videos = []
|
||||||
|
for user, videos in video_data.items():
|
||||||
|
grouped_videos.extend(group_for_concatenation_simple(videos))
|
||||||
|
|
||||||
|
sorted_processed_videos = sorted(grouped_videos, key=sort_type["size"], reverse=True)
|
||||||
|
|
||||||
|
# group the videos for concatenation
|
||||||
|
for video_list in sorted_processed_videos:
|
||||||
|
first_video = video_list[0]
|
||||||
|
|
||||||
|
video_id = os.path.splitext(os.path.basename(first_video['filepath']))[0]
|
||||||
|
|
||||||
|
videos_sum_size = sum([video['size'] for video in video_list])
|
||||||
|
|
||||||
|
print(100*"=")
|
||||||
|
print("\n"*2)
|
||||||
|
print(f"Group {video_id} has {len(video_list)} videos and total size of {videos_sum_size} MB")
|
||||||
|
print("\n"*2)
|
||||||
|
print(100*"=")
|
||||||
|
|
||||||
|
main_video = concatenate_videos(video_list)
|
||||||
|
|
||||||
|
if main_video:
|
||||||
|
print(f"Processed {len(video_list)} input videos into {main_video["filepath"]} output video.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
if MOVE_FUCKED:
|
||||||
|
print(f"Videos are fucked.")
|
||||||
|
|
||||||
|
main_video = video_list[0]
|
||||||
|
video_name = main_video['videoID']
|
||||||
|
|
||||||
|
fucked_dir = os.path.join("concate_fucked", video_name)
|
||||||
|
os.makedirs(fucked_dir, exist_ok=True)
|
||||||
|
for video in video_list:
|
||||||
|
shutil.move(video['filepath'], os.path.join(fucked_dir, os.path.basename(video['filepath'])))
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
organize_videos()
|
||||||
@ -0,0 +1,201 @@
|
|||||||
|
import ffmpeg
|
||||||
|
import subprocess
|
||||||
|
import json
|
||||||
|
from collections import Counter
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
|
||||||
|
def is_av1(filepath):
|
||||||
|
"""Check if a video file is already AV1-encoded."""
|
||||||
|
try:
|
||||||
|
probe = ffmpeg.probe(filepath)
|
||||||
|
for stream in probe['streams']:
|
||||||
|
if stream['codec_type'] == 'video' and 'codec_name' in stream:
|
||||||
|
if stream['codec_name'] == 'av1':
|
||||||
|
return True
|
||||||
|
except ffmpeg.Error as e:
|
||||||
|
print(f"Error probing {filepath}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_video_info(filepath):
|
||||||
|
"""
|
||||||
|
Returns (bitrate_in_kbps, (width, height)) for the specified video file.
|
||||||
|
If probing fails, returns (None, (None, None)).
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
probe = ffmpeg.probe(filepath)
|
||||||
|
format_info = probe['format']
|
||||||
|
video_stream = next(
|
||||||
|
(stream for stream in probe['streams'] if stream['codec_type'] == 'video'),
|
||||||
|
None
|
||||||
|
)
|
||||||
|
if video_stream:
|
||||||
|
# Convert from bits/sec to kbps
|
||||||
|
bitrate_kbps = int(format_info['bit_rate']) // 1000
|
||||||
|
width = video_stream['width']
|
||||||
|
height = video_stream['height']
|
||||||
|
return bitrate_kbps, (width, height)
|
||||||
|
except ffmpeg.Error as e:
|
||||||
|
print(f"Error getting video info for {filepath}: {e}")
|
||||||
|
return None, (None, None)
|
||||||
|
|
||||||
|
def get_fps(filepath):
|
||||||
|
"""Get the frames per second (FPS) of the input video using ffmpeg.probe."""
|
||||||
|
try:
|
||||||
|
probe = ffmpeg.probe(filepath)
|
||||||
|
video_stream = next((s for s in probe['streams'] if s['codec_type'] == 'video'), None)
|
||||||
|
if video_stream and 'r_frame_rate' in video_stream:
|
||||||
|
fps_str = video_stream['r_frame_rate'] # e.g. "30/1", "25/1"
|
||||||
|
num, den = map(int, fps_str.split('/'))
|
||||||
|
return num / den
|
||||||
|
except ffmpeg.Error as e:
|
||||||
|
print(f"Error getting FPS for {filepath}: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_video_metadata(video_path):
|
||||||
|
"""Minimal example to get width/height from FFprobe directly via subprocess."""
|
||||||
|
cmd = [
|
||||||
|
"ffprobe", "-v", "error", "-select_streams", "v:0",
|
||||||
|
"-show_entries", "stream=width,height,bit_rate",
|
||||||
|
"-of", "json", video_path
|
||||||
|
]
|
||||||
|
result = subprocess.run(cmd, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
return {"width": 0, "height": 0, "bit_rate": 0}
|
||||||
|
|
||||||
|
try:
|
||||||
|
data = json.loads(result.stdout)
|
||||||
|
streams = data.get("streams", [])
|
||||||
|
if not streams:
|
||||||
|
return {"width": 0, "height": 0, "bit_rate": 0}
|
||||||
|
stream = streams[0]
|
||||||
|
width = int(stream.get("width", 0))
|
||||||
|
height = int(stream.get("height", 0))
|
||||||
|
br = int(stream.get("bit_rate", 0)) # in bits per second
|
||||||
|
return {"width": width, "height": height, "bit_rate": br}
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
return {"width": 0, "height": 0, "bit_rate": 0}
|
||||||
|
|
||||||
|
def get_target_resolution(group):
|
||||||
|
"""Collect the most common resolution from the group's videos."""
|
||||||
|
resolutions = []
|
||||||
|
for v in group:
|
||||||
|
meta = get_video_metadata(v["filepath"])
|
||||||
|
width, height = meta["width"], meta["height"]
|
||||||
|
if width > 0 and height > 0:
|
||||||
|
resolutions.append((width, height))
|
||||||
|
|
||||||
|
if not resolutions:
|
||||||
|
return (1280, 720)
|
||||||
|
|
||||||
|
counter = Counter(resolutions)
|
||||||
|
return counter.most_common(1)[0][0] # (width, height)
|
||||||
|
|
||||||
|
def get_target_bitrate(width, height):
|
||||||
|
"""Your existing function to choose a bitrate based on resolution."""
|
||||||
|
resolutions = {(854, 480): 1000,(1280, 720): 1500,(1920, 1080): 3000,(2560, 1440): 5000,(3840, 2160): 12000}
|
||||||
|
for res, bitrate in resolutions.items():
|
||||||
|
if width <= res[0] and height <= res[1]:
|
||||||
|
return bitrate
|
||||||
|
return 2500
|
||||||
|
|
||||||
|
def concatenate_videos(videos_list, reencode_concate = False):
|
||||||
|
"""
|
||||||
|
Concatenate pre-grouped videos, then re-encode them using AV1 (NVENC)
|
||||||
|
while forcing a unified resolution and frame rate on each input
|
||||||
|
before final concatenation in one ffmpeg command.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len(videos_list) <= 1:
|
||||||
|
return False
|
||||||
|
|
||||||
|
from concat_helper import copy_concatenate_videos
|
||||||
|
copy_concat = copy_concatenate_videos(videos_list)
|
||||||
|
|
||||||
|
if copy_concat:
|
||||||
|
return copy_concat
|
||||||
|
|
||||||
|
if not reencode_concate:
|
||||||
|
return False
|
||||||
|
|
||||||
|
main_video = videos_list[0]
|
||||||
|
video_path = main_video["filepath"]
|
||||||
|
|
||||||
|
os.makedirs("temp", exist_ok=True)
|
||||||
|
os.makedirs("concated", exist_ok=True)
|
||||||
|
|
||||||
|
temp_path = os.path.join("temp", os.path.basename(video_path))
|
||||||
|
output_path = os.path.join("concated", os.path.basename(video_path))
|
||||||
|
|
||||||
|
current_bitrate, (width, height) = get_video_info(videos_list[0]['filepath'])
|
||||||
|
|
||||||
|
target_width, target_height = get_target_resolution(videos_list)
|
||||||
|
target_bitrate_kbps = get_target_bitrate(target_width, target_height)
|
||||||
|
|
||||||
|
if target_bitrate_kbps > current_bitrate:
|
||||||
|
target_bitrate_kbps = current_bitrate
|
||||||
|
|
||||||
|
max_bitrate_kbps = int(1.5 * target_bitrate_kbps)
|
||||||
|
|
||||||
|
fps_float = get_fps(video_path)
|
||||||
|
if fps_float is None or fps_float <= 0:
|
||||||
|
print(f"Could not determine FPS for {video_path}. Using default keyframe interval of 30.")
|
||||||
|
fps_float = 30.0
|
||||||
|
keyframe_interval = int(fps_float)
|
||||||
|
|
||||||
|
print(f"Concatenating {len(videos_list)} videos into {temp_path}")
|
||||||
|
print(f" Mode Resolution: {target_width}x{target_height}")
|
||||||
|
print(f" Target Bitrate: {target_bitrate_kbps}k (max ~{max_bitrate_kbps}k)")
|
||||||
|
print(f" Keyframe Interval: {keyframe_interval}")
|
||||||
|
|
||||||
|
cmd = ["ffmpeg", "-y"] # Overwrite output if exists
|
||||||
|
for v in videos_list:
|
||||||
|
cmd.extend(["-i", v["filepath"]])
|
||||||
|
|
||||||
|
filter_statements = []
|
||||||
|
concat_streams = []
|
||||||
|
n = len(videos_list)
|
||||||
|
|
||||||
|
unified_fps = 30
|
||||||
|
|
||||||
|
for i in range(n):
|
||||||
|
filter_statements.append(
|
||||||
|
f"[{i}:v]fps={unified_fps},scale={target_width}:{target_height}[v{i}]"
|
||||||
|
)
|
||||||
|
concat_streams.append(f"[v{i}][{i}:a]")
|
||||||
|
|
||||||
|
# Example final: [v0][0:a][v1][1:a]concat=n=2:v=1:a=1[outv][outa]
|
||||||
|
concat_line = "".join(concat_streams) + f"concat=n={n}:v=1:a=1[outv][outa]"
|
||||||
|
filter_statements.append(concat_line)
|
||||||
|
|
||||||
|
filter_complex = ";".join(filter_statements)
|
||||||
|
|
||||||
|
cmd.extend([
|
||||||
|
"-filter_complex", filter_complex,
|
||||||
|
"-map", "[outv]",
|
||||||
|
"-map", "[outa]",
|
||||||
|
"-c:v", "av1_nvenc",
|
||||||
|
"-b:v", f"{target_bitrate_kbps}k",
|
||||||
|
"-maxrate", f"{max_bitrate_kbps}k",
|
||||||
|
"-bufsize", f"{max_bitrate_kbps}k",
|
||||||
|
"-preset", "p5",
|
||||||
|
"-g", str(keyframe_interval),
|
||||||
|
"-c:a", "aac",
|
||||||
|
"-b:a", "192k",
|
||||||
|
temp_path
|
||||||
|
])
|
||||||
|
|
||||||
|
try:
|
||||||
|
subprocess.run(cmd, check=True)
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
for video in videos_list:
|
||||||
|
os.remove(video["filepath"])
|
||||||
|
|
||||||
|
shutil.move(temp_path, output_path)
|
||||||
|
|
||||||
|
return main_video
|
||||||
|
|
||||||
|
def get_file_size_in_mb(file_path):
|
||||||
|
return os.path.getsize(file_path) / (1024 * 1024)
|
||||||
Loading…
Reference in New Issue