peg-this 3.0.2__py3-none-any.whl → 3.0.6__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.

Potentially problematic release.


This version of peg-this might be problematic. Click here for more details.

@@ -0,0 +1,134 @@
1
+
2
+ import subprocess
3
+ import logging
4
+ import sys
5
+
6
+ import ffmpeg
7
+ from rich.console import Console
8
+ from rich.progress import Progress, SpinnerColumn, BarColumn, TextColumn
9
+
10
+ console = Console()
11
+
12
+
13
+ def check_ffmpeg_ffprobe():
14
+ """
15
+ Checks if ffmpeg and ffprobe executables are available in the system's PATH.
16
+ ffmpeg-python requires this.
17
+ """
18
+ try:
19
+ # The library does this internally, but we can provide a clearer error message.
20
+ subprocess.check_call(['ffmpeg', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
21
+ subprocess.check_call(['ffprobe', '-version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
22
+ except FileNotFoundError:
23
+ console.print("[bold red]Error: ffmpeg and ffprobe not found.[/bold red]")
24
+ if sys.platform == "win32":
25
+ console.print("You can install it using Chocolatey: [bold]choco install ffmpeg[/bold]")
26
+ console.print("Or Scoop: [bold]scoop install ffmpeg[/bold]")
27
+ elif sys.platform == "darwin":
28
+ console.print("You can install it using Homebrew: [bold]brew install ffmpeg[/bold]")
29
+ else:
30
+ console.print("You can install it using your system's package manager, e.g., [bold]sudo apt update && sudo apt install ffmpeg[/bold] on Debian/Ubuntu.")
31
+ console.print("Please ensure its location is in your system's PATH.")
32
+ sys.exit(1)
33
+
34
+
35
+ def run_command(stream_spec, description="Processing...", show_progress=False):
36
+ """
37
+ Runs an ffmpeg command using ffmpeg-python.
38
+ - For simple commands, it runs directly.
39
+ - For commands with a progress bar, it generates the ffmpeg arguments,
40
+ runs them as a subprocess, and parses stderr to show progress,
41
+ mimicking the logic from the original script for accuracy.
42
+ """
43
+ console.print(f"[bold cyan]{description}[/bold cyan]")
44
+
45
+ # Get the full command arguments from the ffmpeg-python stream object
46
+ args = stream_spec.get_args()
47
+ full_command = ['ffmpeg'] + args
48
+ logging.info(f"Executing command: {' '.join(full_command)}")
49
+
50
+ if not show_progress:
51
+ try:
52
+ # Use ffmpeg.run() for simple, non-progress tasks. It's cleaner.
53
+ out, err = ffmpeg.run(stream_spec, capture_stdout=True, capture_stderr=True, quiet=True)
54
+ logging.info("Command successful (no progress bar).")
55
+ return out.decode('utf-8')
56
+ except ffmpeg.Error as e:
57
+ error_message = e.stderr.decode('utf-8')
58
+ console.print("[bold red]An error occurred:[/bold red]")
59
+ console.print(error_message)
60
+ logging.error(f"ffmpeg error:{error_message}")
61
+ return None
62
+ else:
63
+ # For the progress bar, we must run ffmpeg as a subprocess and parse stderr.
64
+ duration = 0
65
+ try:
66
+ # Find the primary input file from the command arguments to probe it.
67
+ input_file_path = None
68
+ for i, arg in enumerate(full_command):
69
+ if arg == '-i' and i + 1 < len(full_command):
70
+ # This is a robust way to find the first input file.
71
+ input_file_path = full_command[i+1]
72
+ break
73
+
74
+ if input_file_path:
75
+ probe_info = ffmpeg.probe(input_file_path)
76
+ duration = float(probe_info['format']['duration'])
77
+ else:
78
+ logging.warning("Could not find input file in command to determine duration for progress bar.")
79
+
80
+ except (ffmpeg.Error, KeyError) as e:
81
+ console.print(f"[bold yellow]Warning: Could not determine video duration for progress bar.[/bold yellow]")
82
+ logging.warning(f"Could not probe for duration: {e}")
83
+
84
+ with Progress(
85
+ SpinnerColumn(),
86
+ TextColumn("[progress.description]{task.description}"),
87
+ BarColumn(),
88
+ TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
89
+ console=console,
90
+ ) as progress:
91
+ task = progress.add_task(description, total=100)
92
+
93
+ # Run the command as a subprocess to capture stderr in real-time
94
+ process = subprocess.Popen(
95
+ full_command,
96
+ stdout=subprocess.PIPE,
97
+ stderr=subprocess.PIPE,
98
+ universal_newlines=True,
99
+ encoding='utf-8'
100
+ )
101
+
102
+ for line in process.stderr:
103
+ logging.debug(f"ffmpeg stderr: {line.strip()}")
104
+ if "time=" in line and duration > 0:
105
+ try:
106
+ time_str = line.split("time=")[1].split(" ")[0].strip()
107
+ h, m, s_parts = time_str.split(':')
108
+ s = float(s_parts)
109
+ elapsed_time = int(h) * 3600 + int(m) * 60 + s
110
+ percent_complete = (elapsed_time / duration) * 100
111
+ progress.update(task, completed=min(percent_complete, 100))
112
+ except Exception:
113
+ pass # Ignore any parsing errors
114
+
115
+ process.wait()
116
+ progress.update(task, completed=100)
117
+
118
+ if process.returncode != 0:
119
+ # The error was already logged line-by-line, but we can add a final message.
120
+ log_file = logging.getLogger().handlers[0].baseFilename
121
+ console.print(f"[bold red]An error occurred during processing. Check {log_file} for details.[/bold red]")
122
+ return None
123
+
124
+ logging.info("Command successful (with progress bar).")
125
+ return "Success"
126
+
127
+
128
+ def has_audio_stream(file_path):
129
+ """Check if the media file has an audio stream."""
130
+ try:
131
+ probe = ffmpeg.probe(file_path, select_streams='a')
132
+ return 'streams' in probe and len(probe['streams']) > 0
133
+ except ffmpeg.Error:
134
+ return False
@@ -0,0 +1,43 @@
1
+
2
+ import os
3
+ from pathlib import Path
4
+
5
+ import questionary
6
+ from rich.console import Console
7
+
8
+ try:
9
+ import tkinter as tk
10
+ from tkinter import filedialog
11
+ except ImportError:
12
+ tk = None
13
+
14
+ console = Console()
15
+
16
+
17
+ def get_media_files():
18
+ """Scan the current directory for media files."""
19
+ media_extensions = [".mkv", ".mp4", ".avi", ".mov", ".webm", ".flv", ".wmv", ".mp3", ".flac", ".wav", ".ogg", ".gif"]
20
+ files = [f for f in os.listdir('.') if os.path.isfile(f) and Path(f).suffix.lower() in media_extensions]
21
+ return files
22
+
23
+
24
+ def select_media_file():
25
+ """Display a menu to select a media file, or open a file picker."""
26
+ media_files = get_media_files()
27
+ if not media_files:
28
+ console.print("[bold yellow]No media files found in this directory.[/bold yellow]")
29
+ if tk and questionary.confirm("Would you like to select a file from another location?").ask():
30
+ root = tk.Tk()
31
+ root.withdraw()
32
+ file_path = filedialog.askopenfilename(
33
+ title="Select a media file",
34
+ filetypes=[("Media Files", "*.mkv *.mp4 *.avi *.mov *.webm *.flv *.wmv *.mp3 *.flac *.wav *.ogg *.gif"), ("All Files", "*.*")]
35
+ )
36
+ return file_path if file_path else None
37
+ return None
38
+
39
+ choices = media_files + [questionary.Separator(), "Back"]
40
+ file = questionary.select("Select a media file to process:", choices=choices, use_indicator=True).ask()
41
+
42
+ # Return the absolute path to prevent "file not found" errors
43
+ return os.path.abspath(file) if file and file != "Back" else None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: peg_this
3
- Version: 3.0.2
3
+ Version: 3.0.6
4
4
  Summary: A powerful, intuitive command-line video editor suite, built on FFmpeg.
5
5
  Author-email: Hariharen S S <thisishariharen@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/hariharen9/ffmpeg-this
@@ -26,6 +26,10 @@ Dynamic: license-file
26
26
 
27
27
  A powerful and user-friendly batch script for converting, manipulating, and inspecting media files using the power of FFmpeg. This script provides a simple command-line menu to perform common audio and video tasks without needing to memorize complex FFmpeg commands.
28
28
 
29
+
30
+ <img src="/assets/peg.gif" width="720">
31
+
32
+
29
33
  ## ✨ Features
30
34
 
31
35
  - **Inspect Media Properties**: View detailed information about video and audio streams, including codecs, resolution, frame rate, bitrates, and more.
@@ -39,8 +43,27 @@ A powerful and user-friendly batch script for converting, manipulating, and insp
39
43
 
40
44
 
41
45
  ## 🚀 Usage
46
+ ### Prerequisite: Install FFmpeg
47
+
48
+ > `peg_this` uses a library called `ffmpeg-python` which acts as a controller for the main FFmpeg program. It does not include FFmpeg itself. Therefore, you must have FFmpeg installed on your system and available in your terminal's PATH.
49
+
50
+ For **macOS** users, the easiest way to install it is with [Homebrew](https://brew.sh/):
51
+ ```bash
52
+ brew install ffmpeg
53
+ ```
54
+
55
+ For **Windows** users, you can use a package manager like [Chocolatey](https://chocolatey.org/) or [Scoop](https://scoop.sh/):
56
+ ```bash
57
+ # Using Chocolatey
58
+ choco install ffmpeg
59
+
60
+ # Using Scoop
61
+ scoop install ffmpeg
62
+ ```
63
+
64
+ For other systems, please see the official download page: **[ffmpeg.org/download.html](https://ffmpeg.org/download.html)**
42
65
 
43
- There are three ways to use `peg_this`:
66
+ There are ***three*** ways to use `peg_this`:
44
67
 
45
68
  ### 1. Pip Install (Recommended)
46
69
 
@@ -0,0 +1,19 @@
1
+ peg_this/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ peg_this/peg_this.py,sha256=BqJutllqMHqE312WgPdnd3r9-msKKOfZp9ykV0ejUm8,3739
3
+ peg_this/features/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ peg_this/features/audio.py,sha256=O0_lRjzolualxWVXiSiEke5vOpPGwkz0FbMQy5brO58,1533
5
+ peg_this/features/batch.py,sha256=72yXjNfvg-SCxjtaacFzFudmZ8Yd7_rLpvJzbwB8UdA,5178
6
+ peg_this/features/convert.py,sha256=KLtmN6PGhqIEmY3j40Ur9xqC-rZREu8UBM9Ura1-20o,3865
7
+ peg_this/features/crop.py,sha256=bzlzfFM41OFX_3KEMNOAJu9XvM-6Mmf_AApJ1rN8oaU,3986
8
+ peg_this/features/inspect.py,sha256=xtlmedpsMYQ-vLsnDNzUF59TiZDFLGMyL_wtd8PgiHg,2535
9
+ peg_this/features/join.py,sha256=w_HUsuBv0g9nlOjVvdkeT9dDwxOEvdyvsYCwNocwl98,3414
10
+ peg_this/features/trim.py,sha256=2ZWSNpl4DzSCPt6q7FZloQc-Rga5cX8IW69MfzAZysE,863
11
+ peg_this/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ peg_this/utils/ffmpeg_utils.py,sha256=cxm5SyZYHw99dqaIiNZ9qhdltyvMwecs1FYHXjChwR8,5883
13
+ peg_this/utils/ui_utils.py,sha256=cntxp8m7en_irFNEK5TcrPotYVZr-hzF_QdcXUIBA2Y,1584
14
+ peg_this-3.0.6.dist-info/licenses/LICENSE,sha256=WL1MklYSco7KZvDjbf191tIKOxWQdekqda7dDJc6Wn8,1067
15
+ peg_this-3.0.6.dist-info/METADATA,sha256=_bWnDbXC0vGoPDImzXVd05AyITf8XZIuvJK-uK7K_3c,4310
16
+ peg_this-3.0.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ peg_this-3.0.6.dist-info/entry_points.txt,sha256=9GVTFuE1w_wgY-Tz3--wI5j52BAKrt4atphVD8ioHhQ,52
18
+ peg_this-3.0.6.dist-info/top_level.txt,sha256=kSS5jZg3KN2kJqYZwMvQnI4gvlFxsUNzIm3QJsbKFdc,9
19
+ peg_this-3.0.6.dist-info/RECORD,,
@@ -1,8 +0,0 @@
1
- peg_this/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- peg_this/peg_this.py,sha256=JSxUYO5zjg0URu6SG3I1AWio_yQ8t8wKW9yqP-pRbUY,30228
3
- peg_this-3.0.2.dist-info/licenses/LICENSE,sha256=WL1MklYSco7KZvDjbf191tIKOxWQdekqda7dDJc6Wn8,1067
4
- peg_this-3.0.2.dist-info/METADATA,sha256=suW_MI8jSHxZXEKKOsrry7QnXoHyVZ0s6ZFljc-RKy8,3527
5
- peg_this-3.0.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
- peg_this-3.0.2.dist-info/entry_points.txt,sha256=9GVTFuE1w_wgY-Tz3--wI5j52BAKrt4atphVD8ioHhQ,52
7
- peg_this-3.0.2.dist-info/top_level.txt,sha256=kSS5jZg3KN2kJqYZwMvQnI4gvlFxsUNzIm3QJsbKFdc,9
8
- peg_this-3.0.2.dist-info/RECORD,,