mirror of
https://github.com/denizsafak/abogen.git
synced 2026-07-18 21:50:28 +02:00
refactor: Remove speaker mode handling from various components
This commit is contained in:
+8
-54
@@ -106,13 +106,7 @@ _CHUNK_LEVEL_OPTIONS = [
|
||||
{"value": "sentence", "label": "Sentences"},
|
||||
]
|
||||
|
||||
_SPEAKER_MODE_OPTIONS = [
|
||||
{"value": "single", "label": "Single Speaker"},
|
||||
{"value": "multi", "label": "Multi-Speaker"},
|
||||
]
|
||||
|
||||
_CHUNK_LEVEL_VALUES = {option["value"] for option in _CHUNK_LEVEL_OPTIONS}
|
||||
_SPEAKER_MODE_VALUES = {option["value"] for option in _SPEAKER_MODE_OPTIONS}
|
||||
|
||||
|
||||
_DEFAULT_ANALYSIS_THRESHOLD = 3
|
||||
@@ -835,7 +829,6 @@ def _prepare_speaker_metadata(
|
||||
chapters: List[Dict[str, Any]],
|
||||
chunks: List[Dict[str, Any]],
|
||||
analysis_chunks: Optional[List[Dict[str, Any]]] = None,
|
||||
speaker_mode: str,
|
||||
voice: str,
|
||||
voice_profile: Optional[str],
|
||||
threshold: int,
|
||||
@@ -848,7 +841,7 @@ def _prepare_speaker_metadata(
|
||||
chunk_list = [dict(chunk) for chunk in chunks]
|
||||
analysis_source = [dict(chunk) for chunk in (analysis_chunks or chunks)]
|
||||
threshold_value = max(1, int(threshold))
|
||||
analysis_enabled = speaker_mode == "multi" and run_analysis
|
||||
analysis_enabled = run_analysis
|
||||
settings_state = _load_settings()
|
||||
global_random_languages = [
|
||||
code
|
||||
@@ -1323,10 +1316,7 @@ def _apply_prepare_form(
|
||||
pending.chunk_level = raw_chunk_level
|
||||
chunk_level_literal = cast(ChunkLevel, pending.chunk_level)
|
||||
|
||||
raw_speaker_mode = (form.get("speaker_mode") or pending.speaker_mode or "single").strip().lower()
|
||||
if raw_speaker_mode not in _SPEAKER_MODE_VALUES:
|
||||
raw_speaker_mode = "single"
|
||||
pending.speaker_mode = raw_speaker_mode
|
||||
pending.speaker_mode = "single"
|
||||
|
||||
pending.generate_epub3 = _coerce_bool(form.get("generate_epub3"), False)
|
||||
|
||||
@@ -1660,13 +1650,12 @@ def _template_options() -> Dict[str, Any]:
|
||||
"voice_profiles": ordered_profiles,
|
||||
"voice_profile_options": profile_options,
|
||||
"separate_formats": ["wav", "flac", "mp3", "opus"],
|
||||
"voice_catalog": voice_catalog,
|
||||
"voice_catalog_map": {entry["id"]: entry for entry in voice_catalog},
|
||||
"voice_catalog": voice_catalog,
|
||||
"voice_catalog_map": {entry["id"]: entry for entry in voice_catalog},
|
||||
"sample_voice_texts": SAMPLE_VOICE_TEXTS,
|
||||
"voice_profiles_data": profiles,
|
||||
"speaker_configs": list_configs(),
|
||||
"chunk_levels": _CHUNK_LEVEL_OPTIONS,
|
||||
"speaker_modes": _SPEAKER_MODE_OPTIONS,
|
||||
"speaker_analysis_threshold": current_settings.get(
|
||||
"speaker_analysis_threshold", _DEFAULT_ANALYSIS_THRESHOLD
|
||||
),
|
||||
@@ -1718,7 +1707,6 @@ def _settings_defaults() -> Dict[str, Any]:
|
||||
"chapter_intro_delay": 0.5,
|
||||
"max_subtitle_words": 50,
|
||||
"chunk_level": "paragraph",
|
||||
"speaker_mode": "single",
|
||||
"generate_epub3": False,
|
||||
"speaker_analysis_threshold": _DEFAULT_ANALYSIS_THRESHOLD,
|
||||
"speaker_pronunciation_sentence": "This is {{name}} speaking.",
|
||||
@@ -1788,10 +1776,6 @@ def _normalize_setting_value(key: str, value: Any, defaults: Dict[str, Any]) ->
|
||||
if isinstance(value, str) and value in _CHUNK_LEVEL_VALUES:
|
||||
return value
|
||||
return defaults[key]
|
||||
if key == "speaker_mode":
|
||||
if isinstance(value, str) and value in _SPEAKER_MODE_VALUES:
|
||||
return value
|
||||
return defaults[key]
|
||||
if key == "speaker_random_languages":
|
||||
if isinstance(value, (list, tuple, set)):
|
||||
return [code for code in value if isinstance(code, str) and code in LANGUAGE_DESCRIPTIONS]
|
||||
@@ -1995,9 +1979,6 @@ def settings_page() -> ResponseReturnValue:
|
||||
updated["chunk_level"] = _normalize_setting_value(
|
||||
"chunk_level", form.get("chunk_level"), defaults
|
||||
)
|
||||
updated["speaker_mode"] = _normalize_setting_value(
|
||||
"speaker_mode", form.get("speaker_mode"), defaults
|
||||
)
|
||||
updated["separate_chapters_format"] = _normalize_setting_value(
|
||||
"separate_chapters_format", form.get("separate_chapters_format"), defaults
|
||||
)
|
||||
@@ -2744,11 +2725,7 @@ def enqueue_job() -> ResponseReturnValue:
|
||||
chunk_level_value = raw_chunk_level
|
||||
chunk_level_literal = cast(ChunkLevel, chunk_level_value)
|
||||
|
||||
speaker_mode_default = str(settings.get("speaker_mode", "single")).strip().lower()
|
||||
raw_speaker_mode = (request.form.get("speaker_mode") or speaker_mode_default).strip().lower()
|
||||
if raw_speaker_mode not in _SPEAKER_MODE_VALUES:
|
||||
raw_speaker_mode = "single"
|
||||
speaker_mode_value = raw_speaker_mode
|
||||
speaker_mode_value = "single"
|
||||
|
||||
generate_epub3_default = bool(settings.get("generate_epub3", False))
|
||||
generate_epub3 = _coerce_bool(request.form.get("generate_epub3"), generate_epub3_default)
|
||||
@@ -2764,12 +2741,11 @@ def enqueue_job() -> ResponseReturnValue:
|
||||
maximum=25,
|
||||
)
|
||||
|
||||
initial_analysis = speaker_mode_value == "multi"
|
||||
initial_analysis = False
|
||||
processed_chunks, speakers, analysis_payload, config_languages, _ = _prepare_speaker_metadata(
|
||||
chapters=selected_chapter_sources,
|
||||
chunks=raw_chunks,
|
||||
analysis_chunks=analysis_chunks,
|
||||
speaker_mode=speaker_mode_value,
|
||||
voice=voice,
|
||||
voice_profile=selected_profile or None,
|
||||
threshold=analysis_threshold,
|
||||
@@ -2868,20 +2844,6 @@ def analyze_pending_job(pending_id: str) -> ResponseReturnValue:
|
||||
)
|
||||
abort(400, message)
|
||||
|
||||
if pending.speaker_mode != "multi":
|
||||
setattr(pending, "analysis_requested", False)
|
||||
pending.chunks = []
|
||||
pending.speaker_analysis = {}
|
||||
error_message = "Switch to multi-speaker mode to analyze speakers."
|
||||
if _wants_wizard_json():
|
||||
return _wizard_json_response(
|
||||
pending,
|
||||
"chapters",
|
||||
error=error_message,
|
||||
status=400,
|
||||
)
|
||||
abort(400, error_message)
|
||||
|
||||
if not enabled_overrides:
|
||||
setattr(pending, "analysis_requested", False)
|
||||
pending.chunks = []
|
||||
@@ -2911,7 +2873,6 @@ def analyze_pending_job(pending_id: str) -> ResponseReturnValue:
|
||||
chapters=enabled_overrides,
|
||||
chunks=raw_chunks,
|
||||
analysis_chunks=analysis_chunks,
|
||||
speaker_mode=pending.speaker_mode,
|
||||
voice=pending.voice,
|
||||
voice_profile=pending.voice_profile,
|
||||
threshold=pending.speaker_analysis_threshold,
|
||||
@@ -2981,9 +2942,6 @@ def finalize_job(pending_id: str) -> ResponseReturnValue:
|
||||
)
|
||||
abort(400, message)
|
||||
|
||||
if pending.speaker_mode != "multi":
|
||||
setattr(pending, "analysis_requested", False)
|
||||
|
||||
if not enabled_overrides:
|
||||
pending.chunks = []
|
||||
error_message = "Select at least one chapter to convert."
|
||||
@@ -3003,9 +2961,8 @@ def finalize_job(pending_id: str) -> ResponseReturnValue:
|
||||
normalized_step = _normalize_wizard_step(active_step, pending)
|
||||
raw_chunks = build_chunks_for_chapters(enabled_overrides, level=chunk_level_literal)
|
||||
analysis_chunks = build_chunks_for_chapters(enabled_overrides, level="sentence")
|
||||
is_multi = pending.speaker_mode == "multi"
|
||||
analysis_requested = bool(getattr(pending, "analysis_requested", False))
|
||||
should_force_entities = is_multi and normalized_step != "entities"
|
||||
should_force_entities = analysis_requested and normalized_step != "entities"
|
||||
|
||||
if analysis_requested:
|
||||
existing_roster: Optional[Mapping[str, Any]] = pending.speakers
|
||||
@@ -3019,12 +2976,11 @@ def finalize_job(pending_id: str) -> ResponseReturnValue:
|
||||
|
||||
config_name = pending.applied_speaker_config or selected_config
|
||||
speaker_config_payload = get_config(config_name) if config_name else None
|
||||
run_analysis = is_multi and (should_force_entities or analysis_requested)
|
||||
run_analysis = should_force_entities or analysis_requested
|
||||
processed_chunks, roster, analysis_payload, config_languages, updated_config = _prepare_speaker_metadata(
|
||||
chapters=enabled_overrides,
|
||||
chunks=raw_chunks,
|
||||
analysis_chunks=analysis_chunks,
|
||||
speaker_mode=pending.speaker_mode,
|
||||
voice=pending.voice,
|
||||
voice_profile=pending.voice_profile,
|
||||
threshold=pending.speaker_analysis_threshold,
|
||||
@@ -3183,8 +3139,6 @@ def _normalize_wizard_step(step: Optional[str], pending: Optional[PendingJob] =
|
||||
chosen = normalized
|
||||
else:
|
||||
chosen = default_step
|
||||
if chosen == "entities" and pending is not None and pending.speaker_mode != "multi":
|
||||
return "chapters"
|
||||
return chosen
|
||||
|
||||
|
||||
|
||||
@@ -474,38 +474,6 @@ const initPrepare = (root = document) => {
|
||||
});
|
||||
});
|
||||
|
||||
const speakerModeSelect = form.querySelector("#speaker_mode");
|
||||
const analysisToggleButtons = Array.from(form.querySelectorAll('[data-step-toggle="analysis"]'));
|
||||
const finalizeToggleButtons = Array.from(form.querySelectorAll('[data-step-toggle="finalize"]'));
|
||||
|
||||
const setButtonVisibility = (button, isVisible) => {
|
||||
if (!button) return;
|
||||
if (isVisible) {
|
||||
button.hidden = false;
|
||||
button.removeAttribute("aria-hidden");
|
||||
button.removeAttribute("tabindex");
|
||||
} else {
|
||||
button.hidden = true;
|
||||
button.setAttribute("aria-hidden", "true");
|
||||
button.setAttribute("tabindex", "-1");
|
||||
}
|
||||
};
|
||||
|
||||
const updateStepButtons = () => {
|
||||
if (!speakerModeSelect) {
|
||||
return;
|
||||
}
|
||||
const modeValue = (speakerModeSelect.value || "").toLowerCase();
|
||||
const isMulti = modeValue === "multi";
|
||||
analysisToggleButtons.forEach((button) => setButtonVisibility(button, isMulti));
|
||||
finalizeToggleButtons.forEach((button) => setButtonVisibility(button, !isMulti));
|
||||
};
|
||||
|
||||
if (speakerModeSelect) {
|
||||
updateStepButtons();
|
||||
speakerModeSelect.addEventListener("change", updateStepButtons);
|
||||
}
|
||||
|
||||
const voiceModal = document.querySelector('[data-role="voice-modal"]');
|
||||
let activeGenderFilter = "";
|
||||
|
||||
|
||||
@@ -273,7 +273,18 @@ const submitWizardForm = async (form, submitter) => {
|
||||
}
|
||||
const action = submitter?.getAttribute("formaction") || form.getAttribute("action") || window.location.href;
|
||||
const method = (submitter?.getAttribute("formmethod") || form.getAttribute("method") || "GET").toUpperCase();
|
||||
const stepTarget = submitter?.dataset?.stepTarget || "";
|
||||
const normalizedStepTarget = stepTarget ? stepTarget.toLowerCase() : "";
|
||||
if (normalizedStepTarget) {
|
||||
const activeInput = form.querySelector('[data-role="active-step-input"]');
|
||||
if (activeInput) {
|
||||
activeInput.value = normalizedStepTarget;
|
||||
}
|
||||
}
|
||||
const formData = new FormData(form);
|
||||
if (normalizedStepTarget) {
|
||||
formData.set("active_step", normalizedStepTarget);
|
||||
}
|
||||
if (submitter && submitter.name && !formData.has(submitter.name)) {
|
||||
formData.append(submitter.name, submitter.value ?? "");
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
<li><strong>Max words per subtitle:</strong> {{ job.max_subtitle_words }}</li>
|
||||
<li><strong>Project folder:</strong> {{ 'Yes' if job.save_as_project else 'No' }}</li>
|
||||
<li><strong>Chunk granularity:</strong> {{ job.chunk_level|replace('_', ' ')|title }}</li>
|
||||
<li><strong>Speaker mode:</strong> {{ job.speaker_mode|replace('_', ' ')|title }}</li>
|
||||
<li><strong>Speaker analysis threshold:</strong> {{ job.speaker_analysis_threshold }}</li>
|
||||
<li><strong>Generate EPUB 3:</strong> {{ 'Yes' if job.generate_epub3 else 'No' }}</li>
|
||||
</ul>
|
||||
|
||||
@@ -31,14 +31,6 @@
|
||||
{% else %}
|
||||
{% set generate_epub3 = pending.generate_epub3 if pending else settings_dict.get('generate_epub3', False) %}
|
||||
{% endif %}
|
||||
{% set speaker_mode_value = form_values.get('speaker_mode') if form_values else None %}
|
||||
{% if not speaker_mode_value %}
|
||||
{% if pending and pending.speaker_mode %}
|
||||
{% set speaker_mode_value = pending.speaker_mode %}
|
||||
{% else %}
|
||||
{% set speaker_mode_value = settings_dict.get('speaker_mode', 'single') %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% set chunk_level_value = form_values.get('chunk_level') if form_values else None %}
|
||||
{% if not chunk_level_value %}
|
||||
{% if pending and pending.chunk_level %}
|
||||
@@ -247,25 +239,15 @@
|
||||
<section class="form-section">
|
||||
<h3 class="form-section__title">Entities & casting</h3>
|
||||
<div class="form-section__layout">
|
||||
<div class="field-grid field-grid--two">
|
||||
<div class="field field--stack">
|
||||
<label for="speaker_config">Speaker preset</label>
|
||||
<select id="speaker_config" name="speaker_config" {{ 'disabled' if readonly else '' }}>
|
||||
<option value="">None</option>
|
||||
{% for config in options.speaker_configs %}
|
||||
<option value="{{ config.name }}" {% if selected_config == config.name %}selected{% endif %}>{{ config.name }} · {{ config.speakers|length }} speaker{% if config.speakers|length != 1 %}s{% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="hint">Reuse a saved roster to keep character voices consistent.</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="speaker_mode">Speaker mode</label>
|
||||
<select id="speaker_mode" name="speaker_mode" {{ 'disabled' if readonly else '' }}>
|
||||
{% for option in options.speaker_modes %}
|
||||
<option value="{{ option.value }}" {% if speaker_mode_value == option.value %}selected{% endif %}>{{ option.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field field--stack">
|
||||
<label for="speaker_config">Speaker preset</label>
|
||||
<select id="speaker_config" name="speaker_config" {{ 'disabled' if readonly else '' }}>
|
||||
<option value="">None</option>
|
||||
{% for config in options.speaker_configs %}
|
||||
<option value="{{ config.name }}" {% if selected_config == config.name %}selected{% endif %}>{{ config.name }} · {{ config.speakers|length }} speaker{% if config.speakers|length != 1 %}s{% endif %}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<p class="hint">Reuse a saved roster to keep character voices consistent.</p>
|
||||
</div>
|
||||
<div class="field-grid field-grid--two">
|
||||
<div class="field">
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{% set is_multi_speaker = pending.speaker_mode == 'multi' %}
|
||||
<form method="post"
|
||||
action="{{ url_for('web.finalize_job', pending_id=pending.id) }}"
|
||||
class="prepare-form"
|
||||
@@ -7,12 +6,10 @@
|
||||
data-wizard-form="true"
|
||||
data-step="chapters"
|
||||
data-pending-id="{{ pending.id }}"
|
||||
data-speaker-mode="{{ pending.speaker_mode }}"
|
||||
data-analyze-url="{{ url_for('web.analyze_pending_job', pending_id=pending.id) }}">
|
||||
<input type="hidden" name="active_step" value="chapters" data-role="active-step-input">
|
||||
<div class="wizard-hidden-inputs" aria-hidden="true">
|
||||
<input type="hidden" name="chunk_level" value="{{ pending.chunk_level }}">
|
||||
<input type="hidden" name="speaker_mode" value="{{ pending.speaker_mode }}">
|
||||
<input type="hidden" name="speaker_analysis_threshold" value="{{ pending.speaker_analysis_threshold }}">
|
||||
<input type="hidden" name="chapter_intro_delay" value="{{ '%.2f'|format(pending.chapter_intro_delay) }}">
|
||||
{% if pending.generate_epub3 %}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{% set is_multi_speaker = pending.speaker_mode == 'multi' %}
|
||||
{% set embed_scripts = embed_scripts if embed_scripts is defined else True %}
|
||||
<form method="post"
|
||||
action="{{ url_for('web.finalize_job', pending_id=pending.id) }}"
|
||||
@@ -8,7 +7,6 @@
|
||||
data-wizard-form="true"
|
||||
data-step="entities"
|
||||
data-pending-id="{{ pending.id }}"
|
||||
data-speaker-mode="{{ pending.speaker_mode }}"
|
||||
data-analyze-url="{{ url_for('web.analyze_pending_job', pending_id=pending.id) }}"
|
||||
data-entities-url="{{ url_for('api.api_pending_entities', pending_id=pending.id) }}"
|
||||
data-manual-list-url="{{ url_for('api.api_list_manual_overrides', pending_id=pending.id) }}"
|
||||
|
||||
@@ -23,14 +23,6 @@
|
||||
</select>
|
||||
<p class="hint">Used whenever “Standard voice” is selected for a new job.</p>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="speaker_mode_default">Speaker Mode</label>
|
||||
<select id="speaker_mode_default" name="speaker_mode">
|
||||
{% for option in options.speaker_modes %}
|
||||
<option value="{{ option.value }}" {% if settings.speaker_mode == option.value %}selected{% endif %}>{{ option.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="chunk_level_default">Chunk Granularity</label>
|
||||
<select id="chunk_level_default" name="chunk_level">
|
||||
|
||||
Reference in New Issue
Block a user