@@ -84,7 +84,7 @@ def excepthook(exc_type, exc_value, exc_traceback):
8484
8585class Stream :
8686 def __init__ (
87- self , title : str , url : str , host : str , description : str , recording_finished
87+ self , title : str , url : str , host : str , description : str , recording_finished , upload_callback
8888 ) -> None :
8989 load_dotenv ()
9090 self .title = title
@@ -107,6 +107,7 @@ def __init__(
107107 self .recording_file_path = (
108108 f"{ FOLDER_LOCATION } /CURRENTLY_RECORDING/{ self .recording_file_name } "
109109 )
110+ self .upload_callback = upload_callback
110111 self .send_notification ()
111112
112113 def start_recording (self ):
@@ -192,24 +193,28 @@ def process_file(self):
192193 )
193194 self .uploaded = True
194195
195- self .backup_stream (final_recording_file_path )
196+ # self.backup_stream(final_recording_file_path)
196197 os .remove (final_recording_file_path )
197198 app_log .info (f"Original copy deleted: { self .recording_file_name } " )
198199
199200 def upload_stream (self , file_name : str , file_path : str ):
200201 app_log .info (f"Uploading { self .host } : { self .recording_file_name } " )
201- asyncio .run (
202- filebrowser_uploader .upload (
203- file_name ,
204- file_path ,
205- self .host ,
206- self .description ,
207- self .starting_time .strftime ("%B %d %A %Y %I_%M %p" ),
208- self .audio_file_length ,
209- )
210- )
211- self .uploaded = True
212- app_log .info (f"Uploaded { self .host } : { self .recording_file_name } " )
202+ try :
203+ if self .upload_callback :
204+ self .upload_callback (
205+ file_name ,
206+ file_path ,
207+ self .host ,
208+ self .description ,
209+ self .starting_time .strftime ("%B %d %A %Y %I_%M %p" ),
210+ self .audio_file_length ,
211+ )
212+ self .uploaded = True
213+ app_log .info (f"Uploaded { self .host } : { self .recording_file_name } " )
214+ else :
215+ raise RuntimeError ("No upload callback provided." )
216+ except Exception as e :
217+ app_log .error (f"Failed to upload stream: { e } " )
213218
214219 def backup_stream (self , file_path : str ):
215220 app_log .info (f"Starting compression for { self .recording_file_name } " )
@@ -250,30 +255,28 @@ class StreamRecorder:
250255 def __init__ (self ) -> None :
251256 load_dotenv ()
252257 self .active_streams : dict [str , Stream ] = {}
258+ self .loop = asyncio .new_event_loop ()
259+ asyncio .set_event_loop (self .loop )
260+ threading .Thread (target = self .loop .run_forever , daemon = True ).start ()
253261
254- def fetch_icecast_status_json (
255- self , icecast_source = "https://hbniaudio .hbni.net"
262+ def fetch_broadcast_data (
263+ self , source = "https://broadcasting .hbni.net/get_broadcast_data "
256264 ) -> (
257- dict [
258- Literal ["icestats" ],
259- None | str | dict [Literal ["source" ], dict [str , str ] | list [dict [str , str ]]],
260- ]
265+ list [dict ]
261266 | None
262267 ):
263268 try :
264- response = requests .get (f" { icecast_source } /status-json.xsl" )
269+ response = requests .get (source )
265270 if response .status_code == 200 :
266- json_content = response .text .replace ('"title": - ,' , '"title": null,' )
267- json_data = json .loads (json_content )
268- json_data ["icestats" ]["icecast_source" ] = icecast_source
269- app_log .info (f"Icecast status: { json_data } " )
271+ json_data = json .loads (response .text )
272+ app_log .info (f"Broadcast data: { json_data } " )
270273 return json_data
271274 else :
272- app_log .info (f"Error fetching Icecast status : { response .status_code } " )
273- return self .fetch_icecast_status_json ( "http://hbniaudio.hbni.net:8000" )
275+ app_log .info (f"Error fetching Broadcast data : { response .status_code } " )
276+ return self .fetch_broadcast_data ( )
274277 except Exception as e :
275- app_log .error (f"Error fetching Icecast status : { e } " )
276- return self .fetch_icecast_status_json ( "http://hbniaudio.hbni.net:8000" )
278+ app_log .error (f"Error fetching Broadcast data : { e } " )
279+ return self .fetch_broadcast_data ( )
277280
278281 def process_sources (
279282 self , sources : dict [str , str ] | list [dict [str , str ]]
@@ -289,7 +292,7 @@ def remove_stream(self, host: str):
289292 )
290293 del self .active_streams [host ]
291294 if not list (self .active_streams .keys ()):
292- self .update_recording_status ()
295+ self .loop . create_task ( self . update_recording_status () )
293296
294297 def run (self ):
295298 self .send_notification ()
@@ -300,26 +303,31 @@ def run(self):
300303
301304 while True :
302305 try :
303- status_data = self .fetch_icecast_status_json ()
306+ status_data = self .fetch_broadcast_data ()
304307 if not status_data :
305308 time .sleep (15 )
306309 continue
307310
308- sources = status_data .get ("icestats" , {}).get ("source" , [])
309- sources = self .process_sources (sources )
310-
311- for source in sources :
312- host = source ["listenurl" ].split ("/" )[- 1 ]
311+ for source in status_data :
312+ host : str = source .get ("host" )
313313 description = source .get ("server_description" , "No description" )
314- title = host .replace ("/" , "" ).title ()
315- icecast_source = status_data .get ("icestats" , {}).get ("icecast_source" , "https://hbniaudio.hbni.net" )
314+ title = host .title ()
315+ icecast_source = "https://hbniaudio.hbni.net"
316+ is_private = source .get ("is_private" , False )
316317 is_recording = source .get ("genre" , "various" ) == "RECORDING"
317318
318319 if (
319- host not in self .active_streams and "test" not in host .lower () and "test" not in description .lower ()
320+ host not in self .active_streams and "test" not in host .lower () and "test" not in description .lower () and not is_private
320321 # and not is_recording # It is being recorded by HBNI Audio
321322 ):
322- stream = Stream (title , icecast_source , host , description , self .remove_stream )
323+ stream = Stream (
324+ title ,
325+ icecast_source ,
326+ host ,
327+ description ,
328+ self .remove_stream ,
329+ upload_callback = upload_sync
330+ )
323331 self .active_streams [host ] = stream
324332 stream .start_recording ()
325333 app_log .info (
@@ -328,13 +336,13 @@ def run(self):
328336
329337 # Cleanup inactive streams
330338 active_hosts = {
331- source ["listenurl " ].split ("/" )[- 1 ] for source in sources
339+ source ["host " ].split ("/" )[- 1 ] for source in status_data
332340 }
333341 for host in list (self .active_streams .keys ()):
334342 if host not in active_hosts :
335343 self .remove_stream (host )
336344
337- self .update_recording_status ()
345+ self .loop . create_task ( self . update_recording_status () )
338346
339347 time .sleep (15 )
340348 except Exception as e :
@@ -380,6 +388,7 @@ async def update_recording_status(self):
380388 await conn .execute (query , host , link , length , description , starting_time )
381389
382390 await pool .close ()
391+
383392 def send_notification (self ):
384393 send_email .send (
385394 "HBNI Audio Stream Recorder Started Successfully" ,
@@ -417,16 +426,36 @@ def start_log_server():
417426 httpd .serve_forever ()
418427
419428
429+ def upload_sync (
430+ file_name : str ,
431+ file_path : str ,
432+ host : str ,
433+ description : str ,
434+ date : str ,
435+ length : float ,
436+ ):
437+ return asyncio .run (
438+ filebrowser_uploader .upload (
439+ file_name ,
440+ file_path ,
441+ host ,
442+ description ,
443+ date ,
444+ length ,
445+ )
446+ )
447+
448+
420449def start_recorder ():
421450 stream_recorder = StreamRecorder ()
422451 app_log .info ("Starting stream recorder" )
423452 stream_recorder .run ()
424453
425454
426455def main () -> None :
427-
428- threading .Thread (target = start_recorder ).start ()
456+ # threading.Thread(target=start_recorder).start()
429457 threading .Thread (target = start_log_server ).start ()
458+ start_recorder ()
430459
431460
432461if __name__ == "__main__" :
0 commit comments