Ignore:
Timestamp:
08/06/2026 02:20:19 PM (5 weeks ago)
Author:
roboburr <roboburr@…>
Branches:
main
Children:
7aa3140
Parents:
58cfe5f
git-author:
Robin <roboburr@…> (08/06/2026 02:20:09 PM)
git-committer:
roboburr <roboburr@…> (08/06/2026 02:20:19 PM)
Message:

Elke bijlage zegt waar hij voor was (shaer-pmr, vervolg)

Gevonden bij een oefenherstel: ik vergeleek dertien kolommen, noemde het een
schone rondgang, en vergeleek daarna ALLE kolommen. Toen bleken er drie dingen
weg te vallen.

COVER de bytes zaten in het archief, maar nergens stond dat ze de

cover waren. cover_image_url kwam als null terug, dus de post
stond zonder cover.

C2S-BIJLAGE idem: het audiobestand en zijn poster zaten erin, maar

c2s_attachments werd niet hersteld, dus de speler verdween.

SUB-SECONDE de import zette tijdstempels om naar SQL-notatie en verloor de

milliseconden. Twee posts in dezelfde seconde staan dan in
willekeurige volgorde.

Elke bijlage draagt nu een shaer:role -- cover, coverVideo, inline, c2s, poster,
track -- en een poster wijst met shaer:posterFor naar de bijlage waar hij bij
hoort. De importer bouwt cover_image_url, cover_video_url en c2s_attachments
daaruit terug. Een rol is geen versiering: zonder rol heeft het archief wel de
bytes maar niet het feit dat ze de cover WAREN, en dat zie je pas als je alle
kolommen vergelijkt.

Tijdstempels gaan nu ongewijzigd de database in. Klonkt schrijft zelf in twee
spellingen; het archief normaliseert naar ISO. Het MOMENT overleeft de rondgang,
de spelling niet -- nu ook zo gedocumenteerd in plaats van stilzwijgend.

Rondgang op echte beta-data: geen verschil meer over 29 kolommen, met de tijden
op moment vergeleken in plaats van op tekst. 21 tests. Suite 514/514.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/services/ArchiveExportService.js

    r58cfe5f r812fea9  
    8181  const uit = [];
    8282  const zie = new Set();
    83   const voegToe = (url, name) => {
     83  const voegToe = (url, name, rol, extra = {}) => {
    8484    const u = String(url || '').trim();
    8585    if (!u || zie.has(u)) return;
    8686    zie.add(u);
    87     uit.push({ url: u, name: name || null });
     87    uit.push({ url: u, name: name || null, rol, ...extra });
    8888  };
    89   voegToe(post.cover_image_url, post.cover_alt);
    90   voegToe(post.cover_video_url, post.cover_alt);
    91   for (const m of String(post.content || '').matchAll(/<img[^>]+src=["']([^"']+)["'][^>]*>/gi)) voegToe(m[1]);
     89  // De ROL is niet decoratief. Zonder rol staat er in het archief wel een
     90  // bestand, maar niet dat het de cover was of bij de speler hoorde -- en dan
     91  // komt de post na een herstel zonder cover en zonder speler terug. Gevonden
     92  // door bij de oefenherstel ALLE kolommen te vergelijken in plaats van een
     93  // handjevol.
     94  voegToe(post.cover_image_url, post.cover_alt, 'cover');
     95  voegToe(post.cover_video_url, post.cover_alt, 'coverVideo');
     96  for (const m of String(post.content || '').matchAll(/<img[^>]+src=["']([^"']+)["'][^>]*>/gi)) voegToe(m[1], null, 'inline');
    9297  try {
    9398    for (const a of JSON.parse(post.c2s_attachments || '[]')) {
    94       voegToe(a && a.url, a && a.name);
     99      voegToe(a && a.url, a && a.name, 'c2s');
    95100      // Een audio-bijlage draagt een poster (de omslag die de speler toont). Die
    96101      // staat in een eigen veld en zou anders stil wegvallen -- op beta viel dat
    97102      // pas op bij de export van echte data.
    98       voegToe(a && a.poster, a && a.name ? `${a.name} (poster)` : null);
     103      voegToe(a && a.poster, a && a.name ? `${a.name} (poster)` : null, 'poster', { posterFor: a && a.url });
    99104    }
    100105  } catch { /* kapotte kolom blokkeert de export niet */ }
     
    103108    try {
    104109      const t = db.prepare('SELECT t.title, m.storage_path FROM audio_tracks t LEFT JOIN media m ON m.id = t.media_id WHERE t.id = ?').get(m[1]);
    105       if (t && t.storage_path) voegToe(`/media/${path.relative(path.resolve(MEDIA_ROOT), path.resolve(t.storage_path))}`, t.title);
     110      if (t && t.storage_path) voegToe(`/media/${path.relative(path.resolve(MEDIA_ROOT), path.resolve(t.storage_path))}`, t.title, 'track');
    106111    } catch { /* geen audio-tabellen: niets te doen */ }
    107112  }
     
    251256          'shaer:originalUrl': /^https?:/i.test(ref.url) ? ref.url : `${origin}${ref.url}`,
    252257          'shaer:sha256': hash,
     258          'shaer:role': ref.rol,
     259          'shaer:posterFor': ref.posterFor || undefined,
    253260        });
    254261      } else {
     
    261268          type: as2TypeOf(mime), mediaType: mime, name: ref.name || undefined,
    262269          url: orig, 'shaer:availability': 'missing', 'shaer:originalUrl': orig,
     270          'shaer:role': ref.rol,
     271          'shaer:posterFor': ref.posterFor || undefined,
    263272        });
    264273      }
Note: See TracChangeset for help on using the changeset viewer.