Stille-Suche: Folge erst in Python laden, ffmpeg liest die lokale Datei
Die statische ffmpeg-Build stürzt im Container beim Auflösen von Hostnamen ab (SIGSEGV). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c1bbaccccf
commit
ae72b1d67a
1 changed files with 35 additions and 12 deletions
|
|
@ -7,17 +7,22 @@ episode (PodcastEpisode.silences); the browser then jumps over them while it
|
||||||
plays (podcasts-web.js).
|
plays (podcasts-web.js).
|
||||||
"""
|
"""
|
||||||
import logging
|
import logging
|
||||||
|
import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
from django.db import close_old_connections
|
from django.db import close_old_connections
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
NOISE = '-35dB' # quieter than this counts as silence
|
NOISE = '-35dB' # quieter than this counts as silence
|
||||||
MIN_SILENCE = 0.4 # seconds; shorter pauses stay (they belong to speech)
|
MIN_SILENCE = 0.4 # seconds; shorter pauses stay (they belong to speech)
|
||||||
TIMEOUT = 900 # seconds for one episode (download and decode)
|
TIMEOUT = 900 # seconds for decoding one episode
|
||||||
|
MAX_BYTES = 800 * 1024 * 1024 # a larger file isn't an episode to analyse
|
||||||
MAX_RUNNING = 2 # episodes analysed at the same time
|
MAX_RUNNING = 2 # episodes analysed at the same time
|
||||||
|
|
||||||
_START = re.compile(r'silence_start: (-?[\d.]+)')
|
_START = re.compile(r'silence_start: (-?[\d.]+)')
|
||||||
|
|
@ -47,18 +52,36 @@ def parse(stderr):
|
||||||
|
|
||||||
|
|
||||||
def analyze(url):
|
def analyze(url):
|
||||||
"""The pauses of the audio at `url` (ffmpeg reads it straight from there)."""
|
"""The pauses of the audio at `url`. Downloaded here first: the static ffmpeg
|
||||||
|
build can't look up host names (it crashes doing so), it reads a local file."""
|
||||||
import imageio_ffmpeg
|
import imageio_ffmpeg
|
||||||
proc = subprocess.run(
|
fd, path = tempfile.mkstemp(suffix='.audio')
|
||||||
[imageio_ffmpeg.get_ffmpeg_exe(), '-nostdin', '-hide_banner', '-nostats',
|
try:
|
||||||
'-user_agent', 'diora podcast silence', '-vn', '-sn', '-dn', '-i', url,
|
with os.fdopen(fd, 'wb') as out, requests.get(
|
||||||
'-af', f'silencedetect=noise={NOISE}:d={MIN_SILENCE}', '-f', 'null', '-'],
|
url, stream=True, timeout=(10, 60), headers={'User-Agent': 'diora podcast silence'}) as r:
|
||||||
capture_output=True, text=True, timeout=TIMEOUT,
|
if r.status_code >= 400:
|
||||||
)
|
raise RuntimeError(f'HTTP {r.status_code}')
|
||||||
if proc.returncode != 0:
|
size = 0
|
||||||
tail = (proc.stderr or '').strip().splitlines()[-1:] or ['ffmpeg failed']
|
for chunk in r.iter_content(1 << 20):
|
||||||
raise RuntimeError(tail[0][:200])
|
size += len(chunk)
|
||||||
return parse(proc.stderr)
|
if size > MAX_BYTES:
|
||||||
|
raise RuntimeError('file too large')
|
||||||
|
out.write(chunk)
|
||||||
|
proc = subprocess.run(
|
||||||
|
[imageio_ffmpeg.get_ffmpeg_exe(), '-nostdin', '-hide_banner', '-nostats',
|
||||||
|
'-vn', '-sn', '-dn', '-i', path,
|
||||||
|
'-af', f'silencedetect=noise={NOISE}:d={MIN_SILENCE}', '-f', 'null', '-'],
|
||||||
|
capture_output=True, text=True, timeout=TIMEOUT,
|
||||||
|
)
|
||||||
|
if proc.returncode != 0:
|
||||||
|
tail = (proc.stderr or '').strip().splitlines()[-1:] or [f'ffmpeg failed ({proc.returncode})']
|
||||||
|
raise RuntimeError(tail[0][:200])
|
||||||
|
return parse(proc.stderr)
|
||||||
|
finally:
|
||||||
|
try:
|
||||||
|
os.remove(path)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def ensure(episode_id):
|
def ensure(episode_id):
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue