peg-this 3.0.1__py3-none-any.whl → 4.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- peg_this/features/__init__.py +0 -0
- peg_this/features/audio.py +38 -0
- peg_this/features/batch.py +125 -0
- peg_this/features/convert.py +225 -0
- peg_this/features/crop.py +178 -0
- peg_this/features/inspect.py +60 -0
- peg_this/features/join.py +83 -0
- peg_this/features/trim.py +26 -0
- peg_this/peg_this.py +52 -620
- peg_this/utils/__init__.py +0 -0
- peg_this/utils/ffmpeg_utils.py +129 -0
- peg_this/utils/ui_utils.py +52 -0
- peg_this-4.0.0.dist-info/METADATA +164 -0
- peg_this-4.0.0.dist-info/RECORD +19 -0
- peg_this-3.0.1.dist-info/METADATA +0 -87
- peg_this-3.0.1.dist-info/RECORD +0 -8
- {peg_this-3.0.1.dist-info → peg_this-4.0.0.dist-info}/WHEEL +0 -0
- {peg_this-3.0.1.dist-info → peg_this-4.0.0.dist-info}/entry_points.txt +0 -0
- {peg_this-3.0.1.dist-info → peg_this-4.0.0.dist-info}/licenses/LICENSE +0 -0
- {peg_this-3.0.1.dist-info → peg_this-4.0.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import ffmpeg
|
|
6
|
+
import questionary
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
|
|
9
|
+
from peg_this.utils.ffmpeg_utils import run_command
|
|
10
|
+
from peg_this.utils.ui_utils import get_media_files
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def join_videos():
|
|
16
|
+
"""Join multiple videos into a single file after standardizing their resolutions and sample rates."""
|
|
17
|
+
console.print("[bold cyan]Select videos to join (in order). Press Enter when done.[/bold cyan]")
|
|
18
|
+
|
|
19
|
+
media_files = get_media_files()
|
|
20
|
+
video_files = [f for f in media_files if Path(f).suffix.lower() in [".mp4", ".mkv", ".mov", ".avi", ".webm"]]
|
|
21
|
+
|
|
22
|
+
if len(video_files) < 2:
|
|
23
|
+
console.print("[bold yellow]Not enough video files in the directory to join.[/bold yellow]")
|
|
24
|
+
questionary.press_any_key_to_continue().ask()
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
selected_videos = questionary.checkbox("Select at least two videos to join in order:", choices=video_files).ask()
|
|
28
|
+
|
|
29
|
+
if not selected_videos or len(selected_videos) < 2:
|
|
30
|
+
console.print("[bold yellow]Joining cancelled. At least two videos must be selected.[/bold yellow]")
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
console.print("Videos will be joined in this order:")
|
|
34
|
+
for i, video in enumerate(selected_videos):
|
|
35
|
+
console.print(f" {i+1}. {video}")
|
|
36
|
+
|
|
37
|
+
output_file = questionary.text("Enter the output file name:", default="joined_video.mp4").ask()
|
|
38
|
+
if not output_file: return
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
first_video_path = os.path.abspath(selected_videos[0])
|
|
42
|
+
probe = ffmpeg.probe(first_video_path)
|
|
43
|
+
video_info = next(s for s in probe['streams'] if s['codec_type'] == 'video')
|
|
44
|
+
audio_info = next(s for s in probe['streams'] if s['codec_type'] == 'audio')
|
|
45
|
+
|
|
46
|
+
target_width = video_info['width']
|
|
47
|
+
target_height = video_info['height']
|
|
48
|
+
target_sar = video_info.get('sample_aspect_ratio', '1:1')
|
|
49
|
+
target_sample_rate = audio_info['sample_rate']
|
|
50
|
+
|
|
51
|
+
except Exception as e:
|
|
52
|
+
console.print(f"[bold red]Could not probe first video for target parameters: {e}[/bold red]")
|
|
53
|
+
return
|
|
54
|
+
|
|
55
|
+
console.print(f"Standardizing all videos to: {target_width}x{target_height} resolution and {target_sample_rate} Hz audio.")
|
|
56
|
+
|
|
57
|
+
processed_streams = []
|
|
58
|
+
for video_file in selected_videos:
|
|
59
|
+
stream = ffmpeg.input(os.path.abspath(video_file))
|
|
60
|
+
v = (
|
|
61
|
+
stream.video
|
|
62
|
+
.filter('scale', w=target_width, h=target_height, force_original_aspect_ratio='decrease')
|
|
63
|
+
.filter('pad', w=target_width, h=target_height, x='(ow-iw)/2', y='(oh-ih)/2')
|
|
64
|
+
.filter('setsar', sar=target_sar.replace(':','/'))
|
|
65
|
+
.filter('setpts', 'PTS-STARTPTS')
|
|
66
|
+
)
|
|
67
|
+
a = (
|
|
68
|
+
stream.audio
|
|
69
|
+
.filter('aresample', sample_rate=target_sample_rate)
|
|
70
|
+
.filter('asetpts', 'PTS-STARTPTS')
|
|
71
|
+
)
|
|
72
|
+
processed_streams.append(v)
|
|
73
|
+
processed_streams.append(a)
|
|
74
|
+
|
|
75
|
+
joined = ffmpeg.concat(*processed_streams, v=1, a=1).node
|
|
76
|
+
output_stream = ffmpeg.output(joined[0], joined[1], output_file, **{'c:v': 'libx264', 'crf': 23, 'c:a': 'aac', 'b:a': '192k', 'y': None})
|
|
77
|
+
|
|
78
|
+
if run_command(output_stream, "Joining and re-encoding videos...", show_progress=True):
|
|
79
|
+
console.print(f"[bold green]Successfully joined videos into {output_file}[/bold green]")
|
|
80
|
+
else:
|
|
81
|
+
console.print("[bold red]Failed to join videos.[/bold red]")
|
|
82
|
+
|
|
83
|
+
questionary.press_any_key_to_continue().ask()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import ffmpeg
|
|
5
|
+
import questionary
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
|
|
8
|
+
from peg_this.utils.ffmpeg_utils import run_command
|
|
9
|
+
|
|
10
|
+
console = Console()
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def trim_video(file_path):
|
|
14
|
+
"""Cut a video by specifying start and end times."""
|
|
15
|
+
start_time = questionary.text("Enter start time (HH:MM:SS or seconds):").ask()
|
|
16
|
+
if not start_time: return
|
|
17
|
+
end_time = questionary.text("Enter end time (HH:MM:SS or seconds):").ask()
|
|
18
|
+
if not end_time: return
|
|
19
|
+
|
|
20
|
+
output_file = f"{Path(file_path).stem}_trimmed{Path(file_path).suffix}"
|
|
21
|
+
|
|
22
|
+
stream = ffmpeg.input(file_path, ss=start_time, to=end_time).output(output_file, c='copy', y=None)
|
|
23
|
+
|
|
24
|
+
run_command(stream, "Trimming video...", show_progress=True)
|
|
25
|
+
console.print(f"[bold green]Successfully trimmed to {output_file}[/bold green]")
|
|
26
|
+
questionary.press_any_key_to_continue().ask()
|