Videos nach Chatreihenfolge statt nach Zeitstempel ordnen
WhatsApp stempelt beim Synchronisieren gelegentlich alte Nachrichten neu, im echten Export mehrere Nachrichten am 19.08.26 um 13:17. Die Position im Chat bildet die tatsächliche Reihenfolge verlässlich ab. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
ce84e1103d
commit
6f583c8f5b
2 files changed
+30
-10
No files matched your search
+11
-7
@@ -111,12 +111,16 @@ export function timestampFromFileName(name) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Die Chatreihenfolge ist verlässlicher als die Zeitstempel: WhatsApp stempelt
|
||||||
|
// beim Synchronisieren gelegentlich alte Nachrichten mit einer neuen Zeit.
|
||||||
function compareClips(a, b) {
|
function compareClips(a, b) {
|
||||||
return (
|
return a.order - b.order || a.file.localeCompare(b.file);
|
||||||
a.timestamp.localeCompare(b.timestamp) ||
|
}
|
||||||
a.order - b.order ||
|
|
||||||
a.file.localeCompare(b.file)
|
// Videos ohne Chatzeile werden per Zeitstempel zwischen die Nachrichten einsortiert.
|
||||||
);
|
function orderForTimestamp(messages, timestamp) {
|
||||||
|
const next = messages.find((message) => message.timestamp > timestamp);
|
||||||
|
return next ? next.index - 0.5 : messages.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doppelt gepostete Videos sind bytegleich; die Dateigröße genügt als Merkmal.
|
// Doppelt gepostete Videos sind bytegleich; die Dateigröße genügt als Merkmal.
|
||||||
@@ -131,7 +135,7 @@ function markDuplicates(clips) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// files: [{ name, size }] aus dem Export (Pfade erlaubt).
|
// files: [{ name, size }] aus dem Export (Pfade erlaubt).
|
||||||
// Liefert die Videos chronologisch, dazu im Chat genannte, aber fehlende Dateien.
|
// Liefert die Videos in Chatreihenfolge, dazu im Chat genannte, aber fehlende Dateien.
|
||||||
export function collectClips(messages, files) {
|
export function collectClips(messages, files) {
|
||||||
const videos = new Map();
|
const videos = new Map();
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
@@ -176,7 +180,7 @@ export function collectClips(messages, files) {
|
|||||||
timestamp,
|
timestamp,
|
||||||
sender: null,
|
sender: null,
|
||||||
caption: '',
|
caption: '',
|
||||||
order: Number.MAX_SAFE_INTEGER,
|
order: orderForTimestamp(messages, timestamp),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-3
@@ -16,7 +16,7 @@ const IOS_CHAT = [
|
|||||||
'[21.09.26, 10:16:00] Ben Muster: Stark!',
|
'[21.09.26, 10:16:00] Ben Muster: Stark!',
|
||||||
'Zweite Zeile der Nachricht',
|
'Zweite Zeile der Nachricht',
|
||||||
'\u200e[22.09.26, 08:00:00] ~\u202fCarla: \u200e<Anhang: 00000102-VIDEO-2026-09-22-08-00-00.mp4>',
|
'\u200e[22.09.26, 08:00:00] ~\u202fCarla: \u200e<Anhang: 00000102-VIDEO-2026-09-22-08-00-00.mp4>',
|
||||||
// Außer der Reihe: im Chat später, zeitlich aber früher
|
// Außer der Reihe: im Chat später, laut Zeitstempel aber früher
|
||||||
'\u200e[21.09.26, 07:30:00] Ben Muster: Frühaufsteher \u200e<Anhang: 00000103-VIDEO-2026-09-21-07-30-00.mp4>',
|
'\u200e[21.09.26, 07:30:00] Ben Muster: Frühaufsteher \u200e<Anhang: 00000103-VIDEO-2026-09-21-07-30-00.mp4>',
|
||||||
'\u200e[22.09.26, 08:00:05] ~\u202fCarla: \u200e<Anhang: 00000104-PHOTO-2026-09-22-08-00-05.jpg>',
|
'\u200e[22.09.26, 08:00:05] ~\u202fCarla: \u200e<Anhang: 00000104-PHOTO-2026-09-22-08-00-05.jpg>',
|
||||||
'[22.09.26, 09:00:00] Ben Muster: \u200eDiese Nachricht wurde gelöscht.',
|
'[22.09.26, 09:00:00] Ben Muster: \u200eDiese Nachricht wurde gelöscht.',
|
||||||
@@ -50,20 +50,36 @@ test('iPhone-Export: Nachrichten, Absender, Zeitstempel und Anhänge', () => {
|
|||||||
assert.deepEqual(messages[6].attachments, []);
|
assert.deepEqual(messages[6].attachments, []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Videos werden nach Zeitstempel sortiert, nicht nach Chatreihenfolge', () => {
|
test('Videos folgen der Chatreihenfolge, Videos ohne Chatzeile dem Zeitstempel', () => {
|
||||||
const { clips } = collectClips(parseChat(IOS_CHAT), IOS_FILES);
|
const { clips } = collectClips(parseChat(IOS_CHAT), IOS_FILES);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
clips.map((clip) => clip.file),
|
clips.map((clip) => clip.file),
|
||||||
[
|
[
|
||||||
'00000103-VIDEO-2026-09-21-07-30-00.mp4',
|
|
||||||
'00000101-VIDEO-2026-09-21-10-15-30.mp4',
|
'00000101-VIDEO-2026-09-21-10-15-30.mp4',
|
||||||
'00000102-VIDEO-2026-09-22-08-00-00.mp4',
|
'00000102-VIDEO-2026-09-22-08-00-00.mp4',
|
||||||
|
'00000103-VIDEO-2026-09-21-07-30-00.mp4',
|
||||||
'00000107-VIDEO-2026-09-22-15-00-00.mp4',
|
'00000107-VIDEO-2026-09-22-15-00-00.mp4',
|
||||||
'00000106-VIDEO-2026-09-28-12-00-00.mp4',
|
'00000106-VIDEO-2026-09-28-12-00-00.mp4',
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Ein falsch nachgestempeltes Video bleibt an seinem Platz im Chat', () => {
|
||||||
|
// Wie im echten Export: WhatsApp hat das mittlere Video auf den 19. umgestempelt.
|
||||||
|
const chat = [
|
||||||
|
'\u200e[17.08.26, 09:48:18] Anna: Eins \u200e<Anhang: 00001338-VIDEO-2026-08-17-09-48-18.mp4>',
|
||||||
|
'\u200e[19.08.26, 13:17:37] Ben: Zwei \u200e<Anhang: 00001339-VIDEO-2026-08-19-13-17-37.mp4>',
|
||||||
|
'\u200e[17.08.26, 09:52:15] Carla: Drei \u200e<Anhang: 00001340-VIDEO-2026-08-17-09-52-15.mp4>',
|
||||||
|
].join('\n');
|
||||||
|
const files = [
|
||||||
|
{ name: '00001338-VIDEO-2026-08-17-09-48-18.mp4', size: 1 },
|
||||||
|
{ name: '00001339-VIDEO-2026-08-19-13-17-37.mp4', size: 2 },
|
||||||
|
{ name: '00001340-VIDEO-2026-08-17-09-52-15.mp4', size: 3 },
|
||||||
|
];
|
||||||
|
const { clips } = collectClips(parseChat(chat), files);
|
||||||
|
assert.deepEqual(clips.map((clip) => clip.caption), ['Eins', 'Zwei', 'Drei']);
|
||||||
|
});
|
||||||
|
|
||||||
test('Clips tragen Absender, Beschriftung und Pfad', () => {
|
test('Clips tragen Absender, Beschriftung und Pfad', () => {
|
||||||
const { clips } = collectClips(parseChat(IOS_CHAT), IOS_FILES);
|
const { clips } = collectClips(parseChat(IOS_CHAT), IOS_FILES);
|
||||||
const clip = clips.find((c) => c.file.startsWith('00000101'));
|
const clip = clips.find((c) => c.file.startsWith('00000101'));
|
||||||
|
|||||||
Reference in new issue
Block a user