diff --git a/server/pruef-highlights.mjs b/server/pruef-highlights.mjs
index 83f62b0d..7f23b7a0 100644
--- a/server/pruef-highlights.mjs
+++ b/server/pruef-highlights.mjs
@@ -258,6 +258,52 @@ try {
ok(zuNachher.sagt === "false",
` und sagt es auch Vorleseprogrammen (aria-expanded=${zuNachher.sagt})`);
+ /* =====================================================================
+ DIE ABLAGE LAESST SICH WAEHLEN (22.09.2026)
+ ---------------------------------------------------------------------
+ Filipe: „ich will das wenn man da video holt das man auch aussuchen
+ kann in welcher account es unten angezeigt werden soll."
+
+ Geprueft wird am BILDSCHIRM, nicht im Quelltext: dass die Wahl da
+ ist, dass sie genau die Kanaele anbietet (plus „aus dem Link"), und
+ dass sie das Eingabefeld nicht quetscht. Das Letzte ist kein
+ Schoenheitspunkt -- am 21.09. hat genau so ein drittes Element in
+ derselben Reihe das Chat-Feld auf einen Buchstaben zusammengedrueckt.
+ ================================================================== */
+ {
+ const { KANAELE } = await import("./workspace.js");
+ const v = await s.evaluate(() => {
+ const w = document.getElementById("video-kanal");
+ const feld = document.getElementById("video-url");
+ const knopf = document.getElementById("video-holen");
+ const r = (e) => (e ? e.getBoundingClientRect() : null);
+ const fr = r(feld), kr = r(knopf), wr = r(w);
+ return {
+ da: !!w,
+ werte: w ? [...w.options].map((o) => o.value) : [],
+ erste: w ? w.options[0]?.textContent.trim() : "",
+ vorgabe: w ? w.value : null,
+ feldBreite: fr ? Math.round(fr.width) : 0,
+ /* Steht die Wahl UNTER der Zeile, nicht darin? */
+ unterhalb: wr && kr ? Math.round(wr.top) >= Math.round(kr.bottom) - 2 : false,
+ };
+ });
+ ok(v.da, "die Wahl fuer den Account steht da");
+ /* DIE LISTE IST ABGELEITET, NICHT ABGESCHRIEBEN: sie muss genau die
+ Kanaele des Hauses anbieten, plus den leeren Eintrag. */
+ const soll = ["", ...KANAELE.map((k) => k.wert)];
+ ok(KANAELE.length >= 3
+ && JSON.stringify(v.werte) === JSON.stringify(soll),
+ `sie bietet genau die ${KANAELE.length} Kanaele an, plus den leeren Eintrag `
+ + `(${v.werte.join(", ")})`);
+ ok(v.vorgabe === "" && /Link/.test(v.erste),
+ `und steht auf: ${v.erste} — ohne Zutun bleibt alles wie bisher`);
+ ok(v.feldBreite > 400,
+ `das Eingabefeld bleibt breit (${v.feldBreite} px)`);
+ ok(v.unterhalb,
+ "die Wahl steht unter der Zeile, nicht darin");
+ }
+
await ctx.close();
} finally {
await browser.close();
diff --git a/server/pruef-video.mjs b/server/pruef-video.mjs
index 25e60693..5bfb0fcf 100644
--- a/server/pruef-video.mjs
+++ b/server/pruef-video.mjs
@@ -159,9 +159,10 @@ const an = await fetch(BASIS + "/workspace/api/anmelden", {
const keks = (an.headers.getSetCookie?.() || []).map((z) => z.split(";")[0]).join("; ");
const kopf = { "Content-Type": "application/json", cookie: keks, origin: BASIS };
-async function holen(url, bereich = "highlight") {
+async function holen(url, bereich = "highlight", kanal = undefined) {
const a = await fetch(`${BASIS}/workspace/api/bereich/${bereich}/video`, {
- method: "POST", headers: kopf, body: JSON.stringify({ url }),
+ method: "POST", headers: kopf,
+ body: JSON.stringify(kanal === undefined ? { url } : { url, kanal }),
});
return { status: a.status, daten: await a.json().catch(() => null) };
}
@@ -247,6 +248,58 @@ melde("=== 3. Die drei Kanäle ===");
`drei verschiedene Kanäle in der Liste (${[...kanaele].join(", ")})`);
}
+/* =======================================================================
+ 3b. WOHIN ES GELEGT WIRD, DARF MAN WAEHLEN (22.09.2026)
+ -----------------------------------------------------------------------
+ Filipe: „ich will das wenn man da video holt das man auch aussuchen
+ kann in welcher account es unten angezeigt werden soll."
+
+ Bis heute entschied allein der Link. Das ist gut geraten, aber es IST
+ geraten: Ein Ausschnitt vom Hauptkanal gehoert oft unter „Clips".
+
+ DREI FRAGEN, und die dritte ist die wichtigste:
+ 1. Eine Wahl schlaegt das Erkannte.
+ 2. Ein erfundener Kanal wird abgelehnt.
+ 3. OHNE Wahl bleibt alles wie vorher -- sonst waere aus einer
+ Zugabe eine Verhaltensaenderung geworden, die niemand bestellt
+ hat.
+ ======================================================================= */
+melde("");
+melde("=== 3b. Die Ablage waehlen ===");
+{
+ const { KANAELE } = await import("./workspace.js");
+ const haupt = KANAELE.find((k) => k.wert === "dogfather");
+ const anders = KANAELE.find((k) => k.wert !== "dogfather");
+
+ /* 1. Ein Video VOM Hauptkanal, abgelegt woanders. */
+ const gewaehlt = await holen(
+ videoVon(haupt.handle.replace(/^@/, ""), "901"), "highlight", anders.wert);
+ ok(gewaehlt.status === 201 && gewaehlt.daten?.kanal === anders.wert,
+ `ein Video von ${haupt.handle} laesst sich unter „${anders.name}" ablegen `
+ + `(${gewaehlt.status}, ${gewaehlt.daten?.kanal})`);
+
+ /* 2. Ein erfundener Kanal. Ohne diese Zeile koennte jeder Text in
+ der Spalte landen -- und die Galerie zeigt ihn dann als Fach. */
+ const erfunden = await holen(
+ videoVon(haupt.handle.replace(/^@/, ""), "902"), "highlight", "gibtsnicht");
+ ok(erfunden.status === 400 && erfunden.daten?.fehler === "kanal_unbekannt",
+ `ein erfundener Kanal wird abgelehnt (${erfunden.status}, ${erfunden.daten?.fehler})`);
+
+ /* 3. DIE GEGENPROBE. Ohne Wahl muss weiterhin der Link entscheiden --
+ sonst waere die Zeile oben auch gruen, wenn die Erkennung
+ stillschweigend abgeschaltet worden waere. */
+ const ohne = await holen(videoVon(haupt.handle.replace(/^@/, ""), "903"));
+ ok(ohne.status === 201 && ohne.daten?.kanal === "dogfather",
+ `ohne Wahl entscheidet weiterhin der Link (${ohne.status}, ${ohne.daten?.kanal})`);
+
+ /* 4. UND DIE SCHRANKE BLEIBT. Eine Wahl darf kein Schluessel fuer
+ fremde Inhalte sein -- gewaehlt wird die Spalte, nicht, wer
+ hereindarf. */
+ const fremd = await holen(videoVon("irgendwerfremd", "904"), "highlight", anders.wert);
+ ok(fremd.status === 403,
+ `ein fremder Absender kommt auch MIT Wahl nicht herein (${fremd.status})`);
+}
+
/* =======================================================================
4. WAS NICHT HINEINKOMMT
======================================================================= */
@@ -276,12 +329,21 @@ melde("=== 4. Die Gegenprobe ===");
/* DERSELBE LINK ZWEIMAL. Wer den Knopf zweimal drückt, meint nicht
zwei Videos. */
+ /* GEMESSEN WIRD DER UNTERSCHIED, NICHT DIE SUMME (berichtigt 22.09.2026).
+ Hier stand `coverAbrufe === 3` -- eine Rechnung von dem Tag, an dem
+ die Prüfung drei Videos anlegte. Am 22.09. kam ein Abschnitt dazu,
+ und die Zeile wurde rot, ohne dass irgendetwas kaputt war.
+
+ Die Frage lautet nicht "wie viele Cover insgesamt", sondern
+ "holt derselbe Link ein zweites Mal?" -- das ist eine Differenz
+ und bleibt richtig, egal wie viele Videos davor liefen. */
+ const coverVorher = coverAbrufe;
const wieder = await holen(videoVon("dogfather0804"));
ok(wieder.status === 200 && wieder.daten?.schon === true,
`derselbe Link gibt denselben Eintrag zurück (${wieder.status}, schon=${wieder.daten?.schon})`);
ok(wieder.daten?.id === eintragId, `und zwar wirklich denselben (${wieder.daten?.id})`);
- ok(coverAbrufe === 3,
- `und holt das Cover kein zweites Mal (${coverAbrufe} Abrufe für 3 Videos)`);
+ ok(coverAbrufe === coverVorher,
+ `und holt das Cover kein zweites Mal (${coverVorher} vor, ${coverAbrufe} nach dem zweiten Versuch)`);
const nachher = (await liste()).length;
ok(nachher === vorher, `kein Eintrag ist dazugekommen (${nachher} von ${vorher})`);
diff --git a/server/workspace-video.js b/server/workspace-video.js
index 9447b9f5..20cc3835 100644
--- a/server/workspace-video.js
+++ b/server/workspace-video.js
@@ -237,6 +237,30 @@ videoRouter.post("/workspace/api/bereich/:bereich/video", async (req, res) => {
});
}
+ /* WOHIN ES GELEGT WIRD, DARF MAN WAEHLEN (22.09.2026).
+ ===================================================================
+ Filipe: „ich will das wenn man da video holt das man auch
+ aussuchen kann in welcher account es unten angezeigt werden soll."
+
+ Bis heute entschied das allein der Link: `kanalVonHandle` liest
+ den Account aus der TikTok-Adresse, und dort landete das Video.
+ Das ist richtig geraten, aber es IST geraten -- ein Ausschnitt
+ vom Hauptkanal gehoert oft unter „Clips", und ein Video, das
+ jemand anders hochgeladen hat, steht dann in der falschen Spalte.
+
+ Die Schranke darueber bleibt unangetastet: Der ABSENDER muss
+ weiterhin einer der drei eigenen Kanaele sein. Gewaehlt wird nur
+ die SPALTE, nicht, ob fremde Inhalte hereinduerfen -- sonst waere
+ aus einer Ablagehilfe ein Loch geworden.
+
+ Ohne Angabe bleibt alles wie bisher. Das ist der haeufigste Fall
+ und soll keinen zusaetzlichen Handgriff kosten. */
+ const gewaehlt = String(req.body?.kanal ?? "").trim();
+ if (gewaehlt && !KANAELE.some((k) => k.wert === gewaehlt)) {
+ return res.status(400).json({ fehler: "kanal_unbekannt" });
+ }
+ const ablage = gewaehlt || auskunft.kanal;
+
abgelegt = await coverAblegen(auskunft.cover);
const jetzt = new Date().toISOString();
@@ -250,7 +274,7 @@ videoRouter.post("/workspace/api/bereich/:bereich/video", async (req, res) => {
db().prepare(`UPDATE eintraege SET quelle_url = ?, quelle_kanal = ?,
quelle_geholt_am = ?, geaendert = ?
WHERE id = ?`)
- .run(url, auskunft.kanal, jetzt, jetzt, anhaengenAn);
+ .run(url, ablage, jetzt, jetzt, anhaengenAn);
eintragId = anhaengenAn;
} else {
const { lastInsertRowid } = db().prepare(`
@@ -259,7 +283,7 @@ videoRouter.post("/workspace/api/bereich/:bereich/video", async (req, res) => {
quelle_url, quelle_kanal, quelle_geholt_am, aus_eintrag_id)
VALUES (?,?,?,?, 'offen', ?,?,?,?,?,?)`)
.run(bereich, arten[bereich] || "clip", auskunft.titel, jetzt.slice(0, 10),
- jetzt, req.person.id, url, auskunft.kanal, jetzt, ausQuelle ? aus : null);
+ jetzt, req.person.id, url, ablage, jetzt, ausQuelle ? aus : null);
eintragId = Number(lastInsertRowid);
}
@@ -278,7 +302,13 @@ videoRouter.post("/workspace/api/bereich/:bereich/video", async (req, res) => {
detail: `${auskunft.kanal} ${url}${ausQuelle ? ` (aus #${aus})` : ""}`.slice(0, 120),
});
res.status(201).json({
- id: eintragId, schon: false, kanal: auskunft.kanal,
+ /* GEMELDET WIRD DIE ABLAGE, NICHT DAS ERKANNTE (22.09.2026).
+ Hier stand `auskunft.kanal` -- also das, was aus dem Link
+ gelesen wurde. Seit die Spalte waehlbar ist, sind das zwei
+ verschiedene Dinge, und die Antwort muss sagen, wo der Eintrag
+ wirklich gelandet ist. Sonst meldet die Seite "DogFather",
+ waehrend das Video unter "Clips" steht. */
+ id: eintragId, schon: false, kanal: ablage,
titel: auskunft.titel, cover: !!abgelegt || undefined,
...(ausQuelle ? { aus: aus, angehaengt: !!anhaengenAn } : {}),
});
diff --git a/workspace/anruf-probe.html b/workspace/anruf-probe.html
index d4a7e12b..dfba0cec 100644
--- a/workspace/anruf-probe.html
+++ b/workspace/anruf-probe.html
@@ -16,15 +16,15 @@
verdeckt. Gefunden hat es pruef-struktur.
Richtig sind `gate.css` (die Farbwerte des Hauses) und `haus.css`. -->
-
-
+
+
-
+