Commit Graph

137 Commits

Author SHA1 Message Date
Claude
2fcc4b8075 Dashboard: TV station filter tabs nad jobs-list
- Tabi: Vse / FOLX SLOVENIJA / FOLX DE / ONE DE / ZWEI MUSIC / ADRIA / (brez postaje)
- Števec na vsakem tabu = število NEPOTRJENIH jobov (!hidden_after_upload) za to postajo
- Klik na tab = filter samo tisti station, samo nepotrjeni
- 'Vse' = vsi nepotrjeni (kot do zdaj)
- '(brez postaje)' tab se skrije, če ni jobov brez postaje
- Persist v localStorage (reels_jobs_station_filter)
- Iskanje IGNORIRA station filter (vrne tudi naložene + vse postaje)
- Empty state sporočilo prilagojeno glede na izbran filter
2026-05-03 12:48:31 +00:00
Claude
0d72d70f5d S3 mirror integration: workfiles auto-mirror to s3://folxspeed/reels-app/
- main.py: 4 helper funcs (_persist_to_s3, _ensure_local, _delete_from_s3,
  _ffmpeg_then_persist) - no-op fallback when S3 creds missing
- save_job(): mirror metadata JSON to S3
- process_job(): mirror YT download + render output + analysis/srt/ass to S3
- upload_video(): mirror direct uploads to S3
- _precache_edit_assets(): Popen->threaded with S3 sync after ffmpeg
- read endpoints (download, preview, source_video, waveform, preview_clip,
  get_transcript, recut_job): _ensure_local() fallback fetch from S3
- delete_job(): cascade delete to S3 (mirror unlink)
- cleanup.py: NEW module, deletes local files >48h that exist in S3.
  Verified by S3 head_object + size match. NOT YET ACTIVATED in cron.

Backward compat: lokalna mapa ostane primary. Brez env vars S3_* vsi
helperji vrnejo False (no-op). Production behavior identičen, dokler
ne dobi S3 creds.
2026-05-03 12:24:18 +00:00
Claude
ec1d109e3b S3 storage module: boto3 abstraction for reels-app workfiles (uploads/outputs/jobs prefixes) 2026-05-03 11:57:12 +00:00
OpenClaw Agent
48bf0cf050 UI: Nextcloud gumb \u2014 optimistic feedback. Takoj ob kliku gumb postane oran\u017een "\u23f3 Po\u0161iljam...", disabled, cursor:wait. Po uspe\u0161nem upload-u refreshJobs() zamenja v zelen \u2713 Nextcloud. Pri napaki vrne original state. 2026-05-03 12:27:24 +02:00
OpenClaw Agent
6a9e20da19 Nextcloud upload: mapping Qnet station -> NC folder. Qnet baza ima FOLX SLO/ONE/ZWEI, Nextcloud pa FOLX SLOVENIJA/ONE DE/ZWEI MUSIC \u2014 STATION_TO_NEXTCLOUD_FOLDER dict normalizira pri uploadu. Re\u0161i HTTP 404 NotFound za FOLX SLO jobe (106 jobov). 2026-05-03 12:24:35 +02:00
OpenClaw Agent
a960b7157f UI: dashboard prikazuje Artist - Title (parsed_artist + parsed_title) namesto YT URL. Fallback chain: parsed -> youtube_title -> youtube_url -> filename -> id. Ker commit 968eba7 \u017ee shrani vse YT metapodatke in Qnet match na single video submit, dashboard zdaj prikazuje \u010cisti Artist - Title za vse jobe. 2026-05-03 11:50:45 +02:00
OpenClaw Agent
968eba7205 YT metadata fetch: razširi --info-only output (id, uploader, description, upload_date, view_count, tags, ...). Single video submit fetcha metadata + Qnet match takoj (kot playlist). Worker preskoči info fetch če metadata že obstaja, sicer shrani vsa polja in naredi Qnet match.
- yt_download.py: get_info() probaj najprej yt.biba.live API /download/info (residential IP, sveži cookies), fallback na lokalni yt-dlp. --info-only output razširjen na 17 polj.
- main.py submit_youtube single video: fetcha metadata (yt_get_info) ob submit, shrani youtube_title/uploader/id/description/duration/thumbnail/upload_date in naredi Qnet match (parity s playlist branch).
- main.py worker: skip info fetch če youtube_title in youtube_uploader že obstajata. Sicer shrani VSE polja + Qnet match + parser fallback.
2026-05-02 15:54:28 +00:00
OpenClaw Agent
bc73fd8dd3 Reels-app: integracija z yt.biba.live API. get_cookies_file fetcha sve\u017ee cookies iz /cookies/raw (5 min cache), get_playlist najprej probava /download/playlist API, fallback na lokalni yt-dlp. ENV: YT_API_URL, YT_API_TOKEN. 2026-05-02 13:17:30 +00:00
OpenClaw Agent
77075795ce YouTube playlist support: /api/youtube/playlist-preview za pred-confirm, /api/youtube zaznava 'list=' URL in kreira batch (1 job/video). Qnet auto-match na YT naslovu, Confirm dialog v UI z prvih 5 naslovov. 2026-05-02 12:34:27 +00:00
OpenClaw Agent
7a7d7ea20d Preview cache-busting: Cache-Control: no-cache na endpoint + ?v=Date.now() v frontend URL — browser ne sme cachat starega output-a po recutu 2026-05-02 12:24:19 +00:00
OpenClaw Agent
c1e00b7b73 Final SAR=1 fix: dodaj setsar=1 na konec vfilter-ja v reframe.py + ass filtrom v subtitle.py (kompenzira rounding errore iz scale/crop filtrov, ki dajo SAR 10240:10239 namesto 1:1) 2026-05-02 12:12:38 +00:00
OpenClaw Agent
6279b0ec03 subtitle.py: dodaj -pix_fmt yuv420p v burn-in encode (subtitle re-encode je perpetuiral broadcast yuv422p iz prej\u0161nje stopnje) 2026-05-02 12:05:47 +00:00
OpenClaw Agent
46ec0cec9c Anamorphic source fix: pred-scale na square pixel (PAL DV 720x576 SAR 64:45 \u2192 1024x576) preden track/center/blur filter dela. Brez tega so se reels iz starih MPG-2 (npr. Global Kryner Like a Virgin) razteg-nili horizontalno. 2026-05-02 11:57:09 +00:00
OpenClaw Agent
0fe1a47295 Fix output pixel format: -pix_fmt yuv420p + setsar=1 v vseh ffmpeg ukazih (broadcast .MPG iz Qnet je yuv422p z anamorphic SAR 64:45 \u2192 popa\u010den DAR 4:5 namesto 9:16, ne kompatibilen z Instagram/FB/mobile playerji) 2026-05-02 11:42:08 +00:00
OpenClaw Agent
6270c92b44 STT routing: FOLX DE / ZWEI \u2192 Scribe default (4\u00d7 hitrej\u0161e + brez Mississippi/Mrs. Sadie halucinacij). SLO postaje ostanejo na Soniox. User lahko override v UI. 2026-05-02 11:17:51 +00:00
OpenClaw Agent
1f8565413a Qnet match VEDNO shranjen v job.qnet_match (audit + tv_station auto-fill), tudi ko client po\u0161lje artist+title 2026-05-02 10:57:09 +00:00
OpenClaw Agent
24e1b53aa8 Qnet match v upload queue — auto-prepoznavanje pesmi takoj ko izbere\u0161 fajle (parallel POST /api/qnet/match-batch). \u010ce baza prepozna komad, prikaz Artist \u2014 Title z station badge namesto 'Brez razvidnega imena'. 2026-05-02 10:47:51 +00:00
OpenClaw Agent
b938d1e4d8 Qnet song match — fetcha Songs.txt iz 5 MB playerjev (FOLX DE/SLO, ZWEI, ONE, ADRIA), 20K+ songs, fuzzy match na upload-u → clean parsed_artist/parsed_title + auto tv_station. /api/qnet/{stats,match,sync} 2026-05-02 10:42:35 +00:00
OpenClaw Agent
6f79aaea8d Iskalnik v 'moji reels' (artist+title+filename, debounced 150ms) 2026-05-02 10:02:21 +00:00
4febf0b844 Trailing bare 4K/HD/8K (brez oklepajev)
Edge case: 'Naslov (Official video) 4K.mxf'
Pred: '(Official video)' odstranjen, ampak '4K' brez oklepajev ostane

Pattern: r'\b(?:4K|HD|HQ|8K|1080p|720p)\s*$'
- Samo na koncu stringa
- Ne v sredini (Top 100 hitov ohranjen)
2026-05-02 08:55:44 +00:00
2aec7f7a29 Odstrani trailing 2-4 cifrene številke (leto/verzija)
User: 'včasih številka 23 pri Modrijanih in pri Firbcih, te se pojavlajo
v naslovih'

Razlog: NAS interno označeni kot leto produkcije.
'PA KAJ (Official Video) 23' = leto 2023
'ONA HOČE (Official video) 22' = leto 2022
'S tabo res rad (Official Video) 33' = leto 2033 (?) ali interno

Pattern: r'\s+\d{2,4}\s*$' — trailing 2-4 cifrena številka

Test rezultati:
- 'S tabo res rad 33' → 'S tabo res rad' 
- 'PA KAJ 23' → 'PA KAJ' 
- 'PESEM GORENJSKIH TRAT (2023)' → 'PESEM GORENJSKIH TRAT' 

Edge cases (NI odstranjeno):
- 'Pesem 25 ljubljanskih ulic' (število v sredini) 
- 'Top 100 hitov' 
2026-05-02 08:52:40 +00:00
4e2c690bc5 Bolj agresivno čiščenje filename: () prazni + catch-all noise besede
User: 'Topliška pomlad — KAR PADA NAJ SNEG ( - ) — tile oklepaji
pa Official video itd. Daj ko se nalaga na Nextcloud mora biti samo
izvajalec in naslov komada.'

Dodatni NOISE_PATTERNS:
1. Prazni / dummy oklepaji: '( )', '( - )', '(-)', '(.)' itd.
2. Catch-all za oklepaje z noise besedami:
   video|audio|version|mix|edit|remix|cover|live|hd|hq|4k|8k|
   remaster|extended|clean|explicit|radio|lyric|official|musik
3. Avtor/producer brackets: '(prod. by X)', '(feat. Y)', '(ft. Z)'

Test rezultat:
'Topliška pomlad - KAR PADA NAJ SNEG ( - )(Official 4K Video).mp4'
→ 'Topliška pomlad - KAR PADA NAJ SNEG - REEL.mp4'

'Sarah Connor - FICKA (Offizielles Musikvideo).mp4'
→ 'Sarah Connor - FICKA - REEL.mp4'

Vsi novi uploadi bodo imeli čista imena.
TODO ločeno: rename obstoječih 31 datotek na Nextcloudu (skript pripravljen)
2026-05-02 08:10:30 +00:00
376bb4db09 Manual Nextcloud upload tudi nastavi hidden_after_upload=True
User: 'naredi da ko damo IN sejvamo na nextcloud da izgine iz pregleda
in se vrne če stisnemo Pokaži že naložene'

Bug: 2 mesti uploadamo na Nextcloud:
1. Avto-upload po recut/Save → hidden_after_upload=True 
2. Manual ☁ Nextcloud klik → samo nextcloud_status='uploaded' 

Fix: oba puta nastavi hidden_after_upload=True

Tudi: batch fix obstoječih 19 uploaded jobs ki niso imeli hidden flag,
posredno preko docker exec — zdaj se skrijejo tudi oni.

Workflow zdaj:
- ☁ Nextcloud klik → upload + hidden=true → izgine
- Save (Edit) → re-render + auto-upload + hidden=true → izgine
- ☐→☑ 'Pokaži tudi že naložene' → vidiš vse z zelenim borderjem
2026-05-02 08:01:37 +00:00
f5ef136bf4 Marker center = ORIGINAL clip center (not dynamic)
User: 'če razširim zoom in podaljšam komad potem polovica ni več tam
kjer je reel IN in tam kjer je OUT. Naj se program drži polovice
avtomatično narejenega reela'

Pred (bug):
  center = (trimStart + trimEnd) / 2  # dynamic
  Ko user razširi clip 60-90 → 50-110:
    nov center = 80 (prej 75)
    Klik pri 78s → IN (čeprav user mislil OUT!)

Po (fix):
  initialCenter = (startInit + endInit) / 2  # FIKSEN, izračunan ob open
  Original LLM center se NE spreminja
  Klik pri 78s vedno → OUT (če je 78 > 75)
  Klik pri 70s vedno → IN

Ne glede koliko user razširi/oža clip, marker assignment uporablja
fiksno 'sredino' originalnega LLM-jevega clipa.

Tudi drag handle: če marker zaide čez initialCenter → reset na trim border.
2026-05-02 07:53:44 +00:00
40151f8f57 STROGI marker boundaries: zelen samo levo od center, rdeč samo desno
User: 'še vedno mi skače zeleni trikotnik na konec! Naredi da ne gre
čez polovico ne en ne drug'

Bug analiza: prej Math.min(t, center - 0.1) NI bilo, samo direktna
assignment. Če bi user nekje napačno klikal, ali če je drag handle-a
spremenil center, marker je pristal na napačni strani.

Strogo zdaj:
- markerInTime = Math.min(t, center - 0.1)  → zelen NIKOLI čez center
- markerOutTime = Math.max(t, center + 0.1) → rdeč NIKOLI čez center

Plus: po drag-u handle-ja se markerji preverijo:
- Če je markerIn na desni strani novega centra → reset na trimStart
- Če je markerOut na levi strani novega centra → reset na trimEnd

Plus console.log za debug — vidiš v Dev Tools kateri vrednosti uporabljam.
2026-05-02 07:26:54 +00:00
bdc1d14498 Toggle 'Pokaži tudi že naložene' + recut prepiše Nextcloud
User feedback: 'načeloma bi se moral samo če kaj spregledamo in že
sejvamo pa ne moremo nazaj vrniti in popraviti'

Frontend:
- Nov toggle '☁ Pokaži tudi že naložene' nad 'moji reels' headerjem
- Default OFF: vidiš samo aktivne (ki niso uploaded)
- ON: vidiš VSE, vključno z uploaded (z zelenim borderjem)
- buildJobEl: uploaded reels imajo border-left zelen + bg #4ade80 0.04 opacity
- Listener: change event sproži refreshJobs()

Backend:
- recut endpoint: reset hidden_after_upload=false, nextcloud_status='recutting'
- Po končanem recut: avto-upload na Nextcloud (PUT prepiše obstoječi file)
- Hidden_after_upload=true spet po uspešnem re-uploadu

Workflow:
1. Reel uploaded → hidden, ni v UI
2. Klik toggle 'Pokaži tudi že naložene' → vidiš ga (zelen border)
3. Edit → Save → re-render (visible spet, status='processing')
4. Re-upload PUT na Nextcloud → prepiše obstoječi file (ista pot, isto ime)
5. Hidden=true → izgine spet (osim če toggle on)
2026-05-02 07:23:03 +00:00
129f7d5f33 Marker assignment: split po sredini clipa (ne po bližini handle-a)
User feedback: 'če kliknemo na konec se premika IN, in ne OUT.
Če je bližje OUT ko kliknemo, se mora premikati OUT trikotnik —
tudi če nismo prej izbrali IN. Če je IN ok potem tega ne delamo.'

Pred (bug):
  distToLeft = |t - trimStart|
  distToRight = |t - trimEnd|
  Če enaka razdalja → IN (default '<=')
  Ko si že popravil IN, distance lahko enaka → klik blizu OUT
  pomotoma premakne IN

Po:
  center = (trimStart + trimEnd) / 2
  if t < center → IN
  else → OUT

Logika: leva polovica clipa = IN domain, desna = OUT domain.
Klik kjerkoli blizu desnega handle (= konec) bo VEDNO premaknil OUT.
Klik kjerkoli blizu levega handle (= začetek) bo VEDNO premaknil IN.
Tudi če si že popravil IN, popravljanje OUT ne bo motilo IN markerja.
2026-05-02 07:02:05 +00:00
c58875c072 Upload: timeout + retry + ne ustavi loop ob enem fail-u
User: 'zakaj se je ustavilo? Naložil sem več kot 70.'

Diagnoza: 12/70+ je prišlo do server-ja (vsi 200 OK).
Browser-side problem: en upload je stuck → cel for-loop blokiran.

Fixes:
1. xhr.timeout = 10min per file (prej: večnost)
2. xhr.ontimeout, xhr.upload.ontimeout — proper error handling
3. NEW: uploadFileWithRetry() — 2x retry z 2s/4s eksponentnim delay-om
   za očasne mrežne odpovedi
4. Catch v loop ne kliče liveFail() (kar bi naredil disabled submit-btn)
   ampak samo showLive() z 'preskočil' sporočilo → loop nadaljuje
5. Console.warn v retry attempts za debugging

Sedaj če eno failes:
- Retry 2x avtomatsko (2s + 4s delay)
- Če še vedno ne uspe → preskoči to datoteko, nadaljuj z naslednjo
- Pri koncu vidiš katere so failed (v console + showLive)
2026-04-30 16:28:27 +00:00
2de58ca7a5 Skrij vse settings razen no-subs — vse drugo zapečen preset
User: 'sej je že preset narejen, ne kompliciraj. Vse je enako za reels.
Naredi da so podnapisi po default GOR (brez kljukice). Klukica = brez napisov.
Ostalo zapeči, nimamo kaj spreminjat.'

Hidden defaults (zapečen preset za vse reels):
- mode=track
- quality=medium
- llm-provider=claude
- whisper-model=large-v3
- subtitle-style=reels
- auto-chorus=true (data-checked)
- include-prebuild=false (data-checked)
- duration=30

Vidno samo:
- TV postaja (tabs) — kam gre na Nextcloud
- Brez kljukice = napisi V VIDEO (default)
- Kljukica = brez napisov

Side note: 'Brez kljukice = napisi' je obratno od prej (kljukica = brez).
Default je sedaj 'with subtitles' (no-subs unchecked). Persistence v
localStorage ohranja izbiro med reload-i.

Bonus: odstranjen 'Auto-chorus toggle' handler ki je iskal #manual-times
ki ne obstaja več.
2026-04-30 15:20:52 +00:00
d0e78cca02 Persist UI settings v localStorage (no-subs, station, mode, ...)
User feedback: 'ko refrešam se ponovno vklopi kljukica da ni
podnapisov to bo problem'

Saved fields:
- no-subs (checkbox)
- auto-chorus (checkbox)
- include-prebuild (checkbox)
- mode (track/center/blur)
- quality (fast/medium/high)
- llm-provider (claude/gemini/auto)
- tv-station (FOLX SLOVENIJA / ONE DE / ...)

On page load: re-aplicira saved values
On change: shrani v localStorage
TV station: tudi posodobi aktiven tab style

Defaults ostanejo isti če nikoli niso spremenjeni.
2026-04-30 15:17:16 +00:00
c2a593de78 Fix JS syntax error: duplicate q.appendChild block
Bug: previous str_replace introduced duplicate '});' lines after
file queue rendering. Caused 'Unexpected token )' parse error,
which broke ALL JS — addFilesToQueue was undefined → upload didn't work.

User feedback: 'ne dela upload'

Verified with: node -c on extracted script — passes now.
2026-04-30 15:14:09 +00:00
b9c8c066ec Odstrani emoji iz TV postaj + cleanup all reels
User feedback: 'pa ne maram teh emojijev ki si ji dal k Adria in ZWEI Music.
Daj počisti sedaj kar je že narejeno in gremnalagat.'

UI changes:
- Tab labels: 'FOLX SLOVENIJA', 'FOLX DE', 'ONE DE', 'ZWEI MUSIC', 'ADRIA'
  (without emoji prefixes)
- Job card badge: brez emoji prefix

Cleanup (manual via container):
- /data/jobs/*.json (15 → 0)
- /data/outputs/* (132 → 0)
- /data/uploads/* (15 → 0)

Dedup baza je ohranjena (15 zapisov), tako da če uporabnik poskuša
naložiti komad ki je že naložen na Nextcloud, dobi opozorilo.
2026-04-30 15:10:28 +00:00
5f339e91b5 Bolj jasen tekst: 'Izklopi podnapise' s pojasnilom
User feedback: 'ko ni kljukice so napisi, zato napiši samo izklopi
podnapise in ko je kljukica so izklopljeni'

Pred: 'Brez podnapisov (privzeto — bolj zanesljivo)'
Po:   'Izklopi podnapise (kljukica = brez napisov · brez kljukice = napisi v video)'

Logika ostane ista (no_subs flag), samo label je bolj jasen.
2026-04-30 15:05:22 +00:00
f2034f9970 Dedup: SQLite baza za že obdelane komade
User feedback: 'dodaj da če čekira in shranjuje že obdelani komadi v SQL bazo,
da če nalagamo komad ki smo ga že naložili da ga ne naloži'

NEW: SQLite dedup database at /data/processed.db
Schema: processed_videos
  - normalized_name (PK part 1)
  - tv_station (PK part 2) — isti komad lahko obstaja na različnih postajah
  - filename_orig
  - job_id
  - nextcloud_url
  - file_size_mb
  - uploaded_at

Filename normalization removes noise:
  'BRAJDE (Official Video).mp4' → 'brajde'
  'Brajde (HD).mxf' → 'brajde'
  'BRAJDE - LIVE 2024.mp4' → 'brajde'
(strips parentheses, suffixes like Official/HD/4K/Live, extension, lowercase)

NEW endpoints:
- POST /api/dedup/check — preveri katera imena so že obdelana
- POST /api/dedup/remove — pobriše dedup zapis (Re-process)
- GET /api/dedup/list — seznam vseh obdelanih (opt. filter po tv_station)

Integration:
- Nextcloud upload (manual + auto): zabeleži v dedup po uspešnem PUT
- File queue (frontend): pred dodajanjem preveri dedup
  → prikaže rdeč warning '⚠ Že naložen na ONE DE (29.4.2026) — Re-process'
  → opacity 0.6 (vizualno blediji)
  → submit jih SKIP-a (osim če 'Re-process' kliknil)
2026-04-30 15:00:10 +00:00
16c332b490 Save → avto-upload v Nextcloud → reel izgine iz seznama
User feedback: 'zaenkrat bomo ročno popravljali in pregledovali. Ko kliknemo
Save potem se shrani v pravi folder in izgine.'

Workflow:
1. User izbere TV postajo (zavihek)
2. Naloži komade
3. Reel se renderira (auto chorus)
4. User pregleda + Edit če treba
5. Save → re-render z user popravki
6. Po končanem re-render: AVTO-upload v Nextcloud /folxspeed/REELS/{station}/
7. Reel IZGINE iz seznama (hidden_after_upload flag)

Backend changes:
- RecutRequest: nov field auto_upload (default True)
- update_job: shrani auto_upload_to_nextcloud
- process_job done block: če flag set + Nextcloud configured →
  upload + nextcloud_status='uploaded' + hidden_after_upload=True

Frontend changes:
- refreshJobs: filter out jobs with hidden_after_upload
- TV station badge na vsaki kartici (z emoji + ime postaje)
- Vidiš na prvi pogled kam bo šlo

Workflow rezultat: po Save reel izgine, je avtomatsko v pravi mapi
2026-04-30 14:53:01 +00:00
1c11dfe630 TV station tabs + per-station Nextcloud upload target
User feedback: 'Sarah Connor in Abracadabra grejo v ONE DE, ne v FOLX SLO.
Naredi zavihke za vsako TV postajo (FOLX SLO, FOLX DE, ONE DE, ZWEI MUSIC, ADRIA)
in upload gre v ustrezno Nextcloud podmapo.'

Backend changes:
- StartJobIn + YouTubeJobIn: nov field 'tv_station' (default 'FOLX SLOVENIJA')
- update_job: shrani tv_station v job JSON
- POST /api/jobs/{id}/upload-nextcloud:
  bere tv_station iz job, target_subdir = folxspeed/REELS/{station}

Frontend changes:
- 5 TV station tabs: FOLX SLOVENIJA (active), FOLX DE, ONE DE, ZWEI MUSIC, ADRIA
- Hidden input #tv-station-input drži current selection
- Klik na tab ga aktivira (accent color)
- collectSettings() vključuje tv_station

Manual fix: Sarah Connor in Abracadabra job.json popravljena → tv_station=ONE DE
Reset njihovih nextcloud_* polj da bo upload v pravo mapo.
2026-04-30 14:38:45 +00:00
8284181fb3 Cleanup: odstrani duplikat upload-nextcloud endpoint
Endpoint /api/jobs/{id}/upload-nextcloud je že obstajal (commit dbb8ab3)
in deluje. Moja nova varianta je bila duplikat — odstranjena.

Aktivni endpoint: line 1593 (upload_nextcloud), uporablja
_nextcloud_upload() in _nextcloud_configured() helper-je.

Zdaj imamo:
- _safe_filename_for_nextcloud() helper (ostane, lahko pride prav)
- Frontend gumb '☁ Nextcloud' z 4 stanji (default/uploading/uploaded/failed)
- 14 reelov že uspešno uploadanih v /folxspeed/REELS/FOLX SLOVENIJA/
2026-04-30 14:32:04 +00:00
d03beddd0d Nextcloud: URL-encode path segments + use FOLX SLOVENIJA subfolder
- NEXTCLOUD_FOLDER env updated: folxspeed/REELS → folxspeed/REELS/FOLX SLOVENIJA
- urllib.parse.quote() each segment (handles spaces in folder names)
- e.g. 'FOLX SLOVENIJA' → 'FOLX%20SLOVENIJA' in URL
2026-04-30 14:27:28 +00:00
dbb8ab3059 Nextcloud upload za FOLX SLOVENIJA reels
User wants reels saved directly to Nextcloud /folxspeed/REELS/FOLX SLOVENIJA/

NEW backend endpoint: POST /api/jobs/{id}/upload-nextcloud
- WebDAV PUT preko stdlib urllib (no new deps)
- Uses NEXTCLOUD_URL/USER/PASS/REELS_PATH env vars
- Updates job status: uploading → uploaded / error
- Stores nextcloud_url + nextcloud_error in job

Frontend already had button (☁ Nextcloud) and handler — just needed
backend endpoint. UI states:
- ☁ Nextcloud (blue) — not yet uploaded
- ☁ ✓ Nextcloud (green) — uploaded successfully
- ☁ ✕ Poskusi znova (red) — upload failed (hover for error)

Env vars added in Coolify:
- NEXTCLOUD_URL=https://nextcloud.folx.tv
- NEXTCLOUD_USER=admin
- NEXTCLOUD_PASS=<app token>
- NEXTCLOUD_REELS_PATH=folxspeed/REELS/FOLX SLOVENIJA
2026-04-30 14:23:15 +00:00
faf002d4f8 UI: posodobi zastarel opis (Whisper → Soniox + Claude)
User feedback: 'tukaj imava cel kup stvari ki niso res, kako oblikujemo?'

Old text was misleading:
- 'Whisper, 3-sample voting' → not used since Soniox integration
- 'Model: medium' → irrelevant (Whisper not used)
- 'Whisper + energy → najde refren' → now Soniox + Claude LLM

New text reflects actual stack:
- STT: Soniox (primary) → ElevenLabs Scribe → Gemini fallback
- LLM: Claude Sonnet 4.6
- Energy profile + word-level timestamps + 15 reference examples
- Mention ✏️ Edit button for manual fine-tuning
2026-04-30 14:13:48 +00:00
de094b76a5 Edit: add 'Konec (5s)' button — preview just last 5s of clip
User feedback: 'ko hočemo preveriti konec, predvajaj samo 5s.
začetek ni problematičen ker plej začne od začetka'

NEW button: ▶ Konec (5s) — green
- Seeks to (trimEnd - 5s)
- Plays from there to trimEnd
- Auto-stops at trimEnd (existing logic)
- Quick way to verify if 'OUT' position is correct without
  waiting for full clip playback (which can be 30-60s)

Renamed: '▶ Predvajaj odsek' → '▶ Predvajaj cel' for clarity
(plays full clip from start to end)

Workflow now:
- Adjust handles
- '▶ Predvajaj cel' to hear whole clip (when needed)
- '▶ Konec (5s)' to quickly check if end is right
- Iterate handles until perfect
- Save
2026-04-30 14:04:36 +00:00
02fbae7c4f Edit: triangles INSIDE trim bar (overflow:hidden was clipping them)
Bug: triangles positioned at top:-14px were outside trim bar bounds.
Trim bar has overflow:hidden, so triangles were clipped (invisible).

Fix: top:0 (inside trim bar, at the very top edge).
Triangle 14px tall now sits at top of trim bar (overlapping waveform
slightly but visible, with drop-shadow to make them stand out).
2026-04-30 13:57:09 +00:00
5d817c586b Edit: IN/OUT marker triangles + Postavi IN/OUT buttons
User feedback: Workflow is - click + Enter sets a marker triangle, then
button moves the red handle to that triangle. Triangle near LEFT handle
= IN candidate (green), near RIGHT = OUT candidate (red).

Visual:
- Green triangle (▼) above trim bar = IN candidate position
- Red triangle (▼) above trim bar = OUT candidate position
- White line (playhead) = current video position (moves during playback)
- Red handles (existing) = actual clip start/end

Workflow:
1. Click on waveform → white playhead jumps there
2. Press Enter → playhead starts moving (plays)
   ALSO: triangle gets placed at current position
   - If position closer to LEFT handle → green IN triangle
   - If position closer to RIGHT handle → red OUT triangle
3. Listen, decide 'this is the right spot'
4. Click ▼ Postavi IN button → red LEFT handle jumps to green triangle
   (or ▼ Postavi OUT for right handle)
5. Now red handle and triangle are aligned = clip boundary committed

Triangles persist until next play press (= next candidate).
Buttons styled with matching color (green for IN, red for OUT).
2026-04-30 13:42:30 +00:00
4376f7529f Click = seek only (no auto-play); Enter = play/pause from position
User feedback: 'naj ne začne takoj predvajati. naj začne ko pritisnem
Enter, in pozicija naj ostane črta ker bomo tja dali tracker'

Changes:
- Click on waveform: just seek + render playhead (was: seek + auto-play)
- Click on segment row: just seek + render playhead (was: seek + auto-play)
- Playhead: brighter, with triangle marker on top (tracker placeholder)
- Enter key: play/pause toggle from current position
- Space key: also play/pause (back-compat)
- Hint texts updated to reflect new workflow

Workflow now:
1. Click on waveform/segment → playhead jumps there (no sound)
2. Read transcript, look at waveform around the position
3. Press Enter → plays from there
4. Press Enter again → pauses
5. Click somewhere else → playhead moves there (paused)
6. Press Enter → plays from new position

Allows precise positioning before commit to playback.
2026-04-30 13:02:28 +00:00
1d6af29a23 Parallel workers (3) + pre-cache Edit assets
User feedback: 'ne morem nič drugega delat dokler izvaža reel?
a če bi bile večje mašine bi blo bolj?'

Without GPU upgrade, optimize CPU usage:

1. PARALLEL WORKERS:
   - Was: 1 worker thread, processes 1 job at a time
   - Now: NUM_WORKERS=3 parallel threads (configurable via env)
   - Each worker locks its job atomically (set instead of single var)
   - 3 reels render simultaneously instead of sequentially
   - Edit feature usable while other reels render

2. PRE-CACHE EDIT ASSETS:
   - On job done, fire-and-forget ffmpeg subprocess.Popen for:
     * low-q source video (480p) — used in Edit modal video player
     * waveform PNG (2400x72) — used in Edit modal trim bar
   - Both run in background, don't block pipeline
   - When user later clicks Edit, assets already cached → modal instant
   - On-demand fallback still works if precache failed

Result: Edit modal opens instantly even while other reels render.
3 reels can render in parallel = ~3x throughput on multi-core CPU.
2026-04-30 12:55:38 +00:00
47a114ce6a Edit modal: zoom + play-from-position + Space toggle
User feedback:
1. 'Wave form je premajhen — zoom'
2. 'Ko nastavimo pozicijo, play od začetka — ne moremo predvajat od tam'

NEW Zoom feature:
- 5 zoom levels: 1x, 2x, 5x, 10x, 20x
- Trim bar wrapped in scrollable container
- On zoom: bar width grows to 100*N%, scroll auto-centers on trim region
- Higher zoom = more pixels per second = micro-tuning possible
  (1x: 5px/s, 20x: 100px/s for 4min song)
- Active zoom button highlighted accent red

NEW Play-from-position:
- Click on waveform/trim bar = playhead JUMPS THERE + auto-plays
  (was: just moved playhead, no play)
- Space key = play/pause toggle from current position
  (works anywhere except in input fields)
- '▶ Predvajaj odsek' still does start-to-end of selection
- Cleanup keydown listener on modal close

Waveform now rendered at 2400x72 (higher res) so zoom looks crisp.

User can now:
- Zoom 10x to see exact word boundaries in waveform
- Click anywhere → instant play from there
- Hit Space to toggle while watching
2026-04-30 12:37:06 +00:00
facfd6bd39 Edit modal: waveform + napisi desno + live highlight
User feedback: 'če bi imeli spodaj wave strukturo bi se po tem prmikali,
in narediti da teksti laufajo na desni strani ob robu videja'

NEW: backend /api/waveform/{id}?width&height
- ffmpeg showwavespic generates PNG (~10-50KB)
- Cached forever per song
- Red color (#ff6b6b) matching accent

Frontend layout RESTRUCTURED:
- 1200px max-width (was 900px)
- Top section: GRID 1fr / 320px
  - LEFT: video (16:9)
  - RIGHT: napisi panel (sticky header, scrollable, 55vh max)
- Bottom: trim bar full-width with WAVEFORM as background image
- Hint text updated: 'Klik na valove ali napise = skoči video'

INTERACTIONS:
- Click segment row → seekToSegment() jumps video to that timestamp
- Live highlight: gold (#ffd700) on currently playing segment
- Auto-scroll panel to keep active segment in view
- Drag handles updates segment row colors (in-clip = red bg, outside = gray)
- Click on trim bar (waveform) still works as seek

User can now:
- See visual audio shape (loud parts = vocals, quiet = instrumental)
- See ALL napisi at once on the right
- Click any napis to jump to it
- Watch live highlight follow the song
- Edit any napis text inline
2026-04-30 12:25:08 +00:00
c94e6214ca Edit: instant client-side preview with low-q source
User feedback: 'predvaja odsek in začne iz nule kar ni ok, ne moremo
premikati levo dolj levo... za to bi rabili low-q?'

REPLACED render-on-demand approach with low-q source download:

1. Backend: GET /api/source-video/{id}?quality=low
   - 480p re-encode of full source (cached after first request)
   - veryfast preset, CRF 28
   - First request: ~5-10s (depends on song length)
   - Subsequent: instant (cached)

2. Frontend: Edit modal loads ?quality=low
   - 'Pripravljam predogled (~5s prvič, potem instant)' status
   - Once loaded: ALL preview is client-side instant
   - 'Predvajaj odsek' jumps to trimStart and plays
   - Auto-stop at trimEnd (loops back)
   - Drag handles DURING playback = instant seek (browser scrubs in 5MB)
   - Drag NOT blocked during play (you can fine-tune in/out live)

3. Removed old /api/preview-clip endpoint logic (no longer needed)
   Note: kept the route as cache cleanup for old jobs

Workflow now:
- Open Edit → 5s wait first time
- Drag handles freely (instant scrubbing)
- Click Predvajaj → starts at trimStart immediately
- Drag handles WHILE playing → live preview
- Save when satisfied → 70s full render
2026-04-30 12:14:37 +00:00
63da3ad2e2 Preview-clip: validate cache, support HEAD, cleanup on fail
Bugs from puppeteer inspection:
1. Old buggy renders left 0-byte cache files behind. New code never
   re-rendered because cache_path.exists() was True.
   Fix: validate cache file is >1KB, otherwise re-render.

2. FastAPI @app.get only handles GET, not HEAD. Frontend's HEAD check
   returned 405, then GET re-rendered (correct), but second click also
   returned 405 then 200 again — confusing.
   Fix: use @app.api_route with methods=['GET', 'HEAD']

3. If ffmpeg fails partway, broken file remains in cache.
   Fix: unlink on any failure path.

Also deleted existing empty cache files in container.
2026-04-30 12:05:11 +00:00
69062205fd Fix preview-clip ffmpeg: force even dimensions for libx264
Bug: 'width not divisible by 2 (853x480)' from screenshot.
libx264 requires even width/height. scale=854:480 + decrease can result
in 853x480 (odd width).

Fix: chain second scale filter that truncates to nearest even number:
  scale=trunc(iw/2)*2:trunc(ih/2)*2

Verified locally: 4.4MB clip in 4.8s on CPU.
2026-04-30 12:01:06 +00:00