Corrections to code logic

This commit is contained in:
Deniz Şafak
2025-04-27 05:09:52 +03:00
parent 52f2dbbec1
commit d67cfce896
8 changed files with 190 additions and 163 deletions
-9
View File
@@ -1,9 +0,0 @@
from abogen.utils import get_version
# Program Information
PROGRAM_NAME = "abogen"
PROGRAM_DESCRIPTION = (
"Generate audiobooks from EPUBs, PDFs and text with synchronized captions."
)
GITHUB_URL = "https://github.com/denizsafak/abogen"
VERSION = get_version()
+1 -1
View File
@@ -21,7 +21,7 @@ from PyQt5.QtWidgets import (
QMenu,
)
from PyQt5.QtCore import Qt
from abogen.utils import clean_text, calculate_text_length
from utils import clean_text, calculate_text_length
import os
+117
View File
@@ -0,0 +1,117 @@
from utils import get_version, get_user_config_path
# Program Information
PROGRAM_NAME = "abogen"
PROGRAM_DESCRIPTION = (
"Generate audiobooks from EPUBs, PDFs and text with synchronized captions."
)
GITHUB_URL = "https://github.com/denizsafak/abogen"
VERSION = get_version()
# Language description mapping
LANGUAGE_DESCRIPTIONS = {
"a": "American English",
"b": "British English",
"e": "Spanish",
"f": "French",
"h": "Hindi",
"i": "Italian",
"j": "Japanese",
"p": "Brazilian Portuguese",
"z": "Mandarin Chinese",
}
# Supported languages for subtitle generation
# Currently, only 'a (American English)' and 'b (British English)' are supported for subtitle generation.
# This is because tokens that contain timestamps are not generated for other languages in the Kokoro pipeline.
# Please refer to: https://github.com/hexgrad/kokoro/blob/6d87f4ae7abc2d14dbc4b3ef2e5f19852e861ac2/kokoro/pipeline.py
# 383 English processing (unchanged)
# 384 if self.lang_code in 'ab':
SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION = [
"a",
"b",
]
# Voice and sample text constants
VOICES_INTERNAL = [
"af_alloy",
"af_aoede",
"af_bella",
"af_heart",
"af_jessica",
"af_kore",
"af_nicole",
"af_nova",
"af_river",
"af_sarah",
"af_sky",
"am_adam",
"am_echo",
"am_eric",
"am_fenrir",
"am_liam",
"am_michael",
"am_onyx",
"am_puck",
"am_santa",
"bf_alice",
"bf_emma",
"bf_isabella",
"bf_lily",
"bm_daniel",
"bm_fable",
"bm_george",
"bm_lewis",
"ef_dora",
"em_alex",
"em_santa",
"ff_siwis",
"hf_alpha",
"hf_beta",
"hm_omega",
"hm_psi",
"if_sara",
"im_nicola",
"jf_alpha",
"jf_gongitsune",
"jf_nezumi",
"jf_tebukuro",
"jm_kumo",
"pf_dora",
"pm_alex",
"pm_santa",
"zf_xiaobei",
"zf_xiaoni",
"zf_xiaoxiao",
"zf_xiaoyi",
"zm_yunjian",
"zm_yunxi",
"zm_yunxia",
"zm_yunyang",
]
# Voice and sample text mapping
SAMPLE_VOICE_TEXTS = {
"a": "This is a sample of the selected voice.",
"b": "This is a sample of the selected voice.",
"e": "Este es una muestra de la voz seleccionada.",
"f": "Ceci est un exemple de la voix sélectionnée.",
"h": "यह चयनित आवाज़ का एक नमूना है।",
"i": "Questo è un esempio della voce selezionata.",
"j": "これは選択した声のサンプルです。",
"p": "Este é um exemplo da voz selecionada.",
"z": "这是所选语音的示例。",
}
# flags mapping for voice display
FLAGS = {
"a": "🇺🇸",
"b": "🇬🇧",
"e": "🇪🇸",
"f": "🇫🇷",
"h": "🇮🇳",
"i": "🇮🇹",
"j": "🇯🇵",
"p": "🇧🇷",
"z": "🇨🇳",
}
+3 -2
View File
@@ -5,8 +5,8 @@ import time
from PyQt5.QtCore import QThread, pyqtSignal, Qt
from PyQt5.QtWidgets import QCheckBox, QVBoxLayout, QDialog, QLabel, QDialogButtonBox
import soundfile as sf
from abogen.utils import clean_text, SAMPLE_VOICE_TEXTS, LANGUAGE_DESCRIPTIONS
from abogen import PROGRAM_NAME
from utils import clean_text
from constants import PROGRAM_NAME, LANGUAGE_DESCRIPTIONS, SAMPLE_VOICE_TEXTS
def get_sample_voice_text(lang_code):
@@ -688,6 +688,7 @@ class PlayAudioThread(QThread):
try:
import pygame
import time as _time
pygame.mixer.init()
pygame.mixer.music.load(self.wav_path)
pygame.mixer.music.play()
+13 -7
View File
@@ -46,7 +46,7 @@ from PyQt5.QtGui import (
QColor,
QMovie,
)
from abogen.utils import (
from utils import (
load_config,
save_config,
get_gpu_acceleration,
@@ -56,14 +56,19 @@ from abogen.utils import (
calculate_text_length,
get_resource_path,
LoadPipelineThread,
)
from conversion import ConversionThread, VoicePreviewThread, PlayAudioThread
from book_handler import HandlerDialog
from constants import (
PROGRAM_NAME,
VERSION,
GITHUB_URL,
PROGRAM_DESCRIPTION,
LANGUAGE_DESCRIPTIONS,
FLAGS,
VOICES_INTERNAL,
SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION,
LANGUAGE_DESCRIPTIONS,
)
from abogen.conversion import ConversionThread, VoicePreviewThread, PlayAudioThread
from abogen.book_handler import HandlerDialog
from abogen import PROGRAM_NAME, VERSION, GITHUB_URL, PROGRAM_DESCRIPTION
from threading import Thread
# Import ctypes for Windows-specific taskbar icon
@@ -1271,6 +1276,7 @@ class abogen(QWidget):
if self.preview_playing:
try:
import pygame
pygame.mixer.music.stop()
except Exception:
pass
@@ -1470,7 +1476,7 @@ class abogen(QWidget):
def show_chapter_options_dialog(self, chapter_count):
"""Show dialog to ask user about chapter processing options when chapters are detected in a .txt file"""
from abogen.conversion import ChapterOptionsDialog
from conversion import ChapterOptionsDialog
dialog = ChapterOptionsDialog(chapter_count, parent=self)
dialog.setWindowModality(Qt.ApplicationModal)
@@ -1539,7 +1545,7 @@ class abogen(QWidget):
def reveal_config_in_explorer(self):
"""Open the configuration file location in file explorer."""
from abogen.utils import get_user_config_path
from utils import get_user_config_path
try:
config_path = get_user_config_path()
+11 -4
View File
@@ -3,9 +3,12 @@ import sys
import platform
from PyQt5.QtWidgets import QApplication
from PyQt5.QtGui import QIcon
from abogen.gui import abogen
from abogen.utils import get_resource_path
from abogen import PROGRAM_NAME, VERSION
# Add the directory to Python path
sys.path.insert(0, os.path.join(os.path.dirname(__file__)))
from gui import abogen
from utils import get_resource_path
from constants import PROGRAM_NAME, VERSION
# Ensure sys.stdout and sys.stderr are valid in GUI mode
if sys.stdout is None:
@@ -28,7 +31,11 @@ if platform.system() == "Windows":
if platform.system() == "Linux":
xdg_session = os.environ.get("XDG_SESSION_TYPE", "").lower()
desktop = os.environ.get("XDG_CURRENT_DESKTOP", "").lower()
if "gnome" in desktop and xdg_session == "wayland" and "QT_QPA_PLATFORM" not in os.environ:
if (
"gnome" in desktop
and xdg_session == "wayland"
and "QT_QPA_PLATFORM" not in os.environ
):
os.environ["QT_QPA_PLATFORM"] = "wayland"
+2 -112
View File
@@ -10,114 +10,6 @@ from threading import Thread
os.environ["HF_HUB_DISABLE_SYMLINKS_WARNING"] = "1"
warnings.filterwarnings("ignore")
# Language description mapping
LANGUAGE_DESCRIPTIONS = {
"a": "American English",
"b": "British English",
"e": "Spanish",
"f": "French",
"h": "Hindi",
"i": "Italian",
"j": "Japanese",
"p": "Brazilian Portuguese",
"z": "Mandarin Chinese",
}
# Supported languages for subtitle generation
# Currently, only 'a (American English)' and 'b (British English)' are supported for subtitle generation.
# This is because tokens that contain timestamps are not generated for other languages in the Kokoro pipeline.
# Please refer to: https://github.com/hexgrad/kokoro/blob/6d87f4ae7abc2d14dbc4b3ef2e5f19852e861ac2/kokoro/pipeline.py
# 383 English processing (unchanged)
# 384 if self.lang_code in 'ab':
SUPPORTED_LANGUAGES_FOR_SUBTITLE_GENERATION = [
"a",
"b",
]
# Voice and sample text constants
VOICES_INTERNAL = [
"af_alloy",
"af_aoede",
"af_bella",
"af_heart",
"af_jessica",
"af_kore",
"af_nicole",
"af_nova",
"af_river",
"af_sarah",
"af_sky",
"am_adam",
"am_echo",
"am_eric",
"am_fenrir",
"am_liam",
"am_michael",
"am_onyx",
"am_puck",
"am_santa",
"bf_alice",
"bf_emma",
"bf_isabella",
"bf_lily",
"bm_daniel",
"bm_fable",
"bm_george",
"bm_lewis",
"ef_dora",
"em_alex",
"em_santa",
"ff_siwis",
"hf_alpha",
"hf_beta",
"hm_omega",
"hm_psi",
"if_sara",
"im_nicola",
"jf_alpha",
"jf_gongitsune",
"jf_nezumi",
"jf_tebukuro",
"jm_kumo",
"pf_dora",
"pm_alex",
"pm_santa",
"zf_xiaobei",
"zf_xiaoni",
"zf_xiaoxiao",
"zf_xiaoyi",
"zm_yunjian",
"zm_yunxi",
"zm_yunxia",
"zm_yunyang",
]
# Voice and sample text mapping
SAMPLE_VOICE_TEXTS = {
"a": "This is a sample of the selected voice.",
"b": "This is a sample of the selected voice.",
"e": "Este es una muestra de la voz seleccionada.",
"f": "Ceci est un exemple de la voix sélectionnée.",
"h": "यह चयनित आवाज़ का एक नमूना है।",
"i": "Questo è un esempio della voce selezionata.",
"j": "これは選択した声のサンプルです。",
"p": "Este é um exemplo da voz selecionada.",
"z": "这是所选语音的示例。",
}
# flags mapping for voice display
FLAGS = {
"a": "🇺🇸",
"b": "🇬🇧",
"e": "🇪🇸",
"f": "🇫🇷",
"h": "🇮🇳",
"i": "🇮🇹",
"j": "🇯🇵",
"p": "🇧🇷",
"z": "🇨🇳",
}
def get_resource_path(package, resource):
"""
@@ -174,8 +66,6 @@ def get_user_config_path():
return os.path.join(config_dir, "config.json")
CONFIG_PATH = get_user_config_path()
_sleep_procs = {"Darwin": None, "Linux": None} # Store sleep prevention processes
@@ -193,7 +83,7 @@ def clean_text(text):
def load_config():
try:
with open(CONFIG_PATH, "r", encoding="utf-8") as f:
with open(get_user_config_path(), "r", encoding="utf-8") as f:
return json.load(f)
except Exception:
return {}
@@ -201,7 +91,7 @@ def load_config():
def save_config(config):
try:
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
with open(get_user_config_path(), "w", encoding="utf-8") as f:
json.dump(config, f, indent=2)
except Exception:
pass
+28 -13
View File
@@ -13,23 +13,27 @@ import re
import sys
from datetime import timedelta
def parse_time(s):
# For ASS format
if ':' in s and '.' in s:
h, m, s = s.split(':')
sec, cs = s.split('.')
return timedelta(hours=int(h), minutes=int(m), seconds=int(sec), milliseconds=int(cs) * 10)
if ":" in s and "." in s:
h, m, s = s.split(":")
sec, cs = s.split(".")
return timedelta(
hours=int(h), minutes=int(m), seconds=int(sec), milliseconds=int(cs) * 10
)
# For SRT format (00:00:00,000)
elif ':' in s and ',' in s:
parts = s.split(':')
elif ":" in s and "," in s:
parts = s.split(":")
h = int(parts[0])
m = int(parts[1])
sec_parts = parts[2].split(',')
sec_parts = parts[2].split(",")
sec = int(sec_parts[0])
ms = int(sec_parts[1])
return timedelta(hours=h, minutes=m, seconds=sec, milliseconds=ms)
return None
def format_time(t):
total_seconds = int(t.total_seconds())
cs = int((t.total_seconds() - total_seconds) * 100)
@@ -38,6 +42,7 @@ def format_time(t):
s = total_seconds % 60
return f"{h}:{m:02}:{s:02}.{cs:02}"
# Desired script info and style
DESIRED_SCRIPT_INFO = """[Script Info]
Title: Centered Subs
@@ -51,8 +56,9 @@ Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour,
Style: Default,Arial,60,&H00FFFFFF,&H000000FF,&H00000000,&H64000000,-1,0,0,0,100,100,0,0,1,2,0,5,10,10,10,1
"""
def process_subtitle_file(input_file):
output_file = input_file.replace('.ass', '_demo.ass').replace('.srt', '_demo.ass')
output_file = input_file.replace(".ass", "_demo.ass").replace(".srt", "_demo.ass")
if output_file == input_file:
output_file = f"{input_file}_demo.ass"
@@ -67,7 +73,7 @@ def process_subtitle_file(input_file):
sys.exit(1)
# Check if it's an SRT file
is_srt = input_file.lower().endswith('.srt')
is_srt = input_file.lower().endswith(".srt")
if is_srt:
return convert_srt_to_ass(input_file, output_file, lines)
@@ -124,9 +130,12 @@ def process_subtitle_file(input_file):
if end > next_start:
end = next_start # cut current subtitle to stop at next one's start
fixed_line = re.sub(r"Dialogue: \d,[^,]+,[^,]+,",
fixed_line = re.sub(
r"Dialogue: \d,[^,]+,[^,]+,",
f"Dialogue: 0,{format_time(start)},{format_time(end)},",
line, count=1)
line,
count=1,
)
fixed_lines.append(fixed_line)
try:
@@ -138,6 +147,7 @@ def process_subtitle_file(input_file):
print(f"❌ Error writing output file: {e}")
sys.exit(1)
def convert_srt_to_ass(input_file, output_file, lines):
"""Convert SRT format to ASS format."""
events = []
@@ -167,7 +177,9 @@ def convert_srt_to_ass(input_file, output_file, lines):
timing_line = block[1]
# Parse timing information
timing_match = re.match(r'(\d+:\d+:\d+,\d+)\s+-->\s+(\d+:\d+:\d+,\d+)', timing_line)
timing_match = re.match(
r"(\d+:\d+:\d+,\d+)\s+-->\s+(\d+:\d+:\d+,\d+)", timing_line
)
if not timing_match:
continue
@@ -187,7 +199,9 @@ def convert_srt_to_ass(input_file, output_file, lines):
f.write(DESIRED_SCRIPT_INFO)
f.write(DESIRED_STYLE)
f.write("[Events]\n")
f.write("Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n")
f.write(
"Format: Layer, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text\n"
)
f.writelines(events)
print(f"✅ Successfully converted SRT to ASS. Output file: {output_file}")
return True
@@ -195,6 +209,7 @@ def convert_srt_to_ass(input_file, output_file, lines):
print(f"❌ Error writing output file: {e}")
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("❌ Error: No input file specified")