diff --git a/CHANGELOG.md b/CHANGELOG.md index c7e26db..b1d592b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ Follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) · versioned wi --- +## [1.12.86] — 2026-07-04 + +### Changed +- **Speaker attribution taught the deduction rules it kept missing** — the casting prompt's attribution section grew from 6 to 10 rules covering exactly the patterns behind most false "Unknown"/"Narrator" assignments: the colon rule (an inquit sentence ending in ":" names the next quote's speaker), post-quote attribution across the segment boundary (»Quote« — "ertönte es über ihm. Karyla hatte …" → Karyla spoke), mandatory pronoun resolution to the last-named person of matching gender, the addressee rule ("X wandte sich an Y" → X speaks next, Y answers), strict two-person ping-pong carried through tag-less exchanges (where 'Unknown' is almost never right), and role designations ('Ork', 'Nachbar', 'Der Fremde') as valid speakers instead of 'Unknown'. Saved custom prompts are upgraded in place via the existing prompt-migration mechanism, and the "2nd Quality Run" verification prompt gets the same deduction toolkit. +- **Book language now auto-detected for casting** — the attribution request's language hint previously came only from a manual dropdown that usually sat empty; it now falls back to detecting the language from the book text itself, so German books always get the German grammar/wording instruction without any manual step. + +--- + ## [1.12.85] — 2026-07-03 ### Performance diff --git a/VERSION b/VERSION index 60fe64e..6088240 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.12.85 +1.12.86 diff --git a/static/index.html b/static/index.html index 6f4c296..72d4972 100644 --- a/static/index.html +++ b/static/index.html @@ -10,7 +10,7 @@ - + @@ -27,7 +27,7 @@ - + @@ -365,7 +365,7 @@ window.toggleNavTree = function(treeId, chevronId) { - + diff --git a/static/js/audiobook.js b/static/js/audiobook.js index 4080920..11fe68d 100644 --- a/static/js/audiobook.js +++ b/static/js/audiobook.js @@ -648,7 +648,16 @@ function audiobookSafeLlmModel(model) { function audiobookLlmUrl() { return $('reh-llm-url')?.value.trim() || (typeof _appSettings !== 'undefined' && _appSettings?.llm_url) || (typeof rehDefaultLlmUrl === 'function' ? rehDefaultLlmUrl() : ''); } function audiobookLlmModel() { return audiobookSafeLlmModel($('reh-llm-model')?.value || ''); } -function audiobookLang() { return $('reh-design-lang')?.value || ''; } +// Book language for the attribution LLM: the manual dropdown wins, otherwise +// detect it from the text itself (stop-word heuristic, utils.js detectLang) so +// the server's "keep names and wording in " hint and grammar rules +// are applied even when nobody touched the dropdown. +function audiobookLang(text) { + const manual = $('reh-design-lang')?.value || ''; + if (manual) return manual; + const sample = String(text || _audiobook.lastText || '').slice(0, 4000); + return (sample && typeof detectLang === 'function') ? (detectLang(sample) || '') : ''; +} function audiobookAttributeBody(fields, promptOverride = null) { const prompt = typeof promptOverride === 'string' ? promptOverride.trim() : ''; @@ -878,13 +887,17 @@ GRAMMATIK-REGELN FÜR NARRATION: - Grammatische Probe: Wenn der Text eine Erzähler-Aussage ÜBER das Sprechen ist (Verb + Sprecher + Art und Weise), ist es narration. Nur die tatsächlich geäußerten Wörter innerhalb der Anführungszeichen sind dialogue. - Satzfragmente mit führendem Komma/Punkt wie ", sagte sie leise." oder ". fragte Uriens." sind niemals eigenständige Dialoge; sie gehören als narration zum Erzählertext. -ANALYSE-REGELN FÜR DIE ZUORDNUNG DES SPRECHERS (Sei deduktiv): -1. Direkte Zuordnung: Achte auf Wörter wie "sagte [Name]", "fragte er", "rief sie". Löse Pronomen (er/sie) zum tatsächlichen Namen auf. -2. Handlungs-Hinweise (Action Beats): Wenn ein Charakter eine Handlung ausführt und direkt davor/danach wörtliche Rede steht, spricht meist dieser Charakter (z.B. "Thomas trat ans Fenster. »Es regnet.«"). -3. Das Ping-Pong-Prinzip: Wenn zwei Personen sprechen, wechseln sie sich ab. Verfolge diese Kette lückenlos zurück zur letzten eindeutigen Nennung. -4. Gruppen-Dialoge (3+ Personen): An wen richtet sich die Aussage? Passt die Aussage zum Wissen oder Tonfall eines bestimmten Charakters? -5. Wiederverwendung: Nutze EXAKT die Namen aus der Liste der bekannten Charaktere, FALLS der Name dort aufgeführt ist. Wenn ein neuer Charakter spricht, extrahiere seinen Namen direkt aus dem Text (z.B. 'Karyla', 'Uriens'). -6. Unbekannte Sprecher: Nur wenn eine Zuordnung durch Kontext absolut nicht möglich ist, verwende 'Unknown'. Rate nicht blind, aber bevorzuge immer einen namentlich genannten Charakter gegenüber 'Unknown'. +ANALYSE-REGELN FÜR DIE ZUORDNUNG DES SPRECHERS (Sei deduktiv — arbeite wie ein Lektor): +1. Direkte Zuordnung: "sagte [Name]", "fragte er", "rief sie". Löse Pronomen (er/sie) IMMER zum tatsächlichen Namen auf: Das Pronomen meint die zuletzt genannte Person passenden Geschlechts. +2. Doppelpunkt-Regel: Endet ein Erzählersatz mit ":", spricht das Subjekt dieses Satzes das folgende Zitat (z.B. "Dann richtete er sich auf und rief in die Runde:" → der zuvor genannte Charakter spricht das nächste Zitat; "Ein anderer fragte verschlafen:" → dieser andere spricht). +3. Nachgestellte Zuordnung: Der Erzählersatz NACH einem Zitat verrät oft den Sprecher — auch bei unpersönlicher Formel: "»Was machst du denn da?« ertönte es über ihm. Karyla hatte ihren Hammer weggelegt und war herübergekommen." → Karyla sprach das Zitat. +4. Handlungs-Hinweise (Action Beats): Wer unmittelbar vor oder nach einem Zitat handelt, spricht meist (z.B. "Thomas trat ans Fenster. »Es regnet.«"; "Verächtlich sah sie ihn an. »Das wirst du schon noch merken.«" → sie spricht, und "sie" ist die zuletzt genannte Frau). +5. Adressaten-Regel: "X wandte sich an Y" / "X sah Y an" → X spricht das nächste Zitat, und Y ist der wahrscheinlichste Antwortende. +6. Das Ping-Pong-Prinzip: Sprechen zwei Personen miteinander, wechseln sie sich strikt ab — auch über viele Zitate OHNE Inquit-Formeln hinweg. Verfolge die Kette lückenlos zurück zur letzten eindeutigen Nennung und führe sie bis zum Szenenende fort. In einer Zwei-Personen-Szene ist 'Unknown' fast immer falsch. +7. Gruppen-Dialoge (3+ Personen): An wen richtet sich die Aussage? Passt sie zum Wissen oder Tonfall eines bestimmten Charakters? +8. Namenlose Sprecher: Auch Rollenbezeichnungen aus dem Text sind gültige Sprecher — nutze sie statt 'Unknown' (z.B. 'Ork', 'Der Fremde', 'Nachbar', 'Wächter', 'Junge'). +9. Wiederverwendung: Nutze EXAKT die Namen aus der Liste der bekannten Charaktere, FALLS der Name dort aufgeführt ist. Wenn ein neuer Charakter spricht, extrahiere seinen Namen direkt aus dem Text (z.B. 'Karyla', 'Uriens'). +10. 'Unknown' NUR, wenn eine Zuordnung trotz aller obigen Regeln absolut unmöglich ist. Bevorzuge immer einen genannten Charakter oder eine Rollenbezeichnung gegenüber 'Unknown'. FÜR JEDES SEGMENT GIBST DU FOLGENDES AUS: - speaker: 'Narrator' für Narration/Erzählertext, oder den EXAKTEN Namen des Charakters für gesprochene Dialoge. @@ -914,6 +927,31 @@ STRIKTE FORMAT- UND TEXTREGELN: "- Lasse NIEMALS Wörter aus, fasse nicht zusammen, dupliziere nichts und erfinde keinen Text. Der kombinierte Text all deiner Segmente MUSS den Originaltext exakt und lückenlos Wort für Wort rekonstruieren!", "- Lasse NIEMALS Wörter aus, fasse nicht zusammen, dupliziere nichts und erfinde keinen Text. Der kombinierte Text all deiner Segmente MUSS den Originaltext exakt und lückenlos Wort für Wort rekonstruieren — abgesehen von entfernten äußeren Dialog-Anführungszeichen!" ); + // Upgrade older saved prompts to the deduction-rule set (colon rule, + // post-quote inquits, pronoun resolution, strict ping-pong, role names) + // that cut down false "Unknown"/"Narrator" attributions. + if (!/Doppelpunkt-Regel/.test(p)) { + p = p.replace( + `ANALYSE-REGELN FÜR DIE ZUORDNUNG DES SPRECHERS (Sei deduktiv): +1. Direkte Zuordnung: Achte auf Wörter wie "sagte [Name]", "fragte er", "rief sie". Löse Pronomen (er/sie) zum tatsächlichen Namen auf. +2. Handlungs-Hinweise (Action Beats): Wenn ein Charakter eine Handlung ausführt und direkt davor/danach wörtliche Rede steht, spricht meist dieser Charakter (z.B. "Thomas trat ans Fenster. »Es regnet.«"). +3. Das Ping-Pong-Prinzip: Wenn zwei Personen sprechen, wechseln sie sich ab. Verfolge diese Kette lückenlos zurück zur letzten eindeutigen Nennung. +4. Gruppen-Dialoge (3+ Personen): An wen richtet sich die Aussage? Passt die Aussage zum Wissen oder Tonfall eines bestimmten Charakters? +5. Wiederverwendung: Nutze EXAKT die Namen aus der Liste der bekannten Charaktere, FALLS der Name dort aufgeführt ist. Wenn ein neuer Charakter spricht, extrahiere seinen Namen direkt aus dem Text (z.B. 'Karyla', 'Uriens'). +6. Unbekannte Sprecher: Nur wenn eine Zuordnung durch Kontext absolut nicht möglich ist, verwende 'Unknown'. Rate nicht blind, aber bevorzuge immer einen namentlich genannten Charakter gegenüber 'Unknown'.`, + `ANALYSE-REGELN FÜR DIE ZUORDNUNG DES SPRECHERS (Sei deduktiv — arbeite wie ein Lektor): +1. Direkte Zuordnung: "sagte [Name]", "fragte er", "rief sie". Löse Pronomen (er/sie) IMMER zum tatsächlichen Namen auf: Das Pronomen meint die zuletzt genannte Person passenden Geschlechts. +2. Doppelpunkt-Regel: Endet ein Erzählersatz mit ":", spricht das Subjekt dieses Satzes das folgende Zitat (z.B. "Dann richtete er sich auf und rief in die Runde:" → der zuvor genannte Charakter spricht das nächste Zitat; "Ein anderer fragte verschlafen:" → dieser andere spricht). +3. Nachgestellte Zuordnung: Der Erzählersatz NACH einem Zitat verrät oft den Sprecher — auch bei unpersönlicher Formel: "»Was machst du denn da?« ertönte es über ihm. Karyla hatte ihren Hammer weggelegt und war herübergekommen." → Karyla sprach das Zitat. +4. Handlungs-Hinweise (Action Beats): Wer unmittelbar vor oder nach einem Zitat handelt, spricht meist (z.B. "Thomas trat ans Fenster. »Es regnet.«"; "Verächtlich sah sie ihn an. »Das wirst du schon noch merken.«" → sie spricht, und "sie" ist die zuletzt genannte Frau). +5. Adressaten-Regel: "X wandte sich an Y" / "X sah Y an" → X spricht das nächste Zitat, und Y ist der wahrscheinlichste Antwortende. +6. Das Ping-Pong-Prinzip: Sprechen zwei Personen miteinander, wechseln sie sich strikt ab — auch über viele Zitate OHNE Inquit-Formeln hinweg. Verfolge die Kette lückenlos zurück zur letzten eindeutigen Nennung und führe sie bis zum Szenenende fort. In einer Zwei-Personen-Szene ist 'Unknown' fast immer falsch. +7. Gruppen-Dialoge (3+ Personen): An wen richtet sich die Aussage? Passt sie zum Wissen oder Tonfall eines bestimmten Charakters? +8. Namenlose Sprecher: Auch Rollenbezeichnungen aus dem Text sind gültige Sprecher — nutze sie statt 'Unknown' (z.B. 'Ork', 'Der Fremde', 'Nachbar', 'Wächter', 'Junge'). +9. Wiederverwendung: Nutze EXAKT die Namen aus der Liste der bekannten Charaktere, FALLS der Name dort aufgeführt ist. Wenn ein neuer Charakter spricht, extrahiere seinen Namen direkt aus dem Text (z.B. 'Karyla', 'Uriens'). +10. 'Unknown' NUR, wenn eine Zuordnung trotz aller obigen Regeln absolut unmöglich ist. Bevorzuge immer einen genannten Charakter oder eine Rollenbezeichnung gegenüber 'Unknown'.` + ); + } if (!/GRAMMATIK-REGELN FÜR NARRATION/.test(p)) { p = p.replace( 'Markiere Text NIEMALS als Dialog, nur weil der Name eines Charakters erwähnt wird! (z.B. "Karyla rannte zur Tür." ist Narration, KEIN Dialog).', @@ -2479,6 +2517,14 @@ AUFGABE (2. Qualitätslauf): 3. BEHALTE vorhandene klare Sprecher-Zuweisungen im Kontext bei. Nutze sie als Anker für die Unknown-Zeilen. 4. 'Unknown' ist NUR erlaubt, wenn der Sprecher trotz Kontext absolut nicht bestimmbar ist. +DEDUKTIONS-WERKZEUGE (wende sie in dieser Reihenfolge an): +- Doppelpunkt-Regel: Endet der Erzählersatz vor dem Zitat mit ":", spricht dessen Subjekt das Zitat ("Dann richtete er sich auf und rief in die Runde:" → der zuvor genannte Charakter; "Ein anderer fragte verschlafen:" → dieser andere). +- Nachgestellte Zuordnung: Der Erzählersatz NACH dem Zitat verrät den Sprecher — auch bei unpersönlicher Formel ("»Was machst du denn da?« ertönte es über ihm. Karyla hatte ihren Hammer weggelegt und war herübergekommen." → Karyla sprach). +- Pronomen-Auflösung: er/sie/es in Inquit-Formeln und Action Beats meint die zuletzt genannte Person passenden Geschlechts ("Mit einem Stoß schob sie Uriens zur Seite" → sie = die zuletzt genannte Frau, und die umliegenden Zitate sind ihre). +- Adressaten-Regel: "X wandte sich an Y" → X spricht das nächste Zitat, Y ist der wahrscheinlichste Antwortende. +- Ping-Pong-Prinzip: Zwei Personen im Gespräch wechseln sich strikt ab — auch über viele Zitate ohne Tags hinweg. Verfolge die Kette zur letzten eindeutigen Nennung zurück und führe sie fort. In einer Zwei-Personen-Szene ist 'Unknown' fast immer falsch. +- Rollenbezeichnungen sind gültige Sprecher — nutze sie statt 'Unknown' (z.B. 'Ork', 'Der Fremde', 'Nachbar', 'Wächter', 'Junge'). + DIALOG-ERKENNUNG BEI PDF/OCR-TEXTEN: Viele PDF-Extraktionen verlieren Anführungszeichen oder Guillemets. Ein kurzer Satz kann also trotzdem Dialog sein, auch wenn »...« oder „..." im übergebenen Segment fehlen. Entscheide nach Satzform, Antwortstruktur, Sprecherwechsel, Inquit-Formeln und Szene. Markiere eine Zeile NICHT allein deshalb als Narration, weil sichtbare Anführungszeichen fehlen. @@ -3004,7 +3050,7 @@ async function audiobookCast(overrideUrl, overrideModel, resume) { _audiobook.abort = () => ac.abort(); if (overrideUrl && typeof overrideUrl !== 'string') overrideUrl = null; - const llm_url = overrideUrl || audiobookLlmUrl(), language = audiobookLang(); + const llm_url = overrideUrl || audiobookLlmUrl(), language = audiobookLang(text); let model = audiobookSafeLlmModel(overrideModel || audiobookLlmModel()); const view = audiobookCastView(chunks.length, llm_url, model, false); if (isResume && resume.segments && resume.segments.length) view.rebuild(resume.segments);