Changeset 98acb97 in Klonkt for src/services/MigrationService.js


Ignore:
Timestamp:
08/14/2026 12:03:55 AM (4 weeks ago)
Author:
Robin <roboburr@…>
Branches:
main
Children:
eb071a7
Parents:
9529d7b
Message:

Hoezen en playlists reisden nog niet mee

Robin, na de vorige ronde: "de audio tracks hadden images, die zijn niet
meegegaan, de playlists zijn ook nog niet meegegaan via peer to peer."

Allebei terecht, en de eerste is dezelfde fout als bij de audio zelf, een laag
hoger. cover_url ging wel mee als STRING en het bestand niet. Dus kwam een
nummer aan met een verwijzing naar een plaatje dat er niet was: precies de
halve waarheid die deze hele reeks moest opruimen, en ik liep er zelf opnieuw
in.

De playlists gingen wel via de zip en niet via de ophaalknop. Die liep de
tracks-collectie af en de playlist-collectie niet, en dan heb je alle muziek
en geen enkele plaat. De volgorde staat nergens anders.

Wat er nog meer boven kwam bij het naspelen: over AP komt de duur als
ISO-8601 ("PT212S") binnen en de database wil seconden, en de artiest zit in
summary, niet in een artist-veld. Zonder die twee kwamen nummers naamloos en
zonder duur aan.

En de bronkant moest ook hier open: playlistOpenTracks filtert op fedi_open,
dus de doel-actor kreeg een plaat met gaten. Nu geldt daar dezelfde regel als
bij de outbox en de tracks.

Bewezen op twee draaiende instanties, 3 nummers met hoes plus een plaat in de
volgorde c-a-b:

peer to peer mp3 2503b, hoes 74b, artiest, duur, plaat C -> A -> B
zip idem, plus kind=album

Changed files:
src/services/ArchiveExportService.js

  • hoesToevoegen(): de BYTES van een hoes in het archief, voor tracks en playlists, met shaer:coverFile ernaast

src/services/ArchiveImportService.js

  • hoesTerug(): de hoes op schijf en cover_url daarheen; geen bestand betekent geen cover_url, liever niets dan een img die 404't

src/services/MigrationService.js

  • de ingest haalt de hoes op uit icon/image, ONDERTEKEND
  • de playlists erbij, met hun volgorde en een kaart bron-URI -> nieuw id
  • duurSeconden(): PT212S naar 212; artiest uit summary

src/services/music/index.js

  • playlistOpenTracks({ alles }) voor de doel-actor van een Move

src/routes/activitypub.js

  • de playlist-route geeft die doel-actor de volledige plaat

remarks: over AP komt kind (album/playlist) niet mee, want de AP-collectie
draagt dat veld niet; via de zip wel. Klein verschil, apart te repareren als het
hindert.

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

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/MigrationService.js

    r9529d7b r98acb97  
    219219const AFBEELDING = /^image\//i;
    220220
     221/** AS2 geeft de duur als ISO-8601 ("PT212S"), de database wil seconden. */
     222function duurSeconden(v) {
     223  if (v == null) return null;
     224  if (typeof v === 'number') return Math.round(v) || null;
     225  const m = /^P(?:.*?T)?(?:(\d+)H)?(?:(\d+)M)?(?:([\d.]+)S)?$/.exec(String(v));
     226  if (!m) { const n = Number(v); return Number.isFinite(n) && n > 0 ? Math.round(n) : null; }
     227  const sec = (Number(m[1]) || 0) * 3600 + (Number(m[2]) || 0) * 60 + (Number(m[3]) || 0);
     228  return sec > 0 ? Math.round(sec) : null;
     229}
     230
    221231/**
    222232 * De titel terugwinnen uit de content.
     
    336346  const rapport = {
    337347    bron: bronActor.id, posts: 0, overgeslagen: 0, media: 0, mediaMislukt: 0,
    338     blocks: 0, tracksBinnen: 0, tracksMislukt: 0, overgeslagenTracks: 0, waarschuwingen: [],
     348    blocks: 0, tracksBinnen: 0, tracksMislukt: 0, overgeslagenTracks: 0,
     349    playlistsBinnen: 0, playlistsMislukt: 0, waarschuwingen: [],
    339350  };
    340351
     
    459470    // geldt voor de bestanden zelf, die anders achter de gated audio-route
    460471    // blijven.
     472    const trackKaart = new Map();   // bron-URI van een nummer -> ons nieuwe id
    461473    const streams = [].concat(bronActor.streams || []).filter((u) => typeof u === 'string');
    462474    const tracksUrl = streams.find((u) => /\/tracks\/?$/.test(u));
     
    480492          continue;                       // dezelfde regel als bij de zip: geen bestand, geen track
    481493        }
     494        // De hoes. Die reisde als URL wel mee en als bestand niet, dus kwam een
     495        // nummer aan met een verwijzing naar een plaatje dat er niet is.
     496        let hoes = null;
     497        const hoesUrl = (a.icon && (a.icon.url || a.icon)) || (a.image && (a.image.url || a.image)) || null;
     498        if (hoesUrl && /^https?:\/\//i.test(String(hoesUrl))) {
     499          const h = await haalBijlage(String(hoesUrl), {
     500            safeFetch, mediaRoot, fs, path, maxBytes,
     501            headers: signHeaders ? signHeaders(site.slug, String(hoesUrl), '*/*') : null,
     502          }).catch(() => null);
     503          if (h) { hoes = h.url; rapport.media++; }
     504          else rapport.waarschuwingen.push(`hoes niet opgehaald: ${a.name || hoesUrl}`);
     505        }
    482506        const trackId = crypto.randomUUID();
    483507        const mediaId = crypto.randomUUID();
     
    485509          db.prepare('INSERT INTO media (id, site_id, filename, mime_type, size, storage_path) VALUES (?,?,?,?,?,?)')
    486510            .run(mediaId, site.id, g.filename, g.mediaType, g.size, g.storage_path);
    487           db.prepare(`INSERT INTO audio_tracks (id, site_id, title, artist, album, duration, media_id, fedi_open)
    488                       VALUES (?,?,?,?,?,?,?,0)`)
    489             .run(trackId, site.id, a.name || 'zonder titel', a.artist || null, a.album || null,
    490               Number(a.duration) || null, mediaId);
     511          db.prepare(`INSERT INTO audio_tracks (id, site_id, title, artist, album, duration, media_id, cover_url, fedi_open)
     512                      VALUES (?,?,?,?,?,?,?,?,0)`)
     513            .run(trackId, site.id, a.name || 'zonder titel', a.summary || a.artist || null, a.album || null,
     514              duurSeconden(a.duration), mediaId, hoes);
    491515          recordMigrated(site.slug, { origin: a.id, target: `${me}/ap/tracks/${trackId}`, sourceActor: bronActor.id, isPublic: false });
     516          trackKaart.set(String(a.id), trackId);
    492517          rapport.tracksBinnen++;
    493518        } catch (e) {
     
    499524      rapport.waarschuwingen.push('muziekbibliotheek overgeslagen: geen audiomap meegegeven');
    500525    }
     526
     527    // ── De playlists ──────────────────────────────────────────────
     528    //
     529    // Los van de nummers, want de VOLGORDE is de playlist. Die staat nergens
     530    // anders: haal je alleen de tracks op, dan heb je wel alle muziek en geen
     531    // enkele plaat. De bron geeft ons de volledige lijst omdat we de doel-actor
     532    // zijn; anders zaten er alleen de opengezette nummers in en kreeg je een
     533    // plaat met gaten.
     534    const plUrl = streams.find((u) => /\/playlists\/?$/.test(u));
     535    if (plUrl && trackKaart.size) {
     536      const coll = await getJson(site.slug, plUrl);
     537      const lijst = (coll && (coll.orderedItems || coll.items)) || [];
     538      for (const p of (Array.isArray(lijst) ? lijst : []).slice(0, 200)) {
     539        const uri = typeof p === 'string' ? p : (p && p.id);
     540        if (!uri) continue;
     541        const plc = typeof p === 'object' && (p.orderedItems || p.items) ? p : await getJson(site.slug, uri);
     542        if (!plc) { rapport.playlistsMislukt++; continue; }
     543        const nummers = (plc.orderedItems || plc.items || [])
     544          .map((x) => (x && typeof x === 'object' ? x.id : x))
     545          .map((id) => trackKaart.get(String(id)))
     546          .filter(Boolean);
     547        if (!nummers.length) {
     548          rapport.waarschuwingen.push(`playlist ${plc.name || uri}: geen van de nummers is aangekomen, overgeslagen`);
     549          continue;
     550        }
     551        const plId = crypto.randomUUID();
     552        try {
     553          db.prepare('INSERT INTO playlists (id, site_id, title, artist, year, kind) VALUES (?,?,?,?,?,?)')
     554            .run(plId, site.id, plc.name || 'zonder titel', plc.attributedTo && plc.artist || plc.artist || null,
     555              plc.year || null, plc['shaer:kind'] || null);
     556          const ins = db.prepare('INSERT OR IGNORE INTO playlist_tracks (playlist_id, track_id, position) VALUES (?,?,?)');
     557          nummers.forEach((tid, i) => ins.run(plId, tid, i));
     558          rapport.playlistsBinnen++;
     559          const kwijt = (plc.orderedItems || plc.items || []).length - nummers.length;
     560          if (kwijt > 0) rapport.waarschuwingen.push(`playlist ${plc.name || uri}: ${kwijt} nummer(s) ontbraken en zijn eruit gelaten`);
     561        } catch (e) {
     562          rapport.playlistsMislukt++;
     563          rapport.waarschuwingen.push(`playlist niet opgeslagen: ${plc.name || uri} (${e && e.message})`);
     564        }
     565      }
     566    }
    501567  } catch (e) {
    502568    // 9-bij-mislukking: de vlag blijft OPEN staan. Derden blijven dan kijken,
Note: See TracChangeset for help on using the changeset viewer.