Commit Graph

65 Commits

Author SHA1 Message Date
1cc8e8be35 MXF/MPG broadcast format support: handle multichannel audio properly
Problem: MXF and MPG files (TV broadcast formats) often contain:
- Multiple audio streams (4-8 streams for different language tracks)
- Multichannel layouts (5.1, 7.1) instead of stereo
- Default ffmpeg behavior was -c:a aac without channel limit, which
  meant multichannel got transcoded as multichannel AAC, overwriting
  what should have been clean stereo

Solution:

1. get_audio_streams() helper probes all audio streams with ffprobe
   - Returns codec, channels, sample_rate, language, layout for each

2. build_audio_args() picks best stream + downmix:
   - Prefers first 2-channel stereo stream (usually main mix)
   - Falls back to first stream if none are 2-ch
   - Always: -ac 2 (force stereo downmix), -ar 48000, -c:a aac, -b:a 192k
   - Bitrate raised from 128k to 192k for music quality

3. Smart trim path now detects broadcast formats:
   - .mxf, .mpg, .mpeg, .ts, .m2ts, .mts → transcode (not stream copy)
   - Standard MP4/MOV → stream copy (faster, lossless)

4. Pre-conversion step for broadcast files without trim:
   - Even without --start/--duration, MXF/MPG get converted to MP4
   - Same audio handling as trim path

5. Main render adds explicit -map 0✌️0 -map 0🅰️0? -ac 2 to ensure
   only first video and first audio stream get encoded, with stereo

6. ACR recognize also gets -map 0🅰️0 -ac 2 for MXF compatibility

7. UI accepts: video/*,.mxf,.mpg,.mpeg,.ts,.m2ts,.mts

8. Upload limit raised: 2GB → 10GB (MXF files are large)

This means a TV broadcast MXF with [SLO/EN/DE language tracks] now
correctly outputs stereo MP4 with the main language track preserved.
2026-04-29 14:38:48 +00:00
b543057cee ACRCloud auto-recognition: never block uploads, fall back to fingerprinting
Changes:

1. UI: removed blocking prompt() that asked for artist+title on filename
   that didn't match 'Artist - Title' pattern. Upload always proceeds.
   Instead shows yellow warning saying 'server will try to recognize'.

2. Backend: added scripts/acr_recognize.py — extracts 20s audio sample
   from video (at 15s and 60s offsets for robustness), computes ACRCloud
   fingerprint via native binary (3KB payload), sends to identify API.

3. Pipeline: process_job() now runs ACR recognition step before analysis
   IF parsed_artist or parsed_title is missing. Result is saved to job
   metadata and used for download filename + Scribe/Claude filename hint.

4. Credentials: ACR_HOST + ACR_ACCESS_KEY + ACR_SECRET_KEY env vars
   added to Coolify (using existing keys from openclaw fb-agent metka).

5. requirements.txt: added pyacrcloud==1.0.11 for native fingerprinting.

This unblocks future automation/cron upload pipelines — files don't need
to be perfectly named, ACRCloud will identify them automatically.

Fallback chain:
1. Filename parsing (Artist - Title.mp4)
2. ACRCloud audio fingerprint (works even for '12345.mp4', 'IMG_001.mp4')
3. If both fail: download filename uses 'reel_<id>.mp4' (still works)
2026-04-29 14:24:53 +00:00
3877b822ff Smart download filenames: 'Artist - Title - REEL.mp4' + validation
Two improvements:

1. DOWNLOAD FILENAME: instead of 'reel_<job-id>.mp4' (e.g. reel_25e076af7600.mp4),
   downloads now have descriptive names like:
   - 'Lady Gaga - Abracadabra - REEL.mp4'
   - 'Modrijani - S teboj - REEL.mp4'
   - 'Sarah Connor - FICKA - REEL.mp4'

2. PRE-UPLOAD VALIDATION: when filename doesn't follow 'Artist - Title' format,
   browser prompts user for both fields. Without them, upload is blocked.
   This prevents files with names like '12345.mp4' or 'video_final.mp4' from
   being processed without identifying info.

Implementation:
- parse_artist_title() helper handles common formats:
  - 'Artist - Title.mp4' / 'Artist – Title' (em-dash)
  - 'Artist | Title' / 'Artist : Title'
  - Strips noise: '(Official Music Video)', '(Audio)', '(HD)', '[Lyric Video]'
- Client-side parser mirrors backend (validation before upload)
- Backend accepts artist + title form fields (override parsed)
- Job stored with parsed_artist + parsed_title + has_clean_name fields
- YouTube jobs auto-fetch title via yt-dlp --info-only and parse it
- Filename hint to Scribe/Claude uses parsed values (cleaner than raw filename)
- Download endpoint uses build_download_filename() for content-disposition
- Jobs list shows 'Artist — Title' instead of raw filename

Result: downloaded reels are auto-named correctly for Facebook/Instagram
upload, no more renaming files manually.
2026-04-29 14:15:18 +00:00
671b512917 Fix Preview button in jobs sidebar not opening modal
Root cause: inline onclick with JSON.stringify(title) broke when title
contained quotes, special chars, or was empty. The HTML attribute parser
got confused by mismatched quotes, so click handler never fired.

Fix:
- Replaced inline onclick handlers with data-action attributes
- Added single delegated click listener at document level
- Title stored in element dataset (no HTML quoting issues)
- Added escapeHtml() helper for safe rendering of titles/errors

Now clicking Preview in the right sidebar opens the fullscreen modal
correctly, regardless of filename characters.
2026-04-29 13:39:04 +00:00
389c26d012 Modal preview: click Preview opens fullscreen video player
Previously: clicking Preview in jobs list showed a small inline video
within the job card row.

Now: clicking Preview opens a centered fullscreen modal with:
- Large video player (up to 95vw × 85vh) — same experience as bottom
  live-preview but accessible from jobs list
- Auto-play, controls, native HTML5 video player
- Title shown below video for context
- Download button + Close button
- Click outside or ESC key to close
- Backdrop blur for focus

Removes the obsolete inline <video> element that was rendered hidden
in each job card. Body scroll locked while modal open.
2026-04-29 13:21:36 +00:00
05fb0081c6 Fix preview cutoff + sticky left panel
1. Preview endpoint now supports HTTP Range requests (HTTP 206 Partial)
   - HTML5 video player needs Range support to seek/buffer properly
   - Without it, video would cut off after a few seconds
   - Returns chunks of 64KB on demand

2. Left panel (upload form) is now sticky (position: sticky)
   - Stays in view while right panel (jobs list) scrolls
   - On mobile (<800px) reverts to normal flow
2026-04-29 10:24:32 +00:00
ec71c54570 Upgrade to Sonnet 4.6 + add Gemini 3.1 Pro support
- Refactored analyze_with_claude into shared _build_analysis_prompt + _parse_llm_response helpers
- New analyze_with_gemini() using Gemini 3.1 Pro ($2/M in, MMMLU 92.6% — best multilingual)
- Unified analyze_with_llm(provider) dispatcher with auto-fallback (Claude → Gemini)
- API endpoint accepts llm_provider in StartJobIn (claude/gemini/auto)
- Frontend dropdown to pick LLM
- Default model is now Sonnet 4.6 (was Haiku 4.5) — 3x quality at 3x price (~3 cents/video)
- Gemini support is opt-in: needs GEMINI_API_KEY env var to activate
2026-04-29 08:26:27 +00:00
69fb2f5ce8 Upgrade default Whisper model: small/medium → large-v3 for much better Slovenian/Slavic transcription accuracy 2026-04-29 08:20:18 +00:00
4e123bdabc UI: hide lang/model dropdowns — both are fully automatic now (3-sample lang detection + medium default model) 2026-04-29 08:03:22 +00:00
af3c933c78 Robust language detection + anti-hallucination
- 3-sample voting for auto-detect (start/middle/end of song) prevents lang switching mid-song
- Lock detected language for full transcription
- Anti-hallucination: condition_on_previous_text=False, temperature=0.0
- compression_ratio_threshold=2.4 (rejects repetitive hallucinations)
- log_prob_threshold=-1.0 (rejects low-confidence segments)
- no_speech_threshold=0.6 (more aggressive silence detection)
- Default Whisper model changed: small → medium (better for all langs incl. Slavic)
2026-04-29 07:59:20 +00:00
c870d80726 Fix: extend clip if ends mid-vocal (no chorus cut-off), DejaVu Sans font (supports SLO/HR/BS chars), auto-upgrade to medium Whisper model for Slavic languages 2026-04-29 07:35:00 +00:00
8512076b91 Major: smart selection pipeline (analyze.py) + audio fade + multi-lang auto-detect
- New analyze.py: full transcript + energy + structural analysis
- Smart clip range: includes pre-chorus, can exceed 30s up to max_duration (default 45s)
- Audio fade in/out: auto-detected from vocal boundaries
- Instrumental detection: auto-disables subs if vocals < 10% of duration
- Multi-language: auto-detect via Whisper or explicit (DE/SL/HR/BS/SR/EN/IT/ES/FR)
- Frontend: cleaner UX, added bs language, smart selection description
- reframe.py: --fade-in --fade-out args
- clip.py: propagates fade params
- app/main.py: replaces find_chorus.py call with analyze.py
2026-04-29 06:21:35 +00:00
bf7ced5c7b Reset upload form also after failed jobs (so next upload works) 2026-04-28 16:29:39 +00:00
c34e4aa376 UX: Live progress panel below upload form, stable progress bar, inline preview/download 2026-04-28 16:19:40 +00:00
30b969e4b8 Initial: reels clipper app
- FastAPI backend (auth, jobs, SSE, download)
- Frontend: drag&drop + YouTube URL + jobs panel
- Pipeline: yt_download → find_chorus → reframe → subtitle
- Modes: track (face follow), center, blur
- Whisper for SI/DE/EN subtitles
- Auto-chorus detection via Whisper + RMS energy
- Docker + Coolify ready
2026-04-28 15:28:22 +00:00