source: Klonkt/src/config/paths.js@ 6c4ff7e

main
Last change on this file since 6c4ff7e was 6c4ff7e, checked in by Robin <roboburr@…>, 4 weeks ago

Audio verhuisde niet mee: archiefformaat v2

Robin verhuisde naar soundfabrics.nl en meldde: de berichten zijn er, de
plaatjes zijn er, een deel van de audio staat er maar speelt niet af, en
daarnaast mist er veel. Drie losse oorzaken, alle drie echt.

Gemeten op de twee live instanties:

berichten 36 -> 36
tracks 140 -> 14, waarvan 13 stuk
playlists 11 -> 0

DE EXPORTER LAS storage_path, DE SPELER NIET. routes/audio.js resolvet met
AUDIO_DIR plus bestandsnaam en kijkt niet naar media.storage_path. Op
sound-fabrics.com wees storage_path voor 124 van de 139 audiorijen nog naar
/srv/prutfolio/storage/audio, van voor de dataverhuizing. De site speelde
gewoon door; de exporter vond niets, markeerde availability: missing, en liet
124 nummers stil achter. Vanuit de exporter gezien was het bestand er echt niet,
dus er kwam ook geen klacht. Nu is er een resolveAudioPath die zoekt zoals de
speler zoekt.

DE IMPORTER MAAKTE EEN TRACK ZONDER BESTAND. Bij gewone bijlagen werd
availability: missing netjes gecontroleerd, in de audio-tak eronder niet. Die
zette een media-rij met size 0 en een audio_tracks-rij neer voor bytes die nooit
zijn weggeschreven. Dat is erger dan ontbreken: het ziet eruit alsof de
verhuizing gelukt is, dus je zegt je oude instantie op. Nu geldt er een harde
regel: geen bestand, geen track.

AUDIO PASTE NIET IN HET FORMAAT. Gehoste audio staat buiten MEDIA_ROOT, achter
een eigen gated route, en het archief droeg alleen bestanden onder media/. De
exporter rekende er met path.relative een /media/../audio/x.mp3 van, en
veiligMediaPad weigert dat pad terecht. Audio kon dus principieel niet
aankomen. Daarbovenop ging alleen mee wat met [[track:]] in een bericht stond,
14 van de 140, en playlists helemaal niet.

De ophaalknop leerde audio erbij. Dat vroeg meer dan verwacht: de bron toont
over AP alleen fedi_open-nummers, en de bestanden zitten achter dezelfde poort.
Beide staan nu open voor precies de doel-actor van de Move, en de ingest haalt
de bytes ONDERTEKEND op. De eerste poging gaf drie keer "nummer niet opgehaald",
want haalBijlage deed een kale fetch en kreeg een 403 van onze eigen poort.

Bewezen op twee draaiende instanties, met een track die opzettelijk een
verouderd pad had. Zip: 3/3 included, playlist-volgorde intact, bestanden op
schijf met de goede grootte, /audio/stream geeft 200. Ophaalknop: 5 berichten
plus 3 tracks met echte bytes, ook de gesloten nummers.

Changed files:
src/config/paths.js

  • AUDIO_ROOT en resolveAudioPath(): een audiobestand zoeken zoals de speler het zoekt, met het opgeslagen pad als terugval

src/services/ArchiveExportService.js

  • FORMAT_VERSION 2
  • audioBibliotheek(): de HELE tracklijst plus de playlists, met de bestanden in een eigen audio/-gebied
  • [[track:]] weg uit de media-tak; die kon nooit aankomen
  • audioOf() wijst naar het archiefpad in plaats van naar een bijlage

src/services/ArchiveImportService.js

  • tracksTerug() en playlistsTerug(); de per-post audio-tak is weg
  • bestanden naar AUDIO_ROOT, en geen rij zonder bestand
  • een playlist verwijst nooit naar een nummer dat niet aankwam
  • nieuwe rapportvelden tracks / tracksMissing / playlists

src/services/MigrationService.js

  • de ingest loopt de tracks-collectie af en haalt de bytes ondertekend op
  • haalBijlage kreeg submap en headers

src/services/ActivityPubService.js

  • signedGetHeaders() losgetrokken uit signedGetJson, want een verhuizing moet ook bytes kunnen ophalen

src/services/music/index.js

  • siteOpenTracks({ alles }): de doel-actor ziet ook de gesloten nummers

src/routes/audio.js

  • isMoveTargetAudio(): een ondertekend verzoek van de doel-actor mag alle audio ophalen, ook wat niet fedi_open is

src/routes/activitypub.js

  • magAlles(): een plek voor de vraag "mag deze aanvrager alles zien"

src/routes/admin-migrate.js

  • AUDIO_ROOT en signedGetHeaders doorgegeven aan de ingest

test/archive-export.test.js

  • de audio-testen naar het v2-contract, plus bibliotheek en ontbrekend
  • resolveAudioPath getoetst op VOLGORDE met een nep-fs

New file:
test/archive-audio.test.js

  • de hele rondgang met audio, inclusief het verouderde pad, de playlist- volgorde en de droogloop

remarks: soundfabrics.nl staat nu met 13 lege tracks; die verdwijnen niet
vanzelf bij een nieuwe import. En een archief van voor deze commit is v1 en
bevat de audio niet, dus opnieuw exporteren is nodig. Suite 985 groen;
tegenproef gedaan op de resolver en op de geen-bestand-geen-track-regel.

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

  • Property mode set to 100644
File size: 2.8 KB
Line 
1/**
2 * Where user data lives on disk.
3 *
4 * Every media subdirectory derives from MEDIA_PATH, so one setting moves the
5 * whole media tree out of the checkout. That matters because the deploy checks
6 * out the work-tree: anything the app writes next to its own code can be wiped
7 * by a cleanup on the next deploy.
8 *
9 * Read at import time, like the rest of the config. `dotenv/config` is the first
10 * import in server.js, so the environment is already populated by the time this
11 * module is evaluated.
12 */
13import path from 'path';
14import { fileURLToPath } from 'url';
15
16const __dirname = path.dirname(fileURLToPath(import.meta.url));
17
18/** Root for public, user-uploaded media (served by the /media handler). */
19export const MEDIA_ROOT = path.resolve(
20 process.env.MEDIA_PATH || path.join(__dirname, '..', '..', 'storage', 'media')
21);
22
23/**
24 * Resolve one media subdirectory.
25 *
26 * A per-subdirectory variable still wins, so installs that already pin an
27 * individual path keep working. Only the fallback changed: it now follows
28 * MEDIA_ROOT instead of pointing back into the checkout.
29 *
30 * @param {string} envVar per-subdirectory override, e.g. 'AVATAR_PATH'
31 * @param {string} sub subdirectory name under MEDIA_ROOT, e.g. 'avatars'
32 */
33export function mediaDir(envVar, sub) {
34 return path.resolve(process.env[envVar] || path.join(MEDIA_ROOT, sub));
35}
36
37/**
38 * Waar de gehoste audio staat.
39 *
40 * BEWUST BUITEN MEDIA_ROOT: de publieke /media-handler mag er niet bij, elke
41 * fetch loopt via de gated route in routes/audio.js. Diezelfde route resolvet
42 * met AUDIO_DIR + bestandsnaam, en negeert media.storage_path volledig.
43 */
44export const AUDIO_ROOT = path.resolve(
45 process.env.AUDIO_PATH || path.join(__dirname, '..', '..', 'storage', 'audio'),
46);
47
48/**
49 * Het echte pad van een audiobestand, op DEZELFDE manier als de speler het zoekt.
50 *
51 * Dit bestaat omdat die twee uit elkaar liepen en dat een verhuizing sloopte.
52 * Op sound-fabrics.com wees media.storage_path voor 124 van de 139 tracks nog
53 * naar /srv/prutfolio/storage/audio (van voor de dataverhuizing), terwijl de
54 * bestanden allang op ~/data/prutfolio/audio stonden. De site merkte er niets
55 * van, want de speler kijkt alleen naar de bestandsnaam. De exporter las wel
56 * storage_path, vond niets, en liet 124 nummers stil achter.
57 *
58 * Volgorde: eerst zoals de speler kijkt (bestandsnaam in AUDIO_ROOT), dan pas
59 * het opgeslagen pad. Zo klopt de export met wat de gebruiker hoort, en niet
60 * met wat de database ooit dacht.
61 *
62 * @returns {string|null} een bestaand pad, of null
63 */
64export function resolveAudioPath(storagePath, fs) {
65 const s = String(storagePath || '');
66 if (!s) return null;
67 const kandidaten = [path.join(AUDIO_ROOT, path.basename(s)), path.resolve(s)];
68 for (const p of kandidaten) {
69 try { if (fs.statSync(p).isFile()) return p; } catch { /* volgende kandidaat */ }
70 }
71 return null;
72}
Note: See TracBrowser for help on using the repository browser.