From 692d4fb2fe5dee5d8f0fbee035e1674bc9cd1da3 Mon Sep 17 00:00:00 2001 From: Akamaru Date: Wed, 30 Jul 2025 14:26:37 +0200 Subject: [PATCH] =?UTF-8?q?F=C3=BCge=20Checkbox=20f=C3=BCr=20Serien=20ohne?= =?UTF-8?q?=20klassische=20Staffeln=20hinzu=20und=20aktualisiere=20die=20L?= =?UTF-8?q?ogik=20zur=20Episodensuche,=20um=20auch=20Jahresformate=20und?= =?UTF-8?q?=20"bisher=20X=20Folgen"=20zu=20unterst=C3=BCtzen.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- serien_checker.py | 186 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 142 insertions(+), 44 deletions(-) diff --git a/serien_checker.py b/serien_checker.py index 1f8063f..06aa8e8 100644 --- a/serien_checker.py +++ b/serien_checker.py @@ -10,7 +10,7 @@ from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QLabel, QMessageBox, QSpinBox, QComboBox, QTableWidget, QTableWidgetItem, QHeaderView, QDialog, QDialogButtonBox, QTextEdit, QGroupBox, QToolBar, QSplitter, QFormLayout, - QListWidgetItem) + QListWidgetItem, QCheckBox) from PyQt5.QtCore import Qt, QTimer # Logging Konfiguration @@ -83,6 +83,11 @@ class NewSeriesDialog(QDialog): self.date_pref.addItems(["Bevorzuge Erstausstrahlung", "Bevorzuge TV", "Bevorzuge Streaming", "Bevorzuge deutsche Synchro"]) settings_layout.addRow("Datum Präferenz:", self.date_pref) + # Checkbox für Serien ohne klassische Staffeln + self.no_seasons_check = QCheckBox("Serie ohne klassische Staffeln") + self.no_seasons_check.setToolTip("Aktivieren für Serien mit fortlaufender Nummerierung oder Jahresformat") + settings_layout.addRow("", self.no_seasons_check) + settings_group.setLayout(settings_layout) layout.addWidget(settings_group) @@ -145,7 +150,8 @@ class NewSeriesDialog(QDialog): 'mode': self.staffel_mode.currentText(), 'staffel': self.staffel_spin.value() }, - 'date_preference': self.date_pref.currentText() + 'date_preference': self.date_pref.currentText(), + 'no_seasons': self.no_seasons_check.isChecked() } class SeriesEditDialog(QDialog): @@ -224,6 +230,11 @@ class SeriesEditDialog(QDialog): self.date_pref.addItems(["Bevorzuge Erstausstrahlung", "Bevorzuge TV", "Bevorzuge Streaming", "Bevorzuge deutsche Synchro"]) settings_layout.addRow("Datum Präferenz:", self.date_pref) + # Checkbox für Serien ohne klassische Staffeln + self.no_seasons_check = QCheckBox("Serie ohne klassische Staffeln") + self.no_seasons_check.setToolTip("Aktivieren für Serien mit fortlaufender Nummerierung oder Jahresformat") + settings_layout.addRow("", self.no_seasons_check) + # Speichern Button für Einstellungen save_button = QPushButton("Einstellungen speichern") save_button.clicked.connect(self.save_settings) @@ -269,7 +280,8 @@ class SeriesEditDialog(QDialog): 'mode': self.staffel_mode.currentText(), 'staffel': self.staffel_spin.value() }, - 'date_preference': self.date_pref.currentText() + 'date_preference': self.date_pref.currentText(), + 'no_seasons': self.no_seasons_check.isChecked() }) QMessageBox.information(self, "Erfolg", "Einstellungen wurden gespeichert!") @@ -328,6 +340,9 @@ class SeriesEditDialog(QDialog): if index >= 0: self.date_pref.setCurrentIndex(index) + # Keine Staffeln Checkbox laden + self.no_seasons_check.setChecked(data.get('no_seasons', False)) + def delete_series(self): """Löscht die ausgewählte Serie""" current_item = self.series_list.currentItem() @@ -592,14 +607,25 @@ class SerienChecker(QMainWindow): # 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}") + # Prüfe auf "Folge X" Format + folge_match = re.search(r'Folge (\d+)', episode_info.text) + if folge_match: + folge = int(folge_match.group(1)) + # Bei Serien ohne Staffeln setzen wir Staffel auf 1 + staffel = 1 + logging.debug(f"Gefundene Folge aus 'Folge X' Format: {folge}") + else: + try: + 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}") + except ValueError: + logging.debug(f"Konnte Folge nicht aus Text extrahieren: {episode_info.text}") # Suche nach deutschem Titel if episode_link: @@ -649,34 +675,74 @@ class SerienChecker(QMainWindow): 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}") + headers = soup.find_all('h2', class_='header-2015') + + for header in headers: + link = header.find('a', class_='fs-linkable') + if not link or not link.get('href'): + continue + + href = link['href'] + # Prüfe auf normale Staffel-Links + match = re.search(r'/staffel-(\d+)/(\d+)$', href) + if match: + s_nr = int(match.group(1)) + serie_id = match.group(2) + staffel_links.append(('staffel', s_nr, serie_id)) + logging.debug(f"Gefundene Staffel: {s_nr} mit ID: {serie_id}") + continue + + # Prüfe auf "bisher X Folgen" oder Jahres-Format + match = re.search(r'episodenguide/(\d+)/(\d+)$', href) + if match: + section_id = match.group(1) + serie_id = match.group(2) + # Prüfe den Text für die Art des Eintrags + text = link.text.strip() + if "bisher" in text and "Folgen" in text: + logging.debug(f"Gefunden 'bisher X Folgen' mit ID: {serie_id}") + staffel_links.append(('folgen', section_id, serie_id)) + elif text.isdigit(): # Jahresformat + logging.debug(f"Gefunden Jahr {text} mit ID: {serie_id}") + staffel_links.append(('jahr', section_id, serie_id)) if not staffel_links: - logging.warning("Keine Staffeln gefunden!") + logging.warning("Keine Staffeln oder Episodengruppen 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: + # Suche nach der gewünschten Staffel (nur für normale Staffeln) + for typ, nr, serie_id in staffel_links: + if typ == 'staffel' and 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}") + # Nehme den neuesten Eintrag + if not staffel_links: + return None + + # Sortiere nach Typ und dann nach Nummer/ID + def sort_key(x): + typ, nr, serie_id = x + type_priority = {'staffel': 3, 'jahr': 2, 'folgen': 1} + # Bei Staffeln nach Staffelnummer sortieren, sonst nach section_id + if typ == 'staffel': + return (type_priority.get(typ, 0), int(nr)) # nr ist hier die Staffelnummer + else: + return (type_priority.get(typ, 0), int(nr)) # nr ist hier die section_id + + newest = max(staffel_links, key=sort_key) + typ, nr, serie_id = newest + + if typ == 'staffel': + url = f"https://www.fernsehserien.de/{slug}/episodenguide/staffel-{nr}/{serie_id}" + logging.debug(f"Generierte URL für neueste Staffel ({nr}): {url}") + else: + url = f"https://www.fernsehserien.de/{slug}/episodenguide/{nr}/{serie_id}" + logging.debug(f"Generierte URL für Episodengruppe: {url}") return url except Exception as e: @@ -728,21 +794,53 @@ class SerienChecker(QMainWindow): 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 - }) + page = 1 + # Prüfe ob die Serie als "ohne klassische Staffeln" markiert ist + no_seasons = selected_data.get('no_seasons', False) + + while True: + current_url = f"{url}/{page}" if page > 1 and no_seasons else url + logging.debug(f"Hole Episoden von: {current_url}") + + try: + response = requests.get(current_url) + if response.status_code == 404: + logging.debug(f"Seite {page} nicht gefunden, beende Suche") + break + + # Bei normalen Staffeln nur eine Seite laden + if not no_seasons: + logging.debug("Serie hat klassische Staffeln, lade nur aktuelle Seite") + + response.raise_for_status() # Prüfe auf andere HTTP-Fehler + soup = BeautifulSoup(response.text, 'html.parser') + + # Prüfe ob die Seite Episoden enthält + page_episodes = soup.find_all('section', {'itemprop': 'episode'}) + if not page_episodes: + logging.debug(f"Keine Episoden auf Seite {page} gefunden, beende Suche") + break + + for episode in page_episodes: + 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 + }) + + # Bei normalen Staffeln nach der ersten Seite aufhören + if not no_seasons: + break + page += 1 + + except requests.RequestException as e: + logging.error(f"Fehler beim Abrufen von Seite {page}: {str(e)}") + break if not episodes: self.episodes_table.setRowCount(1) @@ -760,8 +858,8 @@ class SerienChecker(QMainWindow): x['folge'] )) - # Zeige maximal 20 Episoden an - episodes = episodes[:20] + # Zeige alle gefundenen Episoden an + # episodes = episodes[:20] # Alte Begrenzung entfernt # Aktualisiere die Tabelle self.episodes_table.setRowCount(len(episodes))