mirror of
https://github.com/denizsafak/abogen.git
synced 2026-09-20 19:50:59 +02:00
Compare commits
11
Commits
fcec4e9fe5
...
6274a02d5e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6274a02d5e | ||
|
|
342ea0dfac | ||
|
|
27f88b759d | ||
|
|
dbcbb1c8a9 | ||
|
|
bd99ee1ba1 | ||
|
|
d5cddb9749 | ||
|
|
ec55918b04 | ||
|
|
14913b45e9 | ||
|
|
a0fdabd81f | ||
|
|
473631b84e | ||
|
|
0f5003dfdd |
@@ -280,16 +280,28 @@ def _process_regex_sentences(
|
||||
current_sentence = []
|
||||
word_count = 0
|
||||
|
||||
# Add any remaining tokens as a sentence
|
||||
# Add any remaining tokens as a sentence (split multi-sentence FakeToken)
|
||||
if current_sentence:
|
||||
start_time = current_sentence[0]["start"]
|
||||
end_time = current_sentence[-1]["end"]
|
||||
|
||||
# Simplified text joining logic
|
||||
sentence_text = ""
|
||||
for t in current_sentence:
|
||||
sentence_text += t["text"] + (t.get("whitespace") or "")
|
||||
subtitle_entries.append((start_time, end_time, sentence_text.strip()))
|
||||
sentence_text = sentence_text.strip()
|
||||
|
||||
if len(current_sentence) == 1:
|
||||
parts = re.split(rf"(?<={separator})\s+", sentence_text)
|
||||
if len(parts) > 1:
|
||||
d = end_time - start_time
|
||||
for i, p in enumerate(parts):
|
||||
e = end_time if i == len(parts) - 1 else start_time + d * len(p) / len(sentence_text)
|
||||
subtitle_entries.append((start_time, e, p.strip()))
|
||||
start_time = e
|
||||
current_sentence = []
|
||||
|
||||
if current_sentence:
|
||||
subtitle_entries.append((start_time, end_time, sentence_text))
|
||||
|
||||
# Fallback for last entry
|
||||
_apply_fallback_end_time(subtitle_entries, fallback_end_time)
|
||||
|
||||
@@ -19,7 +19,7 @@ def tracked_hf_hub_download(*args, **kwargs):
|
||||
try:
|
||||
local_kwargs = dict(kwargs)
|
||||
local_kwargs["local_files_only"] = True
|
||||
hf_hub_download(*args, **local_kwargs)
|
||||
return hf_hub_download(*args, **local_kwargs)
|
||||
except Exception:
|
||||
repo_id = kwargs.get("repo_id", "<unknown repo>")
|
||||
filename = kwargs.get("filename", "<unknown file>")
|
||||
|
||||
@@ -2,6 +2,7 @@ import os
|
||||
import re
|
||||
import time
|
||||
import hashlib # For generating unique cache filenames
|
||||
from pathlib import Path
|
||||
from platformdirs import user_desktop_dir
|
||||
from PyQt6.QtCore import QThread, pyqtSignal, Qt, QTimer
|
||||
from PyQt6.QtWidgets import QCheckBox, QVBoxLayout, QDialog, QLabel, QDialogButtonBox
|
||||
@@ -36,7 +37,7 @@ from abogen.domain.output_paths import (
|
||||
)
|
||||
from abogen.domain.audio_helpers import build_ffmpeg_command, to_float32
|
||||
from abogen.domain.audio_sink import AudioSink, open_audio_sink
|
||||
from abogen.domain.conversion_engine import synthesize_text, SynthParams, SegmentStats, SegmentInfo
|
||||
from abogen.domain.conversion_engine import run_tts_segment_loop, synthesize_text, SynthParams, SegmentStats, SegmentInfo
|
||||
from abogen.domain.intro_outro import resolve_intro, resolve_outro
|
||||
from abogen.domain.audio_buffer import (
|
||||
create_silence,
|
||||
@@ -550,10 +551,14 @@ class ConversionThread(QThread):
|
||||
|
||||
# --- Compile normalization rules (heteronym + pronunciation) ---
|
||||
from abogen.domain.normalization import TTSContext
|
||||
pronunciation_overrides = merge_pronunciation_overrides(
|
||||
getattr(self, "pronunciation_overrides", None),
|
||||
getattr(self, "manual_overrides", None),
|
||||
)
|
||||
|
||||
class _MergeJob:
|
||||
pronunciation_overrides = getattr(self, "pronunciation_overrides", None)
|
||||
manual_overrides = getattr(self, "manual_overrides", None)
|
||||
heteronym_overrides = getattr(self, "heteronym_overrides", None)
|
||||
language = self.lang_code
|
||||
|
||||
pronunciation_overrides = merge_pronunciation_overrides(_MergeJob())
|
||||
self._tts_context = TTSContext(
|
||||
split_pattern=self.split_pattern,
|
||||
pronunciation_rules=compile_pronunciation_rules(pronunciation_overrides),
|
||||
|
||||
+1
-1
@@ -842,7 +842,7 @@ class WordSubstitutionsDialog(QDialog):
|
||||
self,
|
||||
)
|
||||
instructions.setStyleSheet(
|
||||
"padding: 10px; background-color: #f0f0f0; border-radius: 5px;"
|
||||
f"padding: 10px; background-color: {COLORS['GREY_BACKGROUND']}; border-radius: 5px;"
|
||||
)
|
||||
instructions.setWordWrap(True)
|
||||
layout.addWidget(instructions)
|
||||
|
||||
@@ -46,6 +46,8 @@ except ImportError:
|
||||
print("PyQt6 not installed.")
|
||||
|
||||
|
||||
from abogen.utils import get_resource_path
|
||||
|
||||
# Pre-load "libxcb-cursor" on Linux (fixes #101)
|
||||
if platform.system() == "Linux":
|
||||
arch = platform.machine().lower()
|
||||
@@ -118,6 +120,8 @@ def qt_message_handler(mode, context, message):
|
||||
return # Suppress this specific message
|
||||
if "setGrabPopup called with a parent, QtWaylandClient" in message:
|
||||
return
|
||||
if "Failed to register with host portal" in message:
|
||||
return
|
||||
|
||||
if mode == QtMsgType.QtWarningMsg:
|
||||
print(f"Qt Warning: {message}")
|
||||
|
||||
@@ -21,6 +21,19 @@ SPACY_MODELS = {
|
||||
Language.HI: "xx_sent_ud_sm",
|
||||
}
|
||||
|
||||
# Kokoro single-letter codes -> Language enum (inverse of pipeline_factory._KOKORO_LANG_MAP)
|
||||
_KOKORO_TO_LANGUAGE = {
|
||||
"a": Language.EN_US,
|
||||
"b": Language.EN_GB,
|
||||
"e": Language.ES,
|
||||
"f": Language.FR,
|
||||
"h": Language.HI,
|
||||
"i": Language.IT,
|
||||
"j": Language.JA,
|
||||
"p": Language.PT_BR,
|
||||
"z": Language.ZH,
|
||||
}
|
||||
|
||||
|
||||
def _load_spacy():
|
||||
"""Lazy load spaCy module."""
|
||||
@@ -61,6 +74,9 @@ def get_spacy_model(lang_code, log_callback=None):
|
||||
|
||||
# Normalize to Language enum
|
||||
if not isinstance(lang_code, Language):
|
||||
if isinstance(lang_code, str) and lang_code in _KOKORO_TO_LANGUAGE:
|
||||
lang_code = _KOKORO_TO_LANGUAGE[lang_code]
|
||||
else:
|
||||
try:
|
||||
lang_code = Language.from_str(lang_code)
|
||||
except ValueError:
|
||||
|
||||
@@ -12,7 +12,7 @@ The loader does NOT:
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import importlib.util
|
||||
import re
|
||||
import sys
|
||||
import types
|
||||
|
||||
@@ -41,6 +41,9 @@ class PluginManager:
|
||||
self._engines.clear()
|
||||
|
||||
plugins_path = Path(plugins_dir)
|
||||
if not plugins_path.exists():
|
||||
if plugins_dir == "plugins":
|
||||
plugins_path = Path(__file__).resolve().parent.parent.parent / "plugins"
|
||||
if not plugins_path.exists():
|
||||
self._loaded = True
|
||||
return
|
||||
|
||||
@@ -179,6 +179,12 @@ class Pipeline:
|
||||
|
||||
yield Segment(graphemes=text, audio=audio_array)
|
||||
|
||||
def load_single_voice(self, voice_name: str) -> Any:
|
||||
engine_pipeline = getattr(self._engine, '_pipeline', None)
|
||||
if engine_pipeline is not None and hasattr(engine_pipeline, 'load_single_voice'):
|
||||
return engine_pipeline.load_single_voice(voice_name)
|
||||
raise AttributeError(f"load_single_voice not available on {type(self._engine).__name__}")
|
||||
|
||||
def dispose(self) -> None:
|
||||
if self._session is not None:
|
||||
try:
|
||||
|
||||
@@ -7,6 +7,7 @@ from flask import Blueprint, current_app, render_template, request, redirect, ur
|
||||
from flask.typing import ResponseReturnValue
|
||||
|
||||
from abogen.webui.routes.utils.settings import (
|
||||
load_integration_settings,
|
||||
load_settings,
|
||||
save_settings,
|
||||
SAVE_MODE_LABELS,
|
||||
|
||||
Reference in New Issue
Block a user