2021-06-03 15:20:39 +02:00
|
|
|
import copy
|
2023-02-26 01:27:00 +01:00
|
|
|
import logging
|
2011-03-03 06:22:28 +01:00
|
|
|
import os
|
2021-06-03 15:20:39 +02:00
|
|
|
import time
|
2023-03-01 17:07:17 +01:00
|
|
|
from pathlib import Path
|
2022-08-21 11:28:57 +02:00
|
|
|
from queue import Empty, Queue
|
2023-03-01 17:13:02 +01:00
|
|
|
from subprocess import DEVNULL, PIPE, run
|
2015-05-25 21:37:45 +02:00
|
|
|
from threading import Thread, Timer
|
2023-03-01 20:27:27 +01:00
|
|
|
from typing import Any, Dict, Union
|
2012-06-28 18:12:22 +02:00
|
|
|
|
2022-07-22 16:26:43 +02:00
|
|
|
from libretime_api_client.v1 import ApiClient as LegacyClient
|
|
|
|
from libretime_api_client.v2 import ApiClient
|
2022-08-18 11:48:28 +02:00
|
|
|
from requests import RequestException
|
2021-06-03 15:20:39 +02:00
|
|
|
|
2022-07-18 15:11:47 +02:00
|
|
|
from ..config import CACHE_DIR, POLL_INTERVAL, Config
|
2022-08-16 13:34:02 +02:00
|
|
|
from ..liquidsoap.client import LiquidsoapClient
|
2023-03-01 20:27:27 +01:00
|
|
|
from ..liquidsoap.models import Info, MessageFormatKind, StreamPreferences, StreamState
|
2023-03-04 21:50:12 +01:00
|
|
|
from .events import Events, FileEvent, FileEvents
|
2023-03-23 12:30:37 +01:00
|
|
|
from .liquidsoap import Liquidsoap
|
2024-04-27 20:09:16 +02:00
|
|
|
from .schedule import get_schedule
|
2011-03-03 06:22:28 +01:00
|
|
|
|
2023-02-26 01:27:00 +01:00
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2023-03-01 17:07:17 +01:00
|
|
|
here = Path(__file__).parent
|
|
|
|
|
2013-05-16 18:25:21 +02:00
|
|
|
|
2023-03-01 17:13:02 +01:00
|
|
|
# pylint: disable=too-many-instance-attributes
|
2021-05-27 16:23:02 +02:00
|
|
|
class PypoFetch(Thread):
|
2022-08-14 19:55:39 +02:00
|
|
|
name = "fetch"
|
2022-09-08 17:55:51 +02:00
|
|
|
daemon = True
|
2022-08-14 19:55:39 +02:00
|
|
|
|
2024-09-30 17:08:23 +02:00
|
|
|
# pylint: disable=too-many-positional-arguments
|
2023-03-01 17:13:02 +01:00
|
|
|
# pylint: disable=too-many-arguments
|
2021-05-27 16:23:02 +02:00
|
|
|
def __init__(
|
2022-01-18 20:59:11 +01:00
|
|
|
self,
|
2023-03-21 19:40:56 +01:00
|
|
|
fetch_queue: "Queue[Dict[str, Any]]",
|
|
|
|
push_queue: "Queue[Events]",
|
|
|
|
file_queue: "Queue[FileEvents]",
|
2022-08-16 13:34:02 +02:00
|
|
|
liq_client: LiquidsoapClient,
|
2023-03-23 12:30:37 +01:00
|
|
|
liquidsoap: Liquidsoap,
|
2022-01-18 20:59:11 +01:00
|
|
|
config: Config,
|
2022-07-22 16:26:43 +02:00
|
|
|
api_client: ApiClient,
|
|
|
|
legacy_client: LegacyClient,
|
2021-05-27 16:23:02 +02:00
|
|
|
):
|
2011-03-21 00:34:43 +01:00
|
|
|
Thread.__init__(self)
|
2013-05-02 23:59:03 +02:00
|
|
|
|
2022-07-22 16:26:43 +02:00
|
|
|
self.api_client = api_client
|
|
|
|
self.legacy_client = legacy_client
|
2022-08-21 11:28:57 +02:00
|
|
|
self.fetch_queue = fetch_queue
|
|
|
|
self.push_queue = push_queue
|
|
|
|
self.media_prepare_queue = file_queue
|
2012-06-08 20:57:59 +02:00
|
|
|
self.last_update_schedule_timestamp = time.time()
|
2013-04-26 04:11:26 +02:00
|
|
|
self.config = config
|
2012-06-27 20:37:16 +02:00
|
|
|
self.listener_timeout = POLL_INTERVAL
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2022-08-16 13:34:02 +02:00
|
|
|
self.liq_client = liq_client
|
2023-03-23 12:30:37 +01:00
|
|
|
self.liquidsoap = liquidsoap
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2022-01-18 05:55:16 +01:00
|
|
|
self.cache_dir = CACHE_DIR
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.debug("Cache dir %s", self.cache_dir)
|
2011-03-03 06:22:28 +01:00
|
|
|
|
2023-02-19 18:29:05 +01:00
|
|
|
self.schedule_data: Events = {}
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.info("PypoFetch: init complete")
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2022-07-01 12:23:18 +02:00
|
|
|
# Handle a message from RabbitMQ, put it into our yucky global var.
|
|
|
|
# Hopefully there is a better way to do this.
|
2021-05-27 16:23:02 +02:00
|
|
|
|
2023-03-01 20:27:27 +01:00
|
|
|
def handle_message(self, message: Dict[str, Any]) -> None:
|
2012-06-27 04:41:11 +02:00
|
|
|
try:
|
2023-02-28 17:50:53 +01:00
|
|
|
command = message["event_type"]
|
|
|
|
logger.debug("handling event %s: %s", command, message)
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2021-05-27 16:23:02 +02:00
|
|
|
if command == "update_schedule":
|
2024-04-27 20:09:16 +02:00
|
|
|
self.schedule_data = get_schedule(self.api_client)
|
2012-03-17 18:55:56 +01:00
|
|
|
self.process_schedule(self.schedule_data)
|
2021-05-27 16:23:02 +02:00
|
|
|
elif command == "reset_liquidsoap_bootstrap":
|
2012-09-15 00:20:46 +02:00
|
|
|
self.set_bootstrap_variables()
|
2021-05-27 16:23:02 +02:00
|
|
|
elif command == "update_stream_format":
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.info("Updating stream format...")
|
2023-02-28 17:50:53 +01:00
|
|
|
self.update_liquidsoap_stream_format(message["stream_format"])
|
2022-08-17 15:09:57 +02:00
|
|
|
elif command == "update_message_offline":
|
|
|
|
logger.info("Updating message offline...")
|
2023-02-28 17:50:53 +01:00
|
|
|
self.update_liquidsoap_message_offline(message["message_offline"])
|
2021-05-27 16:23:02 +02:00
|
|
|
elif command == "update_station_name":
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.info("Updating station name...")
|
2023-02-28 17:50:53 +01:00
|
|
|
self.update_liquidsoap_station_name(message["station_name"])
|
2021-05-27 16:23:02 +02:00
|
|
|
elif command == "update_transition_fade":
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.info("Updating transition_fade...")
|
2023-02-28 17:50:53 +01:00
|
|
|
self.update_liquidsoap_transition_fade(message["transition_fade"])
|
2021-05-27 16:23:02 +02:00
|
|
|
elif command == "switch_source":
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.info("switch_on_source show command received...")
|
2023-03-23 12:30:37 +01:00
|
|
|
self.liquidsoap.telnet_liquidsoap.switch_source(
|
2023-02-28 17:50:53 +01:00
|
|
|
message["sourcename"], message["status"]
|
2021-05-27 16:23:02 +02:00
|
|
|
)
|
|
|
|
elif command == "disconnect_source":
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.info("disconnect_on_source show command received...")
|
2023-03-23 12:30:37 +01:00
|
|
|
self.liquidsoap.telnet_liquidsoap.disconnect_source(
|
2023-02-28 17:50:53 +01:00
|
|
|
message["sourcename"]
|
2021-05-27 16:23:02 +02:00
|
|
|
)
|
2012-09-15 01:01:21 +02:00
|
|
|
else:
|
2023-02-26 12:01:59 +01:00
|
|
|
logger.info("Unknown command: %s", command)
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2012-06-08 20:57:59 +02:00
|
|
|
# update timeout value
|
2021-05-27 16:23:02 +02:00
|
|
|
if command == "update_schedule":
|
2012-06-27 20:37:16 +02:00
|
|
|
self.listener_timeout = POLL_INTERVAL
|
2012-06-08 20:57:59 +02:00
|
|
|
else:
|
2023-03-01 17:13:02 +01:00
|
|
|
self.listener_timeout = max(
|
|
|
|
self.last_update_schedule_timestamp - time.time() + POLL_INTERVAL,
|
|
|
|
0,
|
2021-05-27 16:23:02 +02:00
|
|
|
)
|
2023-02-26 12:01:59 +01:00
|
|
|
logger.info("New timeout: %s", self.listener_timeout)
|
2023-03-01 18:17:34 +01:00
|
|
|
except Exception as exception: # pylint: disable=broad-exception-caught
|
2022-08-09 21:05:21 +02:00
|
|
|
logger.exception(exception)
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2022-07-01 12:23:18 +02:00
|
|
|
# Initialize Liquidsoap environment
|
2023-03-01 20:27:27 +01:00
|
|
|
def set_bootstrap_variables(self) -> None:
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.debug("Getting information needed on bootstrap from Airtime")
|
2013-02-04 22:05:58 +01:00
|
|
|
try:
|
2022-08-18 11:48:28 +02:00
|
|
|
info = Info(**self.api_client.get_info().json())
|
|
|
|
preferences = StreamPreferences(
|
|
|
|
**self.api_client.get_stream_preferences().json()
|
|
|
|
)
|
|
|
|
state = StreamState(**self.api_client.get_stream_state().json())
|
2013-02-04 22:05:58 +01:00
|
|
|
|
2022-08-18 11:48:28 +02:00
|
|
|
except RequestException as exception:
|
2023-02-26 12:01:59 +01:00
|
|
|
logger.exception("Unable to get stream settings: %s", exception)
|
2023-03-01 16:47:23 +01:00
|
|
|
return
|
2013-02-04 22:05:58 +01:00
|
|
|
|
2023-02-26 12:01:59 +01:00
|
|
|
logger.debug("info: %s", info)
|
|
|
|
logger.debug("preferences: %s", preferences)
|
|
|
|
logger.debug("state: %s", state)
|
2013-02-04 22:05:58 +01:00
|
|
|
|
2022-08-18 11:48:28 +02:00
|
|
|
try:
|
2023-03-23 12:30:37 +01:00
|
|
|
self.liquidsoap.liq_client.settings_update(
|
2022-08-18 11:48:28 +02:00
|
|
|
station_name=info.station_name,
|
|
|
|
message_format=preferences.message_format,
|
|
|
|
message_offline=preferences.message_offline,
|
|
|
|
input_fade_transition=preferences.input_fade_transition,
|
2022-08-16 13:34:02 +02:00
|
|
|
)
|
2022-08-18 11:48:28 +02:00
|
|
|
|
2023-03-23 12:30:37 +01:00
|
|
|
self.liquidsoap.liq_client.source_switch_status(
|
2022-08-18 11:48:28 +02:00
|
|
|
name="master_dj",
|
|
|
|
streaming=state.input_main_streaming,
|
|
|
|
)
|
2023-03-23 12:30:37 +01:00
|
|
|
self.liquidsoap.liq_client.source_switch_status(
|
2022-08-18 11:48:28 +02:00
|
|
|
name="live_dj",
|
|
|
|
streaming=state.input_show_streaming,
|
|
|
|
)
|
2023-03-23 12:30:37 +01:00
|
|
|
self.liquidsoap.liq_client.source_switch_status(
|
2022-08-18 11:48:28 +02:00
|
|
|
name="scheduled_play",
|
|
|
|
streaming=state.schedule_streaming,
|
|
|
|
)
|
|
|
|
|
2023-03-29 16:47:13 +02:00
|
|
|
except OSError as exception:
|
2022-08-16 13:34:02 +02:00
|
|
|
logger.exception(exception)
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2023-03-23 12:30:37 +01:00
|
|
|
self.liquidsoap.clear_all_queues()
|
|
|
|
self.liquidsoap.clear_queue_tracker()
|
2013-05-06 22:39:17 +02:00
|
|
|
|
2023-03-01 20:27:27 +01:00
|
|
|
def update_liquidsoap_stream_format(
|
|
|
|
self,
|
|
|
|
stream_format: Union[MessageFormatKind, int],
|
|
|
|
) -> None:
|
2012-02-11 00:43:40 +01:00
|
|
|
try:
|
2022-08-16 13:34:02 +02:00
|
|
|
self.liq_client.settings_update(message_format=stream_format)
|
2023-03-29 16:47:13 +02:00
|
|
|
except OSError as exception:
|
2022-08-09 21:05:21 +02:00
|
|
|
logger.exception(exception)
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2023-03-01 20:27:27 +01:00
|
|
|
def update_liquidsoap_message_offline(self, message_offline: str) -> None:
|
2022-08-17 15:09:57 +02:00
|
|
|
try:
|
|
|
|
self.liq_client.settings_update(message_offline=message_offline)
|
2023-03-29 16:47:13 +02:00
|
|
|
except OSError as exception:
|
2022-08-17 15:09:57 +02:00
|
|
|
logger.exception(exception)
|
|
|
|
|
2023-03-01 20:27:27 +01:00
|
|
|
def update_liquidsoap_transition_fade(self, fade: float) -> None:
|
2012-03-21 03:16:17 +01:00
|
|
|
try:
|
2022-08-16 13:34:02 +02:00
|
|
|
self.liq_client.settings_update(input_fade_transition=fade)
|
2023-03-29 16:47:13 +02:00
|
|
|
except OSError as exception:
|
2022-08-09 21:05:21 +02:00
|
|
|
logger.exception(exception)
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2023-03-01 20:27:27 +01:00
|
|
|
def update_liquidsoap_station_name(self, station_name: str) -> None:
|
2011-03-03 06:22:28 +01:00
|
|
|
try:
|
2022-08-16 13:34:02 +02:00
|
|
|
self.liq_client.settings_update(station_name=station_name)
|
2023-03-29 16:47:13 +02:00
|
|
|
except OSError as exception:
|
2022-08-09 21:05:21 +02:00
|
|
|
logger.exception(exception)
|
2012-02-11 00:43:40 +01:00
|
|
|
|
2022-07-01 12:23:18 +02:00
|
|
|
# Process the schedule
|
2023-03-01 17:13:02 +01:00
|
|
|
# - Reads the scheduled entries of a given range (actual time +/- "prepare_ahead" /
|
|
|
|
# "cache_for")
|
2022-07-01 12:23:18 +02:00
|
|
|
# - Saves a serialized file of the schedule
|
2023-03-01 17:13:02 +01:00
|
|
|
# - playlists are prepared. (brought to liquidsoap format) and, if not mounted via
|
|
|
|
# nsf, files are copied to the cache dir
|
|
|
|
# (Folder-structure: cache/YYYY-MM-DD-hh-mm-ss)
|
2022-07-01 12:23:18 +02:00
|
|
|
# - runs the cleanup routine, to get rid of unused cached files
|
2021-05-27 16:23:02 +02:00
|
|
|
|
2023-03-01 20:27:27 +01:00
|
|
|
def process_schedule(self, events: Events) -> None:
|
2012-06-18 05:24:15 +02:00
|
|
|
self.last_update_schedule_timestamp = time.time()
|
2023-02-19 18:29:05 +01:00
|
|
|
logger.debug(events)
|
2023-02-26 00:11:49 +01:00
|
|
|
file_events: FileEvents = {}
|
|
|
|
all_events: Events = {}
|
2011-03-03 06:22:28 +01:00
|
|
|
|
2011-06-15 21:49:42 +02:00
|
|
|
# Download all the media and put playlists in liquidsoap "annotate" format
|
2011-03-21 00:34:43 +01:00
|
|
|
try:
|
2023-02-26 00:11:49 +01:00
|
|
|
for key in events:
|
|
|
|
item = events[key]
|
2023-03-04 21:50:12 +01:00
|
|
|
if isinstance(item, FileEvent):
|
2023-02-26 00:11:49 +01:00
|
|
|
file_events[key] = item
|
|
|
|
all_events[key] = item
|
|
|
|
|
|
|
|
self.media_prepare_queue.put(copy.copy(file_events))
|
2023-03-01 18:17:34 +01:00
|
|
|
except Exception as exception: # pylint: disable=broad-exception-caught
|
2022-08-09 21:05:21 +02:00
|
|
|
logger.exception(exception)
|
2011-03-21 00:34:43 +01:00
|
|
|
|
2011-03-23 06:09:27 +01:00
|
|
|
# Send the data to pypo-push
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.debug("Pushing to pypo-push")
|
2023-02-26 00:11:49 +01:00
|
|
|
self.push_queue.put(all_events)
|
2012-02-27 19:52:35 +01:00
|
|
|
|
2011-03-03 06:22:28 +01:00
|
|
|
# cleanup
|
2020-01-20 13:44:17 +01:00
|
|
|
try:
|
2023-02-26 00:11:49 +01:00
|
|
|
self.cache_cleanup(events)
|
2023-03-01 18:17:34 +01:00
|
|
|
except Exception as exception: # pylint: disable=broad-exception-caught
|
2022-08-09 21:05:21 +02:00
|
|
|
logger.exception(exception)
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2023-03-01 17:13:02 +01:00
|
|
|
def is_file_opened(self, path: str) -> bool:
|
|
|
|
result = run(["lsof", "--", path], stdout=PIPE, stderr=DEVNULL, check=False)
|
|
|
|
return bool(result.stdout)
|
2013-03-01 22:15:28 +01:00
|
|
|
|
2023-02-26 00:11:49 +01:00
|
|
|
def cache_cleanup(self, events: Events):
|
2012-03-06 01:02:46 +01:00
|
|
|
"""
|
2023-03-01 17:13:02 +01:00
|
|
|
Get list of all files in the cache dir and remove them if they aren't being used
|
|
|
|
anymore.
|
|
|
|
Input dict() media, lists all files that are scheduled or currently playing. Not
|
|
|
|
being in this dict() means the file is safe to remove.
|
2012-03-06 01:02:46 +01:00
|
|
|
"""
|
|
|
|
cached_file_set = set(os.listdir(self.cache_dir))
|
|
|
|
scheduled_file_set = set()
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2023-02-26 00:11:49 +01:00
|
|
|
for key in events:
|
|
|
|
item = events[key]
|
2023-03-04 21:50:12 +01:00
|
|
|
if isinstance(item, FileEvent):
|
|
|
|
scheduled_file_set.add(item.local_filepath.name)
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2012-07-25 22:56:33 +02:00
|
|
|
expired_files = cached_file_set - scheduled_file_set
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2023-02-26 12:01:59 +01:00
|
|
|
logger.debug("Files to remove %s", str(expired_files))
|
2023-02-26 00:11:49 +01:00
|
|
|
for expired_file in expired_files:
|
2012-07-25 22:56:33 +02:00
|
|
|
try:
|
2023-02-26 00:11:49 +01:00
|
|
|
expired_filepath = os.path.join(self.cache_dir, expired_file)
|
|
|
|
logger.debug("Removing %s", expired_filepath)
|
2013-03-01 22:15:28 +01:00
|
|
|
|
2021-05-27 16:23:02 +02:00
|
|
|
# check if this file is opened (sometimes Liquidsoap is still
|
|
|
|
# playing the file due to our knowledge of the track length
|
|
|
|
# being incorrect!)
|
2023-02-26 00:11:49 +01:00
|
|
|
if not self.is_file_opened(expired_filepath):
|
|
|
|
os.remove(expired_filepath)
|
|
|
|
logger.info("File '%s' removed", expired_filepath)
|
2013-04-05 23:31:55 +02:00
|
|
|
else:
|
2023-02-26 00:11:49 +01:00
|
|
|
logger.info("File '%s' not removed. Still busy!", expired_filepath)
|
2023-03-01 18:17:34 +01:00
|
|
|
except Exception as exception: # pylint: disable=broad-exception-caught
|
2023-02-26 00:11:49 +01:00
|
|
|
logger.exception(
|
|
|
|
"Problem removing file '%s': %s", expired_file, exception
|
|
|
|
)
|
2011-03-23 06:09:27 +01:00
|
|
|
|
2023-03-01 20:27:27 +01:00
|
|
|
def manual_schedule_fetch(self) -> bool:
|
2020-01-30 14:47:36 +01:00
|
|
|
try:
|
2022-06-24 16:42:46 +02:00
|
|
|
self.schedule_data = get_schedule(self.api_client)
|
2023-02-26 12:01:59 +01:00
|
|
|
logger.debug("Received event from API client: %s", self.schedule_data)
|
2012-08-20 18:11:03 +02:00
|
|
|
self.process_schedule(self.schedule_data)
|
2020-01-30 14:47:36 +01:00
|
|
|
return True
|
2023-03-01 18:17:34 +01:00
|
|
|
except Exception as exception: # pylint: disable=broad-exception-caught
|
2023-02-26 12:01:59 +01:00
|
|
|
logger.exception("Unable to fetch schedule: %s", exception)
|
2020-01-30 14:47:36 +01:00
|
|
|
return False
|
2012-08-20 18:11:03 +02:00
|
|
|
|
2023-03-01 20:27:27 +01:00
|
|
|
def persistent_manual_schedule_fetch(self, max_attempts=1) -> bool:
|
2013-01-25 18:11:50 +01:00
|
|
|
success = False
|
|
|
|
num_attempts = 0
|
|
|
|
while not success and num_attempts < max_attempts:
|
|
|
|
success = self.manual_schedule_fetch()
|
|
|
|
num_attempts += 1
|
|
|
|
|
|
|
|
return success
|
|
|
|
|
2015-05-25 21:37:45 +02:00
|
|
|
# This function makes a request to Airtime to see if we need to
|
|
|
|
# push metadata to TuneIn. We have to do this because TuneIn turns
|
|
|
|
# off metadata if it does not receive a request every 5 minutes.
|
|
|
|
def update_metadata_on_tunein(self):
|
2022-07-22 16:26:43 +02:00
|
|
|
self.legacy_client.update_metadata_on_tunein()
|
2015-05-25 21:37:45 +02:00
|
|
|
Timer(120, self.update_metadata_on_tunein).start()
|
2013-01-25 18:11:50 +01:00
|
|
|
|
2011-09-08 18:17:42 +02:00
|
|
|
def main(self):
|
2021-05-27 16:23:02 +02:00
|
|
|
# Make sure all Liquidsoap queues are empty. This is important in the
|
|
|
|
# case where we've just restarted the pypo scheduler, but Liquidsoap still
|
|
|
|
# is playing tracks. In this case let's just restart everything from scratch
|
|
|
|
# so that we can repopulate our dictionary that keeps track of what
|
|
|
|
# Liquidsoap is playing much more easily.
|
2023-03-23 12:30:37 +01:00
|
|
|
self.liquidsoap.clear_all_queues()
|
2013-04-22 23:33:56 +02:00
|
|
|
|
2013-05-15 23:18:15 +02:00
|
|
|
self.set_bootstrap_variables()
|
|
|
|
|
2015-05-25 21:37:45 +02:00
|
|
|
self.update_metadata_on_tunein()
|
|
|
|
|
2013-05-13 23:52:22 +02:00
|
|
|
# Bootstrap: since we are just starting up, we need to grab the
|
2015-05-25 21:37:45 +02:00
|
|
|
# most recent schedule. After that we fetch the schedule every 8
|
2014-08-14 18:29:52 +02:00
|
|
|
# minutes or wait for schedule updates to get pushed.
|
2013-05-13 23:52:22 +02:00
|
|
|
success = self.persistent_manual_schedule_fetch(max_attempts=5)
|
|
|
|
|
2012-02-27 19:52:35 +01:00
|
|
|
if success:
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.info("Bootstrap schedule received: %s", self.schedule_data)
|
2011-09-20 19:25:29 +02:00
|
|
|
|
2012-06-27 04:41:11 +02:00
|
|
|
loops = 1
|
2011-03-21 00:34:43 +01:00
|
|
|
while True:
|
2023-02-26 12:01:59 +01:00
|
|
|
logger.info("Loop #%s", loops)
|
2015-06-02 20:51:20 +02:00
|
|
|
manual_fetch_needed = False
|
2012-06-27 04:41:11 +02:00
|
|
|
try:
|
2022-07-01 12:23:18 +02:00
|
|
|
# our simple_queue.get() requires a timeout, in which case we
|
|
|
|
# fetch the Airtime schedule manually. It is important to fetch
|
|
|
|
# the schedule periodically because if we didn't, we would only
|
|
|
|
# get schedule updates via RabbitMq if the user was constantly
|
|
|
|
# using the Airtime interface.
|
|
|
|
|
|
|
|
# If the user is not using the interface, RabbitMq messages are not
|
|
|
|
# sent, and we will have very stale (or non-existent!) data about the
|
|
|
|
# schedule.
|
|
|
|
|
|
|
|
# Currently we are checking every POLL_INTERVAL seconds
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2021-05-27 16:23:02 +02:00
|
|
|
message = self.fetch_queue.get(
|
|
|
|
block=True, timeout=self.listener_timeout
|
|
|
|
)
|
2015-06-02 20:51:20 +02:00
|
|
|
manual_fetch_needed = False
|
2012-02-27 19:52:35 +01:00
|
|
|
self.handle_message(message)
|
2022-08-09 21:05:21 +02:00
|
|
|
except Empty:
|
2022-01-13 16:11:37 +01:00
|
|
|
logger.info("Queue timeout. Fetching schedule manually")
|
2015-06-02 20:51:20 +02:00
|
|
|
manual_fetch_needed = True
|
2023-03-01 18:17:34 +01:00
|
|
|
except Exception as exception: # pylint: disable=broad-exception-caught
|
2022-08-09 21:05:21 +02:00
|
|
|
logger.exception(exception)
|
2015-06-02 20:51:20 +02:00
|
|
|
|
|
|
|
try:
|
|
|
|
if manual_fetch_needed:
|
|
|
|
self.persistent_manual_schedule_fetch(max_attempts=5)
|
2023-03-01 18:17:34 +01:00
|
|
|
except Exception as exception: # pylint: disable=broad-exception-caught
|
2023-02-26 12:01:59 +01:00
|
|
|
logger.exception("Failed to manually fetch the schedule: %s", exception)
|
2012-06-27 04:41:11 +02:00
|
|
|
|
2012-02-24 19:12:50 +01:00
|
|
|
loops += 1
|
2011-09-08 18:17:42 +02:00
|
|
|
|
|
|
|
def run(self):
|
2012-02-28 21:32:18 +01:00
|
|
|
"""
|
|
|
|
Entry point of the thread
|
|
|
|
"""
|
|
|
|
self.main()
|