From f6f65696c6c1281be42262fee4fd79b8f8c3aec6 Mon Sep 17 00:00:00 2001 From: Akamaru Date: Tue, 29 Jul 2025 21:03:24 +0200 Subject: [PATCH] Erste Version --- .gitignore | 5 + README.md | 96 ++++++ Serien-Checker.spec | 45 +++ build.bat | 15 + build.py | 20 ++ icon.ico | Bin 0 -> 196688 bytes requirements.txt | 3 + serien_checker.py | 776 ++++++++++++++++++++++++++++++++++++++++++++ start.bat | 2 + 9 files changed, 962 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 Serien-Checker.spec create mode 100644 build.bat create mode 100644 build.py create mode 100644 icon.ico create mode 100644 requirements.txt create mode 100644 serien_checker.py create mode 100644 start.bat diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a533f8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +/build +/dist +/versions +series_config.json +release.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..8ad0ad1 --- /dev/null +++ b/README.md @@ -0,0 +1,96 @@ +# Serien-Checker + +Ein Programm zum Überprüfen von Ausstrahlungsterminen deutscher TV-Serien. Die Daten werden von fernsehserien.de abgerufen. + +## Features + +- Verfolgen Sie mehrere Serien gleichzeitig +- Anzeige deutscher Ausstrahlungstermine (TV und Streaming) +- Staffel-spezifische Filterung +- Datumspräferenz (TV, Streaming oder früheste Ausstrahlung) +- Übersichtliche Episodenliste mit Datum, Staffel, Folge und Titel + +## Installation + +### Option 1: Ausführbare Datei (Windows) + +1. Laden Sie die neueste Version von der [Releases](https://git.ponywave.de/Akamaru/Serien-Checker/releases) Seite herunter +2. Entpacken Sie die ZIP-Datei +3. Starten Sie `Serien-Checker.exe` + +### Option 2: Aus dem Quellcode + +1. Stellen Sie sicher, dass Python 3.8 oder höher installiert ist +2. Klonen Sie das Repository: + ```bash + git clone https://git.ponywave.de/Akamaru/Serien-Checker.git + cd Serien-Checker + ``` +3. Installieren Sie die Abhängigkeiten: + ```bash + pip install -r requirements.txt + ``` +4. Starten Sie das Programm: + ```bash + python serien_checker.py + ``` + +### Executable erstellen + +Um Ihre eigene ausführbare Datei zu erstellen: + +1. Führen Sie `build.bat` aus, oder +2. Manuell: + ```bash + pip install -r requirements.txt + pip install pyinstaller + python build.py + ``` + +Die ausführbare Datei finden Sie dann im `dist` Ordner. + +## Verwendung + +### Serien hinzufügen + +1. Klicken Sie auf "Serien verwalten" +2. Klicken Sie auf "Neue Serie" +3. Geben Sie die URL oder den Slug von fernsehserien.de ein + - Beispiel URL: `https://www.fernsehserien.de/9-1-1-notruf-l-a` + - Beispiel Slug: `9-1-1-notruf-l-a` +4. Wählen Sie die gewünschten Einstellungen: + - Staffel-Modus (Neuste, Alle, Bestimmte) + - Datumspräferenz (Erstausstrahlung, TV, Streaming) + +### Serien verwalten + +- Wählen Sie eine Serie aus der Liste +- Ändern Sie die Einstellungen nach Bedarf +- Klicken Sie auf "Einstellungen speichern" +- Löschen Sie unerwünschte Serien mit dem "Löschen" Button + +### Episoden anzeigen + +- Wählen Sie eine Serie aus der Liste im Hauptfenster +- Die Episoden werden automatisch geladen +- Die Liste wird alle 30 Minuten automatisch aktualisiert +- Klicken Sie auf "Aktualisieren" für sofortige Aktualisierung + +## Konfiguration + +Die Einstellungen werden automatisch in `series_config.json` gespeichert. Diese Datei wird beim ersten Start erstellt und enthält: +- Liste der Serien +- Staffel-Einstellungen pro Serie +- Datumspräferenzen pro Serie + +## Fehlerbehebung + +### Keine Episoden werden angezeigt +- Prüfen Sie Ihre Internetverbindung +- Prüfen Sie, ob die Serie auf fernsehserien.de verfügbar ist +- Prüfen Sie die Staffel-Einstellungen + +### Keine deutschen Titel +- Einige Episoden haben noch keine deutschen Titel +- Diese werden als "Noch kein Titel" angezeigt +- Die Titel werden automatisch aktualisiert, sobald sie verfügbar sind \ No newline at end of file diff --git a/Serien-Checker.spec b/Serien-Checker.spec new file mode 100644 index 0000000..3a94dd7 --- /dev/null +++ b/Serien-Checker.spec @@ -0,0 +1,45 @@ +# -*- mode: python ; coding: utf-8 -*- + + +block_cipher = None + + +a = Analysis( + ['serien_checker.py'], + pathex=[], + binaries=[], + datas=[('series_config.json', '.')], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False, +) +pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) + +exe = EXE( + pyz, + a.scripts, + a.binaries, + a.zipfiles, + a.datas, + [], + name='Serien-Checker', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + upx_exclude=[], + runtime_tmpdir=None, + console=False, + disable_windowed_traceback=False, + argv_emulation=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon=['icon.ico'], +) diff --git a/build.bat b/build.bat new file mode 100644 index 0000000..79ee5e9 --- /dev/null +++ b/build.bat @@ -0,0 +1,15 @@ +@echo off +echo Installing required packages... +pip install -r requirements.txt +pip install pyinstaller + +echo Building executable... +python build.py + +echo Done! +if exist "dist\Serien-Checker.exe" ( + echo Executable created successfully at dist\Serien-Checker.exe +) else ( + echo Error: Build failed! +) +pause \ No newline at end of file diff --git a/build.py b/build.py new file mode 100644 index 0000000..864908c --- /dev/null +++ b/build.py @@ -0,0 +1,20 @@ +import PyInstaller.__main__ +import os +import shutil + +# Lösche alte build und dist Ordner +if os.path.exists('build'): + shutil.rmtree('build') +if os.path.exists('dist'): + shutil.rmtree('dist') + +# PyInstaller Konfiguration +PyInstaller.__main__.run([ + 'serien_checker.py', + '--onefile', + '--windowed', + '--name=Serien-Checker', + '--icon=icon.ico', # Optional: Fügen Sie ein Icon hinzu wenn gewünscht + '--add-data=series_config.json;.', # Fügt die Konfigurationsdatei hinzu wenn sie existiert + '--clean' +]) \ No newline at end of file diff --git a/icon.ico b/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..c1c035b2cc3cd02b9bd5f1a3c53119357db0e9cb GIT binary patch literal 196688 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 auf PonyGit') + info_label.setOpenExternalLinks(True) # Erlaubt das Öffnen des Links + info_label.setTextFormat(Qt.RichText) # Aktiviert HTML-Formatierung + left_layout.addWidget(info_label) + + content_layout.addLayout(left_layout) + + # Rechte Seite - Einstellungen + self.settings_group = QGroupBox("Einstellungen") + settings_layout = QFormLayout() + + # Serien-Eingabe + self.slug_input = QLineEdit() + self.slug_input.setPlaceholderText("Serien-URL oder Slug") + self.slug_input.setEnabled(False) # Deaktiviert, da nur zum Anzeigen + settings_layout.addRow("Serie:", self.slug_input) + + # Staffel-Einstellungen + self.staffel_mode = QComboBox() + self.staffel_mode.addItems(["Neuste Staffel", "Alle Staffeln", "Bestimmte Staffel"]) + self.staffel_mode.currentIndexChanged.connect(self.on_staffel_mode_changed) + settings_layout.addRow("Staffel-Modus:", self.staffel_mode) + + self.staffel_spin = QSpinBox() + self.staffel_spin.setMinimum(1) + self.staffel_spin.setMaximum(100) + self.staffel_spin.setEnabled(False) + settings_layout.addRow("Staffel:", self.staffel_spin) + + # Datumspräferenz + self.date_pref = QComboBox() + self.date_pref.addItems(["Bevorzuge Erstausstrahlung", "Bevorzuge TV", "Bevorzuge Streaming"]) + settings_layout.addRow("Datum Präferenz:", self.date_pref) + + # Speichern Button für Einstellungen + save_button = QPushButton("Einstellungen speichern") + save_button.clicked.connect(self.save_settings) + settings_layout.addRow("", save_button) + + self.settings_group.setLayout(settings_layout) + content_layout.addWidget(self.settings_group) + + # Füge das Content-Layout zum Hauptlayout hinzu + layout.addLayout(content_layout) + + # Dialog Buttons + buttons = QDialogButtonBox( + QDialogButtonBox.Ok | QDialogButtonBox.Cancel, + Qt.Horizontal, self) + buttons.accepted.connect(self.accept) + buttons.rejected.connect(self.reject) + layout.addWidget(buttons) + + # Initialisiere die Liste und deaktiviere die Einstellungen + self.update_series_list() + self.settings_group.setEnabled(False) + + def accept(self): + """Schließt den Dialog""" + super().accept() # Schließe den Dialog einfach + + def save_settings(self): + """Speichert die Einstellungen für die aktuelle Serie""" + current_item = self.series_list.currentItem() + if not current_item: + QMessageBox.warning(self, "Fehler", "Bitte wählen Sie eine Serie zum Bearbeiten aus!") + return + + slug = current_item.data(Qt.UserRole) + if slug not in self.series_data: + QMessageBox.warning(self, "Fehler", "Serie nicht gefunden!") + return + + # Aktualisiere die Einstellungen + self.series_data[slug].update({ + 'staffel_setting': { + 'mode': self.staffel_mode.currentText(), + 'staffel': self.staffel_spin.value() + }, + 'date_preference': self.date_pref.currentText() + }) + + QMessageBox.information(self, "Erfolg", "Einstellungen wurden gespeichert!") + + def add_series(self): + """Öffnet den Dialog zum Hinzufügen einer neuen Serie""" + dialog = NewSeriesDialog(self) + if dialog.exec_() == QDialog.Accepted: + series_data = dialog.get_series_data() + if series_data: + slug = series_data['slug'] + self.series_data[slug] = series_data + self.update_series_list() + # Wähle die neue Serie aus + for i in range(self.series_list.count()): + if self.series_list.item(i).data(Qt.UserRole) == slug: + self.series_list.setCurrentRow(i) + break + QMessageBox.information(self, "Erfolg", "Serie wurde hinzugefügt!") + + def on_staffel_mode_changed(self, index): + """Wird aufgerufen, wenn sich der Staffel-Modus ändert""" + self.staffel_spin.setEnabled(self.staffel_mode.currentText() == "Bestimmte Staffel") + + def update_series_list(self): + """Aktualisiert die Liste der Serien""" + self.series_list.clear() + # Sortiere nach Namen + sorted_series = sorted(self.series_data.items(), key=lambda x: x[1]['name'].lower()) + for slug, data in sorted_series: + item = QListWidgetItem(data['name']) + item.setData(Qt.UserRole, slug) # Speichere den Slug als Zusatzdaten + self.series_list.addItem(item) + + def on_series_selected(self, item): + """Wird aufgerufen, wenn eine Serie ausgewählt wird""" + self.settings_group.setEnabled(bool(item)) + if item: + slug = item.data(Qt.UserRole) # Hole den Slug aus den Zusatzdaten + data = self.series_data.get(slug, {}) + self.slug_input.setText(data.get('slug', slug)) + + # Staffel-Einstellungen laden + staffel_setting = data.get('staffel_setting', {}) + mode = staffel_setting.get('mode', "Neuste Staffel") + staffel = staffel_setting.get('staffel', 1) + + index = self.staffel_mode.findText(mode) + if index >= 0: + self.staffel_mode.setCurrentIndex(index) + self.staffel_spin.setValue(staffel) + + # Datumspräferenz laden + date_pref = data.get('date_preference', "Bevorzuge Erstausstrahlung") + index = self.date_pref.findText(date_pref) + if index >= 0: + self.date_pref.setCurrentIndex(index) + + def delete_series(self): + """Löscht die ausgewählte Serie""" + current_item = self.series_list.currentItem() + if not current_item: + QMessageBox.warning(self, "Fehler", "Bitte wählen Sie eine Serie zum Löschen aus!") + return + + slug = current_item.data(Qt.UserRole) + name = self.series_data[slug]['name'] + + reply = QMessageBox.question( + self, + "Serie löschen", + f"Möchten Sie die Serie '{name}' wirklich löschen?", + QMessageBox.Yes | QMessageBox.No, + QMessageBox.No + ) + + if reply == QMessageBox.Yes: + del self.series_data[slug] + self.update_series_list() + self.slug_input.clear() + QMessageBox.information(self, "Erfolg", f"Serie '{name}' wurde gelöscht!") + +class SerienChecker(QMainWindow): + def __init__(self): + super().__init__() + self.setWindowTitle("Serien Checker") + self.setGeometry(100, 100, 1000, 800) + + # Initialisiere series als leeres Dictionary + self.series = {} + + # Log-Fenster erstellen + self.log_window = LogWindow(self) + + # Zentral-Widget und Layout + central_widget = QWidget() + self.setCentralWidget(central_widget) + layout = QVBoxLayout(central_widget) + + # Toolbar + toolbar = QToolBar() + self.addToolBar(toolbar) + + # Serien verwalten Button + manage_button = QPushButton("Serien verwalten") + manage_button.clicked.connect(self.manage_series) + toolbar.addWidget(manage_button) + + # Debug Log Button + debug_button = QPushButton("Debug Log") + debug_button.clicked.connect(self.show_debug_log) + toolbar.addWidget(debug_button) + + # Serien und Episoden Layout + content_layout = QHBoxLayout() + + # Linke Seite - Serien Liste + list_group = QGroupBox("Gespeicherte Serien") + list_layout = QVBoxLayout() + self.series_list = QListWidget() + self.series_list.currentItemChanged.connect(self.on_series_selected) + list_layout.addWidget(self.series_list) + list_group.setLayout(list_layout) + content_layout.addWidget(list_group) + + # Rechte Seite - Episoden + episodes_group = QGroupBox("Episoden") + episodes_layout = QVBoxLayout() + + self.episodes_table = QTableWidget() + self.episodes_table.setColumnCount(4) + self.episodes_table.setHorizontalHeaderLabels(["Datum", "Staffel", "Folge", "Titel"]) + header = self.episodes_table.horizontalHeader() + header.setSectionResizeMode(0, QHeaderView.ResizeToContents) + header.setSectionResizeMode(1, QHeaderView.ResizeToContents) + header.setSectionResizeMode(2, QHeaderView.ResizeToContents) + header.setSectionResizeMode(3, QHeaderView.Stretch) + episodes_layout.addWidget(self.episodes_table) + + # Aktualisieren Button + refresh_button = QPushButton("Aktualisieren") + refresh_button.clicked.connect(self.refresh_selected_series) + episodes_layout.addWidget(refresh_button) + + episodes_group.setLayout(episodes_layout) + content_layout.addWidget(episodes_group) + + # Füge das Content-Layout zum Hauptlayout hinzu + layout.addLayout(content_layout) + + # Timer für automatische Aktualisierung (alle 30 Minuten) + self.timer = QTimer() + self.timer.timeout.connect(self.refresh_selected_series) + self.timer.start(30 * 60 * 1000) + + # Lade die Konfiguration und aktualisiere die Liste + self.load_config() + self.update_series_list() + + self.show() + + def manage_series(self): + dialog = SeriesEditDialog(self.series, self) + if dialog.exec_() == QDialog.Accepted: + self.series = dialog.series_data + self.save_config() + self.update_series_list() + self.refresh_all() + + def load_config(self): + """Lädt die Konfiguration""" + try: + with open('series_config.json', 'r', encoding='utf-8') as f: + config = json.load(f) + if isinstance(config, dict) and 'series' in config: + self.series = config['series'] + else: + # Alte Konfiguration kompatibel machen + self.series = {} + for slug, data in config.items(): + self.series[slug] = { + 'name': data.get('name', slug), + 'staffel_setting': { + 'mode': data.get('settings', {}).get('mode', "Neuste Staffel"), + 'staffel': data.get('settings', {}).get('staffel', 1) + }, + 'date_preference': "Bevorzuge Erstausstrahlung" + } + except FileNotFoundError: + logging.debug("Keine Konfigurationsdatei gefunden, verwende leeres Dictionary") + self.series = {} + + def save_config(self): + """Speichert die Konfiguration""" + with open('series_config.json', 'w', encoding='utf-8') as f: + json.dump({'series': self.series}, f, indent=4, ensure_ascii=False) + + def update_series_list(self): + """Aktualisiert die Liste der Serien""" + self.series_list.clear() + # Sortiere nach Namen + sorted_series = sorted(self.series.items(), key=lambda x: x[1]['name'].lower()) + for slug, data in sorted_series: + item = QListWidgetItem(data['name']) + item.setData(Qt.UserRole, slug) # Speichere den Slug als Zusatzdaten + self.series_list.addItem(item) + + def parse_date(self, date_str): + try: + return datetime.strptime(date_str.split()[0], "%d.%m.%Y") + except: + return None + + def get_premiere_date(self, episode): + """Extrahiert das erste deutsche Ausstrahlungsdatum (TV oder Streaming) basierend auf der Präferenz""" + logging.debug("Suche nach Premierendaten") + try: + tv_date = None + streaming_date = None + + # Suche nach allen deutschen Premieren + for ea_angabe in episode.find_all('ea-angabe'): + titel_elem = ea_angabe.find('ea-angabe-titel') + if not titel_elem: + continue + + titel = titel_elem.text.strip() + logging.debug(f"Gefundene Premiere: {titel}") + + if "Deutsche" in titel: + datum_elem = ea_angabe.find('ea-angabe-datum') + if datum_elem: + date_str = datum_elem.text.strip() + if '. ' in date_str: + date_str = date_str.split('. ', 1)[1] + + parsed_date = self.parse_date(date_str) + if parsed_date: + if "TV-Premiere" in titel: + tv_date = (parsed_date, date_str) + logging.debug(f"Deutsche TV-Premiere gefunden: {date_str}") + elif "Streaming-Premiere" in titel: + streaming_date = (parsed_date, date_str) + logging.debug(f"Deutsche Streaming-Premiere gefunden: {date_str}") + + # Hole die Datumspräferenz für diese Serie + current_series = self.series_list.currentItem() + if current_series: + slug = current_series.data(Qt.UserRole) # Hole den Slug aus den Zusatzdaten + pref = self.series[slug].get('date_preference', "Bevorzuge Erstausstrahlung") + else: + pref = "Bevorzuge Erstausstrahlung" + + if pref == "Bevorzuge TV" and tv_date: + result = tv_date[1] + logging.debug(f"TV-Premiere gewählt: {result}") + elif pref == "Bevorzuge Streaming" and streaming_date: + result = streaming_date[1] + logging.debug(f"Streaming-Premiere gewählt: {result}") + else: # Bevorzuge Erstausstrahlung oder wenn bevorzugtes Datum nicht verfügbar + dates = [] + if tv_date: + dates.append(tv_date) + if streaming_date: + dates.append(streaming_date) + + if dates: + result = min(dates, key=lambda x: x[0])[1] + logging.debug(f"Frühestes Datum gewählt: {result}") + else: + logging.warning("Keine deutschen Premierendaten gefunden!") + return "TBA" + + return result + + except Exception as e: + logging.error(f"Fehler beim Extrahieren des Premierendatums: {str(e)}") + return "TBA" + + def on_date_preference_changed(self, series_slug): + """Wird aufgerufen, wenn sich die Datumspräferenz einer Serie ändert""" + logging.debug(f"Datumspräferenz für Serie {series_slug} wurde geändert") + self.refresh_selected_series() + + def get_episode_info(self, episode): + """Extrahiert Folgeninformationen aus einem Episode-Element""" + try: + logging.debug("Versuche Episodeninformationen zu extrahieren") + + # Suche nach Staffel und Folge in der Episodennummer (z.B. "7.01") + header = episode.find('h3', class_='episode-output-titel') + if header: + episode_link = header.find('a') + if episode_link: + # URL enthält die Episodennummer (z.B. "7x01") + href = episode_link.get('href', '') + match = re.search(r'(\d+)x(\d+)-', href) + if match: + staffel = int(match.group(1)) + folge = int(match.group(2)) + logging.debug(f"Gefundene Staffel/Folge aus URL: {staffel}/{folge}") + else: + # Alternative: Suche in der Episoden-Zeile + episode_info = episode.find('div', {'itemprop': 'episodeNumber'}) + if episode_info and episode_info.text: + folge = int(episode_info.text) + # Staffel aus übergeordnetem Element + staffel_info = episode.find_previous('h2', class_='header-2015') + if staffel_info: + staffel_match = re.search(r'Staffel (\d+)', staffel_info.text) + if staffel_match: + staffel = int(staffel_match.group(1)) + logging.debug(f"Gefundene Staffel/Folge aus Text: {staffel}/{folge}") + + # Suche nach deutschem Titel + if episode_link: + # Prüfe zuerst, ob ein deutscher Titel existiert + title_spans = episode_link.find_all('span') + title = "Noch kein Titel" + + for span in title_spans: + # Überspringe Folgen-Nummer + if span.text.isdigit(): + continue + # Überspringe englischen Titel + if span.get('class') and 'episode-output-originaltitel' in span.get('class'): + continue + # Überspringe Platzhalter für fehlenden Titel + if span.get('title') == "Titel unbekannt": + continue + # Wenn wir hier sind und der span itemprop="name" hat, ist es der deutsche Titel + if span.get('itemprop') == 'name' and span.text.strip() not in ['–', '-']: + title = span.text.strip() + logging.debug(f"Gefundener deutscher Titel: {title}") + break + + logging.debug(f"Finaler Titel: {title}") + else: + title = "Noch kein Titel" + logging.debug("Kein Link gefunden, verwende Standardtitel") + + if not all([staffel, folge]): + logging.warning(f"Unvollständige Daten: Staffel={staffel}, Folge={folge}") + return None, None, None + + return staffel, folge, title + + except Exception as e: + logging.error(f"Fehler beim Extrahieren der Episodeninformationen: {str(e)}") + return None, None, None + + def get_staffel_url(self, slug, staffel_nr=None): + """Generiert die URL für eine bestimmte Staffel""" + logging.debug(f"Generiere Staffel-URL für Slug: {slug}, Staffel: {staffel_nr}") + try: + # Hole die Übersichtsseite + url = f"https://www.fernsehserien.de/{slug}/episodenguide" + logging.debug(f"Hole Übersichtsseite: {url}") + response = requests.get(url) + soup = BeautifulSoup(response.text, 'html.parser') + + staffel_links = [] + for link in soup.find_all('a', href=True): + # Suche nach Staffel-Links und extrahiere die ID + if 'staffel-' in link['href']: + match = re.search(r'/staffel-(\d+)/(\d+)$', link['href']) + if match: + s_nr = int(match.group(1)) + serie_id = match.group(2) + staffel_links.append((s_nr, serie_id)) + logging.debug(f"Gefundene Staffel: {s_nr} mit ID: {serie_id}") + + if not staffel_links: + logging.warning("Keine Staffeln gefunden!") + return None + + if staffel_nr: + # Suche nach der gewünschten Staffel + for s_nr, serie_id in staffel_links: + if s_nr == staffel_nr: + url = f"https://www.fernsehserien.de/{slug}/episodenguide/staffel-{staffel_nr}/{serie_id}" + logging.debug(f"Generierte URL für spezifische Staffel: {url}") + return url + logging.warning(f"Staffel {staffel_nr} nicht gefunden!") + return None + else: + # Nehme die neueste Staffel + newest_staffel, serie_id = max(staffel_links, key=lambda x: x[0]) + url = f"https://www.fernsehserien.de/{slug}/episodenguide/staffel-{newest_staffel}/{serie_id}" + logging.debug(f"Generierte URL für neueste Staffel ({newest_staffel}): {url}") + return url + + except Exception as e: + logging.error(f"Fehler beim Generieren der Staffel-URL: {str(e)}") + return None + + def on_series_selected(self, current, previous): + """Wird aufgerufen, wenn eine Serie in der Liste ausgewählt wird""" + if current: + slug = current.data(Qt.UserRole) # Hole den Slug aus den Zusatzdaten + if slug in self.series: + self.refresh_selected_series() + else: + logging.warning(f"Serie {slug} nicht gefunden!") + else: + self.episodes_table.setRowCount(0) + + def refresh_selected_series(self): + """Aktualisiert die Episodenliste für die ausgewählte Serie""" + current_item = self.series_list.currentItem() + if not current_item: + logging.warning("Keine Serie ausgewählt!") + return + + slug = current_item.data(Qt.UserRole) # Hole den Slug aus den Zusatzdaten + if slug not in self.series: + logging.warning(f"Serie {slug} nicht gefunden!") + return + + selected_data = self.series[slug] + logging.debug(f"Aktualisiere Serie: {selected_data['name']}") + + try: + # Bestimme die Staffel-URL basierend auf den Einstellungen + settings = selected_data.get('staffel_setting', {}) + mode = settings.get('mode', "Neuste Staffel") + staffel_nr = settings.get('staffel') if mode == "Bestimmte Staffel" else None + + url = self.get_staffel_url(slug, staffel_nr) + if not url: + self.episodes_table.setRowCount(1) + if mode == "Bestimmte Staffel": + error_msg = f"Staffel {staffel_nr} wurde nicht gefunden!" + else: + error_msg = "Keine Staffeln gefunden!" + self.episodes_table.setItem(0, 0, QTableWidgetItem(error_msg)) + for i in range(1, 4): + self.episodes_table.setItem(0, i, QTableWidgetItem("")) + logging.error(error_msg) + return + + logging.debug(f"Hole Episoden von: {url}") + response = requests.get(url) + soup = BeautifulSoup(response.text, 'html.parser') + + episodes = [] + for episode in soup.find_all('section', {'itemprop': 'episode'}): + staffel, folge, titel = self.get_episode_info(episode) + if all([staffel, folge, titel]): + datum = self.get_premiere_date(episode) + episodes.append({ + 'date': datum, + 'staffel': staffel, + 'folge': folge, + 'titel': titel + }) + + if not episodes: + self.episodes_table.setRowCount(1) + error_msg = "Keine Episoden gefunden!" + self.episodes_table.setItem(0, 0, QTableWidgetItem(error_msg)) + for i in range(1, 4): + self.episodes_table.setItem(0, i, QTableWidgetItem("")) + logging.warning(error_msg) + return + + # Sortiere nach Datum (wenn verfügbar) und Staffel/Folge + episodes.sort(key=lambda x: ( + datetime.strptime(x['date'], '%d.%m.%Y') if x['date'] != 'TBA' else datetime.max, + x['staffel'], + x['folge'] + )) + + # Zeige maximal 20 Episoden an + episodes = episodes[:20] + + # Aktualisiere die Tabelle + self.episodes_table.setRowCount(len(episodes)) + for row, episode in enumerate(episodes): + self.episodes_table.setItem(row, 0, QTableWidgetItem(episode['date'])) + self.episodes_table.setItem(row, 1, QTableWidgetItem(str(episode['staffel']))) + self.episodes_table.setItem(row, 2, QTableWidgetItem(str(episode['folge']))) + self.episodes_table.setItem(row, 3, QTableWidgetItem(episode['titel'])) + + except Exception as e: + logging.error(f"Fehler beim Aktualisieren der Serie: {str(e)}") + self.episodes_table.setRowCount(1) + self.episodes_table.setItem(0, 0, QTableWidgetItem(f"Fehler: {str(e)}")) + for i in range(1, 4): + self.episodes_table.setItem(0, i, QTableWidgetItem("")) + + def refresh_all(self): + self.update_series_list() + if self.series_list.selectedItems(): + self.refresh_selected_series() + + def show_debug_log(self): + self.log_window.show() + +if __name__ == '__main__': + app = QApplication(sys.argv) + window = SerienChecker() + window.show() + sys.exit(app.exec_()) \ No newline at end of file diff --git a/start.bat b/start.bat new file mode 100644 index 0000000..03fa647 --- /dev/null +++ b/start.bat @@ -0,0 +1,2 @@ +@echo off +start "" /b pythonw.exe serien_checker.py \ No newline at end of file