Ejemplo n.º 1
0
 def connect(self):
     """Create connection to CasparCG Server"""
     try:
         self.connection = telnetlib.Telnet(self.host, self.port, timeout=self.timeout)
     except Exception:
         log_traceback()
         return False
     return True
Ejemplo n.º 2
0
def get_plugin_path(group=False):
    try:
        plugin_path = os.path.join(
            storages[int(config.get("plugin_storage", 1))].local_path,
            config.get("plugin_root", ".nx/scripts/v5"),
        )
    except Exception:
        log_traceback()
        return ""

    if group:
        plugin_path = os.path.join(plugin_path, group)
    if not os.path.exists(plugin_path):
        return ""
    return plugin_path
Ejemplo n.º 3
0
    def load(self):
        if not os.path.exists(self.cache_path):
            return
        start_time = time.time()
        try:
            data = json.load(open(self.cache_path))
        except Exception:
            log_traceback("Corrupted cache file '{}'".format(self.cache_path))
            return

        for meta in data:
            self.data[int(meta["id"])] = Asset(meta=meta)
        logging.debug("Loaded {} assets from cache in {:.03f}s".format(
            len(self.data),
            time.time() - start_time))
Ejemplo n.º 4
0
    def query(self, query, **kwargs):
        """Send AMCP command"""
        if not self.connection:
            if not self.connect():
                return CasparResponse(500, "Unable to connect CasparCG server")

        query = query.strip()
        if kwargs.get("verbose", True):
            if not query.startswith("INFO"):
                logging.debug("Executing AMCP: {}".format(query))
        query += "\r\n"

        if PYTHON_VERSION >= 3:
            query = bytes(query.encode("utf-8"))
            delim = bytes("\r\n".encode("utf-8"))
        else:
            delim = "\r\n"

        try:
            self.connection.write(query)
            result = self.connection.read_until(delim).strip()
        except Exception:
            log_traceback()
            return CasparResponse(500, "Query failed")

        if PYTHON_VERSION >= 3:
            result = result.decode("UTF-8")

        if not result:
            return CasparResponse(500, "No result")

        try:
            if result[0:3] == "202":
                return CasparResponse(202, "No result")

            elif result[0:3] in ["201", "200"]:
                stat = int(result[0:3])
                result = decode_if_py3(
                    self.connection.read_until(delim)).strip()
                return CasparResponse(stat, result)

            elif int(result[0:1]) > 3:
                stat = int(result[0:3])
                return CasparResponse(stat, result)
        except Exception:
            log_traceback()
            return CasparResponse(500, "Malformed result: {}".format(result))
        return CasparResponse(500, "Unexpected result: {}".format(result))
Ejemplo n.º 5
0
    def save(self, key, value):
        if config.get("mc_thread_safe", False):
            return self.threaded_save(key, value)

        key = self.site + "-" + key
        for i in range(MAX_RETRIES):
            try:
                self.conn.set(str(key), str(value))
                break
            except Exception:
                log_traceback(f"Cache save failed ({key})")
                time.sleep(0.1)
                self.connect()
        else:
            critical_error("Memcache save failed. This should never happen.")
        return True
Ejemplo n.º 6
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")
Ejemplo n.º 7
0
 def restore_state(self):
     state = get_app_state(self.app.app_state_path)
     if "main_window/geometry" in state.allKeys():
         self.restoreGeometry(state.value("main_window/geometry"))
         self.restoreState(state.value("main_window/state"))
     else:
         self.resize(800, 600)
         qr = self.frameGeometry()
         cp = QDesktopWidget().availableGeometry().center()
         qr.moveCenter(cp)
         self.move(qr.topLeft())
     if "main_window/app" in state.allKeys():
         try:
             self.app_state = state.value("main_window/app")
         except Exception:
             log_traceback()
Ejemplo n.º 8
0
    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)
Ejemplo n.º 9
0
    def query(self, query, **kwargs):
        """Send AMCP command"""
        if not self.connection:
            if not self.connect():
                return CasparResponse(500, "Unable to connect CasparCG server")

        query = query.strip()
        if kwargs.get("verbose", True):
            if not query.startswith("INFO"):
                logging.debug("Executing AMCP: {}".format(query))
        query += "\r\n"

        if PYTHON_VERSION >= 3:
            query = bytes(query.encode("utf-8"))
            delim = bytes("\r\n".encode("utf-8"))
        else:
            delim = "\r\n"

        try:
            self.connection.write(query)
            result = self.connection.read_until(delim).strip()
        except Exception:
            log_traceback()
            return CasparResponse(500, "Query failed")

        if PYTHON_VERSION >= 3:
            result = result.decode("UTF-8")

        if not result:
            return CasparResponse(500, "No result")

        try:
            if result[0:3] == "202":
                return CasparResponse(202, "No result")

            elif result[0:3] in ["201", "200"]:
                stat = int(result[0:3])
                result = decode_if_py3(self.connection.read_until(delim)).strip()
                return CasparResponse(stat, result)

            elif int(result[0:1]) > 3:
                stat = int(result[0:3])
                return CasparResponse(stat, result)
        except Exception:
            log_traceback()
            return CasparResponse(500, "Malformed result: {}".format(result))
        return CasparResponse(500, "Unexpected result: {}".format(result))
Ejemplo n.º 10
0
    def run(self, method, callback, **kwargs):
        logging.debug("Executing {}{} query".format(
            "" if callback == -1 else "async ", method))
        kwargs["session_id"] = config["session_id"]
        kwargs["initiator"] = CLIENT_ID

        if method in ["ping", "login", "logout"]:
            method = "/" + method
            mime = QVariant("application/x-www-form-urlencoded")
            post_data = QUrlQuery()
            for key in kwargs:
                post_data.addQueryItem(key, kwargs[key])
            data = post_data.toString(QUrl.FullyEncoded).encode("ascii")
        else:
            method = "/api/" + method
            mime = QVariant("application/json")
            data = json.dumps(kwargs).encode("ascii")

        request = QNetworkRequest(QUrl(config["hub"] + method))
        request.setHeader(QNetworkRequest.ContentTypeHeader, mime)
        request.setHeader(
            QNetworkRequest.UserAgentHeader,
            QVariant(f"nebula-firefly/{FIREFLY_VERSION}"),
        )

        try:
            query = self.manager.post(request, data)
            if callback != -1:
                query.finished.connect(
                    functools.partial(self.handler, query, callback))
            self.queries.append(query)
        except Exception:
            log_traceback()
            if callback:
                r = NebulaResponse(400, "Unable to send request")
                if callback == -1:
                    return r
                else:
                    callback(r)
            return

        if callback == -1:
            while not query.isFinished():
                time.sleep(0.0001)
                QApplication.processEvents()
            return self.handler(query, -1)
Ejemplo n.º 11
0
    def __init__(self):
        """Load initial config."""

        super(Config, self).__init__()
        self["site_name"] = "Unnamed"
        self["user"] = "******"
        self["host"] = socket.gethostname()
        self["storages"] = {}
        self["rights"] = {}
        self["folders"] = {}
        self["playout_channels"] = {}
        self["ingest_channels"] = {}
        self["cs"] = {}
        self["views"] = {}
        self["meta_types"] = {}
        self["actions"] = {}
        self["services"] = {}

        if len(sys.argv) > 1 and os.path.exists(sys.argv[1]):
            local_settings_path = sys.argv[1]
        else:
            local_settings_path = "settings.json"

        settings_files = ["/etc/nebula.json", local_settings_path]
        settings = {}
        if "--daemon" in sys.argv:
            logging.file = os.devnull
            settings["daemon_mode"] = True

        for settings_file in settings_files:
            if os.path.exists(settings_file):
                try:
                    settings.update(json.load(open(settings_file)))
                    break
                except Exception:
                    log_traceback(handlers=False)

        for key, value in dict(os.environ).items():
            if key.lower().startswith("nebula_"):
                key = key.lower().replace("nebula_", "", 1)
                settings[key] = value

        if not settings:
            critical_error("Unable to open site settings")
        self.update(settings)
Ejemplo n.º 12
0
def parse_request(**kwargs):
    data = kwargs

    if cherrypy.request.method == "POST":
        try:
            raw_body = cherrypy.request.body.read().decode("utf-8")
            if raw_body.strip():
                data.update(json.loads(raw_body))
        except Exception:
            pass
    if not data.get("session_id", None):
        try:
            data["session_id"] = cherrypy.request.cookie["session_id"].value
        except KeyError:
            pass
        except Exception:
            log_traceback()
    return data
Ejemplo n.º 13
0
 def threaded_save(self, key, value):
     if not self.pool:
         self.pool = pylibmc.ThreadMappedPool(self.conn)
     key = self.site + "-" + key
     with self.pool.reserve() as mc:
         for i in range(MAX_RETRIES):
             try:
                 mc.set(str(key), str(value))
                 break
             except Exception:
                 log_traceback(f"Cache save failed ({key})")
                 time.sleep(0.3)
                 self.connect()
         else:
             critical_error(
                 "Memcache save failed. This should never happen.")
     self.pool.relinquish()
     return True
Ejemplo n.º 14
0
 def event(self):
     if not hasattr(self, "_event"):
         self.db.query(
             """
             SELECT meta FROM events
             WHERE id_magic=%s
             """,
             [self.id],
         )  # TODO: playout only
         try:
             self._event = Event(meta=self.db.fetchall()[0][0])
         except IndexError:
             logging.error(f"Unable to get {self} event")
             self._event = False
         except Exception:
             log_traceback()
             self._event = False
     return self._event
Ejemplo n.º 15
0
    def __init__(self, parent):
        super(BrowserModule, self).__init__(parent)
        self.tabs = QTabWidget(self)
        self.tabs.setTabsClosable(True)
        self.tabs.tabCloseRequested.connect(self.close_tab)
        self.tabs.currentChanged.connect(self.on_tab_switch)

        self.layout = QVBoxLayout(self)
        self.layout.setSpacing(0)
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.addWidget(self.tabs)

        self.setLayout(self.layout)

        tabscfg = self.app_state.get("browser_tabs", [])
        created_tabs = 0
        current_index = 0
        for tabcfg in tabscfg:
            try:
                if tabcfg["id_view"] not in config["views"]:
                    continue
                if tabcfg.get("active"):
                    current_index = self.tabs.count()
                try:
                    del tabcfg["active"]
                except KeyError:
                    pass
                title = False
                if tabcfg.get("title"):
                    title = tabcfg.get("title")
                try:
                    del tabcfg["title"]
                except KeyError:
                    pass

                self.new_tab(title, **tabcfg)
                created_tabs += 1
            except Exception:
                log_traceback()
                logging.warning("Unable to restore tab")
        if not created_tabs:
            self.new_tab()

        self.tabs.setCurrentIndex(current_index)
Ejemplo n.º 16
0
def format_log_message(message):
    try:
        log = "{}\t{}\t{}@{}\t{}\n".format(
            time.strftime("%H:%M:%S"),
            {
                0: "DEBUG    ",
                1: "INFO     ",
                2: "WARNING  ",
                3: "ERROR    ",
                4: "GOOD NEWS",
            }[message.data["message_type"]],
            message.data["user"],
            message.host,
            message.data["message"],
        )
    except Exception:
        log_traceback()
        return None
    return log
Ejemplo n.º 17
0
def get_validator(object_type, **kwargs):
    plugin_path = get_plugin_path("validator")
    if not plugin_path:
        return

    f = FileObject(plugin_path, object_type + ".py")
    if f.exists:
        try:
            py_mod = imp.load_source(object_type, f.path)
        except Exception:
            log_traceback(f"Unable to load plugin {f.base_name}")
            return
    else:
        return

    if "Plugin" not in dir(py_mod):
        logging.error(f"No plugin class found in {f.base_name}")
        return
    return py_mod.Plugin(**kwargs)
Ejemplo n.º 18
0
 def export_template(self):
     data = dump_template(self.calendar)
     try:
         if not os.path.exists("templates"):
             os.makedirs("templates")
     except Exception:
         log_traceback()
     save_file_path = QFileDialog.getSaveFileName(
         self,
         "Save scheduler template",
         os.path.abspath("templates"),
         "Templates (*.xml)",
     )[0]
     if os.path.splitext(save_file_path)[1].lower() != ".xml":
         save_file_path += ".xml"
     try:
         with open(save_file_path, "wb") as save_file:
             save_file.write(data.encode("utf-8"))
     except Exception:
         log_traceback()
Ejemplo n.º 19
0
def t(*args):
    tools_dir = get_plugin_path("tools")
    if not tools_dir:
        return
    plugin_name = args[0]

    try:
        fp, pathname, description = imp.find_module(plugin_name, [tools_dir])
    except ImportError:
        critical_error("unable to locate module: " + plugin_name)

    try:
        module = imp.load_module(plugin_name, fp, pathname, description)
    except Exception:
        log_traceback()
        critical_error("Unable ot open tool " + plugin_name)

    logging.user = plugin_name
    margs = args[1:] if len(args) > 1 else []
    module.Plugin(*margs)
Ejemplo n.º 20
0
    def __init__(self, id_service, settings=False):
        logging.debug(f"Initializing service ID {id_service}")
        self.id_service = id_service
        self.settings = settings
        config["id_service"] = id_service

        try:
            self.on_init()
        except SystemExit:
            sys.exit(1)
        except Exception:
            log_traceback(f"Unable to initialize service ID {id_service}")
            self.shutdown()
        else:
            db = DB()
            db.query(
                "UPDATE services SET last_seen = %s, state=1 WHERE id=%s",
                [time.time(), self.id_service],
            )
            db.commit()
        logging.goodnews("Service started")
Ejemplo n.º 21
0
    def on_change(self):
        if not self.controller.current_item:
            return

        item = self.controller.current_item
        db = DB()

        self.current_asset = item.asset or Asset()
        self.current_event = item.event or Event()

        logging.info(f"Advanced to {item}")

        if self.last_run:
            db.query(
                """
                UPDATE asrun SET stop = %s
                WHERE id = %s""",
                [int(time.time()), self.last_run],
            )
            db.commit()

        if self.current_item:
            db.query(
                """
                INSERT INTO asrun (id_channel, id_item, start)
                VALUES (%s, %s, %s)
                """,
                [self.id_channel, item.id,
                 time.time()],
            )
            self.last_run = db.lastid()
            db.commit()
        else:
            self.last_run = False

        for plugin in self.plugins:
            try:
                plugin.on_change()
            except Exception:
                log_traceback("Plugin on-change failed")
Ejemplo n.º 22
0
def version_backup(asset):
    target_dir = os.path.join(
        storages[asset["id_storage"]].local_path,
        ".nx",
        "versions",
        f"{int(asset.id/1000):04d}",
        f"{asset.id:d}",
    )

    ext = os.path.splitext(asset.file_path)[1]
    target_fname = f"{asset['mtime']:d}{ext}"

    if not os.path.isdir(target_dir):
        try:
            os.makedirs(target_dir)
        except IOError:
            pass
    try:
        os.rename(asset.file_path, os.path.join(target_dir, target_fname))
    except IOError:
        log_traceback()
        logging.warning(f"Unable to create version backup of {asset}")
Ejemplo n.º 23
0
    def on_message(self, *args):
        data = args[-1]

        if not self.active:
            logging.goodnews("[LISTENER] connected", handlers=False)
            self.active = True
        try:
            message = SeismicMessage(json.loads(data))
        except Exception:
            log_traceback(handlers=False)
            logging.debug(f"[LISTENER] Malformed message: {data}",
                          handlers=False)
            return

        if message.site_name != self.site_name:
            return

        self.last_msg = time.time()

        if message.data and message.data.get("initiator", None) == CLIENT_ID:
            return

        self.queue.put(message)
Ejemplo n.º 24
0
    def send_message(self, method, **data):
        if not (self.connection and self.channel):
            if not self.connect():
                time.sleep(1)
                return

        message = json.dumps(
            [time.time(), config["site_name"], config["host"], method, data]
        )

        try:
            self.channel.basic_publish(
                exchange="", routing_key=config["site_name"], body=message
            )
        except pika.exceptions.ChannelWrongStateError:
            logging.warning("RabbitMQ: nobody's listening", handlers=[])
            return
        except pika.exceptions.StreamLostError:
            logging.error("RabbitMQ connection lost", handlers=[])
            self.connection = self.channel = None
        except Exception:
            log_traceback("RabbitMQ error", handlers=[])
            logging.debug("Unable to send message", message, handlers=[])
            self.connection = self.channel = None
Ejemplo n.º 25
0
    def load(self):
        logging.info("Reloading webtools")
        self.tools = {}
        tooldir = get_plugin_path("webtools")
        if not tooldir:
            return
        for plugin_entry in os.listdir(tooldir):
            entry_path = os.path.join(tooldir, plugin_entry)
            if os.path.isdir(entry_path):
                plugin_module_path = os.path.join(entry_path, plugin_entry + ".py")
                if not os.path.exists(plugin_module_path):
                    continue
            elif not os.path.splitext(plugin_entry)[1] == ".py":
                continue
            else:
                plugin_module_path = os.path.join(tooldir, plugin_entry)

            plugin_module_path = FileObject(plugin_module_path)
            plugin_name = plugin_module_path.base_name
            try:
                py_mod = imp.load_source(plugin_name, plugin_module_path.path)
            except Exception:
                log_traceback(f"Unable to load plugin {plugin_name}")
                continue

            if "Plugin" not in dir(py_mod):
                logging.error(f"No plugin class found in {plugin_module_path}")
                continue

            Plugin = py_mod.Plugin
            if hasattr(Plugin, "title"):
                title = Plugin.title
            else:
                title = plugin_name.capitalize()
            logging.info(f"Loaded plugin {plugin_name} ({plugin_module_path})")
            self.tools[plugin_name] = [Plugin, title]
Ejemplo n.º 26
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)
        )
Ejemplo n.º 27
0
 def main_thread(self):
     try:
         self.on_main()
     except Exception:
         log_traceback()
     self.busy = False
Ejemplo n.º 28
0
    def build(self, *args, **kwargs):
        self["name"] = "passreset"
        self["title"] = "Password reset"
        self["mode"] = "email-entry"

        #
        # REQUEST EMAIL
        #

        if "email" in kwargs:
            email = kwargs["email"].strip()

            if not re.match(EMAIL_REGEXP, email):
                self.context.message("Invalid e-mail address specified", "error")
                return

            db = DB()
            db.query(
                """
                SELECT meta
                FROM users where LOWER(meta->>'email') = LOWER(%s)
                """,
                [email],
            )
            try:
                user = User(meta=db.fetchall()[0][0], db=db)
            except IndexError:
                self.context.message("No such user", "error")
                return

            if time.time() - user.meta.get("pass_reset_time", 0) < 3600:
                self.context.message(
                    "Only one password reset request per hour is allowed", "error"
                )
                return

            token = get_guid()

            user["pass_reset_time"] = time.time()
            user["pass_reset_code"] = token

            mailvars = {
                "name": user["full_name"] or user["login"],
                "site_name": config["site_name"],
                "hub_url": config.get(
                    "hub_url", f"https://{config['site_name']}.nbla.cloud"
                ),
                "token": token,
            }

            body = MAIL_BODY.format(**mailvars)

            try:
                send_mail(email, "Nebula password reset", body)
            except Exception:
                log_traceback()
                self.context.message(
                    """Unable to send password reset email.
                Please contact your system administrator""",
                    "error",
                )
                return

            user.save()

            self["mode"] = "mail-sent"
            return

        #
        # GOT TOKEN
        #

        elif "token" in kwargs:
            token = kwargs["token"].strip()
            self["mode"] = False
            self["token"] = token

            if not re.match(GUID_REGEXP, token):
                self.context.message("Invalid token specified", "error")
                return

            db = DB()
            db.query(
                "SELECT meta FROM users WHERE meta->>'pass_reset_code' = %s", [token]
            )
            try:
                user = User(meta=db.fetchall()[0][0], db=db)
            except IndexError:
                self.context.message("No such token", "error")
                return

            if user["pass_reset_time"] < time.time() - 3600:
                self.context.message("Token expired.", "error")
                self["mode"] = "email-entry"
                return

            pass1 = kwargs.get("pass1", False)
            pass2 = kwargs.get("pass2", False)
            if pass1 and pass2:
                if pass1 != pass2:
                    self["mode"] = "pass-entry"
                    self.context.message("Passwords don't match", "error")
                    return

                if len(pass1) < 8:
                    self["mode"] = "pass-entry"
                    self.context.message(
                        "The password is weak. Must be at least 8 characters", "error"
                    )
                    return

                user.set_password(pass1)
                del user.meta["pass_reset_code"]
                del user.meta["pass_reset_time"]
                user.save()

                self["mode"] = "finished"
                return

            self["mode"] = "pass-entry"
            return
Ejemplo n.º 29
0
def send_to(
    id_asset,
    id_action,
    settings=None,
    id_user=None,
    priority=3,
    restart_existing=True,
    restart_running=False,
    db=False,
):
    db = db or DB()
    if not id_asset:
        NebulaResponse(401, message="You must specify existing object")

    if settings is None:
        settings = {}

    db.query(
        """
        SELECT id
        FROM jobs
        WHERE id_asset=%s AND id_action=%s AND settings=%s
        """,
        [id_asset, id_action, json.dumps(settings)],
    )
    res = db.fetchall()
    if res:
        if restart_existing:
            conds = "0,5"
            if not restart_running:
                conds += ",1"

            db.query(
                f"""
                UPDATE jobs SET
                    id_user=%s,
                    id_service=NULL,
                    message='Restart requested',
                    status=5,
                    retries=0,
                    creation_time=%s,
                    start_time=NULL,
                    end_time=NULL
                WHERE id=%s
                    AND status NOT IN ({conds})
                RETURNING id
                """,
                [id_user, time.time(), res[0][0]],
            )
            db.commit()
            if db.fetchall():
                messaging.send(
                    "job_progress",
                    id=res[0][0],
                    id_asset=id_asset,
                    id_action=id_action,
                    progress=0,
                )
                return NebulaResponse(201, message="Job restarted")
            return NebulaResponse(200, message="Job exists. Not restarting")
        else:
            return NebulaResponse(200, message="Job exists. Not restarting")

    #
    # Create a new job
    #

    db.query(
        """INSERT INTO jobs (
            id_asset,
            id_action,
            id_user,
            settings,
            priority,
            message,
            creation_time
        ) VALUES (
            %s,
            %s,
            %s,
            %s,
            %s,
            'Pending',
            %s
        )
        RETURNING id
        """,
        [
            id_asset, id_action, id_user,
            json.dumps(settings), priority,
            time.time()
        ],
    )

    try:
        id_job = db.fetchall()[0][0]
        db.commit()
    except Exception:
        log_traceback()
        return NebulaResponse(500, "Unable to create job")

    messaging.send(
        "job_progress",
        id=id_job,
        id_asset=id_asset,
        id_action=id_action,
        progress=0,
        message="Job created",
    )
    return NebulaResponse(201, message="Job created")
Ejemplo n.º 30
0
def get_job(id_service, action_ids, db=False):
    assert type(action_ids) == list, "action_ids must be list of integers"
    if not action_ids:
        return False
    db = db or DB()
    now = time.time()

    running_jobs_count = {}
    db.query("""
        select id_action, count(id)
        from jobs
        where status=1
        group by id_action
        """)
    for id_action, cnt in db.fetchall():
        running_jobs_count[id_action] = cnt

    q = """
        SELECT
            id,
            id_action,
            id_asset,
            id_user,
            settings,
            priority,
            retries,
            status
        FROM jobs
        WHERE
            status IN (0,3,5)
            AND id_action IN %s
            AND id_service IS NULL
            AND retries < %s
            ORDER BY priority DESC, creation_time DESC
        """
    db.query(q, [tuple(action_ids), MAX_RETRIES])

    for (
            id_job,
            id_action,
            id_asset,
            id_user,
            settings,
            priority,
            retries,
            status,
    ) in db.fetchall():
        asset = Asset(id_asset, db=db)
        action = actions[id_action]
        job = Job(id_job, db=db)
        job._asset = asset
        job._settings = settings
        job.priority = priority
        job.retries = retries
        job.id_user = id_user

        max_running_jobs = action.settings.attrib.get("max_jobs", 0)
        try:
            max_running_jobs = int(max_running_jobs)
        except ValueError:
            max_running_jobs = 0
        if max_running_jobs:
            running_jobs = running_jobs_count.get(id_action, 0)
            if running_jobs >= max_running_jobs:
                continue  # Maximum allowed jobs already running. skip

        for pre in action.settings.findall("pre"):
            if pre.text:
                try:
                    exec(pre.text)
                except Exception:
                    log_traceback()
                    continue
        if not action:
            logging.warning(
                f"Unable to get job. No such action ID {id_action}")
            continue

        if status != 5 and action.should_skip(asset):
            logging.info(f"Skipping {job}")
            db.query(
                """
                UPDATE jobs SET
                    status=6,
                    message='Skipped',
                    start_time=%s,
                    end_time=%s
                WHERE id=%s
                """,
                [now, now, id_job],
            )
            db.commit()
            continue

        if action.should_start(asset):
            if job.take(id_service):
                return job
            else:
                logging.warning(f"Unable to take {job}")
                continue
        else:
            db.query("UPDATE jobs SET message='Waiting' WHERE id=%s", [id_job])
            messaging.send(
                "job_progress",
                id=id_job,
                id_asset=id_asset,
                id_action=id_action,
                status=status,
                progress=0,
                message="Waiting",
            )
            db.commit()
    return False
Ejemplo n.º 31
0
def api_set(**kwargs):
    object_type = kwargs.get("object_type", "asset")
    objects = kwargs.get("objects", [])
    data = kwargs.get("data", {})
    user = kwargs.get("user", anonymous)
    db = kwargs.get("db", DB())
    initiator = kwargs.get("initiator", None)

    if not user:
        return NebulaResponse(401)

    if not (data and objects):
        return NebulaResponse(200, "No object created or modified")

    object_type_class = {
        "asset": Asset,
        "item": Item,
        "bin": Bin,
        "event": Event,
        "user": User,
    }.get(object_type, None)

    if object_type_class is None:
        return NebulaResponse(400, f"Unsupported object type {object_type}")

    changed_objects = []
    affected_bins = []

    if "_password" in data:
        hpass = get_hash(data["_password"])
        del data["_password"]
        data["password"] = hpass

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

        if object_type == "asset":
            id_folder = data.get("id_folder", False) or obj["id_folder"]
            if not user.has_right("asset_edit", id_folder):
                folder_title = config["folders"][id_folder]["title"]
                return NebulaResponse(
                    403,
                    f"{user} is not allowed to edit {folder_title} folder")
        elif object_type == "user":
            if obj.id:
                if not user.has_right("user_edit"):
                    return NebulaResponse(
                        403, f"{user} is not allowed to edit users data")
            else:
                if not user.has_right("user_create"):
                    return NebulaResponse(
                        403, f"{user} is not allowed to add new users")

        changed = False
        for key in data:
            value = data[key]
            old_value = obj[key]
            obj[key] = value
            if obj[key] != old_value:
                changed = True

        validator = get_validator(object_type, db=db)

        if changed and validator:
            logging.debug("Executing validation script")
            tt = obj.__repr__()
            try:
                obj = validator.validate(obj)
            except Exception:
                return NebulaResponse(
                    500, log_traceback("Unable to validate object changes."))

            if not isinstance(obj, BaseObject):
                # TODO: use 409-conflict?
                return NebulaResponse(400, f"Unable to save {tt}:\n\n{obj}")

        if changed:
            obj.save(notify=False)
            changed_objects.append(obj.id)
            if object_type == "item" and obj["id_bin"] not in affected_bins:
                affected_bins.append(obj["id_bin"])

    if changed_objects:
        messaging.send(
            "objects_changed",
            objects=changed_objects,
            object_type=object_type,
            user="******".format(user),
            initiator=initiator,
        )

    if affected_bins:
        bin_refresh(affected_bins, db=db, initiator=initiator)

    return NebulaResponse(200, data=changed_objects)
Ejemplo n.º 32
0
    def main(self):
        info = self.parser.get_info(self.caspar_feed_layer)
        if not info:
            logging.warning("Channel {} update stat failed".format(self.id_channel))
            self.bad_requests += 1
            if self.bad_requests > 10:
                logging.error("Connection lost. Reconnecting...")
                if self.connect():
                    logging.goodnews("Connection estabilished")
                else:
                    logging.error("Connection call failed")
                    time.sleep(2)
            time.sleep(0.3)
            return
        else:
            self.request_time = time.time()
        self.bad_requests = 0

        current_fname = info["current"]
        cued_fname = info["cued"]

        #
        #
        # Auto recovery
        #
        #        if not current_fname and time.time() - self.recovery_time > 20:
        #            self.parent.channel_recover()
        #            return
        #        self.recovery_time = time.time()

        if (
            cued_fname
            and (not self.paused)
            and (info["pos"] == self.fpos)
            and not self.parent.current_live
            and self.cued_item
            and (not self.cued_item["run_mode"])
        ):
            if self.stalled and self.stalled < time.time() - 5:
                logging.warning("Stalled for a long time")
                logging.warning("Taking stalled clip (pos: {})".format(self.fpos))
                self.take()
            elif not self.stalled:
                logging.debug("Playback is stalled")
                self.stalled = time.time()
        elif self.stalled:
            logging.debug("No longer stalled")
            self.stalled = False

        self.fpos = info["pos"]
        self.fdur = info["dur"]

        #
        # Playlist advancing
        #

        advanced = False

        if (
            self.cueing
            and self.cueing == current_fname
            and not cued_fname
            and not self.parent.cued_live
        ):
            logging.warning("Using short clip workaround")
            self.current_item = self.cued_item
            self.current_fname = current_fname
            self.current_in = self.cued_in
            self.current_out = self.cued_out
            self.cued_in = self.cued_out = 0
            advanced = True
            self.cued_item = False
            self.cueing = False
            self.cued_fname = False

        elif (not cued_fname) and (current_fname) and not self.parent.cued_live:
            if current_fname == self.cued_fname:
                self.current_item = self.cued_item
                self.current_fname = self.cued_fname
                self.current_in = self.cued_in
                self.current_out = self.cued_out
                self.cued_in = self.cued_out = 0
                advanced = True
            self.cued_item = False

        elif (not current_fname) and (not cued_fname) and self.parent.cued_live:
            self.current_item = self.cued_item
            self.current_fname = "LIVE"
            self.current_in = 0
            self.current_out = 0
            self.cued_in = self.cued_out = 0
            advanced = True
            self.cued_item = False
            self.parent.on_live_enter()

        if advanced:
            try:
                self.parent.on_change()
            except Exception:
                log_traceback("Playout on_change failed")

        if self.current_item and not self.cued_item and not self.cueing:
            self.parent.cue_next()

        elif self.force_cue:
            logging.info("Forcing cue next")
            self.parent.cue_next()
            self.force_cue = False

        if self.cueing:
            if cued_fname == self.cued_fname:
                logging.debug("Cued", self.cueing)
                self.cueing = False
            else:
                logging.debug("Cueing", self.cueing)

        if (
            self.cued_item
            and cued_fname
            and cued_fname != self.cued_fname
            and not self.cueing
        ):
            logging.warning(
                "Cue mismatch: IS: {} SHOULDBE: {}".format(cued_fname, self.cued_fname)
            )
            self.cued_item = False

        try:
            self.parent.on_progress()
        except Exception:
            log_traceback("Playout on_main failed")
        self.current_fname = current_fname
        self.cued_fname = cued_fname