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