Videos: Stufe 7 -- aus einem Wunsch wird ein Video, mit Namen
"Ein Video aus einem Wunsch zeigt den Namen." Wer sich etwas gewuenscht hat, soll sehen, dass daraus etwas geworden ist -- das ist der Sinn eines Wunschbretts. Ohne die Verbindung ist die Galerie eine Sammlung, und der Wunsch von letzter Woche bleibt unbeantwortet im Raum stehen. DER KNOPF STEHT AM WUNSCH, nicht im Formular oben. Das ist dieselbe Entscheidung wie bei den Kettenknoepfen daneben, aus demselben Grund (dort woertlich): "Wer ein Formular ausfuellt, denkt nicht an den Wunsch von letzter Woche." Hier denkt er an nichts anderes. DAS LECK, das es nicht geben darf: Der Titel der Quelle wird auf der Kachel ANGEZEIGT. Eine Kette in die Content-Planung waere deshalb kein Schoenheitsfehler, sondern ein Weg, interne Arbeit in die Community zu tragen. Die Quelle muss ein Community-Brett sein -- dieselbe Regel wie beim "Daraus"-Knopf. ZWEI WEGE, EIN ZIEL: "Daraus ein Highlight machen" gibt es laenger. Wer ihn geht und DANACH das Video einfuegt, bekaeme sonst ein zweites Highlight aus demselben Wunsch -- zwei Wege zum selben Ziel, die nichts voneinander wissen. Das Video haengt sich jetzt an den vorhandenen Eintrag. Der Titel bleibt dabei der des Wunsches: Er stammt von einem Menschen, der von TikTok ist eine Bildunterschrift mit Schlagwoertern. Geprueft: pruef-video 51 -> 67. Gegenprobe (Brett-Pruefung und Anhaengen ausgebaut) macht 6 rot, darunter "ein Eintrag aus der Content-Planung kommt NICHT als Quelle durch (201)". UND DAS BILDSCHIRMFOTO HAT ETWAS GEFUNDEN, das keine Zahl gemeldet haette: Das Eingabefeld erschien am ENDE der Karte, unter der Fusszeile -- getrennt von dem Knopf, den man gerade gedrueckt hatte. Alle Pruefungen waren gruen, das Feld war da und reagierte. Jetzt haengt es an der Knopfreihe. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -49,6 +49,7 @@ import {
|
||||
KANAELE, kanalVonHandle, TREFF_BRETTER,
|
||||
} from "./workspace.js";
|
||||
import { darfSchreiben } from "./workspace-treff.js";
|
||||
import { sichtbar } from "./workspace-bereiche.js";
|
||||
import { bildArtVon } from "./workspace-dateien.js";
|
||||
|
||||
export const videoRouter = express.Router();
|
||||
@@ -170,6 +171,36 @@ videoRouter.post("/workspace/api/bereich/:bereich/video", async (req, res) => {
|
||||
return res.status(400).json({ fehler: "Das ist keine TikTok-Adresse." });
|
||||
}
|
||||
|
||||
/* ---- DER KREISLAUF (Stufe 7, 18.09.2026) --------------------------
|
||||
|
||||
„Ein Video aus einem Wunsch zeigt den Namen." Wer sich etwas
|
||||
gewuenscht hat, soll sehen, dass daraus etwas geworden ist --
|
||||
das ist der ganze Sinn eines Wunschbretts. Ohne diese Verbindung
|
||||
ist die Galerie eine Sammlung, und der Wunsch von letzter Woche
|
||||
bleibt unbeantwortet im Raum stehen.
|
||||
|
||||
DIE QUELLE MUSS EIN COMMUNITY-BRETT SEIN. Eine Kette, die in die
|
||||
Content-Planung eines Creators fuehrt, waere ein Leck: Dort steht
|
||||
Arbeit, die die Community nichts angeht -- und der TITEL der
|
||||
Quelle wird auf der Kachel angezeigt. Dieselbe Regel wie beim
|
||||
„Daraus"-Knopf, aus demselben Grund. */
|
||||
const aus = Number(req.body?.aus || 0);
|
||||
let ausQuelle = null;
|
||||
if (req.body?.aus !== undefined && req.body?.aus !== null && req.body?.aus !== "") {
|
||||
if (!Number.isInteger(aus) || aus <= 0) {
|
||||
return res.status(400).json({ fehler: "Die Quelle ist keine Nummer." });
|
||||
}
|
||||
if (!sichtbar(req.person)) return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
ausQuelle = db().prepare(
|
||||
"SELECT id, bereich, titel FROM eintraege WHERE id = ?").get(aus);
|
||||
/* Nicht da, oder kein Community-Brett: dieselbe Antwort. Sonst
|
||||
liesse sich durch Ausprobieren herausfinden, welche Nummern es
|
||||
gibt und in welchem Bereich sie liegen. */
|
||||
if (!ausQuelle || !TREFF_BRETTER.includes(ausQuelle.bereich)) {
|
||||
return res.status(404).json({ fehler: "nicht_gefunden" });
|
||||
}
|
||||
}
|
||||
|
||||
/* SCHON DA? Derselbe Link zweimal soll dasselbe Ergebnis geben,
|
||||
nicht zwei Eintraege. Wer den Knopf zweimal drueckt, meint nicht
|
||||
zwei Videos. */
|
||||
@@ -177,6 +208,22 @@ videoRouter.post("/workspace/api/bereich/:bereich/video", async (req, res) => {
|
||||
"SELECT id FROM eintraege WHERE quelle_url = ? AND bereich = ?").get(url, bereich);
|
||||
if (schon) return res.status(200).json({ id: schon.id, schon: true });
|
||||
|
||||
/* IST AUS DIESEM WUNSCH SCHON ETWAS GEWORDEN?
|
||||
|
||||
Den Weg „Daraus ein Highlight machen" gibt es laenger als diesen
|
||||
hier. Wer ihn gegangen ist und DANACH das Video einfuegt, bekaeme
|
||||
sonst ein ZWEITES Highlight aus demselben Wunsch -- zwei Wege zum
|
||||
selben Ziel, die nichts voneinander wissen. Das Video wird
|
||||
deshalb an den vorhandenen Eintrag gehaengt. */
|
||||
let anhaengenAn = 0;
|
||||
if (ausQuelle) {
|
||||
const da = db().prepare(
|
||||
"SELECT id, quelle_url FROM eintraege WHERE aus_eintrag_id = ? AND bereich = ?")
|
||||
.get(aus, bereich);
|
||||
if (da?.quelle_url) return res.status(200).json({ id: da.id, schon: true });
|
||||
if (da) anhaengenAn = da.id;
|
||||
}
|
||||
|
||||
const auskunft = await videoAuskunft(url);
|
||||
if (auskunft.fehler) return res.status(502).json({ fehler: auskunft.fehler });
|
||||
|
||||
@@ -194,14 +241,27 @@ videoRouter.post("/workspace/api/bereich/:bereich/video", async (req, res) => {
|
||||
|
||||
const jetzt = new Date().toISOString();
|
||||
const arten = { highlight: "clip", anschlag: "hinweis", treff: "lob" };
|
||||
const { lastInsertRowid } = db().prepare(`
|
||||
INSERT INTO eintraege
|
||||
(bereich, art, titel, datum, status, erstellt, erstellt_von,
|
||||
quelle_url, quelle_kanal, quelle_geholt_am)
|
||||
VALUES (?,?,?,?, 'offen', ?,?,?,?,?)`)
|
||||
.run(bereich, arten[bereich] || "clip", auskunft.titel, jetzt.slice(0, 10),
|
||||
jetzt, req.person.id, url, auskunft.kanal, jetzt);
|
||||
const eintragId = Number(lastInsertRowid);
|
||||
let eintragId;
|
||||
if (anhaengenAn) {
|
||||
/* DER TITEL WIRD NICHT UEBERSCHRIEBEN. Er stammt vom Wunsch und
|
||||
damit von einem Menschen; der von TikTok ist eine Bildunter-
|
||||
schrift mit Schlagwoertern. Wer den ersetzte, naehme dem
|
||||
Eintrag genau das, was ihn mit dem Wunsch verbindet. */
|
||||
db().prepare(`UPDATE eintraege SET quelle_url = ?, quelle_kanal = ?,
|
||||
quelle_geholt_am = ?, geaendert = ?
|
||||
WHERE id = ?`)
|
||||
.run(url, auskunft.kanal, jetzt, jetzt, anhaengenAn);
|
||||
eintragId = anhaengenAn;
|
||||
} else {
|
||||
const { lastInsertRowid } = db().prepare(`
|
||||
INSERT INTO eintraege
|
||||
(bereich, art, titel, datum, status, erstellt, erstellt_von,
|
||||
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);
|
||||
eintragId = Number(lastInsertRowid);
|
||||
}
|
||||
|
||||
if (abgelegt) {
|
||||
db().prepare(`
|
||||
@@ -215,11 +275,12 @@ videoRouter.post("/workspace/api/bereich/:bereich/video", async (req, res) => {
|
||||
|
||||
protokolliere("video_eingelesen", {
|
||||
personId: req.person.id, rolle: req.person.rolle, ip: echteIp(req),
|
||||
detail: `${auskunft.kanal} ${url}`.slice(0, 120),
|
||||
detail: `${auskunft.kanal} ${url}${ausQuelle ? ` (aus #${aus})` : ""}`.slice(0, 120),
|
||||
});
|
||||
res.status(201).json({
|
||||
id: eintragId, schon: false, kanal: auskunft.kanal,
|
||||
titel: auskunft.titel, cover: !!abgelegt || undefined,
|
||||
...(ausQuelle ? { aus: aus, angehaengt: !!anhaengenAn } : {}),
|
||||
});
|
||||
} catch (fehler) {
|
||||
/* GING ETWAS SCHIEF, NACHDEM DIE DATEI SCHON LAG: wegraeumen. Eine
|
||||
|
||||
Reference in New Issue
Block a user