#!/usr/bin/env python3 """ Mixcloud RSS Feed Generator Erstellt einen RSS-Feed aus Mixcloud-Tracks für Podcast-Apps. """ import requests import xml.etree.ElementTree as ET from datetime import datetime import json import time from urllib.parse import quote import argparse import os class MixcloudRSSGenerator: def __init__(self, username, output_file="mixcloud_feed.xml"): self.username = username self.output_file = output_file self.base_url = "https://api.mixcloud.com" self.user_url = f"{self.base_url}/{username}/" def get_user_info(self): """Holt Benutzerinformationen von Mixcloud.""" try: response = requests.get(self.user_url) response.raise_for_status() return response.json() except requests.RequestException as e: print(f"Fehler beim Abrufen der Benutzerinformationen: {e}") return None def get_cloudcasts(self, limit=50): """Holt die neuesten Cloudcasts (Tracks) des Benutzers.""" cloudcasts_url = f"{self.user_url}cloudcasts/" params = {"limit": limit} try: response = requests.get(cloudcasts_url, params=params) response.raise_for_status() data = response.json() return data.get("data", []) except requests.RequestException as e: print(f"Fehler beim Abrufen der Cloudcasts: {e}") return [] def format_duration(self, seconds): """Formatiert die Dauer in HH:MM:SS Format.""" hours = seconds // 3600 minutes = (seconds % 3600) // 60 seconds = seconds % 60 return f"{hours:02d}:{minutes:02d}:{seconds:02d}" def get_stream_url(self, cloudcast_key): """ Generiert eine Stream-URL für den Cloudcast. Hinweis: Mixcloud erlaubt direktes Streaming nur über ihre eigene API. """ # Dies ist eine vereinfachte URL - in der Praxis müsste man # die offizielle Mixcloud-Streaming-API verwenden return f"https://www.mixcloud.com{cloudcast_key}stream/" def create_rss_feed(self): """Erstellt den RSS-Feed aus den Mixcloud-Daten.""" user_info = self.get_user_info() if not user_info: return False cloudcasts = self.get_cloudcasts() if not cloudcasts: print("Keine Cloudcasts gefunden.") return False # RSS Root Element rss = ET.Element("rss") rss.set("version", "2.0") rss.set("xmlns:itunes", "http://www.itunes.com/dtds/podcast-1.0.dtd") rss.set("xmlns:content", "http://purl.org/rss/1.0/modules/content/") # Channel Element channel = ET.SubElement(rss, "channel") # Channel Metadaten title = ET.SubElement(channel, "title") title.text = f"{user_info.get('name', self.username)} - Mixcloud Feed" description = ET.SubElement(channel, "description") description.text = user_info.get('biog', f"Mixcloud-Feed von {self.username}") link = ET.SubElement(channel, "link") link.text = f"https://www.mixcloud.com/{self.username}/" language = ET.SubElement(channel, "language") language.text = "de-DE" # iTunes-spezifische Tags itunes_author = ET.SubElement(channel, "itunes:author") itunes_author.text = user_info.get('name', self.username) itunes_summary = ET.SubElement(channel, "itunes:summary") itunes_summary.text = user_info.get('biog', f"Mixcloud-Feed von {self.username}") itunes_category = ET.SubElement(channel, "itunes:category") itunes_category.set("text", "Music") # Bild falls vorhanden if user_info.get('pictures', {}).get('large'): image = ET.SubElement(channel, "image") image_url = ET.SubElement(image, "url") image_url.text = user_info['pictures']['large'] image_title = ET.SubElement(image, "title") image_title.text = title.text image_link = ET.SubElement(image, "link") image_link.text = link.text itunes_image = ET.SubElement(channel, "itunes:image") itunes_image.set("href", user_info['pictures']['large']) # Items (Episoden) hinzufügen for cloudcast in cloudcasts: item = ET.SubElement(channel, "item") # Titel item_title = ET.SubElement(item, "title") item_title.text = cloudcast.get('name', 'Unbekannter Titel') # Beschreibung item_description = ET.SubElement(item, "description") description_text = cloudcast.get('description', '') if not description_text: description_text = f"Mix von {self.username}" item_description.text = description_text # Link zur Mixcloud-Seite item_link = ET.SubElement(item, "link") item_link.text = cloudcast.get('url', '') # GUID item_guid = ET.SubElement(item, "guid") item_guid.text = cloudcast.get('key', '') item_guid.set("isPermaLink", "false") # Veröffentlichungsdatum item_pubdate = ET.SubElement(item, "pubDate") created_time = cloudcast.get('created_time') if created_time: # Konvertiere ISO-Format zu RFC 2822 dt = datetime.fromisoformat(created_time.replace('Z', '+00:00')) item_pubdate.text = dt.strftime('%a, %d %b %Y %H:%M:%S %z') # Audio-Enclosure # Hinweis: Mixcloud erlaubt kein direktes Audio-Streaming ohne Autorisierung # Dies ist ein Platzhalter - für echtes Streaming müsste man die Mixcloud API verwenden enclosure = ET.SubElement(item, "enclosure") stream_url = f"https://www.mixcloud.com{cloudcast.get('key', '')}" enclosure.set("url", stream_url) enclosure.set("type", "audio/mpeg") # Dauer duration = cloudcast.get('audio_length', 0) if duration: item_duration = ET.SubElement(item, "itunes:duration") item_duration.text = self.format_duration(duration) # iTunes-spezifische Tags itunes_title = ET.SubElement(item, "itunes:title") itunes_title.text = item_title.text itunes_summary = ET.SubElement(item, "itunes:summary") itunes_summary.text = description_text # Tags hinzufügen tags = cloudcast.get('tags', []) if tags: keywords = ", ".join([tag['name'] for tag in tags[:5]]) # Nur erste 5 Tags itunes_keywords = ET.SubElement(item, "itunes:keywords") itunes_keywords.text = keywords # XML in Datei schreiben tree = ET.ElementTree(rss) ET.indent(tree, space=" ", level=0) try: tree.write(self.output_file, encoding='utf-8', xml_declaration=True) print(f"RSS-Feed erfolgreich erstellt: {self.output_file}") print(f"Anzahl der Episoden: {len(cloudcasts)}") return True except Exception as e: print(f"Fehler beim Schreiben der XML-Datei: {e}") return False def serve_feed(self, port=8000): """Startet einen einfachen HTTP-Server für den RSS-Feed.""" import http.server import socketserver import os # Wechsle in das Verzeichnis mit der XML-Datei os.chdir(os.path.dirname(os.path.abspath(self.output_file))) handler = http.server.SimpleHTTPRequestHandler try: with socketserver.TCPServer(("", port), handler) as httpd: print(f"Server läuft auf http://localhost:{port}") print(f"RSS-Feed verfügbar unter: http://localhost:{port}/{os.path.basename(self.output_file)}") print("Drücke Ctrl+C zum Beenden") httpd.serve_forever() except KeyboardInterrupt: print("\nServer beendet.") except Exception as e: print(f"Fehler beim Starten des Servers: {e}") def main(): parser = argparse.ArgumentParser(description="Erstellt einen RSS-Feed aus Mixcloud-Tracks") parser.add_argument("username", help="Mixcloud-Benutzername (z.B. serman_dj)") parser.add_argument("-o", "--output", default="mixcloud_feed.xml", help="Ausgabedatei für den RSS-Feed (Standard: mixcloud_feed.xml)") parser.add_argument("-l", "--limit", type=int, default=50, help="Anzahl der zu holenden Tracks (Standard: 50)") parser.add_argument("--serve", action="store_true", help="Startet einen HTTP-Server für den RSS-Feed") parser.add_argument("--port", type=int, default=8000, help="Port für den HTTP-Server (Standard: 8000)") args = parser.parse_args() generator = MixcloudRSSGenerator(args.username, args.output) print(f"Erstelle RSS-Feed für Mixcloud-User: {args.username}") success = generator.create_rss_feed() if success and args.serve: generator.serve_feed(args.port) if __name__ == "__main__": main()