Exemple #1
0
def api_solve(**kwargs):
    id_item = kwargs.get("id_item", False)
    solver_name = kwargs.get("solver", False)
    items = kwargs.get("items", [])
    user = kwargs.get("user", anonymous)

    if not user.has_right("rundown_edit", anyval=True):
        return NebulaResponse(403)

    if id_item:
        items.append(id_item)

    if not (items and solver_name):
        return NebulaResponse(
            400, "You must specify placeholder item ID and a solver name"
        )

    Solver = get_solver(solver_name)
    if Solver is None:
        return NebulaResponse(500, "Unable to load the solver. Check logs for details")

    db = DB()
    for id_item in items:
        solver = Solver(Item(id_item, db=db), db=db)
        response = solver.main()
        if response.is_error:
            return response

    return response
Exemple #2
0
 def retake(self, **kwargs):
     layer = kwargs.get("layer", self.caspar_feed_layer)
     if self.parent.current_live:
         return NebulaResponse(409, "Unable to retake live item")
     seekparam = str(int(self.current_item.mark_in() * self.fps))
     if self.current_item.mark_out():
         seekparam += " LENGTH {}".format(
             int(
                 (self.current_item.mark_out() - self.current_item.mark_in())
                 * self.parser.seek_fps
             )
         )
     q = "PLAY {}-{} {} SEEK {}".format(
         self.caspar_channel, layer, self.current_fname, seekparam
     )
     self.paused = False
     result = self.query(q)
     if result.is_success:
         message = "Retake OK"
         self.stalled = False
         self.paused = False
         self.parent.cue_next()
     else:
         message = "Take command failed: " + result.data
     return NebulaResponse(result.response, message)
Exemple #3
0
    def freeze(self, **kwargs):
        layer = kwargs.get("layer", self.caspar_feed_layer)
        if self.parent.current_live:
            return NebulaResponse(409, "Unable to freeze live item")
        if not self.paused:
            q = "PAUSE {}-{}".format(self.caspar_channel, layer)
            message = "Playback paused"
            new_val = True
        else:
            if self.parser.protocol >= 2.07:
                q = "RESUME {}-{}".format(self.caspar_channel, layer)
            else:
                length = "LENGTH {}".format(
                    int((self.current_out or self.fdur) - self.fpos)
                )
                q = "PLAY {}-{} {} SEEK {} {}".format(
                    self.caspar_channel, layer, self.current_fname, self.fpos, length
                )
            message = "Playback resumed"
            new_val = False

        result = self.query(q)
        if result.is_success:
            self.paused = new_val
            if self.parser.protocol < 2.07 and not new_val:
                self.force_cue = True
        else:
            message = result.data
        return NebulaResponse(result.response, message)
Exemple #4
0
def api_rundown(**kwargs):
    """Rundown API endpoint."""
    user = kwargs.get("user", anonymous)
    id_channel = int(kwargs.get("id_channel", -1))
    start_time = kwargs.get("start_time", 0)

    if not (user.has_right("rundown_view", id_channel)
            or user.has_right("rundown_edit", id_channel)):
        return NebulaResponse(401)

    process_start_time = time.time()

    if id_channel not in config["playout_channels"]:
        return NebulaResponse(400, "Invalid playout channel specified")

    rows = []
    i = 0
    for event in get_rundown(id_channel, start_time):
        row = event.meta
        row["object_type"] = "event"
        row["is_empty"] = len(event.items) == 0
        row["id_bin"] = event["id_magic"]
        rows.append(row)
        i += 1
        for item in event.items:
            row = item.meta
            row["object_type"] = "item"
            rows.append(row)
            i += 1

    process_time = time.time() - process_start_time
    return NebulaResponse(200,
                          f"Rundown loaded in {process_time:.02f} seconds",
                          data=rows)
Exemple #5
0
def api_actions(**kwargs):
    objects = kwargs.get("objects") or kwargs.get("ids", [])
    db = kwargs.get("db", DB())
    user = kwargs.get("user", anonymous)

    if not user:
        return NebulaResponse(401, "You are not allowed to execute any actions")

    if not objects:
        return NebulaResponse(400, "No asset selected")

    result = []

    db.query("SELECT id, title, settings FROM actions ORDER BY id ASC")
    for id, title, settings in db.fetchall():
        allow = False  # noqa
        try:
            cond = xml(settings).find("allow_if").text
        except Exception:
            log_traceback()
            continue

        for id_asset in objects:
            asset = Asset(id_asset, db=db)  # noqa
            if not eval(cond):
                break
        else:
            if user.has_right("job_control", id):
                result.append((id, title))

    return NebulaResponse(200, data=result)
Exemple #6
0
def api_settings(**kwargs):
    """Return system settings."""
    if not kwargs.get("user", None):
        return NebulaResponse(401)
    data = {}
    for key in [
        "actions",
        "cs",
        "folders",
        "ingest_channels",
        "meta_types",
        "playout_channels",
        "proxy_url",
        "services",
        "seismic_addr",
        "seismic_port",
        "site_name",
        "views",
        "language",
    ]:
        if key in config:
            data[key] = config[key]

    data["storages"] = {}
    for k in config["storages"]:
        data["storages"][k] = {"title": storages[k].title}
    return NebulaResponse(200, data=data)
 def retake(self, **kwargs):
     if self.parent.current_live:
         return NebulaResponse(409, "Unable to retake live item")
     if not self.current:
         return NebulaResponse(400, "Unable to retake. No item is playing.")
     self.media_player.set_time(self.current.mark_in_ms)
     message = "Retake OK"
     self.parent.cue_next()
     return NebulaResponse(200, message)
    def freeze(self, **kwargs):
        if self.parent.current_live:
            return NebulaResponse(409, "Unable to freeze live item")
        if not self.paused:
            self.media_player.set_pause(True)
            message = "Playback paused"
        else:
            self.media_player.set_pause(False)
            message = "Playback resumed"

        return NebulaResponse(200, message)
Exemple #9
0
 def freeze(self, **kwargs):
     layer = kwargs.get("layer", self.caspar_feed_layer)
     if self.parent.current_live:
         return NebulaResponse(409, "Unable to freeze live item")
     if self.paused:
         query = f"RESUME {self.caspar_channel}-{layer}"
         message = "Playback resumed"
     else:
         query = f"PAUSE {self.caspar_channel}-{layer}"
         message = "Playback paused"
     result = self.query(query)
     return NebulaResponse(result.response, message)
Exemple #10
0
 def take(self, **kwargs):
     layer = kwargs.get("layer", self.caspar_feed_layer)
     if not self.cued_item or self.cueing:
         return NebulaResponse(400, "Unable to take. No item is cued.")
     result = self.query(f"PLAY {self.caspar_channel}-{layer}")
     if result.is_success:
         if self.parent.current_live:
             self.parent.on_live_leave()
         message = "Take OK"
         self.stalled = False
     else:
         message = "Take failed: {result.data}"
     return NebulaResponse(result.response, message)
Exemple #11
0
 def set(self, **kwargs):
     """Set a controller property.
     This is controller specific.
     Args:
         key (str): Name of the property
         value: Value to be set
     """
     key = kwargs.get("key", None)
     value = kwargs.get("value", None)
     if (key is None) or (value is None):
         return NebulaResponse(400)
     if hasattr(self.controller, "set"):
         return self.controller.set(key, value)
     return NebulaResponse(501)
Exemple #12
0
 def take(self, **kwargs):
     layer = kwargs.get("layer", self.caspar_feed_layer)
     if not self.cued_item or self.cueing:
         return NebulaResponse(400, "Unable to take. No item is cued.")
     self.paused = False
     result = self.query("PLAY {}-{}".format(self.caspar_channel, layer))
     if result.is_success:
         if self.parent.current_live:
             self.parent.on_live_leave()
         message = "Take OK"
         self.stalled = False
         self.paused = False
     else:
         message = "Take command failed: " + result.data
     return NebulaResponse(result.response, message)
Exemple #13
0
 def abort(self, **kwargs):
     layer = kwargs.get("layer", self.caspar_feed_layer)
     if not self.cued_item:
         return NebulaResponse(400, "Unable to abort. No item is cued.")
     query = f"LOAD {self.caspar_channel}-{layer} {self.cued_fname}"
     if self.cued_item.mark_in():
         seek = int(self.cued_item.mark_in() * self.channel_fps)
         query += f" SEEK {seek}"
     if self.cued_item.mark_out():
         length = int(
             (self.cued_item.mark_out() - self.cued_item.mark_in()) *
             self.channel_fps)
         query += f" LENGTH {length}"
     result = self.query(query)
     return NebulaResponse(result.response, result.data)
Exemple #14
0
 def set(self, key, value):
     if key == "loop":
         do_loop = int(str(value) in ["1", "True", "true"])
         result = self.query(
             f"CALL {self.caspar_channel}-{self.caspar_feed_layer} LOOP {do_loop}"
         )
         if self.current_item and bool(
                 self.current_item["loop"] != bool(do_loop)):
             self.current_item["loop"] = bool(do_loop)
             self.current_item.save(notify=False)
             bin_refresh([self.current_item["id_bin"]],
                         db=self.current_item.db)
         return NebulaResponse(result.response, f"SET LOOP: {result.data}")
     else:
         return NebulaResponse(400)
    def do_POST(self):
        ctype = self.headers.get("content-type")
        if ctype != "application/x-www-form-urlencoded":
            self.error(400, "Play service received a bad request.")
            return

        length = int(self.headers.get("content-length"))
        postvars = urllib.parse.parse_qs(self.rfile.read(length),
                                         keep_blank_values=1)

        method = self.path.lstrip("/").split("/")[0]
        params = {}
        for key in postvars:
            params[key.decode("utf-8")] = postvars[key][0].decode("utf-8")

        if method not in self.server.methods:
            self.error(501)
            return

        try:
            result = self.server.methods[method](**params)
            if result.is_error:
                logging.error(result.message)
            elif result["message"]:
                logging.info(result.message)
            self.result(result.dict)
        except Exception:
            msg = log_traceback()
            self.result(NebulaResponse(500, msg).dict)
Exemple #16
0
 def cue_backward(self, **kwargs):
     cc = self.controller.cued_item
     if not cc:
         return NebulaResponse(204)
     db = DB()
     nc = get_next_item(cc.id, db=db, force="prev")
     return self.cue(item=nc, db=db, level=5)
Exemple #17
0
 def plugin_exec(self, **kwargs):
     action = kwargs.get("action_name", False)
     data = json.loads(kwargs.get("data", "{}"))
     id_plugin = int(kwargs["id_plugin"])
     logging.debug("Executing playout plugin:", action, id_plugin, data)
     if not action:
         return NebulaResponse(400, "No plugin action requested")
     try:
         plugin = self.plugins[id_plugin]
     except (KeyError, IndexError):
         log_traceback()
         return NebulaResponse(400, "No such action")
     if plugin.on_command(action, **data):
         return NebulaResponse(200)
     else:
         return NebulaResponse(500, "Playout plugin failed")
Exemple #18
0
    def cue(self,
            fname,
            item,
            layer=None,
            play=False,
            auto=True,
            loop=False,
            **kwargs):
        layer = layer or self.caspar_feed_layer

        query_list = ["PLAY" if play else "LOADBG"]
        query_list.append(f"{self.caspar_channel}-{layer}")
        query_list.append(fname)

        if auto:
            query_list.append("AUTO")
        if loop:
            query_list.append("LOOP")
        if item.mark_in():
            query_list.append(f"SEEK {int(item.mark_in() * self.channel_fps)}")
        if item.mark_out():
            query_list.append(
                f"LENGTH {int(item.duration * self.channel_fps)}")

        query = " ".join(query_list)

        self.cueing = fname
        self.cueing_item = item
        self.cueing_time = time.time()

        result = self.query(query)

        if result.is_error:
            message = f'Unable to cue "{fname}" {result.data}'
            self.cued_item = Item()
            self.cued_fname = False
            self.cueing = False
            self.cueing_item = False
            self.cueing_time = 0
            return NebulaResponse(result.response, message)
        if play:
            self.cueing = False
            self.cueing_item = False
            self.cueing_time = 0
            self.current_item = item
            self.current_fname = fname
        return NebulaResponse(200)
Exemple #19
0
 def abort(self, **kwargs):
     layer = kwargs.get("layer", self.caspar_feed_layer)
     if not self.cued_item:
         return NebulaResponse(400, "Unable to abort. No item is cued.")
     q = "LOAD {}-{} {}".format(self.caspar_channel, layer, self.cued_fname)
     if self.cued_item.mark_in():
         q += " SEEK {}".format(int(self.cued_item.mark_in() * self.parser.seek_fps))
     if self.cued_item.mark_out():
         q += " LENGTH {}".format(
             int(
                 (self.cued_item.mark_out() - self.cued_item.mark_in())
                 * self.parser.seek_fps
             )
         )
     result = self.query(q)
     if result.is_success:
         self.paused = True
     return NebulaResponse(result.response, result.data)
    def cue(self, item, full_path, **kwargs):
        self.cued = VlcMedia(self.instance, full_path, item, **kwargs)
        play = kwargs.get("play", False)

        if play:
            return self.take()

        message = "Cued item {} ({})".format(self.cued_item, full_path)

        return NebulaResponse(200, message)
Exemple #21
0
    def main(self, debug=False, counter=0):
        logging.info("Solving {}".format(self.placeholder))
        message = "Solver returned no items. Keeping placeholder."
        try:
            for new_item in self.solve():
                self.new_items.append(new_item)
                if debug:
                    logging.debug("Appending {}".format(new_item.asset))
        except Exception:
            message = log_traceback("Error occured during solving")
            return NebulaResponse(501, message)

        if debug:
            return NebulaResponse(202)

        if not self.new_items:
            return NebulaResponse(204, message)

        i = 0
        for item in self.bin.items:
            i += 1
            if item.id == self.placeholder.id:
                item.delete()
                for new_item in self.new_items:
                    i += 1
                    new_item["id_bin"] = self.bin.id
                    new_item["position"] = i
                    new_item.save(notify=False)
            if item["position"] != i:
                item["position"] = i
                item.save(notify=False)

        if self.bin.id not in self.affected_bins:
            self.affected_bins.append(self.bin.id)

        if self.solve_next:
            self.init_solver(self.solve_next)
            return self.main(debug=debug, counter=len(self.new_items) + counter)

        bin_refresh(self.affected_bins, db=self.db)
        return NebulaResponse(
            200, "Created {} new items".format(len(self.new_items) + counter)
        )
Exemple #22
0
 def retake(self, **kwargs):
     layer = kwargs.get("layer", self.caspar_feed_layer)
     if self.parent.current_live:
         return NebulaResponse(409, "Unable to retake live item")
     seekparams = "SEEK " + str(
         int(self.current_item.mark_in() * self.channel_fps))
     if self.current_item.mark_out():
         seekparams += " LENGTH " + str(
             int((self.current_item.mark_out() -
                  self.current_item.mark_in()) * self.channel_fps))
     query = f"PLAY {self.caspar_channel}-{layer} {self.current_fname} {seekparams}"
     result = self.query(query)
     if result.is_success:
         message = "Retake OK"
         self.stalled = False
         self.parent.cue_next()
     else:
         message = "Take command failed: " + result.data
     return NebulaResponse(result.response, message)
Exemple #23
0
def api_send(**kwargs):
    objects = kwargs.get("objects") or kwargs.get(
        "ids", [])  # TODO: ids is deprecated. use objects instead
    id_action = kwargs.get("id_action", False)
    settings = kwargs.get("settings", {})
    db = kwargs.get("db", DB())
    user = kwargs.get("user", anonymous)
    restart_existing = kwargs.get("restart_existing", True)
    restart_running = kwargs.get("restart_running", False)

    if not user.has_right("job_control", anyval=True):
        return NebulaResponse(403,
                              "You are not allowed to execute this action")
        # TODO: Better ACL

    if not id_action:
        return NebulaResponse(400, "No valid action selected")

    if not objects:
        return NebulaResponse(400, "No asset selected")

    if not user.has_right("job_control", id_action):
        return NebulaResponse(400, "You are not allowed to start this action")

    logging.info(
        "{} is starting action {} for the following assets: {}".format(
            user, id_action, ", ".join([str(i) for i in objects])))

    for id_object in objects:
        send_to(
            id_object,
            id_action,
            settings=settings,
            id_user=user.id,
            restart_existing=restart_existing,
            restart_running=restart_running,
            db=db,
        )

    return NebulaResponse(
        200, "Starting {} job{}".format(len(objects),
                                        "s" if len(objects) > 1 else ""))
Exemple #24
0
 def plugin_list(self, **kwargs):
     result = []
     for id_plugin, plugin in enumerate(self.plugins):
         if not plugin.slots:
             continue
         result.append({
             "id": id_plugin,
             "title": plugin.title,
             "slots": plugin.slot_manifest,
         })
     return NebulaResponse(200, data=result)
Exemple #25
0
    def finalize(self):
        if self.proc.return_code > 0:
            logging.error(self.proc.stderr.read())
            return NebulaResponse(
                500, message=f"Encoding failed\n{self.proc.error_log}"
            )

        for temp_path in self.files:
            target_path = self.files[temp_path]
            try:
                logging.debug(f"Moving {temp_path} to {target_path}")
                os.rename(temp_path, target_path)
            except IOError:
                return NebulaResponse(
                    500,
                    message=f"""Unable to move output file
    Source: {temp_path}
    Target: {target_path}""",
                )

        return NebulaResponse(200, message="Task finished")
    def take(self, **kwargs):
        if not self.cued_item:
            return NebulaResponse(400, "Unable to take. No item is cued.")

        self.media_player.set_media(self.cued.media)
        self.media_player.play()

        self.current = self.cued
        self.cued = None

        self.parent.on_change()

        # TODO: Check if media actually starts playing
        if True:
            if self.parent.current_live:
                self.parent.on_live_leave()
            code = 200
            message = "Take OK"
        else:
            code = 500
            message = "Take command failed"
        return NebulaResponse(code, message)
Exemple #27
0
    def build(self, *args, **kwargs):
        mode = "active"
        if len(args) > 1:
            if args[1] in ["finished", "failed"]:
                mode = args[1]

        id_asset = kwargs.get("id_asset", 0)
        id_action = kwargs.get("id_action", 0)
        try:
            id_asset = int(id_asset)
        except ValueError:
            id_asset = 0
        try:
            id_action = int(id_action)
        except ValueError:
            id_action = 0

        query = kwargs.get("q", "")

        if cherrypy.request.method == "POST":
            if id_asset and id_action:
                # TODO: how to select restert_existing/running?
                response = api_send(ids=[id_asset],
                                    id_action=id_action,
                                    user=self["user"])

                if response.is_error:
                    self.context.message(response.message, level="error")
                else:
                    self.context.message(response.message)

            # do not use filter: show all active jobs to see queue
            id_asset = id_action = 0

        if id_asset:
            db = DB()
            asset = Asset(id_asset, db=db)
            actions = api_actions(user=self["user"], db=db, ids=[id_asset])
        else:
            actions = NebulaResponse(404)
            asset = False

        self["name"] = "jobs"
        self["js"] = ["/static/js/jobs.js"]
        self["title"] = mode.capitalize() + " jobs"
        self["mode"] = mode
        self["id_asset"] = id_asset
        self["asset"] = asset
        self["actions"] = actions.data if actions.is_success else []
        self["id_action"] = id_asset
        self["query"] = query
    def cue(self, item, full_path, **kwargs):
        kwargs["item"] = item
        kwargs["meta"] = item.asset.meta
        self.cued = NebulaContiSource(self.conti, full_path, **kwargs)
        # TODO: add per-source filters here
        self.cued.open()
        self.cueing = False

        if not self.cued:
            return NebulaResponse(500)

        if len(self.conti.playlist) > 1:
            del self.conti.playlist[1:]
        self.conti.playlist.append(self.cued)

        if not self.conti.started:
            logging.info("Starting Conti")
            self.conti.start()

        if kwargs.get("play", False):
            return self.take()
        message = "Cued item {} ({})".format(self.cued_item, full_path)
        return NebulaResponse(200, message)
Exemple #29
0
    def cue(self, fname, item, **kwargs):
        auto = kwargs.get("auto", True)
        layer = kwargs.get("layer", self.caspar_feed_layer)
        play = kwargs.get("play", False)
        loop = kwargs.get("loop", False)
        mark_in = item.mark_in()
        mark_out = item.mark_out()

        marks = ""
        if loop:
            marks += " LOOP"
        if mark_in:
            marks += " SEEK {}".format(int(mark_in * self.parser.seek_fps))
        if mark_out and mark_out < item["duration"] and mark_out > mark_in:
            marks += " LENGTH {}".format(
                int((mark_out - mark_in) * self.parser.seek_fps)
            )

        if play:
            q = "PLAY {}-{} {}{}".format(self.caspar_channel, layer, fname, marks)
        else:
            q = "LOADBG {}-{} {} {} {}".format(
                self.caspar_channel, layer, fname, ["", "AUTO"][auto], marks
            )

        self.cueing = fname
        result = self.query(q)

        if result.is_error:
            message = 'Unable to cue "{}" {} - args: {}'.format(
                fname, result.data, str(kwargs)
            )
            self.cued_item = Item()
            self.cued_fname = False
            self.cueing = False
        else:
            self.cued_item = item
            self.cued_fname = fname
            self.cued_in = mark_in * self.fps
            self.cued_out = mark_out * self.fps
            message = "Cued item {} ({})".format(self.cued_item, fname)
        return NebulaResponse(result.response, message)
Exemple #30
0
def api_delete(**kwargs):
    object_type = kwargs.get("object_type", "asset")
    # TODO: ids is deprecated. use objects instead
    objects = kwargs.get("objects") or kwargs.get("ids", [])
    db = kwargs.get("db", DB())
    user = kwargs.get("user", anonymous)
    initiator = kwargs.get("initiator", None)

    if not user:
        return NebulaResponse(401)

    if not (objects):
        return NebulaResponse(200, "No object deleted")

    object_type_class = {
        "asset": Asset,
        "item": Item,
        "bin": Bin,
        "event": Event,
    }[object_type]

    num = 0
    affected_bins = []

    for id_object in objects:
        obj = object_type_class(id_object, db=db)

        if object_type == "item":
            if not user.has_right("rundown_edit", anyval=True):
                return NebulaResponse(403)
            try:
                obj.delete()
            except psycopg2.IntegrityError:
                return NebulaResponse(
                    423, f"Unable to delete {obj}. Already aired.")
            if obj["id_bin"] not in affected_bins:
                affected_bins.append(obj["id_bin"])
        else:
            return NebulaResponse(
                501, f"{object_type} deletion is not implemented")

        num += 1

    if affected_bins:
        bin_refresh(affected_bins, db=db, initiator=initiator)
    return NebulaResponse(200, f"{num} objects deleted")