Skip to content

Commit 5a9e50d

Browse files
committed
fixing some things
1 parent a10442f commit 5a9e50d

14 files changed

+1339
-269
lines changed

nowplaying/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@ def _defaults_chat_services(self, settings: QSettings) -> None:
236236
def _defaults_quirks(settings: QSettings) -> None:
237237
''' default values for quirks '''
238238
settings.setValue('quirks/pollingobserver', False)
239+
settings.setValue('quirks/pollinginterval', 5.0)
239240
settings.setValue('quirks/filesubst', False)
240241
settings.setValue('quirks/slashmode', 'nochange')
241242

nowplaying/inputs/djuced.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ async def setup_watcher(self, configkey='djuced/directory'):
7373
self.event_handler.on_created = self._fs_event
7474

7575
if self.config.cparser.value('quirks/pollingobserver', type=bool):
76-
logging.debug('Using polling observer')
77-
self.observer = PollingObserver(timeout=5)
76+
polling_interval = self.config.cparser.value('quirks/pollinginterval', type=float)
77+
logging.debug('Using polling observer with %s second interval', polling_interval)
78+
self.observer = PollingObserver(timeout=polling_interval)
7879
else:
7980
logging.debug('Using fsevent observer')
8081
self.observer = Observer()

nowplaying/inputs/icecast.py

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,6 @@
2121
#from nowplaying.exceptions import PluginVerifyError
2222
from nowplaying.inputs import InputPlugin
2323

24-
METADATA = {}
25-
2624
METADATALIST = ['artist', 'title', 'album', 'key', 'filename', 'bpm']
2725

2826
PLAYLIST = ['name', 'filename']
@@ -31,9 +29,11 @@
3129
class IcecastProtocol(asyncio.Protocol):
3230
''' a terrible implementation of the Icecast SOURCE protocol '''
3331

34-
def __init__(self):
32+
def __init__(self, metadata_callback=None):
3533
self.streaming = False
3634
self.previous_page = b''
35+
self.metadata_callback = metadata_callback
36+
self._current_metadata = {}
3737

3838
def connection_made(self, transport):
3939
''' initial connection gives us a transport to use '''
@@ -89,29 +89,46 @@ def _parse_page(self, dataio):
8989
self.previous_page = b''
9090
header_data = dataio.read(27)
9191

92-
@staticmethod
93-
def _query_parse(data):
92+
def _query_parse(self, data):
9493
''' try to parse the query '''
95-
global METADATA # pylint: disable=global-statement
9694
logging.debug('Processing updinfo')
9795

98-
METADATA = {}
99-
text = data.decode('utf-8').replace('GET ', 'http://localhost').split()[0]
100-
url = urllib.parse.urlparse(text)
96+
metadata = {}
97+
try:
98+
text = data.decode('utf-8').replace('GET ', 'http://localhost').split()[0]
99+
url = urllib.parse.urlparse(text)
100+
except UnicodeDecodeError:
101+
logging.warning('Failed to decode icecast query data as UTF-8')
102+
return
103+
except (IndexError, ValueError) as error:
104+
logging.warning('Failed to parse icecast query URL: %s', error)
105+
return
101106
if url.path == '/admin/metadata':
102-
query = urllib.parse.parse_qs(url.query)
107+
query = urllib.parse.parse_qs(url.query, keep_blank_values=True)
103108
if query.get('mode') == ['updinfo']:
104109
if query.get('artist'):
105-
METADATA['artist'] = query['artist'][0]
110+
metadata['artist'] = query['artist'][0]
106111
if query.get('title'):
107-
METADATA['title'] = query['title'][0]
108-
if query.get('song'):
109-
METADATA['title'], METADATA['artist'] = query['song'][0].split('-')
110-
111-
@staticmethod
112-
def _parse_vorbis_comment(fh): # pylint: disable=invalid-name
112+
metadata['title'] = query['title'][0]
113+
if 'song' in query:
114+
song_text = query['song'][0].strip()
115+
if ' - ' not in song_text:
116+
# No separator found, treat entire string as title
117+
metadata['title'] = song_text
118+
else:
119+
# Split on first occurrence of ' - ' (with spaces)
120+
# This handles cases like "Artist - Song - Remix" correctly
121+
artist, title = song_text.split(' - ', 1)
122+
metadata['artist'] = artist.strip()
123+
metadata['title'] = title.strip()
124+
125+
# Update instance metadata and notify callback
126+
self._current_metadata.update(metadata)
127+
if self.metadata_callback:
128+
self.metadata_callback(self._current_metadata.copy())
129+
130+
def _parse_vorbis_comment(self, fh): # pylint: disable=invalid-name
113131
''' from tinytag, with slight modifications, pull out metadata '''
114-
global METADATA # pylint: disable=global-statement
115132
comment_type_to_attr_mapping = {
116133
'album': 'album',
117134
'albumartist': 'albumartist',
@@ -127,7 +144,7 @@ def _parse_vorbis_comment(fh): # pylint: disable=invalid-name
127144
}
128145

129146
logging.debug('Processing vorbis comment')
130-
METADATA = {}
147+
metadata = {}
131148

132149
vendor_length = struct.unpack('I', fh.read(4))[0]
133150
fh.seek(vendor_length, os.SEEK_CUR) # jump over vendor
@@ -141,7 +158,12 @@ def _parse_vorbis_comment(fh): # pylint: disable=invalid-name
141158
if '=' in keyvalpair:
142159
key, value = keyvalpair.split('=', 1)
143160
if fieldname := comment_type_to_attr_mapping.get(key.lower()):
144-
METADATA[fieldname] = value
161+
metadata[fieldname] = value
162+
163+
# Update instance metadata and notify callback
164+
self._current_metadata.update(metadata)
165+
if self.metadata_callback:
166+
self.metadata_callback(self._current_metadata.copy())
145167

146168

147169
class Plugin(InputPlugin):
@@ -154,6 +176,11 @@ def __init__(self, config=None, qsettings=None):
154176
self.server = None
155177
self.mode = None
156178
self.lastmetadata = {}
179+
self._current_metadata = {}
180+
181+
def _metadata_callback(self, metadata):
182+
''' Callback to receive metadata from the protocol '''
183+
self._current_metadata = metadata
157184

158185
def install(self):
159186
''' auto-install for Icecast '''
@@ -181,8 +208,8 @@ def desc_settingsui(self, qwidget):
181208
#### Data feed methods
182209

183210
async def getplayingtrack(self):
184-
''' give back the metadata global '''
185-
return METADATA
211+
''' give back the current metadata '''
212+
return self._current_metadata
186213

187214
async def getrandomtrack(self, playlist):
188215
return None
@@ -196,7 +223,10 @@ async def start_port(self, port):
196223
loop = asyncio.get_running_loop()
197224
logging.debug('Launching Icecast on %s', port)
198225
try:
199-
self.server = await loop.create_server(IcecastProtocol, '', port)
226+
# Create protocol factory that passes the metadata callback
227+
def protocol_factory():
228+
return IcecastProtocol(metadata_callback=self._metadata_callback)
229+
self.server = await loop.create_server(protocol_factory, '', port)
200230
except Exception as error: #pylint: disable=broad-except
201231
logging.error('Failed to launch icecast: %s', error)
202232

0 commit comments

Comments
 (0)