first changes to original scripts
This commit is contained in:
12
README.md
12
README.md
@@ -31,17 +31,7 @@ python scene_cutter.py "path/to/your/video.mkv"
|
|||||||
```
|
```
|
||||||
|
|
||||||
### 3. Encode Segments
|
### 3. Encode Segments
|
||||||
Choose one of the encoder scripts to process the files from the `cuts/` directory. Encoded files will be placed in the `segments/` directory.
|
Use [`static_encoder.py`](static_encoder.py) to encode all segments from the `cuts/` directory with a single, fixed CRF value. Encoded files will be placed in the `segments/` directory.
|
||||||
|
|
||||||
**Option A: VMAF-based Encoding (Recommended)**
|
|
||||||
Use [`vmaf_encoder.py`](vmaf_encoder.py) to encode each segment to a target VMAF quality level.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python vmaf_encoder.py --target-vmaf 96.0
|
|
||||||
```
|
|
||||||
|
|
||||||
**Option B: Static CRF Encoding**
|
|
||||||
Use [`static_encoder.py`](static_encoder.py) to encode all segments with a single, fixed CRF value.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python static_encoder.py --crf 27
|
python static_encoder.py --crf 27
|
||||||
|
|||||||
@@ -66,7 +66,44 @@ def get_video_duration(video_path):
|
|||||||
|
|
||||||
# --- Core Logic Functions ---
|
# --- Core Logic Functions ---
|
||||||
|
|
||||||
def detect_scenes(video_path, json_output_path, hwaccel=None, threshold=0.4):
|
def detect_crop(video_path, hwaccel=None):
|
||||||
|
"""
|
||||||
|
Detects black bars using FFmpeg's cropdetect filter and returns the crop filter string.
|
||||||
|
Analyzes the first 60 seconds of the video for efficiency.
|
||||||
|
"""
|
||||||
|
print("\nStarting crop detection...")
|
||||||
|
command = ['ffmpeg', '-hide_banner']
|
||||||
|
if hwaccel:
|
||||||
|
command.extend(['-hwaccel', hwaccel])
|
||||||
|
|
||||||
|
# Analyze a portion of the video to find crop values
|
||||||
|
command.extend(['-i', video_path, '-t', '60', '-vf', 'cropdetect', '-f', 'null', '-'])
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Using Popen to read stderr line by line
|
||||||
|
process = subprocess.Popen(command, stderr=subprocess.PIPE, text=True, encoding='utf-8')
|
||||||
|
|
||||||
|
last_crop_line = ""
|
||||||
|
for line in iter(process.stderr.readline, ''):
|
||||||
|
if 'crop=' in line:
|
||||||
|
last_crop_line = line.strip()
|
||||||
|
|
||||||
|
process.wait()
|
||||||
|
|
||||||
|
if last_crop_line:
|
||||||
|
# Extract the crop filter part, e.g., "crop=1920:800:0:140"
|
||||||
|
crop_filter = last_crop_line.split('] ')[-1]
|
||||||
|
print(f"Crop detection finished. Recommended filter: {crop_filter}")
|
||||||
|
return crop_filter
|
||||||
|
else:
|
||||||
|
print("Could not determine crop. No black bars detected or an error occurred.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
except (FileNotFoundError, Exception) as e:
|
||||||
|
print(f"\nAn error occurred during crop detection: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def detect_scenes(video_path, json_output_path, hwaccel=None, threshold=0.4, crop_filter=None):
|
||||||
"""Uses FFmpeg to detect scene changes and saves timestamps to a JSON file."""
|
"""Uses FFmpeg to detect scene changes and saves timestamps to a JSON file."""
|
||||||
print(f"\nStarting scene detection for: {os.path.basename(video_path)}")
|
print(f"\nStarting scene detection for: {os.path.basename(video_path)}")
|
||||||
command = ['ffmpeg', '-hide_banner']
|
command = ['ffmpeg', '-hide_banner']
|
||||||
@@ -74,7 +111,13 @@ def detect_scenes(video_path, json_output_path, hwaccel=None, threshold=0.4):
|
|||||||
print(f"Attempting to use hardware acceleration: {hwaccel}")
|
print(f"Attempting to use hardware acceleration: {hwaccel}")
|
||||||
command.extend(['-hwaccel', hwaccel])
|
command.extend(['-hwaccel', hwaccel])
|
||||||
|
|
||||||
filter_string = f"select='gt(scene,{threshold})',showinfo"
|
filters = []
|
||||||
|
if crop_filter:
|
||||||
|
print(f"Applying crop filter during scene detection: {crop_filter}")
|
||||||
|
filters.append(crop_filter)
|
||||||
|
filters.append(f"select='gt(scene,{threshold})',showinfo")
|
||||||
|
filter_string = ",".join(filters)
|
||||||
|
|
||||||
command.extend(['-i', video_path, '-vf', filter_string, '-f', 'null', '-'])
|
command.extend(['-i', video_path, '-vf', filter_string, '-f', 'null', '-'])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -105,7 +148,7 @@ def detect_scenes(video_path, json_output_path, hwaccel=None, threshold=0.4):
|
|||||||
print(f"\nAn error occurred during scene detection: {e}")
|
print(f"\nAn error occurred during scene detection: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def cut_video_into_scenes(video_path, json_path, max_segment_length, hwaccel=None):
|
def cut_video_into_scenes(video_path, json_path, max_segment_length, hwaccel=None, crop_filter=None):
|
||||||
"""Cuts a video into segments, ensuring no segment exceeds a maximum length."""
|
"""Cuts a video into segments, ensuring no segment exceeds a maximum length."""
|
||||||
print(f"\nStarting segment cutting for: {os.path.basename(video_path)}")
|
print(f"\nStarting segment cutting for: {os.path.basename(video_path)}")
|
||||||
try:
|
try:
|
||||||
@@ -148,7 +191,13 @@ def cut_video_into_scenes(video_path, json_path, max_segment_length, hwaccel=Non
|
|||||||
if hwaccel:
|
if hwaccel:
|
||||||
command.extend(['-hwaccel', hwaccel])
|
command.extend(['-hwaccel', hwaccel])
|
||||||
|
|
||||||
command.extend(['-i', video_path, '-c:v', 'utvideo', '-an', '-sn', '-dn', '-map_metadata', '-1', '-map_chapters', '-1', '-f', 'segment', '-segment_times', segment_times_str, '-segment_start_number', '1', '-reset_timestamps', '1', output_pattern])
|
command.extend(['-i', video_path])
|
||||||
|
|
||||||
|
if crop_filter:
|
||||||
|
print(f"Applying crop filter during cutting: {crop_filter}")
|
||||||
|
command.extend(['-vf', crop_filter])
|
||||||
|
|
||||||
|
command.extend(['-c:v', 'utvideo', '-an', '-sn', '-dn', '-map_metadata', '-1', '-map_chapters', '-1', '-f', 'segment', '-segment_times', segment_times_str, '-segment_start_number', '1', '-reset_timestamps', '1', output_pattern])
|
||||||
|
|
||||||
print("\nStarting FFmpeg to cut all segments in a single pass...")
|
print("\nStarting FFmpeg to cut all segments in a single pass...")
|
||||||
try:
|
try:
|
||||||
@@ -167,6 +216,11 @@ def main():
|
|||||||
formatter_class=argparse.RawTextHelpFormatter
|
formatter_class=argparse.RawTextHelpFormatter
|
||||||
)
|
)
|
||||||
parser.add_argument("video_path", help="Path to the input video file.")
|
parser.add_argument("video_path", help="Path to the input video file.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--autocrop",
|
||||||
|
action='store_true',
|
||||||
|
help="Automatically detect and apply cropping to remove black bars. Default: False"
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--so", "--sceneonly",
|
"--so", "--sceneonly",
|
||||||
action='store_true',
|
action='store_true',
|
||||||
@@ -195,21 +249,24 @@ def main():
|
|||||||
json_path = f"{base_name}.scenes.json"
|
json_path = f"{base_name}.scenes.json"
|
||||||
|
|
||||||
hwaccel_method = get_best_hwaccel()
|
hwaccel_method = get_best_hwaccel()
|
||||||
|
crop_filter = None
|
||||||
|
if args.autocrop:
|
||||||
|
crop_filter = detect_crop(args.video_path, hwaccel_method)
|
||||||
|
|
||||||
if args.sceneonly:
|
if args.sceneonly:
|
||||||
detect_scenes(args.video_path, json_path, hwaccel_method, args.threshold)
|
detect_scenes(args.video_path, json_path, hwaccel_method, args.threshold, crop_filter)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# --- Full Workflow (Detect if needed, then Cut) ---
|
# --- Full Workflow (Detect if needed, then Cut) ---
|
||||||
if not os.path.isfile(json_path):
|
if not os.path.isfile(json_path):
|
||||||
print("--- Scene file not found, running detection first ---")
|
print("--- Scene file not found, running detection first ---")
|
||||||
if not detect_scenes(args.video_path, json_path, hwaccel_method, args.threshold):
|
if not detect_scenes(args.video_path, json_path, hwaccel_method, args.threshold, crop_filter):
|
||||||
print("\nScene detection failed. Aborting process.")
|
print("\nScene detection failed. Aborting process.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
else:
|
else:
|
||||||
print(f"--- Found existing scene file: {os.path.basename(json_path)} ---")
|
print(f"--- Found existing scene file: {os.path.basename(json_path)} ---")
|
||||||
|
|
||||||
cut_video_into_scenes(args.video_path, json_path, args.segtime, hwaccel_method)
|
cut_video_into_scenes(args.video_path, json_path, args.segtime, hwaccel_method, crop_filter)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
Reference in New Issue
Block a user