|
| 1 | +#!/usr/bin/env python |
| 2 | +# -*- coding: utf-8 -*- |
| 3 | + |
| 4 | +import sys |
| 5 | +import xbmc |
| 6 | +import xbmcgui |
| 7 | +import xbmcplugin |
| 8 | +import xbmcaddon |
| 9 | +import requests |
| 10 | +from urllib.parse import urlencode, parse_qsl |
| 11 | + |
| 12 | +# Addon info |
| 13 | +ADDON = xbmcaddon.Addon() |
| 14 | +ADDON_ID = ADDON.getAddonInfo('id') |
| 15 | +ADDON_NAME = ADDON.getAddonInfo('name') |
| 16 | +ADDON_VERSION = ADDON.getAddonInfo('version') |
| 17 | + |
| 18 | +# Base URL for c3lounge |
| 19 | +BASE_URL = "https://c3lounge.de" |
| 20 | +STREAM_BASE_URL = "https://live.c3lounge.de:8000" |
| 21 | +API_URL = f"{STREAM_BASE_URL}/status-json.xsl" |
| 22 | + |
| 23 | + |
| 24 | +def log(msg, level=xbmc.LOGINFO): |
| 25 | + """Log message to Kodi log""" |
| 26 | + xbmc.log(f"[{ADDON_ID}] {msg}", level) |
| 27 | + |
| 28 | + |
| 29 | +def get_url(**kwargs): |
| 30 | + """Create a URL for calling the plugin""" |
| 31 | + return f"{sys.argv[0]}?{urlencode(kwargs)}" |
| 32 | + |
| 33 | + |
| 34 | +def get_now_playing(): |
| 35 | + """Get now playing information from Icecast API""" |
| 36 | + try: |
| 37 | + response = requests.get(API_URL, timeout=10) |
| 38 | + response.raise_for_status() |
| 39 | + data = response.json() |
| 40 | + |
| 41 | + # Icecast returns data in icestats.source array |
| 42 | + if 'icestats' in data and 'source' in data['icestats']: |
| 43 | + sources = data['icestats']['source'] |
| 44 | + if isinstance(sources, list) and len(sources) > 0: |
| 45 | + # Return the first mp3 stream source with metadata |
| 46 | + for source in sources: |
| 47 | + if 'artist' in source and 'title' in source: |
| 48 | + return source |
| 49 | + return sources[0] |
| 50 | + return sources |
| 51 | + return None |
| 52 | + except Exception as e: |
| 53 | + log(f"Error fetching now playing data: {e}", xbmc.LOGERROR) |
| 54 | + return None |
| 55 | + |
| 56 | + |
| 57 | +def get_stream_url(): |
| 58 | + """Get the stream URL from the station info""" |
| 59 | + try: |
| 60 | + # Get quality setting |
| 61 | + quality = ADDON.getSetting('stream_quality') |
| 62 | + |
| 63 | + # Map quality to stream format |
| 64 | + quality_map = { |
| 65 | + 'High': '192.mp3', # 192kbps MP3 |
| 66 | + 'Medium': '64.opus', # 64kbps OPUS |
| 67 | + 'Low': '32.opus' # 32kbps OPUS |
| 68 | + } |
| 69 | + |
| 70 | + stream_file = quality_map.get(quality, '192.mp3') |
| 71 | + return f"{STREAM_BASE_URL}/{stream_file}" |
| 72 | + |
| 73 | + except Exception as e: |
| 74 | + log(f"Error getting stream URL: {e}", xbmc.LOGERROR) |
| 75 | + return f"{STREAM_BASE_URL}/192.mp3" |
| 76 | + |
| 77 | + |
| 78 | +def play_stream(): |
| 79 | + """Play the radio stream""" |
| 80 | + stream_url = get_stream_url() |
| 81 | + log(f"Playing stream: {stream_url}") |
| 82 | + |
| 83 | + # Get now playing info for metadata |
| 84 | + now_playing = get_now_playing() |
| 85 | + |
| 86 | + # Create list item |
| 87 | + list_item = xbmcgui.ListItem(path=stream_url) |
| 88 | + list_item.setProperty('IsPlayable', 'true') |
| 89 | + |
| 90 | + # Set metadata if available |
| 91 | + if now_playing: |
| 92 | + try: |
| 93 | + # Icecast provides artist and title directly |
| 94 | + title = now_playing.get('title', 'c3lounge Radio') |
| 95 | + artist = now_playing.get('artist', '') |
| 96 | + |
| 97 | + list_item.setInfo('music', { |
| 98 | + 'title': title, |
| 99 | + 'artist': artist, |
| 100 | + 'mediatype': 'song' |
| 101 | + }) |
| 102 | + |
| 103 | + log(f"Now playing: {artist} - {title}") |
| 104 | + except Exception as e: |
| 105 | + log(f"Error setting metadata: {e}", xbmc.LOGERROR) |
| 106 | + |
| 107 | + list_item.setContentLookup(False) |
| 108 | + |
| 109 | + # Play the stream |
| 110 | + xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listitem=list_item) |
| 111 | + |
| 112 | + |
| 113 | +def show_now_playing(): |
| 114 | + """Show now playing information""" |
| 115 | + now_playing = get_now_playing() |
| 116 | + |
| 117 | + if now_playing: |
| 118 | + try: |
| 119 | + # Icecast provides artist and title directly |
| 120 | + title = now_playing.get('title', 'Unknown') |
| 121 | + artist = now_playing.get('artist', 'Unknown') |
| 122 | + listeners = now_playing.get('listeners', 0) |
| 123 | + |
| 124 | + message = f"Artist: {artist}\nTitle: {title}" |
| 125 | + message += f"\n\nListeners: {listeners}" |
| 126 | + |
| 127 | + xbmcgui.Dialog().ok('Now Playing', message) |
| 128 | + except Exception as e: |
| 129 | + log(f"Error showing now playing: {e}", xbmc.LOGERROR) |
| 130 | + xbmcgui.Dialog().ok('Error', 'Could not fetch now playing information') |
| 131 | + else: |
| 132 | + xbmcgui.Dialog().ok('Error', 'Could not fetch now playing information') |
| 133 | + |
| 134 | + |
| 135 | +def list_categories(): |
| 136 | + """List available categories/actions""" |
| 137 | + # Add "Play c3lounge Radio" item |
| 138 | + list_item = xbmcgui.ListItem(label="Play c3lounge Radio") |
| 139 | + list_item.setProperty('IsPlayable', 'true') |
| 140 | + list_item.setInfo('music', { |
| 141 | + 'title': 'c3lounge Radio Stream', |
| 142 | + 'mediatype': 'song' |
| 143 | + }) |
| 144 | + url = get_url(action='play') |
| 145 | + xbmcplugin.addDirectoryItem(int(sys.argv[1]), url, list_item, False) |
| 146 | + |
| 147 | + xbmcplugin.endOfDirectory(int(sys.argv[1])) |
| 148 | + |
| 149 | + |
| 150 | +def router(paramstring): |
| 151 | + """Route to the appropriate function based on the provided paramstring""" |
| 152 | + params = dict(parse_qsl(paramstring)) |
| 153 | + |
| 154 | + if not params: |
| 155 | + # No parameters - show main menu |
| 156 | + list_categories() |
| 157 | + else: |
| 158 | + action = params.get('action') |
| 159 | + |
| 160 | + if action == 'play': |
| 161 | + play_stream() |
| 162 | + elif action == 'nowplaying': |
| 163 | + show_now_playing() |
| 164 | + # Refresh the directory to go back to menu |
| 165 | + xbmc.executebuiltin('Container.Refresh') |
| 166 | + else: |
| 167 | + list_categories() |
| 168 | + |
| 169 | + |
| 170 | +if __name__ == '__main__': |
| 171 | + log(f"Starting {ADDON_NAME} v{ADDON_VERSION}") |
| 172 | + router(sys.argv[2][1:]) |
0 commit comments