Fotoposts von TikTok kommen jetzt an -- das war die Ursache

GEFUNDEN HAT ES DAS PROTOKOLL, das eine halbe Stunde vorher dazukam.
Filipes Versuche standen Minuten spaeter als `video_gescheitert`
da, mit Grund:

  highlight .../@dogfather0804/photo/7693113561568136470 -- 400
  highlight https://vm.tiktok.com/ZN8kdxP9N/             -- 400

Am echten Konto nachgemessen:

  .../photo/7693113561568136470   -> 400 "Something went wrong"
  .../video/7693113561568136470   -> 200, mit Titel
  vm.tiktok.com/ZN8kdxP9N/        -> leitet auf die /photo/-Adresse

TikToks Auskunft kennt FOTOPOSTS (Slideshows) nicht -- dieselbe
Nummer unter /video/ aber schon. Filipe postet seit kurzem solche
Beitraege. Deshalb "es ging doch immer bis jetzt": Es lag weder an
einer Auslieferung noch am Haus, sondern an der Art des Posts. Der
Server war die ganze Zeit unveraendert (ein Neustart seit 04.10.),
und genau das hat die Suche in die falsche Richtung geschickt.

Jetzt wird bei einem Fehlschlag ein zweites Mal gefragt: erst den
Kurzlink aufloesen -- er verraet von aussen nicht, was dahinter
steckt --, dann /photo/ auf /video/ drehen. Aufgeloest wird nur ueber
den Ort-Kopf (`redirect: "manual"`), die Seite selbst wird nie
geholt; sie ist ein Megabyte gross und enthaelt nichts, was wir
brauchen.

NUR BEIM FEHLSCHLAG: Ein gewoehnliches Video kostet keine einzige
zusaetzliche Anfrage. Gemessen: Video 204 ms wie bisher, Fotopost
676 ms, Kurzlink auf Fotopost 827 ms -- alle drei mit Titel und
Vorschaubild.

Gegengemessen an genau den zwei Adressen, die bei ihm gescheitert
sind: beide liefern jetzt Titel und Bild. pruef-video 74,
pruef-teilen 27 -- 0 Fehler.

WAS ICH DARAUS MITNEHME: Ich habe heute stundenlang von aussen
ausgeschlossen, was es NICHT ist -- Adresse, Haus, Sitzung,
Manifest, Service Worker, Zwischenspeicher. Beantwortet hat die
Frage am Ende eine einzige Protokollzeile. Die haette am Anfang
stehen muessen, nicht nach der halben Suche. Dieselbe Lehre wie am
06.09. im Shop, und ich habe sie heute zweimal gebraucht.

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-10-05 12:47:42 +02:00
co-authored by Claude Opus 5
parent b42bbb1d88
commit 25cf6a0163
+70 -3
View File
@@ -140,14 +140,81 @@ async function holeMitFrist(adresse, kopf = {}) {
const OEMBED_BASIS = process.env.TIKTOK_OEMBED_BASIS || "https://www.tiktok.com/oembed";
/** Die Auskunft zu einem Video: Titel, Cover-Adresse, Account. */
export async function videoAuskunft(url) {
/** Wohin fuehrt ein Kurzlink wirklich -- ohne die Seite zu laden.
*
* `vm.tiktok.com/ZN8kdxP9N/` verraet von aussen nicht, ob dahinter
* ein Video oder ein Fotopost steckt. Nur die Weiterleitung sagt es.
* Gelesen wird deshalb NUR der Ort-Kopf (`redirect: "manual"`); die
* Seite selbst wird nie geholt -- sie ist ein Megabyte gross und
* enthaelt nichts, was wir brauchen. */
async function endgueltigeAdresse(url) {
let jetzt = String(url);
for (let i = 0; i < 5; i++) {
const abbruch = new AbortController();
const uhr = setTimeout(() => abbruch.abort(), FRIST_MS);
let a;
try {
a = await fetch(jetzt, { method: "GET", redirect: "manual", signal: abbruch.signal });
} catch { return jetzt; } finally { clearTimeout(uhr); }
/* Den Koerper wegwerfen, nicht lesen -- sonst bleibt die
Verbindung offen und wir laden die ganze Seite umsonst. */
try { await a.body?.cancel(); } catch { /* egal */ }
const ort = a.headers.get("location");
if (!ort) return jetzt;
try { jetzt = new URL(ort, jetzt).toString(); } catch { return jetzt; }
}
return jetzt;
}
/** Ein Fotopost unter derselben Nummer als Video angefragt.
*
* WARUM DAS NOETIG IST (05.10.2026, gemessen am echten Konto):
*
* .../photo/7693113561568136470 -> 400 "Something went wrong"
* .../video/7693113561568136470 -> 200 mit Titel
*
* TikToks Auskunft kennt Fotoposts (Slideshows) nicht, dieselbe
* Nummer unter /video/ aber schon. Filipe postet seit kurzem solche
* Beitraege -- deshalb "es ging doch immer bis jetzt": Es lag weder
* an einer Auslieferung noch am Haus, sondern an der Art des Posts.
*
* Gibt es nichts zu drehen, kommt `null` zurueck -- dann bleibt es
* bei der ersten Antwort und ihrer Fehlermeldung. */
function alsVideoFassung(url) {
try {
const u = new URL(String(url));
if (!/\/photo\/\d+/.test(u.pathname)) return null;
u.pathname = u.pathname.replace("/photo/", "/video/");
return u.toString();
} catch { return null; }
}
async function auskunftHolen(url) {
const a = await holeMitFrist(
`${OEMBED_BASIS}?url=${encodeURIComponent(url)}`,
{ accept: "application/json" });
if (!a.ok) return { fehler: `TikTok antwortet nicht (${a.status}).` };
if (!a.ok) return { status: a.status, daten: null };
let j = null;
try { j = JSON.parse(a.text()); } catch { j = null; }
if (!j || !j.title) return { fehler: "TikTok hat nichts zu diesem Video geschickt." };
return { status: a.status, daten: j };
}
export async function videoAuskunft(url) {
let versuch = await auskunftHolen(url);
/* ZWEITER ANLAUF FUER FOTOPOSTS. Nur wenn der erste scheitert --
bei einem gewoehnlichen Video kostet das keine einzige zusaetzliche
Anfrage. Erst den Kurzlink aufloesen (er verraet von aussen nicht,
was dahinter steckt), dann /photo/ auf /video/ drehen. */
if (!versuch.daten) {
const echt = await endgueltigeAdresse(url);
const gedreht = alsVideoFassung(echt);
if (gedreht) versuch = await auskunftHolen(gedreht);
}
const j = versuch.daten;
if (!j) return { fehler: `TikTok antwortet nicht (${versuch.status}).` };
if (!j.title) return { fehler: "TikTok hat nichts zu diesem Video geschickt." };
/* Der Account steht in `author_url` (…/@name). `author_name` ist der
ANZEIGENAME und aendert sich, wenn jemand ihn umstellt -- als
Schluessel waere er die falsche Wahl. */