From 0a6e9ed1d89b0029a116f868a616dea8a0b61f6c Mon Sep 17 00:00:00 2001 From: David Kifer Date: Mon, 12 Jan 2026 10:05:57 -0500 Subject: [PATCH] sync --- .../__pycache__/extractor.cpython-313.pyc | Bin 4529 -> 3905 bytes .../ai_transcriber_v2/extractor.py | 76 +++++++----------- 2 files changed, 28 insertions(+), 48 deletions(-) diff --git a/video_transcription/ai_transcriber_v2/__pycache__/extractor.cpython-313.pyc b/video_transcription/ai_transcriber_v2/__pycache__/extractor.cpython-313.pyc index 958760b4305f612dfc28f644383fa0217189fdd9..4899a45c24ebe1f27f14e3eb9bf32bf12a9af2f9 100644 GIT binary patch delta 2175 zcmZ9MO>7fK6o6;F{u%$RiQ`~M9A^>?iGer?e}br`4GB^CY4{o3O10W(vtBz3_O3g- zF2on-7$G4N?SULPY)w<#a+^>2c)XEr~#>JRjD&;8v>)e^LK^Aox$*Nu^GExkr=txm@Lo5{JEYegor!J3) zH~k$O{(&|Bz&3EO`QW0k{OPh(6Z+QSzRghN-)o!xSZl5($m=loODMACqRRj1-PDV( zJI4HA)gL(%Y?=AhYKAHyDi+$LS12~8U z9KySBTbdgRsC*{WGE4I<249xbAsU(t7c+YtE`M7~4~NYmM@O_h&Am>1noEF8*!EWVN!t$&+4|Bw5Z<7-q@_(#l4Oekdezv1H7p zDv_~rR?(1r%OIj;P%T3nMSrr&Px=Whc0gDi6E732yez1f3W~w>Sp*0xP{NXFdC%UG zSQRIZ7C1%SbxS!|uc}!h&FvfM_G_%plm@bXsICkOOoq%Cufk!H6(FcVFS3 zd1B^&?vbh+c!LYq>oB-*lL@^Jg++fI2I#Ypufz7O5Dd8IC!R+D3@;`&Jkd2zbTgD# z$!vs1)|+#BWJVh(#(nUs>r62zBgI2x#^*cSgwM0 zQzjOa)vSI~F=lO=8gL;xpY)J@Obt@!rrDiE671JGL$5uHXKs7x-8C&~S$R}yV8+73 zFr%$OT1aLNO;bICo=MipajG#&U;O|j-Dd4)L96~AAJ~Ye*5awh@!^g5skQj2Zw_q4 z$JXLwOTHh<^XIlGgk8(y3^__-U}2U)=-Md)3pw`dt;5dcb9K0P`7{-^uF*Q|S!u7s z*z%2?$o96`$R21STYZz+_E@d^{NwfuwcrJt*^vi_9(31)cb>p^pHYT|fhRCY`sp!} z+*A@aBFsP~y;NAPvY{08Mj%;%+688x{0m@PHDUwbDr(tsK|W26o}^l=@^xmv#d93@ g6nLM4wx_`NE9n2r8R9yg_w{m-7n9rochFwrf7$dI=>Px# literal 4529 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 diff --git a/video_transcription/ai_transcriber_v2/extractor.py b/video_transcription/ai_transcriber_v2/extractor.py index 00fb93f..d39f7e2 100644 --- a/video_transcription/ai_transcriber_v2/extractor.py +++ b/video_transcription/ai_transcriber_v2/extractor.py @@ -3,17 +3,26 @@ import subprocess import sys from utils import verify_file_not_empty +def run_ffmpeg(args): + """ + Runs ffmpeg, automatically escaping Flatpak sandbox if necessary. + """ + in_flatpak = os.path.exists("/.flatpak-info") + cmd = ["flatpak-spawn", "--host", "ffmpeg"] + args if in_flatpak else ["ffmpeg"] + args + + try: + subprocess.run(cmd, check=True) + return True + except subprocess.CalledProcessError as e: + print(f"FFmpeg Error: {e}") + return False + except FileNotFoundError: + print("Error: 'ffmpeg' command not found. Please ensure it is installed on your host system.") + return False + def extract_audio(video_path, output_path=None): """ Extracts audio from a video file using ffmpeg. - - Args: - video_path (str): Path to the input video file. - output_path (str, optional): Path for the output audio file. - If None, defaults to same name with .wav extension. - - Returns: - str: Path to the generated audio file. """ if not os.path.exists(video_path): raise FileNotFoundError(f"Video file not found: {video_path}") @@ -22,49 +31,33 @@ def extract_audio(video_path, output_path=None): base_name = os.path.splitext(video_path)[0] output_path = f"{base_name}.wav" - # Check if output file already exists to avoid redundant processing if verify_file_not_empty(output_path): print(f"Audio file already exists: {output_path}") return output_path print(f"Extracting audio from {video_path}...") - # Command matching the user's preferred settings: 16kHz, Mono, PCM s16le - # -y overwrites without asking (though we checked existence above, this is for safety if we force it) - command = [ - "ffmpeg", + args = [ "-i", video_path, "-ar", "16000", "-ac", "1", "-c:a", "pcm_s16le", "-y", - "-v", "error", # Less verbose + "-v", "error", output_path ] - try: - subprocess.run(command, check=True) - + if run_ffmpeg(args): if not verify_file_not_empty(output_path): - raise Exception("FFmpeg command succeeded but output file is empty or missing.") - + raise Exception("FFmpeg succeeded but output is empty.") print(f"Audio extracted to: {output_path}") return output_path - except subprocess.CalledProcessError as e: - print(f"Error extracting audio: {e}") - sys.exit(1) - except Exception as e: - print(f"Error: {e}") + else: sys.exit(1) def embed_subtitles(video_path, srt_path, output_path=None): """ Embeds subtitles into the video file (Soft Subs) and sets them as primary. - - Args: - video_path (str): Path to the input video. - srt_path (str): Path to the SRT file. - output_path (str, optional): Path for the output video. """ if not os.path.exists(video_path) or not os.path.exists(srt_path): print("Error: Video or SRT file not found for embedding.") @@ -76,18 +69,9 @@ def embed_subtitles(video_path, srt_path, output_path=None): print(f"Embedding subtitles into: {output_path}...") - # Determine subtitle codec based on container sub_codec = "mov_text" if video_path.lower().endswith(".mp4") else "srt" - # Command breakdown: - # -map 0:v -map 0:a -> Keep all video and audio from source - # -map 1:0 -> Add the subtitle from the 2nd input (srt_path) - # -c copy -> Copy video/audio streams (no re-encoding) - # -disposition:s:0 default -> Make the first subtitle track (ours) the default - # -metadata:s:s:0 -> Set metadata for the first subtitle stream - - command = [ - "ffmpeg", + args = [ "-ignore_editlist", "1", "-i", video_path, "-i", srt_path, @@ -106,13 +90,9 @@ def embed_subtitles(video_path, srt_path, output_path=None): output_path ] - try: - subprocess.run(command, check=True) + if run_ffmpeg(args): if not verify_file_not_empty(output_path): - raise Exception("FFmpeg command succeeded but output video is empty or missing.") - - print(f"Subtitles embedded successfully: {output_path} (Set as primary)") - except subprocess.CalledProcessError as e: - print(f"Error embedding subtitles: {e}") - except Exception as e: - print(f"Error embedding subtitles: {e}") + raise Exception("FFmpeg succeeded but output video is empty.") + print(f"Subtitles embedded successfully: {output_path}") + else: + print("Error: Embedding failed.")