예제 #1
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)
예제 #2
0
    def channel_recover(self):
        logging.warning("Performing recovery")

        db = DB()
        db.query(
            """
            SELECT id_item, start FROM asrun
            WHERE id_channel = %s ORDER BY id DESC LIMIT 1
            """,
            [self.id_channel],
        )
        try:
            last_id_item, last_start = db.fetchall()[0]
        except IndexError:
            logging.error("Unable to perform recovery.")
        last_item = Item(last_id_item, db=db)
        last_item.asset

        self.controller.current_item = last_item
        self.controller.cued_item = False
        self.controller.cued_fname = False

        if last_start + last_item.duration <= time.time():
            logging.info(f"Last {last_item} has been broadcasted.")
            new_item = self.cue_next(item=last_item, db=db, play=True)
        else:
            logging.info(f"Last {last_item} has not been fully broadcasted.")
            new_item = self.cue_next(item=last_item, db=db)

        if not new_item:
            logging.error("Recovery failed. Unable to cue")
            return

        self.on_change()
예제 #3
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
예제 #4
0
 def db(self):
     if not self._db:
         if "db" in self.kwargs:
             self._db = self.kwargs["db"]
         else:
             self._db = DB()
     return self._db
예제 #5
0
 def on_init(self):
     self.actions = []
     db = DB()
     db.query("SELECT id, title, settings FROM actions")
     for id, title, settings in db.fetchall():
         settings = xml(settings)
         self.actions.append(Action(id, title, settings))
예제 #6
0
 def on_main(self):
     db = DB()
     db.query("SELECT id, meta FROM assets WHERE status=%s",
              [ObjectStatus.ONLINE])
     for _, meta in db.fetchall():
         asset = Asset(meta=meta, db=db)
         self.proc(asset)
예제 #7
0
    def build(self, *args, **kwargs):
        services = []
        db = DB()
        db.query("""
            SELECT id, service_type, host, title, autostart, state, last_seen
            FROM services ORDER BY id
            """)
        for id, service_type, host, title, autostart, state, last_seen in db.fetchall(
        ):
            service = {
                "id": id,
                "service_type": service_type,
                "host": host,
                "title": title,
                "autostart": autostart,
                "state": state,
                "last_seen": last_seen,
                "message": "",
            }
            if time.time() - last_seen > 120:
                nrtime = s2words(time.time() - last_seen)
                service["message"] = f"Not responding for {nrtime}"
            services.append(service)

        self["name"] = "services"
        self["title"] = "Services"
        self["js"] = ["/static/js/services.js"]
        self["data"] = services
예제 #8
0
def passwd(*args):
    print()
    try:
        login = input("Login: "******"Password (will be echoed): ").strip()
        is_admin = input("Admin (yes/no): ").strip()
    except KeyboardInterrupt:
        print()
        logging.warning("Interrupted by user")
        sys.exit(0)

    db = DB()
    db.query("SELECT id FROM users WHERE login=%s", [login])
    res = db.fetchall()
    if not res:
        critical_error("Unable to set password: no such user")

    u = User(res[0][0], db=db)
    if login:
        u["login"] = u["full_name"] = login
    u["is_admin"] = 1 if is_admin == "yes" else 0
    u.set_password(password)
    u.save()
    print()
    logging.goodnews("Password changed")
예제 #9
0
 def __init__(self, id_channel, **kwargs):
     self.db = kwargs.get("db", DB())
     self.id_channel = id_channel
     self.playout_config = config["playout_channels"][id_channel]
     self.status_key = "playout_status/{}".format(self.id_channel)
     self.send_action = self.playout_config.get("send_action", False)
     self.scheduled_ids = []
예제 #10
0
def get_scheduled_assets(id_channel, **kwargs):
    db = kwargs.get("db", DB())
    db.query(
        """
            SELECT
                a.meta, dist
            FROM (
                SELECT
                    i.id_asset,
                    MIN(ABS(e.start - extract(epoch from now()))) AS dist
                FROM
                    events as e, items as i
                WHERE
                    e.start > extract(epoch from now()) - 86400*7
                    AND e.id_channel = %s
                    AND i.id_bin = e.id_magic
                    AND i.id_asset > 0
                GROUP BY i.id_asset) i
                LEFT JOIN assets a ON a.id = i.id_asset
            ORDER BY
                dist ASC
        """,
        [id_channel],
    )
    for meta, dist in db.fetchall():
        yield Asset(meta=meta, db=db), dist < 86400
예제 #11
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)
예제 #12
0
def meta_exists(key, value, db=False):
    if not db:
        db = DB()
    db.query("SELECT id, meta FROM assets WHERE meta->>%s = %s",
             [str(key), str(value)])
    for _, meta in db.fetchall():
        return Asset(meta=meta, db=db)
    return False
예제 #13
0
    def on_main(self):
        if not self.import_dir:
            return

        if not os.path.isdir(self.import_dir):
            logging.error("Import directory does not exist. Shutting down.")
            self.import_path = False
            self.shutdown(no_restart=True)
            return

        db = DB()
        for import_file in get_files(self.import_dir, exts=self.exts):
            idec = import_file.base_name
            try:
                with import_file.open("rb") as f:
                    f.seek(0, 2)
                    fsize = f.tell()
            except IOError:
                logging.debug(f"Import file {import_file.base_name} is busy.")
                continue

            if not (import_file.path in self.filesizes
                    and self.filesizes[import_file.path] == fsize):
                self.filesizes[import_file.path] = fsize
                logging.debug(f"New file '{import_file.base_name}' detected")
                continue

            db.query(
                """
                SELECT meta FROM assets
                WHERE meta->>%s = %s
                """,
                [self.identifier, idec],
            )
            for (meta, ) in db.fetchall():
                asset = Asset(meta=meta, db=db)

                if not (asset["id_storage"] and asset["path"]):
                    mk_error(import_file, "This file has no target path.")
                    continue

                if self.versioning and os.path.exists(asset.file_path):
                    version_backup(asset)

                do_import(self, import_file, asset)
                break
            else:
                mk_error(import_file, "This file is not expected.")

        for fname in os.listdir(self.import_dir):
            if not fname.endswith(".error.txt"):
                continue
            idec = fname.replace(".error.txt", "")
            if idec not in [
                    os.path.splitext(f)[0] for f in os.listdir(self.import_dir)
            ]:
                os.remove(os.path.join(self.import_dir, fname))
예제 #14
0
 def shutdown(self, no_restart=False):
     logging.info("Shutting down")
     if no_restart:
         db = DB()
         db.query("UPDATE services SET autostart=FALSE WHERE id=%s",
                  [self.id_service])
         db.commit()
     self.on_shutdown()
     sys.exit(0)
예제 #15
0
 def on_init(self):
     self.services = {}
     db = DB()
     db.query("SELECT id, pid FROM services WHERE host=%s", [config["host"]])
     for _, pid in db.fetchall():
         if pid:
             self.kill_service(pid)
     db.query("UPDATE services SET state = 0 WHERE host=%s", [config["host"]])
     db.commit()
예제 #16
0
def asset_by_full_path(path, db=False):
    if not db:
        db = DB()
    for id_storage in storages:
        if path.startswith(storages[id_storage].local_path):
            return asset_by_path(id_storage,
                                 path.lstrip(storages[id_storage]["path"]),
                                 db=db)
    return False
예제 #17
0
    def build(self, *args, **kwargs):
        db = DB()
        id_user = int(kwargs.get("id_user", 0))
        if id_user and self["user"]["is_admin"]:
            user = User(id_user, db=db)
        else:
            user = self["user"]

        self["user"] = user

        password = kwargs.get("password", False)
        full_name = kwargs.get("full_name", False)
        dashboard = kwargs.get("dashboard", "")
        user_changed = False

        if cherrypy.request.method == "POST":
            if full_name:
                user["full_name"] = kwargs["full_name"]
                user_changed = True

            if password:
                if len(password) < 8:
                    self.context.message(WEAK_PASS_MSG, "error")
                    return
                user.set_password(kwargs["password"])
                user_changed = True

            if dashboard != user["dashboard"]:
                user["dashboard"] = dashboard
                user_changed = True

            if user_changed:
                user.save()
                if self["user"].id == user.id:
                    self.context["user"] = user.meta
                self.context.message("User profile saved")

        db.query("SELECT meta FROM users WHERE meta->>'is_admin' = 'true'")

        self["admins"] = [User(meta=meta) for meta, in db.fetchall()]
        self["name"] = "profile"
        self["title"] = "User profile"
        self["rights"] = [
            ["asset_edit", "Edit assets", "folders"],
            ["asset_create", "Create assets", "folders"],
            ["rundown_view", "View rundown", "playout_channels"],
            ["rundown_edit", "Edit rundown", "playout_channels"],
            ["scheduler_view", "View scheduler", "playout_channels"],
            ["scheduler_edit", "Modify scheduler", "playout_channels"],
            ["job_control", "Control jobs", "actions"],
            ["service_control", "Control services", "services"],
            ["mcr", "Control playout", "playout_channels"],
        ]
예제 #18
0
파일: j.py 프로젝트: nebulabroadcast/nebula
def j(*args):
    print
    db = DB()
    db.query("""
        SELECT
            j.id,
            j.id_action,
            j.settings,
            j.priority,
            j.retries,
            j.status,
            j.progress,
            j.message,
            j.creation_time,
            j.start_time,
            j.end_time,
            a.meta
        FROM
            jobs AS j,
            assets AS a
        WHERE
            a.id = j.id_asset
        AND j.status in (0,1,5)

        ORDER BY
            id DESC LIMIT 50
            """)

    for (
            id,
            id_action,
            settings,
            priority,
            retries,
            status,
            progress,
            message,
            creation_time,
            start_time,
            end_time,
            meta,
    ) in db.fetchall():
        asset = Asset(meta=meta)

        line = "{:<30}".format(asset)
        line += "{} {:.02f}%\n".format(status, progress)

        try:
            sys.stdout.write(line)
            sys.stdout.flush()
        except IOError:
            pass
예제 #19
0
 def on_init(self):
     self.service_type = "conv"
     self.actions = []
     db = DB()
     db.query("""
         SELECT id, title, service_type, settings
         FROM actions ORDER BY id
         """)
     for id_action, title, service_type, settings in db.fetchall():
         if service_type == self.service_type:
             logging.debug(f"Registering action {title}")
             self.actions.append(Action(id_action, title, xml(settings)))
     self.reset_jobs()
예제 #20
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
예제 #21
0
def get_item_event(id_item, **kwargs):
    db = kwargs.get("db", DB())
    db.query("""
        SELECT e.id, e.meta
        FROM items AS i, events AS e
        WHERE e.id_magic = i.id_bin
        AND i.id = {}
        AND e.id_channel in ({})
        """.format(
        id_item,
        ", ".join([str(f) for f in config["playout_channels"].keys()])))
    for _, meta in db.fetchall():
        return Event(meta=meta, db=db)
    return False
예제 #22
0
def get_bin_first_item(id_bin, db=False):
    if not db:
        db = DB()
    db.query(
        """
        SELECT id, meta FROM items
        WHERE id_bin=%s
        ORDER BY position LIMIT 1
        """,
        [id_bin],
    )
    for _, meta in db.fetchall():
        return Item(meta=meta, db=db)
    return False
예제 #23
0
 def on_init(self):
     self.last_update = 0
     db = DB()
     try:
         db.query(
             """
             INSERT INTO hosts(hostname, last_seen)
             VALUES (%s, %s) ON CONFLICT DO NOTHING
             """,
             [config["host"], time.time()],
         )
     except psycopg2.IntegrityError:
         pass
     else:
         db.commit()
예제 #24
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")
예제 #25
0
def get_day_events(id_channel, date, num_days=1):
    chconfig = config["playout_channels"][id_channel]
    start_time = datestr2ts(date, *chconfig.get("day_start", [6, 0]))
    end_time = start_time + (3600 * 24 * num_days)
    db = DB()
    db.query(
        """
        SELECT id, meta
        FROM events
        WHERE id_channel=%s
        AND start > %s
        AND start < %s
        """,
        (id_channel, start_time, end_time),
    )
    for _, meta in db.fetchall():
        yield Event(meta=meta)
예제 #26
0
def asset_by_path(id_storage, path, db=False):
    id_storage = str(id_storage)
    path = path.replace("\\", "/")
    if not db:
        db = DB()
    db.query(
        """
            SELECT id, meta FROM assets
                WHERE media_type = %s
                AND meta->>'id_storage' = %s
                AND meta->>'path' = %s
        """,
        [MediaType.FILE, id_storage, path],
    )
    for id, meta in db.fetchall():
        return Asset(meta=meta, db=db)
    return False
예제 #27
0
def get_user(login, password, db=False):
    if not db:
        db = DB()
    try:
        db.query(
            """
            SELECT meta FROM users
            WHERE login=%s AND password=%s
            """,
            [login, get_hash(password)],
        )
    except ValueError:
        return False
    res = db.fetchall()
    if not res:
        return False
    return User(meta=res[0][0])
예제 #28
0
def get_item_runs(id_channel, from_ts, to_ts, db=False):
    db = db or DB()
    db.query(
        """
        SELECT id_item, start, stop
        FROM asrun
        WHERE start >= %s
        AND start < %s
        ORDER BY start DESC
        """,
        [int(from_ts), int(to_ts)],
    )
    result = {}
    for id_item, start, stop in db.fetchall():
        if id_item not in result:
            result[id_item] = (start, stop)
    return result
예제 #29
0
    def build(self, *args, **kwargs):
        if args[-1] == "reload_settings":
            load_settings()
            webtools.load()
            raise cherrypy.HTTPRedirect("/settings")

        module = "settings"
        if len(args) > 1:
            if args[1] in modules:
                module = args[1]
        db = DB()
        modules[module]["context"](self, db, **kwargs)

        self["name"] = "settings"
        self["title"] = "Settings"
        self["modules"] = modules
        self["module"] = module
예제 #30
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 ""))