Erweiterte Staffelwahl
This commit is contained in:
@@ -401,14 +401,30 @@ class SerienChecker(QMainWindow):
|
||||
# Serien und Episoden Layout
|
||||
content_layout = QHBoxLayout()
|
||||
|
||||
# Linke Seite - Serien Liste
|
||||
# Linke Seite - Serien Liste und Staffelauswahl
|
||||
left_widget = QWidget()
|
||||
left_layout = QVBoxLayout(left_widget)
|
||||
|
||||
# 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)
|
||||
left_layout.addWidget(list_group)
|
||||
|
||||
# Staffelauswahl
|
||||
season_group = QGroupBox("Staffelauswahl")
|
||||
season_layout = QVBoxLayout()
|
||||
self.season_combo = QComboBox()
|
||||
self.season_combo.addItem("Alle Staffeln")
|
||||
self.season_combo.currentIndexChanged.connect(self.on_season_selected)
|
||||
season_layout.addWidget(self.season_combo)
|
||||
season_group.setLayout(season_layout)
|
||||
left_layout.addWidget(season_group)
|
||||
|
||||
content_layout.addWidget(left_widget)
|
||||
|
||||
# Rechte Seite - Episoden
|
||||
episodes_group = QGroupBox("Episoden")
|
||||
@@ -664,47 +680,78 @@ class SerienChecker(QMainWindow):
|
||||
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}")
|
||||
def get_available_seasons(self, slug):
|
||||
"""Holt alle verfügbaren Staffeln einer Serie"""
|
||||
logging.debug(f"Hole verfügbare Staffeln für Slug: {slug}")
|
||||
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 = []
|
||||
headers = soup.find_all('h2', class_='header-2015')
|
||||
|
||||
for header in headers:
|
||||
link = header.find('a', class_='fs-linkable')
|
||||
# Suche nach dem Episodenguide-Menü
|
||||
menu_items = soup.find_all('li')
|
||||
for item in menu_items:
|
||||
link = item.find('a')
|
||||
if not link or not link.get('href'):
|
||||
continue
|
||||
|
||||
|
||||
href = link['href']
|
||||
title = link.text.strip()
|
||||
|
||||
# Überspringe irrelevante Links
|
||||
if any(x in href for x in ['/news', '/cast-crew', '/sendetermine']):
|
||||
continue
|
||||
|
||||
# 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}")
|
||||
staffel_links.append(('staffel', s_nr, serie_id, title))
|
||||
logging.debug(f"Gefundene Staffel: {title} 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))
|
||||
# Prüfe auf andere Formate (Jahre, Specials, etc.)
|
||||
if href.startswith('#'):
|
||||
# Überspringe "zurück nach oben" Link
|
||||
if title.lower() in ['zurück nach oben', 'nach oben']:
|
||||
continue
|
||||
|
||||
# Lokaler Anker, extrahiere den Namen
|
||||
section_name = href[1:] # Entferne das #
|
||||
staffel_links.append(('section', section_name, '0', title))
|
||||
logging.debug(f"Gefundene Sektion: {title}")
|
||||
else:
|
||||
# Prüfe auf andere Episodenguide-Links
|
||||
match = re.search(r'episodenguide/([^/]+)/(\d+)$', href)
|
||||
if match:
|
||||
section_id = match.group(1)
|
||||
serie_id = match.group(2)
|
||||
staffel_links.append(('other', section_id, serie_id, title))
|
||||
logging.debug(f"Gefundene andere Staffel: {title} mit ID: {serie_id}")
|
||||
|
||||
# Entferne Duplikate (basierend auf Titel)
|
||||
unique_links = []
|
||||
seen_titles = set()
|
||||
for link in staffel_links:
|
||||
if link[3] not in seen_titles:
|
||||
unique_links.append(link)
|
||||
seen_titles.add(link[3])
|
||||
|
||||
return unique_links
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"Fehler beim Abrufen der Staffeln: {str(e)}")
|
||||
return []
|
||||
|
||||
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:
|
||||
staffel_links = self.get_available_seasons(slug)
|
||||
|
||||
if not staffel_links:
|
||||
logging.warning("Keine Staffeln oder Episodengruppen gefunden!")
|
||||
@@ -712,7 +759,7 @@ class SerienChecker(QMainWindow):
|
||||
|
||||
if staffel_nr:
|
||||
# Suche nach der gewünschten Staffel (nur für normale Staffeln)
|
||||
for typ, nr, serie_id in staffel_links:
|
||||
for typ, nr, serie_id, title 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}")
|
||||
@@ -726,23 +773,42 @@ class SerienChecker(QMainWindow):
|
||||
|
||||
# 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
|
||||
typ, nr, serie_id, title = x
|
||||
type_priority = {'staffel': 3, 'other': 2, 'section': 1}
|
||||
|
||||
# Versuche nr als Zahl zu interpretieren, wenn möglich
|
||||
try:
|
||||
num_nr = int(nr)
|
||||
except (ValueError, TypeError):
|
||||
num_nr = 0
|
||||
|
||||
return (type_priority.get(typ, 0), num_nr)
|
||||
|
||||
# Finde den neuesten Eintrag
|
||||
newest = max(staffel_links, key=sort_key)
|
||||
typ, nr, serie_id = newest
|
||||
typ, nr, serie_id, title = newest
|
||||
|
||||
# Hole die Serie-ID aus einem beliebigen Link
|
||||
serie_id = None
|
||||
for t, n, sid, _ in staffel_links:
|
||||
if t == 'other' and sid != '0':
|
||||
serie_id = sid
|
||||
break
|
||||
|
||||
if not serie_id:
|
||||
logging.warning("Keine gültige Serie-ID gefunden!")
|
||||
return None
|
||||
|
||||
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}")
|
||||
logging.debug(f"Generierte URL für neueste Staffel ({title}): {url}")
|
||||
elif typ == 'section':
|
||||
# Bei Sektionen (Jahren) verwende die erste Sektion mit der Serie-ID
|
||||
url = f"https://www.fernsehserien.de/{slug}/episodenguide/1/{serie_id}"
|
||||
logging.debug(f"Generierte URL für Sektion ({title}): {url}")
|
||||
else:
|
||||
url = f"https://www.fernsehserien.de/{slug}/episodenguide/{nr}/{serie_id}"
|
||||
logging.debug(f"Generierte URL für Episodengruppe: {url}")
|
||||
logging.debug(f"Generierte URL für Episodengruppe ({title}): {url}")
|
||||
return url
|
||||
|
||||
except Exception as e:
|
||||
@@ -754,11 +820,103 @@ class SerienChecker(QMainWindow):
|
||||
if current:
|
||||
slug = current.data(Qt.UserRole) # Hole den Slug aus den Zusatzdaten
|
||||
if slug in self.series:
|
||||
# Aktualisiere das Staffel-Dropdown
|
||||
self.season_combo.clear()
|
||||
self.season_combo.addItem("Alle Staffeln")
|
||||
|
||||
# Hole verfügbare Staffeln
|
||||
staffel_links = self.get_available_seasons(slug)
|
||||
# Sortiere: Erst normale Staffeln (nach Nummer), dann andere (alphabetisch)
|
||||
sorted_links = sorted(staffel_links,
|
||||
key=lambda x: (x[0] != 'staffel', # Staffeln zuerst
|
||||
int(x[1]) if x[0] == 'staffel' else 0, # Nach Staffelnummer
|
||||
x[3].lower())) # Dann alphabetisch nach Titel
|
||||
|
||||
for typ, nr, serie_id, title in sorted_links:
|
||||
self.season_combo.addItem(title, (typ, nr, serie_id))
|
||||
|
||||
self.refresh_selected_series()
|
||||
else:
|
||||
logging.warning(f"Serie {slug} nicht gefunden!")
|
||||
else:
|
||||
self.episodes_table.setRowCount(0)
|
||||
self.season_combo.clear()
|
||||
self.season_combo.addItem("Alle Staffeln")
|
||||
|
||||
def get_section_episodes(self, soup, section_id):
|
||||
"""Extrahiert Episoden aus einer bestimmten Sektion"""
|
||||
episodes = []
|
||||
|
||||
# Finde die Sektion anhand der ID
|
||||
section = soup.find(id=section_id)
|
||||
if section:
|
||||
logging.debug(f"Sektion {section_id} gefunden")
|
||||
# Suche nach allen Episoden in dieser Sektion
|
||||
# Suche zuerst in der Tabelle
|
||||
episode_table = section.find('table', class_='episode-output')
|
||||
if episode_table:
|
||||
logging.debug("Episodentabelle gefunden")
|
||||
for row in episode_table.find_all('tr'):
|
||||
# Überspringe Header-Zeilen
|
||||
if row.find('th'):
|
||||
continue
|
||||
|
||||
cells = row.find_all('td')
|
||||
if len(cells) >= 4: # Datum, Folge, Titel, ...
|
||||
try:
|
||||
# Extrahiere das Datum
|
||||
datum_cell = cells[0]
|
||||
datum = datum_cell.text.strip()
|
||||
|
||||
# Extrahiere die Folgennummer
|
||||
folge_cell = cells[1]
|
||||
folge_text = folge_cell.text.strip()
|
||||
folge_match = re.search(r'(\d+)', folge_text)
|
||||
if folge_match:
|
||||
folge = int(folge_match.group(1))
|
||||
else:
|
||||
continue
|
||||
|
||||
# Extrahiere den Titel
|
||||
titel_cell = cells[2]
|
||||
titel = titel_cell.text.strip()
|
||||
if not titel:
|
||||
titel = "Noch kein Titel"
|
||||
|
||||
# Verwende die Jahreszahl als Staffelnummer
|
||||
staffel = int(section_id) if section_id.isdigit() else 1
|
||||
|
||||
episodes.append({
|
||||
'date': datum,
|
||||
'staffel': staffel,
|
||||
'folge': folge,
|
||||
'titel': titel
|
||||
})
|
||||
logging.debug(f"Episode gefunden: Staffel {staffel}, Folge {folge}, Titel: {titel}")
|
||||
|
||||
except (ValueError, AttributeError) as e:
|
||||
logging.debug(f"Fehler beim Parsen einer Zeile: {str(e)}")
|
||||
continue
|
||||
else:
|
||||
logging.debug("Keine Episodentabelle gefunden")
|
||||
|
||||
# Wenn keine Tabelle gefunden wurde, suche nach einzelnen Episoden-Sektionen
|
||||
if not episodes:
|
||||
episode_sections = section.find_all('section', {'itemprop': 'episode'})
|
||||
for episode in episode_sections:
|
||||
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
|
||||
})
|
||||
else:
|
||||
logging.debug(f"Sektion {section_id} nicht gefunden")
|
||||
|
||||
return episodes
|
||||
|
||||
def refresh_selected_series(self):
|
||||
"""Aktualisiert die Episodenliste für die ausgewählte Serie"""
|
||||
@@ -776,18 +934,32 @@ class SerienChecker(QMainWindow):
|
||||
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
|
||||
# Hole die ausgewählte Staffel aus dem Dropdown
|
||||
current_index = self.season_combo.currentIndex()
|
||||
if current_index <= 0: # "Alle Staffeln" oder keine Auswahl
|
||||
# Verwende die Einstellungen aus der Konfiguration
|
||||
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)
|
||||
else:
|
||||
# Verwende die ausgewählte Staffel
|
||||
dropdown_data = self.season_combo.itemData(current_index)
|
||||
if dropdown_data:
|
||||
typ, nr, serie_id = dropdown_data
|
||||
if typ == 'staffel':
|
||||
url = f"https://www.fernsehserien.de/{slug}/episodenguide/staffel-{nr}/{serie_id}"
|
||||
elif typ == 'section':
|
||||
# Bei lokalen Ankern die Hauptseite verwenden und nach der Sektion suchen
|
||||
url = f"https://www.fernsehserien.de/{slug}/episodenguide"
|
||||
else:
|
||||
url = f"https://www.fernsehserien.de/{slug}/episodenguide/{nr}/{serie_id}"
|
||||
else:
|
||||
url = 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!"
|
||||
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(""))
|
||||
@@ -795,33 +967,35 @@ class SerienChecker(QMainWindow):
|
||||
return
|
||||
|
||||
episodes = []
|
||||
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(url)
|
||||
response.raise_for_status()
|
||||
soup = BeautifulSoup(response.text, 'html.parser')
|
||||
|
||||
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
|
||||
# Wenn es eine spezifische Sektion ist, suche nur in dieser
|
||||
if current_index > 0:
|
||||
dropdown_data = self.season_combo.itemData(current_index)
|
||||
if dropdown_data and dropdown_data[0] == 'section':
|
||||
section_id = dropdown_data[1]
|
||||
episodes = self.get_section_episodes(soup, section_id)
|
||||
logging.debug(f"Suche Episoden in Sektion: {section_id}")
|
||||
else:
|
||||
# Normale Episodensuche für Staffeln
|
||||
page_episodes = soup.find_all('section', {'itemprop': 'episode'})
|
||||
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
|
||||
})
|
||||
else:
|
||||
# Bei "Alle Staffeln" alle Episoden laden
|
||||
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]):
|
||||
@@ -833,14 +1007,13 @@ class SerienChecker(QMainWindow):
|
||||
'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
|
||||
except requests.RequestException as e:
|
||||
logging.error(f"Fehler beim Abrufen der Episoden: {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(""))
|
||||
return
|
||||
|
||||
if not episodes:
|
||||
self.episodes_table.setRowCount(1)
|
||||
@@ -883,6 +1056,11 @@ class SerienChecker(QMainWindow):
|
||||
|
||||
def show_debug_log(self):
|
||||
self.log_window.show()
|
||||
|
||||
def on_season_selected(self, index):
|
||||
"""Wird aufgerufen, wenn eine andere Staffel im Dropdown ausgewählt wird"""
|
||||
if self.series_list.currentItem():
|
||||
self.refresh_selected_series()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
Reference in New Issue
Block a user