This commit is contained in:
2026-05-21 08:16:10 +02:00
parent 9690367d70
commit 54c7b40737
5 changed files with 431 additions and 9 deletions
+374
View File
@@ -0,0 +1,374 @@
import customtkinter as ctk
import threading
import sys
from io import StringIO
from tkinter import filedialog, messagebox
from src.EpubHandler import EpubHandler
from src.WebScrapper import WebScrapper
class LogRedirector:
"""Leitet print() Ausgaben in ein Textfeld um"""
def __init__(self, text_widget):
self.text_widget = text_widget
self.buffer = StringIO()
def write(self, message):
self.text_widget.configure(state="normal")
self.text_widget.insert("end", message)
self.text_widget.see("end")
self.text_widget.configure(state="disabled")
self.text_widget.update_idletasks()
def flush(self):
pass
class App(ctk.CTk):
def __init__(self):
super().__init__()
self.title("J-Novel Scrapper & Translator")
self.geometry("900x700")
# Theme
ctk.set_appearance_mode("dark")
ctk.set_default_color_theme("blue")
# Tabview
self.tabview = ctk.CTkTabview(self, width=850, height=650)
self.tabview.pack(padx=20, pady=20, fill="both", expand=True)
# Tabs erstellen
self.tabview.add("WebScrapper")
self.tabview.add("EpubHandler")
# Tab-Inhalte aufbauen
self.setup_webscrapper_tab()
self.setup_epubhandler_tab()
# ==================== WebScrapper Tab ====================
def setup_webscrapper_tab(self):
tab = self.tabview.tab("WebScrapper")
# Frame für Input-Felder
input_frame = ctk.CTkFrame(tab)
input_frame.pack(padx=10, pady=10, fill="x")
# Base URL
ctk.CTkLabel(input_frame, text="Base URL:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.ws_base_url = ctk.CTkEntry(input_frame, width=400, placeholder_text="https://example.com/novel/")
self.ws_base_url.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
# HTML Folder Path
ctk.CTkLabel(input_frame, text="HTML Ordner:").grid(row=1, column=0, padx=5, pady=5, sticky="w")
self.ws_html_path = ctk.CTkEntry(input_frame, width=300, placeholder_text=r"E:\temp\WN\Novel\HTML")
self.ws_html_path.grid(row=1, column=1, padx=5, pady=5, sticky="ew")
ctk.CTkButton(input_frame, text="...", width=50, command=self.browse_ws_html_folder).grid(row=1, column=2, padx=5, pady=5)
# URI Format
ctk.CTkLabel(input_frame, text="URI Format:").grid(row=2, column=0, padx=5, pady=5, sticky="w")
self.ws_uri_format = ctk.CTkEntry(input_frame, width=400, placeholder_text="chapter_{}.html oder {}/")
self.ws_uri_format.grid(row=2, column=1, padx=5, pady=5, sticky="ew")
# From/To Chapter
ctk.CTkLabel(input_frame, text="Von Kapitel:").grid(row=3, column=0, padx=5, pady=5, sticky="w")
self.ws_from_chapter = ctk.CTkEntry(input_frame, width=100, placeholder_text="1")
self.ws_from_chapter.grid(row=3, column=1, padx=5, pady=5, sticky="w")
ctk.CTkLabel(input_frame, text="Bis Kapitel:").grid(row=4, column=0, padx=5, pady=5, sticky="w")
self.ws_to_chapter = ctk.CTkEntry(input_frame, width=100, placeholder_text="100")
self.ws_to_chapter.grid(row=4, column=1, padx=5, pady=5, sticky="w")
# Sleep Time
ctk.CTkLabel(input_frame, text="Sleep Time (s):").grid(row=5, column=0, padx=5, pady=5, sticky="w")
self.ws_sleep_time = ctk.CTkEntry(input_frame, width=100, placeholder_text="0")
self.ws_sleep_time.grid(row=5, column=1, padx=5, pady=5, sticky="w")
# Language
ctk.CTkLabel(input_frame, text="Sprache:").grid(row=6, column=0, padx=5, pady=5, sticky="w")
self.ws_language = ctk.CTkComboBox(input_frame, values=["en", "jp", "de"], width=100)
self.ws_language.set("en")
self.ws_language.grid(row=6, column=1, padx=5, pady=5, sticky="w")
input_frame.columnconfigure(1, weight=1)
# Start Button
self.ws_start_btn = ctk.CTkButton(tab, text="Scraping starten", command=self.start_webscrapper, height=40)
self.ws_start_btn.pack(padx=10, pady=10, fill="x")
# Progress Bar
self.ws_progress = ctk.CTkProgressBar(tab, width=400)
self.ws_progress.pack(padx=10, pady=5, fill="x")
self.ws_progress.set(0)
# Log Textfeld (readonly)
ctk.CTkLabel(tab, text="Logs:", anchor="w").pack(padx=10, pady=(10, 0), fill="x")
self.ws_log = ctk.CTkTextbox(tab, height=200, state="disabled")
self.ws_log.pack(padx=10, pady=5, fill="both", expand=True)
def browse_ws_html_folder(self):
folder = filedialog.askdirectory()
if folder:
self.ws_html_path.delete(0, "end")
self.ws_html_path.insert(0, folder)
def start_webscrapper(self):
# Validierung
if not self.ws_base_url.get() or not self.ws_html_path.get() or not self.ws_uri_format.get():
messagebox.showerror("Fehler", "Bitte alle Pflichtfelder ausfüllen!")
return
try:
from_chapter = int(self.ws_from_chapter.get() or 1)
to_chapter = int(self.ws_to_chapter.get() or 1)
sleep_time = float(self.ws_sleep_time.get() or 0)
except ValueError:
messagebox.showerror("Fehler", "Kapitel und Sleep Time müssen Zahlen sein!")
return
# Button deaktivieren
self.ws_start_btn.configure(state="disabled", text="Läuft...")
self.ws_progress.set(0)
self.ws_log.configure(state="normal")
self.ws_log.delete("1.0", "end")
self.ws_log.configure(state="disabled")
# Thread starten
thread = threading.Thread(
target=self.run_webscrapper,
args=(self.ws_base_url.get(), self.ws_html_path.get(), self.ws_language.get(),
self.ws_uri_format.get(), from_chapter, to_chapter, sleep_time),
daemon=True
)
thread.start()
def run_webscrapper(self, base_url, html_path, language, uri_format, from_ch, to_ch, sleep_time):
# Log umleiten
old_stdout = sys.stdout
sys.stdout = LogRedirector(self.ws_log)
try:
scrapper = WebScrapper(base_url, html_path, language)
total = to_ch - from_ch + 1
for i, chapter in enumerate(range(from_ch, to_ch + 1)):
# Progress aktualisieren
progress = (i + 1) / total
self.ws_progress.set(progress)
# Einzelnes Kapitel scrapen
scrapper.getHtml(uri_format, chapter, chapter, sleep_time)
print("\n✅ Scraping erfolgreich abgeschlossen!")
messagebox.showinfo("Erfolg", "Scraping abgeschlossen!")
except Exception as e:
print(f"\n❌ Fehler: {str(e)}")
messagebox.showerror("Fehler", f"Ein Fehler ist aufgetreten:\n{str(e)}")
finally:
sys.stdout = old_stdout
self.ws_start_btn.configure(state="normal", text="Scraping starten")
self.ws_progress.set(1.0)
# ==================== EpubHandler Tab ====================
def setup_epubhandler_tab(self):
tab = self.tabview.tab("EpubHandler")
# Frame für Input-Felder
input_frame = ctk.CTkFrame(tab)
input_frame.pack(padx=10, pady=10, fill="x")
# HTML Folder Path
ctk.CTkLabel(input_frame, text="HTML Ordner:").grid(row=0, column=0, padx=5, pady=5, sticky="w")
self.eh_html_path = ctk.CTkEntry(input_frame, width=300, placeholder_text=r"E:\temp\WN\Novel\HTML")
self.eh_html_path.grid(row=0, column=1, padx=5, pady=5, sticky="ew")
ctk.CTkButton(input_frame, text="...", width=50, command=self.browse_eh_html_folder).grid(row=0, column=2, padx=5, pady=5)
# EPUB Folder Path
ctk.CTkLabel(input_frame, text="EPUB Ordner:").grid(row=1, column=0, padx=5, pady=5, sticky="w")
self.eh_epub_path = ctk.CTkEntry(input_frame, width=300, placeholder_text=r"E:\temp\WN\Novel\EPUB")
self.eh_epub_path.grid(row=1, column=1, padx=5, pady=5, sticky="ew")
ctk.CTkButton(input_frame, text="...", width=50, command=self.browse_eh_epub_folder).grid(row=1, column=2, padx=5, pady=5)
# Cover Image Path
ctk.CTkLabel(input_frame, text="Cover Bild (optional):").grid(row=2, column=0, padx=5, pady=5, sticky="w")
self.eh_cover_path = ctk.CTkEntry(input_frame, width=300, placeholder_text="cover.jpg (optional)")
self.eh_cover_path.grid(row=2, column=1, padx=5, pady=5, sticky="ew")
ctk.CTkButton(input_frame, text="...", width=50, command=self.browse_eh_cover_image).grid(row=2, column=2, padx=5, pady=5)
# Language
ctk.CTkLabel(input_frame, text="Sprache:").grid(row=3, column=0, padx=5, pady=5, sticky="w")
self.eh_language = ctk.CTkComboBox(input_frame, values=["en", "jp", "de"], width=100)
self.eh_language.set("en")
self.eh_language.grid(row=3, column=1, padx=5, pady=5, sticky="w")
input_frame.columnconfigure(1, weight=1)
# Buttons Frame
button_frame = ctk.CTkFrame(tab)
button_frame.pack(padx=10, pady=10, fill="x")
# HTML zu EPUB Button
self.eh_html_to_epub_btn = ctk.CTkButton(
button_frame,
text="HTML → EPUB konvertieren",
command=self.start_html_to_epub,
height=40
)
self.eh_html_to_epub_btn.pack(side="left", padx=5, fill="x", expand=True)
# EPUB zu HTML Button
self.eh_epub_to_html_btn = ctk.CTkButton(
button_frame,
text="EPUB → HTML konvertieren",
command=self.start_epub_to_html,
height=40,
fg_color="gray40"
)
self.eh_epub_to_html_btn.pack(side="left", padx=5, fill="x", expand=True)
# Progress Bar
self.eh_progress = ctk.CTkProgressBar(tab, width=400)
self.eh_progress.pack(padx=10, pady=5, fill="x")
self.eh_progress.set(0)
# Log Textfeld (readonly)
ctk.CTkLabel(tab, text="Logs:", anchor="w").pack(padx=10, pady=(10, 0), fill="x")
self.eh_log = ctk.CTkTextbox(tab, height=200, state="disabled")
self.eh_log.pack(padx=10, pady=5, fill="both", expand=True)
def browse_eh_html_folder(self):
folder = filedialog.askdirectory()
if folder:
self.eh_html_path.delete(0, "end")
self.eh_html_path.insert(0, folder)
def browse_eh_epub_folder(self):
folder = filedialog.askdirectory()
if folder:
self.eh_epub_path.delete(0, "end")
self.eh_epub_path.insert(0, folder)
def browse_eh_cover_image(self):
file = filedialog.askopenfilename(
title="Cover Bild auswählen",
filetypes=[("Bilddateien", "*.jpg *.jpeg *.png"), ("Alle Dateien", "*.*")]
)
if file:
self.eh_cover_path.delete(0, "end")
self.eh_cover_path.insert(0, file)
def start_html_to_epub(self):
# Validierung
if not self.eh_html_path.get() or not self.eh_epub_path.get():
messagebox.showerror("Fehler", "Bitte HTML- und EPUB-Ordner angeben!")
return
# Buttons deaktivieren
self.eh_html_to_epub_btn.configure(state="disabled", text="Läuft...")
self.eh_epub_to_html_btn.configure(state="disabled")
self.eh_progress.set(0)
self.eh_log.configure(state="normal")
self.eh_log.delete("1.0", "end")
self.eh_log.configure(state="disabled")
# Thread starten
thread = threading.Thread(
target=self.run_html_to_epub,
args=(self.eh_html_path.get(), self.eh_epub_path.get(),
self.eh_language.get(), self.eh_cover_path.get() or None),
daemon=True
)
thread.start()
def run_html_to_epub(self, html_path, epub_path, language, cover_path):
# Log umleiten
old_stdout = sys.stdout
sys.stdout = LogRedirector(self.eh_log)
try:
self.eh_progress.set(0.3)
print("Starte HTML → EPUB Konvertierung...")
epub_handler = EpubHandler(html_path, epub_path)
self.eh_progress.set(0.5)
epub_handler.convertHtmlToEpub(language, cover_path)
self.eh_progress.set(1.0)
print("\n✅ EPUB erfolgreich erstellt!")
messagebox.showinfo("Erfolg", "EPUB wurde erfolgreich erstellt!")
except Exception as e:
print(f"\n❌ Fehler: {str(e)}")
messagebox.showerror("Fehler", f"Ein Fehler ist aufgetreten:\n{str(e)}")
finally:
sys.stdout = old_stdout
self.eh_html_to_epub_btn.configure(state="normal", text="HTML → EPUB konvertieren")
self.eh_epub_to_html_btn.configure(state="normal")
def start_epub_to_html(self):
# Validierung
if not self.eh_epub_path.get() or not self.eh_html_path.get():
messagebox.showerror("Fehler", "Bitte HTML- und EPUB-Ordner angeben!")
return
# EPUB-Datei auswählen
epub_file = filedialog.askopenfilename(
title="EPUB-Datei auswählen",
initialdir=self.eh_epub_path.get(),
filetypes=[("EPUB Dateien", "*.epub"), ("Alle Dateien", "*.*")]
)
if not epub_file:
return
# Buttons deaktivieren
self.eh_html_to_epub_btn.configure(state="disabled")
self.eh_epub_to_html_btn.configure(state="disabled", text="Läuft...")
self.eh_progress.set(0)
self.eh_log.configure(state="normal")
self.eh_log.delete("1.0", "end")
self.eh_log.configure(state="disabled")
# Thread starten
thread = threading.Thread(
target=self.run_epub_to_html,
args=(self.eh_html_path.get(), self.eh_epub_path.get(), epub_file),
daemon=True
)
thread.start()
def run_epub_to_html(self, html_path, epub_path, epub_file):
# Log umleiten
old_stdout = sys.stdout
sys.stdout = LogRedirector(self.eh_log)
try:
self.eh_progress.set(0.3)
print("Starte EPUB → HTML Konvertierung...")
epub_handler = EpubHandler(html_path, epub_path)
self.eh_progress.set(0.5)
epub_handler.epub_to_html(epub_file)
self.eh_progress.set(1.0)
print("\n✅ HTML-Dateien erfolgreich erstellt!")
messagebox.showinfo("Erfolg", "HTML-Dateien wurden erfolgreich erstellt!")
except Exception as e:
print(f"\n❌ Fehler: {str(e)}")
messagebox.showerror("Fehler", f"Ein Fehler ist aufgetreten:\n{str(e)}")
finally:
sys.stdout = old_stdout
self.eh_html_to_epub_btn.configure(state="normal")
self.eh_epub_to_html_btn.configure(state="normal", text="EPUB → HTML konvertieren")
if __name__ == "__main__":
app = App()
app.mainloop()