From f90c17f404dca697a6ba884d5eb7ef4ee4c57929 Mon Sep 17 00:00:00 2001 From: Manuel Weiser Date: Sat, 5 Jul 2025 20:11:58 +0200 Subject: [PATCH] =?UTF-8?q?Aktualisiere=20Basis-URL=20auf=20'https://www.s?= =?UTF-8?q?erman.club'=20in=20local=5Fpodcast=5Fgenerator.py=20und=20main.?= =?UTF-8?q?py;=20f=C3=BCge=20Funktion=20zum=20Extrahieren=20und=20Speicher?= =?UTF-8?q?n=20von=20Episode-Covern=20hinzu.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- local_podcast_generator.py | 62 +++++++++++++++++++++++++++++++++----- main.py | 4 +-- 2 files changed, 57 insertions(+), 9 deletions(-) diff --git a/local_podcast_generator.py b/local_podcast_generator.py index 64ba312..faa18af 100644 --- a/local_podcast_generator.py +++ b/local_podcast_generator.py @@ -16,7 +16,7 @@ from pathlib import Path class LocalPodcastGenerator: - def __init__(self, audio_dir="../httpdocs/_audio", output_file="podcast_feed.xml", base_url="http://localhost:8087"): + def __init__(self, audio_dir="../httpdocs/_audio", output_file="podcast_feed.xml", base_url="https://www.serman.club"): self.audio_dir = audio_dir self.output_file = output_file self.base_url = base_url.rstrip('/') @@ -46,11 +46,21 @@ class LocalPodcastGenerator: artist = None album = None duration = None + cover_data = None if audio.tags: title = str(audio.tags.get('TIT2', [''])[0]) if audio.tags.get('TIT2') else None artist = str(audio.tags.get('TPE1', [''])[0]) if audio.tags.get('TPE1') else None album = str(audio.tags.get('TALB', [''])[0]) if audio.tags.get('TALB') else None + + # Cover-Art extrahieren (APIC = Attached Picture) + if 'APIC:' in audio.tags: + cover_data = audio.tags.get('APIC:').data + elif audio.tags.get('APIC'): + # Fallback für andere APIC-Varianten + apic_tags = [tag for tag in audio.tags if tag.startswith('APIC')] + if apic_tags: + cover_data = audio.tags.get(apic_tags[0]).data # Dauer in Sekunden if hasattr(audio, 'info') and audio.info.length: @@ -73,7 +83,8 @@ class LocalPodcastGenerator: 'duration': duration, 'file_size': file_size, 'pub_date': pub_date, - 'filename': file_path.name + 'filename': file_path.name, + 'cover_data': cover_data } except Exception as e: @@ -86,9 +97,38 @@ class LocalPodcastGenerator: 'duration': None, 'file_size': file_path.stat().st_size, 'pub_date': datetime.fromtimestamp(file_path.stat().st_mtime), - 'filename': file_path.name + 'filename': file_path.name, + 'cover_data': None } + def extract_episode_cover(self, metadata, episode_index): + """Extrahiert und speichert das Episode-Cover.""" + if not metadata['cover_data']: + return None + + try: + # Erstelle Cover-Dateiname basierend auf MP3-Dateiname + cover_filename = f"cover_{Path(metadata['filename']).stem}.jpg" + + # Speichere Cover im httpdocs-Verzeichnis + if "../httpdocs" in self.audio_dir: + cover_path = Path("../httpdocs") / cover_filename + else: + cover_path = Path(cover_filename) + + # Schreibe Cover-Daten in Datei + with open(cover_path, 'wb') as f: + f.write(metadata['cover_data']) + + # Rückgabe der URL zum Cover + cover_url = f"{self.base_url}/{cover_filename}" + print(f" 🖼️ Cover extrahiert: {cover_filename}") + return cover_url + + except Exception as e: + print(f" ⚠️ Fehler beim Extrahieren des Covers: {e}") + return None + def format_duration(self, seconds): """Formatiert die Dauer in HH:MM:SS Format.""" if not seconds: @@ -153,7 +193,7 @@ Ich spezialisiere mich auf House Music, die mehr als nur Beats bietet – sie er itunes_explicit.text = "false" # Standard-Bild (kann später angepasst werden) - image_url = f"{self.base_url}/podcast-cover.jpg" + image_url = f"{self.base_url}/_img/podcast-cover.png" image = ET.SubElement(channel, "image") image_url_elem = ET.SubElement(image, "url") image_url_elem.text = image_url @@ -168,9 +208,12 @@ Ich spezialisiere mich auf House Music, die mehr als nur Beats bietet – sie er print(f"📦 Erstelle RSS-Feed mit {len(mp3_files)} Episoden...") # Items (Episoden) hinzufügen - for mp3_file in mp3_files: + for episode_index, mp3_file in enumerate(mp3_files): metadata = self.get_mp3_metadata(mp3_file) + # Episode-Cover extrahieren + episode_cover_url = self.extract_episode_cover(metadata, episode_index) + item = ET.SubElement(channel, "item") # Titel @@ -220,6 +263,11 @@ Ich spezialisiere mich auf House Music, die mehr als nur Beats bietet – sie er itunes_explicit_item = ET.SubElement(item, "itunes:explicit") itunes_explicit_item.text = "false" + # Episode-Cover hinzufügen (falls vorhanden) + if episode_cover_url: + itunes_image_item = ET.SubElement(item, "itunes:image") + itunes_image_item.set("href", episode_cover_url) + # Keywords/Tags basierend auf Dateiname if any(keyword in metadata['title'].lower() for keyword in ['organic', 'house']): itunes_keywords = ET.SubElement(item, "itunes:keywords") @@ -309,8 +357,8 @@ def main(): help="Verzeichnis mit MP3-Dateien (Standard: ../httpdocs/_audio)") parser.add_argument("-o", "--output", default="podcast_feed.xml", help="Ausgabedatei für den RSS-Feed (Standard: podcast_feed.xml)") - parser.add_argument("-u", "--base-url", default="http://localhost:8087", - help="Basis-URL für Audio-Dateien (Standard: http://localhost:8087)") + parser.add_argument("-u", "--base-url", default="https://www.serman.club", + help="Basis-URL für Audio-Dateien (Standard: https://www.serman.club)") parser.add_argument("-t", "--title", default="SERMAN - Organic House Podcast", help="Titel des Podcasts") parser.add_argument("--author", default="SERMAN", diff --git a/main.py b/main.py index 0653887..ee20618 100644 --- a/main.py +++ b/main.py @@ -16,8 +16,8 @@ def main(): help="Verzeichnis mit MP3-Dateien (Standard: ../httpdocs/_audio)") parser.add_argument("-o", "--output", default="serman_podcast.xml", help="Ausgabedatei für den RSS-Feed (Standard: serman_podcast.xml)") - parser.add_argument("-u", "--base-url", default="http://localhost:8087", - help="Basis-URL für Audio-Dateien (Standard: http://localhost:8087)") + parser.add_argument("-u", "--base-url", default="https://www.serman.club", + help="Basis-URL für Audio-Dateien (Standard: https://www.serman.club)") parser.add_argument("--serve", action="store_true", help="Startet automatisch einen HTTP-Server nach der Generierung") parser.add_argument("--port", type=int, default=8087,