Skip to content
Snippets Groups Projects
service.py 7.19 KiB
Newer Older
  • Learn to ignore specific revisions
  • David Trattnig's avatar
    David Trattnig committed
    #
    # Aura Engine API (https://gitlab.servus.at/aura/engine-api)
    #
    # Copyright (C) 2020 - The Aura Engine Team.
    #
    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU Affero General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU Affero General Public License for more details.
    #
    # You should have received a copy of the GNU Affero General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    
    David Trattnig's avatar
    David Trattnig committed
    
    
    import datetime
    
    import requests
    
    David Trattnig's avatar
    David Trattnig committed
    
    
    from enum import Enum
    
    from models import PlayLog, PlayLogSchema, TrackSchema
    
    David Trattnig's avatar
    David Trattnig committed
    
    
    class NodeType(Enum):
        """
        Types of API Server deployment models.
        """
        MAIN = "main"
        SYNC = "sync"
    
    
    
    
    class ApiService():
        """
    
    David Trattnig's avatar
    David Trattnig committed
        Service handling for API actions.
    
        """
    
        config = None
        logger = None
    
        node_type = None
        sync_host = None
        main_hosts = None
    
    David Trattnig's avatar
    David Trattnig committed
        active_source = None
    
    
        def __init__(self, config, logger):
    
            """
            Initialize Service.
            """
    
            self.config = config
            self.logger = logger
    
    
            # Evaluate deployment mode
    
            node_type = NodeType.MAIN
            host_id = config.get("host_id")
            if host_id == 0:
                node_type = NodeType.SYNC
    
            # Configured as Sync node
            if not node_type == NodeType.MAIN:
    
                self.node_type = NodeType.SYNC
                self.main_hosts = [ config.get("main_host_1"), config.get("main_host_2") ]
    
                if not self.main_hosts[0] and not self.main_hosts[1]:
                    self.logger.warn("Not a single main host defined. Be aware what you are doing.")
    
    David Trattnig's avatar
    David Trattnig committed
                    msg = "No sync possible as no host nodes are configured"
    
                else:
                    msg = "Syncing data of hosts '%s'" % (self.main_hosts)
    
            # Configured as Main node
    
            else:
                self.node_type = NodeType.MAIN
                self.sync_host = config.get("sync_host")
                if not self.sync_host:
    
    David Trattnig's avatar
    David Trattnig committed
                    msg = "No child node for synchronization defined"
    
                else:
                    msg = "Pushing data to '%s'" % (self.sync_host)
    
    
    David Trattnig's avatar
    David Trattnig committed
            # Set active source
            self.active_source = ActivityLog.get_active_source()
            if not self.active_source:
                if self.node_type == NodeType.MAIN:
                    source_number = self.config.get("host_id")
                else:
                    source_number = self.config.get("default_source")
                self.set_active_source(source_number)
            self.logger("Active source: %s" % self.active_source)
    
            self.logger.info("Running in '%s' mode. %s." % (self.node_type, msg))
    
    
        def current_track(self):  
            """
            Retrieves the currently playing track.  
    
            Returns:
                (PlayLogEntry)
            """
            track = PlayLog.select_current()
    
    David Trattnig's avatar
    David Trattnig committed
            track_schema = TrackSchema()
    
            return track_schema.dump(track)
    
    
    
    David Trattnig's avatar
    David Trattnig committed
        def list_tracks(self, page=None, size=None):  
    
    David Trattnig's avatar
    David Trattnig committed
            Lists track-service entries with pagination.
    
    David Trattnig's avatar
    David Trattnig committed
                page (Integer): The number of the page to return
                size (Integer): The numbers of items to return
    
    
            Returns:
                (List[PlayLogEntry])
            """
    
    David Trattnig's avatar
    David Trattnig committed
            if not size or size > 50 or size < 1: size = 20
    
            tracklist = PlayLog.paginate(page, size, None)
    
    David Trattnig's avatar
    David Trattnig committed
            tracklist_schema = TrackSchema(many=True)
            return tracklist_schema.dump(tracklist)
    
        def list_playlog(self, page=None, size=None, since_time=None):  
            """
            Get paginated playlog entries for since the given timestamp.  
    
            Args:
                page (Integer): The number of items to skip before starting to collect the result set
                size (Integer): The numbers of items to return per page
    
                since_time (datetime): Optionally, get entries after this timestamp (e.g. "2020-08-29T09:12:33.001Z")
    
    
            Returns:
                (List[PlayLogEntry])
            """
            tracklist = PlayLog.paginate(page, size, since_time)
            tracklist_schema = TrackSchema(many=True)
            return tracklist_schema.dump(tracklist)
    
    
    
        def store_playlog(self, data): 
    
            """
            Stores the passed playlog entry.
    
            Returns:
                (PlayLogEntry)
            """
    
            if not data.log_source:
                data.log_source = self.host_id
    
    
    David Trattnig's avatar
    David Trattnig committed
            # Main Node: Alway log entry, independed of the source
            # Sync Node: Only log entry when it's coming from an active source
            if self.node_type == NodeType.MAIN or \
                (self.node_type == NodeType.SYNC and data.log_source == self.active_source):
    
                playlog = PlayLog(data)
                playlog.save()
                self.logger.debug("Stored playlog for '%s'" % playlog.track_start)
    
                # Main Node: Push to Sync Node, if enabled
                if self.node_type == NodeType.MAIN and self.sync_host:
                    playlog_schema = PlayLogSchema()
                    json_playlog = playlog_schema.dump(playlog)
    
                    try:
                        api_url = self.sync_host + self.config.get("sync_api_store_playlog")
                        r = requests.post(api_url, json=json_playlog)
                        if r.status_code == 200:
                            self.logger.info("Successfully pushed playlog for '%s' to '%s'" % (playlog.track_start, self.sync_host))
                            playlog.is_synced = True
                            playlog.save()
                        else:
                            self.logger.error("Error while pushing playlog to sync-node: " + r.json())
                    except Exception as e:
                        self.logger.error("Error while posting to sync-node API '%s'!" % (api_url), e)
            else:
                self.logger.info("Ditching playlog sent from an inactive source")
    
        def clock_info(self):
            """
    
    David Trattnig's avatar
    David Trattnig committed
            Retrieves the dataset required to render the studio clock.  
    
    David Trattnig's avatar
    David Trattnig committed
            return "studio clock data"
    
    David Trattnig's avatar
    David Trattnig committed
        def set_active_source(self, source_number):
    
    David Trattnig's avatar
    David Trattnig committed
            """
    
    David Trattnig's avatar
    David Trattnig committed
            Sets the active source (engine1, engine2, other) identified by its source number.
    
    David Trattnig's avatar
    David Trattnig committed
    
            Args:
    
    David Trattnig's avatar
    David Trattnig committed
                source_number (Integer): Number of the engine
    
    David Trattnig's avatar
    David Trattnig committed
            """
    
    David Trattnig's avatar
    David Trattnig committed
            if self.active_source > 0:
                self.active_source = source_number
                activity_log = ActivityLog(active_source)
                activity_log.save()
    
    David Trattnig's avatar
    David Trattnig committed
        def get_active_source(self):
    
    David Trattnig's avatar
    David Trattnig committed
            """
    
    David Trattnig's avatar
    David Trattnig committed
            Retrieves number of the currently active source (engine1, engine2, other)
    
    David Trattnig's avatar
    David Trattnig committed
    
            Returns:
    
    David Trattnig's avatar
    David Trattnig committed
                (Integer)
    
    David Trattnig's avatar
    David Trattnig committed
            """
    
    David Trattnig's avatar
    David Trattnig committed
            return self.active_source
    
    David Trattnig's avatar
    David Trattnig committed
        def get_source_health(self, source_number):
    
    David Trattnig's avatar
    David Trattnig committed
            """
    
    David Trattnig's avatar
    David Trattnig committed
            Retrieves the most recent health info of the requested source
    
    David Trattnig's avatar
    David Trattnig committed
    
            Args:
    
    David Trattnig's avatar
    David Trattnig committed
                source_number (Integer): Number of the play-out source
    
    David Trattnig's avatar
    David Trattnig committed
    
    
            Returns:
    
    David Trattnig's avatar
    David Trattnig committed
                (HealthHistoryEntry)
    
    David Trattnig's avatar
    David Trattnig committed
            """
            return 'do some magic!'
    
    
    
    David Trattnig's avatar
    David Trattnig committed
        def log_source_health(self, source_number):
    
    David Trattnig's avatar
    David Trattnig committed
            """
    
    David Trattnig's avatar
    David Trattnig committed
            Logs another health entry for the given source
    
    David Trattnig's avatar
    David Trattnig committed
    
            Args:
    
    David Trattnig's avatar
    David Trattnig committed
                source_number (Integer): Number of the play-out source
    
    David Trattnig's avatar
    David Trattnig committed
    
            """
            return 'do some magic!'