示例#1
0
    def __init__(self, logger, telnet_lock, host, port):
        self.logger = logger
        self.liq_queue_tracker = {
            "s0": None,
            "s1": None,
            "s2": None,
            "s3": None,
        }

        self.telnet_liquidsoap = TelnetLiquidsoap(telnet_lock, \
                logger,\
                host,\
                port)
示例#2
0
    def __init__(self, logger, telnet_lock, host, port):
        self.logger = logger
        self.liq_queue_tracker = {
                "s0": None,
                "s1": None,
                "s2": None,
                "s3": None,
                }

        self.telnet_liquidsoap = TelnetLiquidsoap(telnet_lock, \
                logger,\
                host,\
                port)
示例#3
0
class PypoLiquidsoap():
    def __init__(self, logger, host, port):
        self.logger = logger
        self.liq_queue_tracker = {
                "s0": None,
                "s1": None,
                "s2": None,
                "s3": None,
                }

        self.telnet_liquidsoap = TelnetLiquidsoap(
                logger,\
                host,\
                port,\
                self.liq_queue_tracker.keys())

    def get_telnet_dispatcher(self):
        return self.telnet_liquidsoap


    def play(self, media_item):
        if media_item["type"] == eventtypes.FILE:
            self.handle_file_type(media_item)
        elif media_item["type"] == eventtypes.EVENT:
            self.handle_event_type(media_item)
        elif media_item["type"] == eventtypes.STREAM_BUFFER_START:
            self.telnet_liquidsoap.start_web_stream_buffer(media_item)
        elif media_item["type"] == eventtypes.STREAM_OUTPUT_START:
            if media_item['row_id'] != \
                    self.telnet_liquidsoap.current_prebuffering_stream_id:
                #this is called if the stream wasn't scheduled sufficiently 
                #ahead of time so that the prebuffering stage could take 
                #effect. Let's do the prebuffering now.
                self.telnet_liquidsoap.start_web_stream_buffer(media_item)
            self.telnet_liquidsoap.start_web_stream(media_item)
        elif media_item['type'] == eventtypes.STREAM_BUFFER_END:
            self.telnet_liquidsoap.stop_web_stream_buffer()
        elif media_item['type'] == eventtypes.STREAM_OUTPUT_END:
            self.telnet_liquidsoap.stop_web_stream_output()
        else: raise UnknownMediaItemType(str(media_item))

    def handle_file_type(self, media_item):
        """
        Wait maximum 5 seconds (50 iterations) for file to become ready, 
        otherwise give up on it.
        """
        iter_num = 0
        while not media_item['file_ready'] and iter_num < 50:
            time.sleep(0.1)
            iter_num += 1

        if media_item['file_ready']:
            available_queue = self.find_available_queue()

            print
            print '---------------------------------'
            print 'available queue:'
            print available_queue
            print '---------------------------------'
            print

            try:
                self.telnet_liquidsoap.queue_push(available_queue, media_item)
                self.liq_queue_tracker[available_queue] = media_item
            except Exception as e:
                self.logger.error(e)
                raise
        else:
            self.logger.warn("File %s did not become ready in less than 5 " + 
                    "seconds. Skipping...", media_item['dst'])

    def handle_event_type(self, media_item):
        if media_item['event_type'] == "kick_out":
            self.telnet_liquidsoap.disconnect_source("live_dj")
        elif media_item['event_type'] == "switch_off":
            self.telnet_liquidsoap.switch_source("live_dj", "off")


    def is_media_item_finished(self, media_item):
        if media_item is None:
            return True
        else:
            return datetime.now() > media_item['end']

    def find_available_queue(self):
        available_queue = None
        for i in self.liq_queue_tracker:
            mi = self.liq_queue_tracker[i]
            if mi == None or self.is_media_item_finished(mi):
                #queue "i" is available. Push to this queue
                available_queue = i

        if available_queue == None:
            raise NoQueueAvailableException()

        return available_queue


    def verify_correct_present_media(self, scheduled_now):
        #verify whether Liquidsoap is currently playing the correct files.
        #if we find an item that Liquidsoap is not playing, then push it
        #into one of Liquidsoap's queues. If Liquidsoap is already playing
        #it do nothing. If Liquidsoap is playing a track that isn't in
        #currently_playing then stop it.

        #Check for Liquidsoap media we should source.skip
        #get liquidsoap items for each queue. Since each queue can only have 
        #one item, we should have a max of 8 items.

        #2013-03-21-22-56-00_0: {
        #id: 1,
        #type: "stream_output_start",
        #row_id: 41,
        #uri: "http://stream2.radioblackout.org:80/blackout.ogg",
        #start: "2013-03-21-22-56-00",
        #end: "2013-03-21-23-26-00",
        #show_name: "Untitled Show",
        #independent_event: true
        #},


        scheduled_now_files = \
                filter(lambda x: x["type"] == eventtypes.FILE, scheduled_now)

        scheduled_now_webstream = \
                filter(lambda x: x["type"] == eventtypes.STREAM_OUTPUT_START, 
                        scheduled_now)

        schedule_ids = set(map(lambda x: x["row_id"], scheduled_now_files))

        row_id_map = {}
        liq_queue_ids = set()
        for i in self.liq_queue_tracker:
            mi = self.liq_queue_tracker[i]
            if not self.is_media_item_finished(mi):
                liq_queue_ids.add(mi["row_id"])
                row_id_map[mi["row_id"]] = mi

        to_be_removed = set()
        to_be_added = set()

        #Iterate over the new files, and compare them to currently scheduled
        #tracks. If already in liquidsoap queue still need to make sure they 
        #don't have different attributes such replay_gain etc.
        for i in scheduled_now_files:
            if i["row_id"] in row_id_map:
                mi = row_id_map[i["row_id"]]
                correct = mi['start'] == i['start'] and \
                        mi['end'] == i['end'] and \
                        mi['row_id'] == i['row_id'] and \
                        mi['replay_gain'] == i['replay_gain']

                if not correct:
                    #need to re-add
                    self.logger.info("Track %s found to have new attr." % i)
                    to_be_removed.add(i["row_id"])
                    to_be_added.add(i["row_id"])


        to_be_removed.update(liq_queue_ids - schedule_ids)
        to_be_added.update(schedule_ids - liq_queue_ids)

        if to_be_removed:
            self.logger.info("Need to remove items from Liquidsoap: %s" %
                    to_be_removed)

            #remove files from Liquidsoap's queue
            for i in self.liq_queue_tracker:
                mi = self.liq_queue_tracker[i]
                if mi is not None and mi["row_id"] in to_be_removed:
                    self.stop(i)

        if to_be_added:
            self.logger.info("Need to add items to Liquidsoap *now*: %s" %
                    to_be_added)

            for i in scheduled_now:
                if i["row_id"] in to_be_added:
                    self.modify_cue_point(i)
                    self.play(i)

        #handle webstreams
        current_stream_id = self.telnet_liquidsoap.get_current_stream_id()
        if scheduled_now_webstream:
            if current_stream_id != scheduled_now_webstream[0]:
                self.play(scheduled_now_webstream[0])
        elif current_stream_id != "-1":
            #something is playing and it shouldn't be.
            self.telnet_liquidsoap.stop_web_stream_buffer()
            self.telnet_liquidsoap.stop_web_stream_output()

    def stop(self, queue):
        self.telnet_liquidsoap.queue_remove(queue)
        self.liq_queue_tracker[queue] = None

    def is_file(self, media_item):
        return media_item["type"] == eventtypes.FILE

    def clear_queue_tracker(self):
        for i in self.liq_queue_tracker.keys():
            self.liq_queue_tracker[i] = None

    def modify_cue_point(self, link):
        if not self.is_file(link):
            return

        tnow = datetime.now()

        link_start = link['start']

        diff_td = tnow - link_start
        diff_sec = pure.date_interval_to_seconds(diff_td)

        if diff_sec > 0:
            self.logger.debug("media item was supposed to start %s ago. " + 
                    "Preparing to start..", diff_sec)
            original_cue_in_td = timedelta(seconds=float(link['cue_in']))
            link['cue_in'] = \
                pure.date_interval_to_seconds(original_cue_in_td) + diff_sec

    def clear_all_queues(self):
        self.telnet_liquidsoap.queue_clear_all()

    def get_liquidsoap_version(self, version_string):
        m = re.match(r"Liquidsoap (\d+.\d+.\d+)", version_string)
        if m:
            return m.group(1)
        else:
            return None

    def liquidsoap_startup_test(self):
        liquidsoap_version_string = \
                self.get_liquidsoap_version(
                        self.telnet_liquidsoap.liquidsoap_get_info())
        while not liquidsoap_version_string:
            self.logger.warning("Liquidsoap doesn't appear to be running!, " +
                   "Sleeping and trying again")
            time.sleep(1)
            liquidsoap_version_string = \
                self.get_liquidsoap_version(
                        self.telnet_liquidsoap.liquidsoap_get_info())

        while pure.version_cmp(
                liquidsoap_version_string, 
                constants.LIQUIDSOAP_MIN_VERSION) < 0:
            self.logger.warning("Liquidsoap is running but in incorrect " + 
                    "version! Make sure you have at least Liquidsoap %s " + 
                    "installed",
                    constants.LIQUIDSOAP_MIN_VERSION)
            time.sleep(1)
            liquidsoap_version_string = \
                self.get_liquidsoap_version(
                        self.telnet_liquidsoap.liquidsoap_get_info())
            
        self.logger.info("Liquidsoap version string found %s",
                liquidsoap_version_string)
示例#4
0
logging.captureWarnings(True)

telnet_lock = Lock()
pypoPush_q = Queue()

pypoLiq_q = Queue()
liq_queue_tracker = {
    "s0": None,
    "s1": None,
    "s2": None,
    "s3": None,
}

#dummy_telnet_liquidsoap = DummyTelnetLiquidsoap(telnet_lock, logging)
dummy_telnet_liquidsoap = TelnetLiquidsoap(telnet_lock, logging, \
        "localhost", \
        1234)

plq = PypoLiqQueue(pypoLiq_q, telnet_lock, logging, liq_queue_tracker, \
        dummy_telnet_liquidsoap)
plq.daemon = True
plq.start()

print "Time now: %s" % datetime.utcnow()

media_schedule = {}

start_dt = datetime.utcnow() + timedelta(seconds=1)
end_dt = datetime.utcnow() + timedelta(seconds=6)

media_schedule[start_dt] = {"id": 5, \
示例#5
0
class PypoLiquidsoap():
    def __init__(self, logger, telnet_lock, host, port):
        self.logger = logger
        self.liq_queue_tracker = {
            "s0": None,
            "s1": None,
            "s2": None,
            "s3": None,
        }

        self.telnet_liquidsoap = TelnetLiquidsoap(telnet_lock, \
                logger,\
                host,\
                port)

    def play(self, media_item):
        if media_item["type"] == eventtypes.FILE:
            self.handle_file_type(media_item)
        elif media_item["type"] == eventtypes.EVENT:
            self.handle_event_type(media_item)
        elif media_item["type"] == eventtypes.STREAM_BUFFER_START:
            self.telnet_liquidsoap.start_web_stream_buffer(media_item)
        elif media_item["type"] == eventtypes.STREAM_OUTPUT_START:
            if media_item[
                    'row_id'] != self.telnet_liquidsoap.current_prebuffering_stream_id:
                #this is called if the stream wasn't scheduled sufficiently ahead of time
                #so that the prebuffering stage could take effect. Let's do the prebuffering now.
                self.telnet_liquidsoap.start_web_stream_buffer(media_item)
            self.telnet_liquidsoap.start_web_stream(media_item)
        elif media_item['type'] == eventtypes.STREAM_BUFFER_END:
            self.telnet_liquidsoap.stop_web_stream_buffer()
        elif media_item['type'] == eventtypes.STREAM_OUTPUT_END:
            self.telnet_liquidsoap.stop_web_stream_output()
        else:
            raise UnknownMediaItemType(str(media_item))

    def handle_file_type(self, media_item):
        """
        Wait maximum 5 seconds (50 iterations) for file to become ready, 
        otherwise give up on it.
        """
        iter_num = 0
        while not media_item['file_ready'] and iter_num < 50:
            time.sleep(0.1)
            iter_num += 1

        if media_item['file_ready']:
            available_queue = self.find_available_queue()

            try:
                self.telnet_liquidsoap.queue_push(available_queue, media_item)
                self.liq_queue_tracker[available_queue] = media_item
            except Exception as e:
                self.logger.error(e)
                raise
        else:
            self.logger.warn(
                "File %s did not become ready in less than 5 seconds. Skipping...",
                media_item['dst'])

    def handle_event_type(self, media_item):
        if media_item['event_type'] == "kick_out":
            PypoFetch.disconnect_source(self.logger, self.telnet_lock,
                                        "live_dj")
        elif media_item['event_type'] == "switch_off":
            PypoFetch.switch_source(self.logger, self.telnet_lock, "live_dj",
                                    "off")

    def is_media_item_finished(self, media_item):
        if media_item is None:
            return True
        else:
            return datetime.utcnow() > media_item['end']

    def find_available_queue(self):
        available_queue = None
        for i in self.liq_queue_tracker:
            mi = self.liq_queue_tracker[i]
            if mi == None or self.is_media_item_finished(mi):
                #queue "i" is available. Push to this queue
                available_queue = i

        if available_queue == None:
            raise NoQueueAvailableException()

        return available_queue

    def get_queues():
        return self.liq_queue_tracker

    def verify_correct_present_media(self, scheduled_now):
        #verify whether Liquidsoap is currently playing the correct files.
        #if we find an item that Liquidsoap is not playing, then push it
        #into one of Liquidsoap's queues. If Liquidsoap is already playing
        #it do nothing. If Liquidsoap is playing a track that isn't in
        #currently_playing then stop it.

        #Check for Liquidsoap media we should source.skip
        #get liquidsoap items for each queue. Since each queue can only have one
        #item, we should have a max of 8 items.

        #TODO: Verify start, end, replay_gain is the same

        #2013-03-21-22-56-00_0: {
        #id: 1,
        #type: "stream_output_start",
        #row_id: 41,
        #uri: "http://stream2.radioblackout.org:80/blackout.ogg",
        #start: "2013-03-21-22-56-00",
        #end: "2013-03-21-23-26-00",
        #show_name: "Untitled Show",
        #independent_event: true
        #},


        scheduled_now_files = \
                filter(lambda x: x["type"] == eventtypes.FILE, scheduled_now)

        scheduled_now_webstream = \
                filter(lambda x: x["type"] == eventtypes.STREAM_OUTPUT_START, \
                        scheduled_now)

        schedule_ids = set(map(lambda x: x["row_id"], scheduled_now_files))

        row_id_map = {}
        liq_queue_ids = set()
        for i in self.liq_queue_tracker:
            mi = self.liq_queue_tracker[i]
            if not self.is_media_item_finished(mi):
                liq_queue_ids.add(mi["row_id"])
                row_id_map[mi["row_id"]] = mi

        to_be_removed = set()
        to_be_added = set()

        #Iterate over the new files, and compare them to currently scheduled
        #tracks. If already in liquidsoap queue still need to make sure they don't
        #have different attributes such replay_gain etc.
        for i in scheduled_now_files:
            if i["row_id"] in row_id_map:
                mi = row_id_map[i["row_id"]]
                correct = mi['start'] == i['start'] and \
                        mi['end'] == i['end'] and \
                        mi['replay_gain'] == i['replay_gain']

                if not correct:
                    #need to re-add
                    self.logger.info("Track %s found to have new attr." % i)
                    to_be_removed.add(i["row_id"])
                    to_be_added.add(i["row_id"])

        to_be_removed.update(liq_queue_ids - schedule_ids)
        to_be_added.update(schedule_ids - liq_queue_ids)

        if len(to_be_removed):
            self.logger.info("Need to remove items from Liquidsoap: %s" % \
                    to_be_removed)

            #remove files from Liquidsoap's queue
            for i in self.liq_queue_tracker:
                mi = self.liq_queue_tracker[i]
                if mi is not None and mi["row_id"] in to_be_removed:
                    self.stop(i)

        if len(to_be_added):
            self.logger.info("Need to add items to Liquidsoap *now*: %s" % \
                    to_be_added)

            for i in scheduled_now:
                if i["row_id"] in to_be_added:
                    self.modify_cue_point(i)
                    self.play(i)

        #handle webstreams
        current_stream_id = self.telnet_liquidsoap.get_current_stream_id()
        if len(scheduled_now_webstream):
            if current_stream_id != scheduled_now_webstream[0]:
                self.play(scheduled_now_webstream[0])
        elif current_stream_id != "-1":
            #something is playing and it shouldn't be.
            self.telnet_liquidsoap.stop_web_stream_buffer()
            self.telnet_liquidsoap.stop_web_stream_output()

    def stop(self, queue):
        self.telnet_liquidsoap.queue_remove(queue)
        self.liq_queue_tracker[queue] = None

    def is_file(self, media_item):
        return media_item["type"] == eventtypes.FILE

    def modify_cue_point(self, link):
        if not self.is_file(link):
            return

        tnow = datetime.utcnow()

        link_start = link['start']

        diff_td = tnow - link_start
        diff_sec = self.date_interval_to_seconds(diff_td)

        if diff_sec > 0:
            self.logger.debug(
                "media item was supposed to start %s ago. Preparing to start..",
                diff_sec)
            original_cue_in_td = timedelta(seconds=float(link['cue_in']))
            link['cue_in'] = self.date_interval_to_seconds(
                original_cue_in_td) + diff_sec

    def date_interval_to_seconds(self, interval):
        """
        Convert timedelta object into int representing the number of seconds. If
        number of seconds is less than 0, then return 0.
        """
        seconds = (interval.microseconds + \
                   (interval.seconds + interval.days * 24 * 3600) * 10 ** 6) / float(10 ** 6)
        if seconds < 0: seconds = 0

        return seconds
示例#6
0
class PypoLiquidsoap():
    def __init__(self, logger, telnet_lock, host, port):
        self.logger = logger
        self.liq_queue_tracker = {
                "s0": None,
                "s1": None,
                "s2": None,
                "s3": None,
                }

        self.telnet_liquidsoap = TelnetLiquidsoap(telnet_lock, \
                logger,\
                host,\
                port)


    def play(self, media_item):
        if media_item["type"] == eventtypes.FILE:
            self.handle_file_type(media_item)
        elif media_item["type"] == eventtypes.EVENT:
            self.handle_event_type(media_item)
        elif media_item["type"] == eventtypes.STREAM_BUFFER_START:
            self.telnet_liquidsoap.start_web_stream_buffer(media_item)
        elif media_item["type"] == eventtypes.STREAM_OUTPUT_START:
            if media_item['row_id'] != self.telnet_liquidsoap.current_prebuffering_stream_id:
                #this is called if the stream wasn't scheduled sufficiently ahead of time
                #so that the prebuffering stage could take effect. Let's do the prebuffering now.
                self.telnet_liquidsoap.start_web_stream_buffer(media_item)
            self.telnet_liquidsoap.start_web_stream(media_item)
        elif media_item['type'] == eventtypes.STREAM_BUFFER_END:
            self.telnet_liquidsoap.stop_web_stream_buffer()
        elif media_item['type'] == eventtypes.STREAM_OUTPUT_END:
            self.telnet_liquidsoap.stop_web_stream_output()
        else: raise UnknownMediaItemType(str(media_item))

    def handle_file_type(self, media_item):
        """
        Wait maximum 5 seconds (50 iterations) for file to become ready, 
        otherwise give up on it.
        """
        iter_num = 0
        while not media_item['file_ready'] and iter_num < 50:
            time.sleep(0.1)
            iter_num += 1

        if media_item['file_ready']:
            available_queue = self.find_available_queue()

            try:
                self.telnet_liquidsoap.queue_push(available_queue, media_item)
                self.liq_queue_tracker[available_queue] = media_item
            except Exception as e:
                self.logger.error(e)
                raise
        else:
            self.logger.warn("File %s did not become ready in less than 5 seconds. Skipping...", media_item['dst'])

    def handle_event_type(self, media_item):
        if media_item['event_type'] == "kick_out":
            PypoFetch.disconnect_source(self.logger, self.telnet_lock, "live_dj")
        elif media_item['event_type'] == "switch_off":
            PypoFetch.switch_source(self.logger, self.telnet_lock, "live_dj", "off")


    def is_media_item_finished(self, media_item):
        if media_item is None:
            return True
        else:
            return datetime.utcnow() > media_item['end']

    def find_available_queue(self):
        available_queue = None
        for i in self.liq_queue_tracker:
            mi = self.liq_queue_tracker[i]
            if mi == None or self.is_media_item_finished(mi):
                #queue "i" is available. Push to this queue
                available_queue = i

        if available_queue == None:
            raise NoQueueAvailableException()

        return available_queue

    def get_queues():
        return self.liq_queue_tracker


    def verify_correct_present_media(self, scheduled_now):
        #verify whether Liquidsoap is currently playing the correct files.
        #if we find an item that Liquidsoap is not playing, then push it
        #into one of Liquidsoap's queues. If Liquidsoap is already playing
        #it do nothing. If Liquidsoap is playing a track that isn't in
        #currently_playing then stop it.

        #Check for Liquidsoap media we should source.skip
        #get liquidsoap items for each queue. Since each queue can only have one
        #item, we should have a max of 8 items.

        #TODO: Verify start, end, replay_gain is the same

        #2013-03-21-22-56-00_0: {
        #id: 1,
        #type: "stream_output_start",
        #row_id: 41,
        #uri: "http://stream2.radioblackout.org:80/blackout.ogg",
        #start: "2013-03-21-22-56-00",
        #end: "2013-03-21-23-26-00",
        #show_name: "Untitled Show",
        #independent_event: true
        #},


        scheduled_now_files = \
                filter(lambda x: x["type"] == eventtypes.FILE, scheduled_now)

        scheduled_now_webstream = \
                filter(lambda x: x["type"] == eventtypes.STREAM_OUTPUT_START, \
                        scheduled_now)

        schedule_ids = set(map(lambda x: x["row_id"], scheduled_now_files))

        row_id_map = {}
        liq_queue_ids = set()
        for i in self.liq_queue_tracker:
            mi = self.liq_queue_tracker[i]
            if not self.is_media_item_finished(mi):
                liq_queue_ids.add(mi["row_id"])
                row_id_map[mi["row_id"]] = mi

        to_be_removed = set()
        to_be_added = set()

        #Iterate over the new files, and compare them to currently scheduled
        #tracks. If already in liquidsoap queue still need to make sure they don't
        #have different attributes such replay_gain etc.
        for i in scheduled_now_files:
            if i["row_id"] in row_id_map:
                mi = row_id_map[i["row_id"]]
                correct = mi['start'] == i['start'] and \
                        mi['end'] == i['end'] and \
                        mi['replay_gain'] == i['replay_gain']

                if not correct:
                    #need to re-add
                    self.logger.info("Track %s found to have new attr." % i)
                    to_be_removed.add(i["row_id"])
                    to_be_added.add(i["row_id"])


        to_be_removed.update(liq_queue_ids - schedule_ids)
        to_be_added.update(schedule_ids - liq_queue_ids)

        if len(to_be_removed):
            self.logger.info("Need to remove items from Liquidsoap: %s" % \
                    to_be_removed)

            #remove files from Liquidsoap's queue
            for i in self.liq_queue_tracker:
                mi = self.liq_queue_tracker[i]
                if mi is not None and mi["row_id"] in to_be_removed:
                    self.stop(i)

        if len(to_be_added):
            self.logger.info("Need to add items to Liquidsoap *now*: %s" % \
                    to_be_added)

            for i in scheduled_now:
                if i["row_id"] in to_be_added:
                    self.modify_cue_point(i)
                    self.play(i)

        #handle webstreams
        current_stream_id = self.telnet_liquidsoap.get_current_stream_id()
        if len(scheduled_now_webstream):
            if current_stream_id != scheduled_now_webstream[0]:
                self.play(scheduled_now_webstream[0])
        elif current_stream_id != "-1":
            #something is playing and it shouldn't be.
            self.telnet_liquidsoap.stop_web_stream_buffer()
            self.telnet_liquidsoap.stop_web_stream_output()

    def stop(self, queue):
        self.telnet_liquidsoap.queue_remove(queue)
        self.liq_queue_tracker[queue] = None

    def is_file(self, media_item):
        return media_item["type"] == eventtypes.FILE

    def modify_cue_point(self, link):
        if not self.is_file(link):
            return

        tnow = datetime.utcnow()

        link_start = link['start']

        diff_td = tnow - link_start
        diff_sec = self.date_interval_to_seconds(diff_td)

        if diff_sec > 0:
            self.logger.debug("media item was supposed to start %s ago. Preparing to start..", diff_sec)
            original_cue_in_td = timedelta(seconds=float(link['cue_in']))
            link['cue_in'] = self.date_interval_to_seconds(original_cue_in_td) + diff_sec

    def date_interval_to_seconds(self, interval):
        """
        Convert timedelta object into int representing the number of seconds. If
        number of seconds is less than 0, then return 0.
        """
        seconds = (interval.microseconds + \
                   (interval.seconds + interval.days * 24 * 3600) * 10 ** 6) / float(10 ** 6)
        if seconds < 0: seconds = 0

        return seconds
示例#7
0
class PypoLiquidsoap():
    def __init__(self, logger, host, port):
        self.logger = logger
        self.liq_queue_tracker = {
            "s0": None,
            "s1": None,
            "s2": None,
            "s3": None,
        }

        self.telnet_liquidsoap = TelnetLiquidsoap(
                logger,\
                host,\
                port,\
                self.liq_queue_tracker.keys())

    def get_telnet_dispatcher(self):
        return self.telnet_liquidsoap

    def play(self, media_item):
        if media_item["type"] == eventtypes.FILE:
            self.handle_file_type(media_item)
        elif media_item["type"] == eventtypes.EVENT:
            self.handle_event_type(media_item)
        elif media_item["type"] == eventtypes.STREAM_BUFFER_START:
            self.telnet_liquidsoap.start_web_stream_buffer(media_item)
        elif media_item["type"] == eventtypes.STREAM_OUTPUT_START:
            if media_item['row_id'] != \
                    self.telnet_liquidsoap.current_prebuffering_stream_id:
                #this is called if the stream wasn't scheduled sufficiently
                #ahead of time so that the prebuffering stage could take
                #effect. Let's do the prebuffering now.
                self.telnet_liquidsoap.start_web_stream_buffer(media_item)
            self.telnet_liquidsoap.start_web_stream(media_item)
        elif media_item['type'] == eventtypes.STREAM_BUFFER_END:
            self.telnet_liquidsoap.stop_web_stream_buffer()
        elif media_item['type'] == eventtypes.STREAM_OUTPUT_END:
            self.telnet_liquidsoap.stop_web_stream_output()
        else:
            raise UnknownMediaItemType(str(media_item))

    def handle_file_type(self, media_item):
        """
        Wait maximum 5 seconds (50 iterations) for file to become ready, 
        otherwise give up on it.
        """
        iter_num = 0
        while not media_item['file_ready'] and iter_num < 50:
            time.sleep(0.1)
            iter_num += 1

        if media_item['file_ready']:
            available_queue = self.find_available_queue()

            print
            print '---------------------------------'
            print 'available queue:'
            print available_queue
            print '---------------------------------'
            print

            try:
                self.telnet_liquidsoap.queue_push(available_queue, media_item)
                self.liq_queue_tracker[available_queue] = media_item
            except Exception as e:
                self.logger.error(e)
                raise
        else:
            self.logger.warn(
                "File %s did not become ready in less than 5 " +
                "seconds. Skipping...", media_item['dst'])

    def handle_event_type(self, media_item):
        if media_item['event_type'] == "kick_out":
            self.telnet_liquidsoap.disconnect_source("live_dj")
        elif media_item['event_type'] == "switch_off":
            self.telnet_liquidsoap.switch_source("live_dj", "off")

    def is_media_item_finished(self, media_item):
        if media_item is None:
            return True
        else:
            return datetime.now() > media_item['end']

    def find_available_queue(self):
        available_queue = None
        for i in self.liq_queue_tracker:
            mi = self.liq_queue_tracker[i]
            if mi == None or self.is_media_item_finished(mi):
                #queue "i" is available. Push to this queue
                available_queue = i

        if available_queue == None:
            raise NoQueueAvailableException()

        return available_queue

    def verify_correct_present_media(self, scheduled_now):
        #verify whether Liquidsoap is currently playing the correct files.
        #if we find an item that Liquidsoap is not playing, then push it
        #into one of Liquidsoap's queues. If Liquidsoap is already playing
        #it do nothing. If Liquidsoap is playing a track that isn't in
        #currently_playing then stop it.

        #Check for Liquidsoap media we should source.skip
        #get liquidsoap items for each queue. Since each queue can only have
        #one item, we should have a max of 8 items.

        #2013-03-21-22-56-00_0: {
        #id: 1,
        #type: "stream_output_start",
        #row_id: 41,
        #uri: "http://stream2.radioblackout.org:80/blackout.ogg",
        #start: "2013-03-21-22-56-00",
        #end: "2013-03-21-23-26-00",
        #show_name: "Untitled Show",
        #independent_event: true
        #},


        scheduled_now_files = \
                filter(lambda x: x["type"] == eventtypes.FILE, scheduled_now)

        scheduled_now_webstream = \
                filter(lambda x: x["type"] == eventtypes.STREAM_OUTPUT_START,
                        scheduled_now)

        schedule_ids = set(map(lambda x: x["row_id"], scheduled_now_files))

        row_id_map = {}
        liq_queue_ids = set()
        for i in self.liq_queue_tracker:
            mi = self.liq_queue_tracker[i]
            if not self.is_media_item_finished(mi):
                liq_queue_ids.add(mi["row_id"])
                row_id_map[mi["row_id"]] = mi

        to_be_removed = set()
        to_be_added = set()

        #Iterate over the new files, and compare them to currently scheduled
        #tracks. If already in liquidsoap queue still need to make sure they
        #don't have different attributes such replay_gain etc.
        for i in scheduled_now_files:
            if i["row_id"] in row_id_map:
                mi = row_id_map[i["row_id"]]
                correct = mi['start'] == i['start'] and \
                        mi['end'] == i['end'] and \
                        mi['row_id'] == i['row_id'] and \
                        mi['replay_gain'] == i['replay_gain']

                if not correct:
                    #need to re-add
                    self.logger.info("Track %s found to have new attr." % i)
                    to_be_removed.add(i["row_id"])
                    to_be_added.add(i["row_id"])

        to_be_removed.update(liq_queue_ids - schedule_ids)
        to_be_added.update(schedule_ids - liq_queue_ids)

        if to_be_removed:
            self.logger.info("Need to remove items from Liquidsoap: %s" %
                             to_be_removed)

            #remove files from Liquidsoap's queue
            for i in self.liq_queue_tracker:
                mi = self.liq_queue_tracker[i]
                if mi is not None and mi["row_id"] in to_be_removed:
                    self.stop(i)

        if to_be_added:
            self.logger.info("Need to add items to Liquidsoap *now*: %s" %
                             to_be_added)

            for i in scheduled_now:
                if i["row_id"] in to_be_added:
                    self.modify_cue_point(i)
                    self.play(i)

        #handle webstreams
        current_stream_id = self.telnet_liquidsoap.get_current_stream_id()
        if scheduled_now_webstream:
            if current_stream_id != scheduled_now_webstream[0]:
                self.play(scheduled_now_webstream[0])
        elif current_stream_id != "-1":
            #something is playing and it shouldn't be.
            self.telnet_liquidsoap.stop_web_stream_buffer()
            self.telnet_liquidsoap.stop_web_stream_output()

    def stop(self, queue):
        self.telnet_liquidsoap.queue_remove(queue)
        self.liq_queue_tracker[queue] = None

    def is_file(self, media_item):
        return media_item["type"] == eventtypes.FILE

    def clear_queue_tracker(self):
        for i in self.liq_queue_tracker.keys():
            self.liq_queue_tracker[i] = None

    def modify_cue_point(self, link):
        if not self.is_file(link):
            return

        tnow = datetime.now()

        link_start = link['start']

        diff_td = tnow - link_start
        diff_sec = pure.date_interval_to_seconds(diff_td)

        if diff_sec > 0:
            self.logger.debug(
                "media item was supposed to start %s ago. " +
                "Preparing to start..", diff_sec)
            original_cue_in_td = timedelta(seconds=float(link['cue_in']))
            link['cue_in'] = \
                pure.date_interval_to_seconds(original_cue_in_td) + diff_sec

    def clear_all_queues(self):
        self.telnet_liquidsoap.queue_clear_all()

    def get_liquidsoap_version(self, version_string):
        m = re.match(r"Liquidsoap (\d+.\d+.\d+)", version_string)
        if m:
            return m.group(1)
        else:
            return None

    def liquidsoap_startup_test(self):
        liquidsoap_version_string = \
                self.get_liquidsoap_version(
                        self.telnet_liquidsoap.liquidsoap_get_info())
        while not liquidsoap_version_string:
            self.logger.warning("Liquidsoap doesn't appear to be running!, " +
                                "Sleeping and trying again")
            time.sleep(1)
            liquidsoap_version_string = \
                self.get_liquidsoap_version(
                        self.telnet_liquidsoap.liquidsoap_get_info())

        while pure.version_cmp(liquidsoap_version_string,
                               constants.LIQUIDSOAP_MIN_VERSION) < 0:
            self.logger.warning(
                "Liquidsoap is running but in incorrect " +
                "version! Make sure you have at least Liquidsoap %s " +
                "installed", constants.LIQUIDSOAP_MIN_VERSION)
            time.sleep(1)
            liquidsoap_version_string = \
                self.get_liquidsoap_version(
                        self.telnet_liquidsoap.liquidsoap_get_info())

        self.logger.info("Liquidsoap version string found %s",
                         liquidsoap_version_string)