From 82e930ce5b190caad97957f89db86f4da985eee6 Mon Sep 17 00:00:00 2001 From: Martastain Date: Fri, 3 Sep 2021 14:49:47 +0200 Subject: [PATCH] Updated to 5.32.2 --- README.md | 7 +- firefly/api.py | 2 +- firefly/application.py | 13 +--- firefly/base_module.py | 1 - firefly/common.py | 8 +- firefly/dialogs/batch_ops.py | 2 +- firefly/dialogs/rundown.py | 2 +- firefly/dialogs/send_to.py | 4 +- firefly/filesystem.py | 4 +- firefly/listener.py | 2 +- firefly/main_window.py | 23 ++---- firefly/modules/browser.py | 22 +++--- firefly/modules/detail.py | 84 +++++++-------------- firefly/modules/detail_subclips.py | 11 ++- firefly/modules/jobs_model.py | 6 +- firefly/modules/rundown.py | 31 ++++---- firefly/modules/rundown_mcr.py | 65 +++++++++------- firefly/modules/rundown_plugins.py | 8 +- firefly/modules/scheduler.py | 9 ++- firefly/modules/scheduler_model.py | 41 +++------- firefly/modules/scheduler_utils.py | 6 +- firefly/version.py | 4 +- firefly/view.py | 2 +- firefly/widgets/__init__.py | 14 +--- firefly/widgets/combo.py | 6 +- firefly/widgets/comboutils.py | 1 - firefly/widgets/simple.py | 6 +- nx/cellformat.py | 2 +- res/splash.svg | 115 +++++++++++++++++++++++++++++ 29 files changed, 271 insertions(+), 230 deletions(-) create mode 100644 res/splash.svg diff --git a/README.md b/README.md index 501be94..d64395f 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,8 @@ following command: sudo pip3 install PyQT5 websocket-client ``` -For video playback, you will also need **libmpv1** package. +The following packages are also needed, in case you don't have them already installed, +run `sudo apt install libmpv1 libxcb-util1` on Ubuntu or `sudo apt install libmpv1 libxcb-util0` on Debian. ### Windows @@ -35,8 +36,8 @@ Edit **settings.json** file to set your server address and site name. ```json { "sites" : [{ - "site_name" : "nebulatv", - "hub" : "https://nebulatv.example.com" + "site_name" : "nebula", + "hub" : "https://nebula.example.com" }] } ``` diff --git a/firefly/api.py b/firefly/api.py index ef387ef..57555e9 100644 --- a/firefly/api.py +++ b/firefly/api.py @@ -41,7 +41,7 @@ def run(self, method, callback, **kwargs): ) request.setHeader( QNetworkRequest.UserAgentHeader, - QVariant("nebula-firefly/{}".format(FIREFLY_VERSION)) + QVariant(f"nebula-firefly/{FIREFLY_VERSION}") ) try: diff --git a/firefly/application.py b/firefly/application.py index 5d1f10b..498d116 100644 --- a/firefly/application.py +++ b/firefly/application.py @@ -1,8 +1,5 @@ import sys import locale -import copy - -from pprint import pprint from .common import * from .filesystem import load_filesystem @@ -25,7 +22,7 @@ def check_login(wnd): if data["response"] > 403: QMessageBox.critical( wnd, - "Error {}".format(data["response"]), + f"Error {data['response']}", data["message"] ) return False @@ -34,9 +31,7 @@ def check_login(wnd): class FireflyApplication(Application): def __init__(self, **kwargs): - title = "Firefly {}".format(FIREFLY_VERSION) - if FIREFLY_STATUS: - title += " " + FIREFLY_STATUS + title = f"Firefly {FIREFLY_VERSION}" super(FireflyApplication, self).__init__(name="firefly", title=title) locale.setlocale(locale.LC_NUMERIC, 'C') self.splash = QSplashScreen(pix_lib['splash']) @@ -55,8 +50,8 @@ def __init__(self, **kwargs): config.update(config["sites"][i]) del(config["sites"]) - self.app_state_path = os.path.join(app_dir, "ffdata.{}.appstate".format(config["site_name"])) - self.auth_key_path = os.path.join(app_dir, "ffdata.{}.key".format(config["site_name"])) + self.app_state_path = os.path.join(app_dir, f"ffdata.{config['site_name']}.appstate") + self.auth_key_path = os.path.join(app_dir, f"ffdata.{config['site_name']}.key") # Login diff --git a/firefly/base_module.py b/firefly/base_module.py index 3b85d23..6cb7e9e 100644 --- a/firefly/base_module.py +++ b/firefly/base_module.py @@ -17,7 +17,6 @@ def id_channel(self): @id_channel.setter def id_channel(self, value): - logging.info("Set id_channel to", value) self.main_window.id_channel = int(value) @property diff --git a/firefly/common.py b/firefly/common.py index 8fbdffa..11d8536 100644 --- a/firefly/common.py +++ b/firefly/common.py @@ -11,8 +11,6 @@ logging.user = "" logging.handlers = [] - - class FontLib(): def __init__(self): self.data = {} @@ -72,9 +70,9 @@ def get_pix(name): color = 0xaaaaaa icn.fill(QColor(color)) return icn - pixmap = QPixmap(":/images/{}.png".format(name)) + pixmap = QPixmap(f":/images/{name}.png") if not pixmap.width(): - pix_file = os.path.join(app_dir, "images", "{}.png".format(name)) + pix_file = os.path.join(app_dir, "images", f"{name}.png") if os.path.exists(pix_file): return QPixmap(pix_file) return None @@ -129,7 +127,7 @@ def notify_send(text, level=INFO): ERROR : ["error", 10], GOOD_NEWS : ["good news", 5] }[level] - caption = "Firefly {}".format(caption) + caption = f"Firefly {caption}" if level < WARNING: return diff --git a/firefly/dialogs/batch_ops.py b/firefly/dialogs/batch_ops.py index 410abfe..5886a57 100644 --- a/firefly/dialogs/batch_ops.py +++ b/firefly/dialogs/batch_ops.py @@ -8,7 +8,7 @@ class BatchOpsDialog(QDialog): def __init__(self, parent, objects): super(BatchOpsDialog, self).__init__(parent) self.objects = sorted(objects, key=lambda obj: obj.id) - self.setWindowTitle("Batch modify: {} assets".format(len(self.objects))) + self.setWindowTitle(f"Batch modify: {len(self.objects)} assets") id_folder = self.objects[0]["id_folder"] self.keys = config["folders"][id_folder]["meta_set"] self.form = MetaEditor(self, self.keys) diff --git a/firefly/dialogs/rundown.py b/firefly/dialogs/rundown.py index 9240cb4..0cf503d 100644 --- a/firefly/dialogs/rundown.py +++ b/firefly/dialogs/rundown.py @@ -53,7 +53,7 @@ class SubclipSelectDialog(QDialog): def __init__(self, parent, asset): super(SubclipSelectDialog, self).__init__(parent) self.setModal(True) - self.setWindowTitle("Select {} subclip to use".format(asset)) + self.setWindowTitle(f"Select {asset} subclip to use") self.ok = False self.asset = asset self.subclips = asset.meta.get("subclips", []) diff --git a/firefly/dialogs/send_to.py b/firefly/dialogs/send_to.py index 5289349..99eff69 100644 --- a/firefly/dialogs/send_to.py +++ b/firefly/dialogs/send_to.py @@ -13,9 +13,9 @@ def __init__(self, parent, objects=[]): if len(self.objects) == 1: what = self.objects[0]["title"] else: - what = "{} objects".format(len(self.objects)) + what = f"{len(self.objects)} objects" - self.setWindowTitle("Send {} to...".format(what)) + self.setWindowTitle(f"Send {what} to...") self.actions = [] response = api.actions(objects=self.assets) diff --git a/firefly/filesystem.py b/firefly/filesystem.py index 9240612..90d368f 100644 --- a/firefly/filesystem.py +++ b/firefly/filesystem.py @@ -19,7 +19,7 @@ def load_filesystem(handler=False): for letter in get_available_drives(): if handler: handler(letter) - base_path = "{}:\\".format(letter) + base_path = f"{letter}:\\" if not os.path.exists(base_path): continue @@ -40,4 +40,4 @@ def load_filesystem(handler=False): if id_storage in config["storages"]: config["storages"][id_storage]["protocol"] = "local" config["storages"][id_storage]["path"] = base_path - logging.debug("Mapped storage {} to {}".format(id_storage, base_path)) + logging.debug(f"Mapped storage {id_storage} to {base_path}") diff --git a/firefly/listener.py b/firefly/listener.py index 1de22dc..76b4744 100644 --- a/firefly/listener.py +++ b/firefly/listener.py @@ -66,7 +66,7 @@ def on_message(self, *args): message = SeismicMessage(json.loads(data)) except Exception: log_traceback(handlers=False) - logging.debug("[LISTENER] Malformed message: {}".format(data), handlers=False) + logging.debug(f"[LISTENER] Malformed message: {data}", handlers=False) return if message.site_name != self.site_name: diff --git a/firefly/main_window.py b/firefly/main_window.py index 81eb6b9..6fb05a2 100644 --- a/firefly/main_window.py +++ b/firefly/main_window.py @@ -12,7 +12,7 @@ class FireflyMainWidget(QWidget): def __init__(self, main_window): super(FireflyMainWidget, self).__init__(main_window) self.main_window = main_window - current_tab = self.main_window.app_state.get("current_module",0) + current_tab = self.main_window.app_state.get("current_module", 0) self.perform_on_switch_tab = True self.tabs = QTabWidget(self) @@ -68,14 +68,12 @@ def __init__(self, main_window): self.tabs.currentChanged.connect(self.on_switch_tab) - def on_close(self): self.detail.check_changed() self.main_window.listener.halt() QApplication.quit() logging.debug("[MAIN WINDOW] Window closed") - @property def app(self): return self.main_window.app @@ -119,9 +117,7 @@ def __init__(self, parent, MainWidgetClass): super(FireflyMainWindow, self).__init__(parent, MainWidgetClass) self.setWindowIcon(QIcon(get_pix("icon"))) - title = "Firefly {}".format(FIREFLY_VERSION) - if FIREFLY_STATUS: - title += " " + FIREFLY_STATUS + title = f"Firefly {FIREFLY_VERSION}" title += f" ({user['login']}@{config['site_name']})" self.setWindowTitle(title) self.setAttribute(Qt.WA_AlwaysShowToolTips) @@ -143,12 +139,8 @@ def __init__(self, parent, MainWidgetClass): self.set_channel(self.id_channel) break - logging.info("[MAIN WINDOW] Firefly is ready") - - - def load_window_state(self): self.window_state = self.app_state.get("window_state", {}) self.showMaximized() @@ -250,11 +242,11 @@ def set_channel(self, id_channel): for action in self.menu_scheduler.actions(): if hasattr(action, "id_channel") and action.id_channel == id_channel: action.setChecked(True) + self.id_channel = id_channel if self.scheduler: - self.scheduler.set_channel(id_channel) + self.scheduler.on_channel_changed() if self.rundown: - self.rundown.set_channel(id_channel) - self.id_channel = id_channel + self.rundown.on_channel_changed() def show_detail(self): if self.main_widget.tabs.currentIndex() == 0: @@ -315,7 +307,7 @@ def add_subscriber(self, module, methods): def seismic_handler(self, message): if message.method == "objects_changed" and message.data["object_type"] == "asset": objects = message.data["objects"] - logging.debug("[MAIN WINDOW] {} asset(s) have been changed".format(len(objects))) + logging.debug(f"[MAIN WINDOW] {len(objects)} asset(s) have been changed") asset_cache.request([[aid, message.timestamp + 1] for aid in objects]) return @@ -327,9 +319,8 @@ def seismic_handler(self, message): if message.method in methods: module.seismic_handler(message) - def on_assets_update(self, *assets): - logging.debug("[MAIN WINDOW] Updating {} assets in views".format(len(assets))) + logging.debug(f"[MAIN WINDOW] Updating {len(assets)} assets in views") self.browser.refresh_assets(*assets) self.detail.refresh_assets(*assets) diff --git a/firefly/modules/browser.py b/firefly/modules/browser.py index 8a3231f..013697d 100644 --- a/firefly/modules/browser.py +++ b/firefly/modules/browser.py @@ -50,16 +50,12 @@ def selectionChanged(self, selected, deselected): tot_dur += obj.duration days = math.floor(tot_dur / (24*3600)) - durstr = "{} days {}".format(days, s2time(tot_dur)) if days else s2time(tot_dur) + durstr = f"{days} days {s2time(tot_dur)}" if days else s2time(tot_dur) if self.selected_objects: self.main_window.focus(asset_cache[self.selected_objects[-1].id]) if len(self.selected_objects) > 1 and tot_dur: - logging.debug( - "[BROWSER] {} objects selected. Total duration {}".format( - len(self.selected_objects), durstr - ) - ) + logging.debug(f"[BROWSER] {len(self.selected_objects)} objects selected. Total duration {durstr}") super(FireflyView, self).selectionChanged(selected, deselected) @property @@ -79,7 +75,7 @@ def on_header_clicked(self, index): trend = "asc" else: trend = "asc" - self.parent().search_query["order"] = "{} {}".format(value, trend) + self.parent().search_query["order"] = f"{value} {trend}" self.parent().load() @@ -89,7 +85,7 @@ def on_activate(self, mi): val = obj.show(key) QApplication.clipboard().setText(str(val)) - logging.info("Copied \"{}\" to clipboard".format(val)) + logging.info(f"Copied \"{val}\" to clipboard") def set_page(self, current_page, page_count): self.current_page = current_page @@ -109,7 +105,7 @@ def set_page(self, current_page, page_count): else: self.parent().pager.btn_next.setEnabled(True) - self.parent().pager.info.setText("Page {}".format(current_page)) + self.parent().pager.info.setText(f"Page {current_page}") class PagerButton(QPushButton): @@ -236,7 +232,7 @@ def load_view_menu(self): action = QAction(view["title"], self) action.setCheckable(True) if i < 10: - action.setShortcut("ALT+{}".format(i)) + action.setShortcut(f"ALT+{i}") action.id_view = id_view action.triggered.connect(functools.partial(self.set_view, id_view)) self.action_search.addAction(action) @@ -376,7 +372,7 @@ def link_exec(self, obj, **kwargs): self._parent.new_tab( obj["title"], id_view=kwargs["id_view"], - conds=["'{}' = '{}'".format(param, value)] + conds=[f"'{param}' = '{value}'"] ) self._parent.redraw_tabs() @@ -410,7 +406,7 @@ def on_trash(self): return ret = QMessageBox.question(self, "Trash", - "Do you really want to trash {} selected asset(s)?".format(len(objects)), + f"Do you really want to trash {len(objects)} selected asset(s)?", QMessageBox.Yes | QMessageBox.No ) if ret == QMessageBox.Yes: @@ -444,7 +440,7 @@ def on_archive(self): return ret = QMessageBox.question(self, "Archive", - "Do you really want to move {} selected asset(s) to archive?".format(len(objects)), + f"Do you really want to move {len(objects)} selected asset(s) to archive?", QMessageBox.Yes | QMessageBox.No ) if ret == QMessageBox.Yes: diff --git a/firefly/modules/detail.py b/firefly/modules/detail.py index 9a2a5a8..e0ecce3 100644 --- a/firefly/modules/detail.py +++ b/firefly/modules/detail.py @@ -71,21 +71,16 @@ def on_focus(self): def search_by_key(self, key, id_view=False): b = self.parent().parent().parent().main_window.browser id_view = id_view or b.tabs.widget(b.tabs.currentIndex()).id_view + view_title = config["views"][id_view]["title"] + asset = self.parent().parent().parent().asset b.new_tab( - "{}: {} ({})".format( - config["views"][id_view]["title"], - self.parent().parent().parent().asset.show(key), - meta_types[key].alias(), - ), + f"{view_title}: {asset.show(key)} ({meta_types[key].alias})", id_view=id_view, - conds=["'{}' = '{}'".format(key, self.form[key])] + conds=[f"'{key}' = '{self.form[key]}'"] ) b.redraw_tabs() - - - class MetaList(QTextEdit): def __init__(self, parent): super(MetaList, self).__init__(parent) @@ -122,7 +117,7 @@ def load(self, asset, **kwargs): tag_title = meta_types[tag].alias() value = asset.format_display(tag) or asset["tag"] or "" if value: - data += "{:<40}: {}\n".format(tag_title, value) + data += f"{tag_title:<40}: {value}\n" data += "\n\n" self.setText(data) @@ -130,10 +125,10 @@ def load(self, asset, **kwargs): class DetailTabTechnical(MetaList): def load(self, asset, **kwargs): self.tag_groups = { - "File" : [], - "Format" : [], - "QC" : [] - } + "File" : [], + "Format" : [], + "QC" : [] + } for tag in sorted(meta_types): if tag.startswith("file") or tag in ["id_storage", "path", "origin"]: self.tag_groups["File"].append(tag) @@ -151,7 +146,7 @@ def load(self, asset, **kwargs): tag_title = meta_types[tag].alias() value = asset.format_display(tag) or asset["tag"] or "" if value: - data += "{:<40}: {}\n".format(tag_title, value) + data += f"{tag_title:<40}: {value}\n" data += "\n\n" self.setText(data) @@ -188,7 +183,7 @@ def load(self, asset, **kwargs): def load_video(self): if self.current_asset and not self.loaded: proxy_url = config["hub"] + self.current_asset.proxy_url - logging.debug("[DETAIL] Opening {} preview: {}".format(self.current_asset, proxy_url)) + logging.debug(f"[DETAIL] Opening {self.current_asset} preview: {proxy_url}") self.player.fps = self.current_asset.fps if self.current_asset["poster_frame"]: markers = {"poster_frame" : {"position" : self.current_asset["poster_frame"]}} @@ -351,10 +346,9 @@ def check_changed(self): reply = QMessageBox.question( self, "Save changes?", - "Following data has been changed in the {}\n\n{}".format( - self.asset, "\n".join( - [meta_types[k].alias() for k in changed] - )), + f"Following data has been changed in the {self.asset}" + \ + "\n\n" + \ + "\n".join([meta_types[k].alias() for k in changed]), QMessageBox.Yes | QMessageBox.No ) @@ -363,10 +357,10 @@ def check_changed(self): def focus(self, asset, silent=False, force=False): if not isinstance(asset, Asset): - logging.debug("[DETAIL] Only assets can be focused. Is: {}".format(type(asset))) + logging.debug(f"[DETAIL] Only assets can be focused. Is: {type(asset)}") return - logging.debug("[DETAIL] Focusing", asset) + logging.debug(f"[DETAIL] Focusing {asset}") if self._is_loading: self._load_queue = [asset] @@ -385,7 +379,7 @@ def focus(self, asset, silent=False, force=False): self.folder_select.setEnabled(True) self.asset = Asset(meta=asset.meta) # asset deep copy - self.parent().setWindowTitle("Detail of {}".format(self.asset)) + self.parent().setWindowTitle(f"Detail of {self.asset}") self.detail_tabs.load(self.asset, force=force) self.folder_select.set_value(self.asset["id_folder"]) @@ -471,25 +465,6 @@ def on_apply(self): if self.preview.changed: data.update(self.preview.changed) - - if config.get("debug", False): - reply = QMessageBox.question( - self, - "Save changes?", - "{}".format( - "\n".join("{} : {}".format(k, data[k]) for k in data if data[k]) - ), - QMessageBox.Yes | QMessageBox.No - ) - - if reply == QMessageBox.Yes: - pass - else: - logging.debug("[DETAIL] Save aborted") - return - -# self.form.setEnabled(False) # reenable on seismic message with new data - self.setCursor(Qt.BusyCursor) response = api.set(objects=[self.asset.id], data=data) if not response: @@ -505,32 +480,23 @@ def on_apply(self): #self.form.setEnabled(True) - - def on_revert(self): if self.asset: self.focus(asset_cache[self.asset.id], silent=True) def on_set_qc(self, state): - report = "{} : {} flagged the asset as {}".format( - format_time(time.time()), - user["login"], - { - 0 : "New", - 3 : "Rejected", - 4 : "Approved" - }[state] - ) + state_name = {0 : 'New', 3 : 'Rejected', 4 : 'Approved'}[state] + report = f"{format_time(time.time())} : {user['login']} flagged the asset as {state_name}" if self.asset["qc/report"]: report = self.asset["qc/report"] + "\n" + report response = api.set( - objects=[self.asset.id], - data={ - "qc/state" : state, - "qc/report" : report - } - ) + objects=[self.asset.id], + data={ + "qc/state" : state, + "qc/report" : report + } + ) if not response: logging.error(response.message) return diff --git a/firefly/modules/detail_subclips.py b/firefly/modules/detail_subclips.py index 04202d9..65bc355 100644 --- a/firefly/modules/detail_subclips.py +++ b/firefly/modules/detail_subclips.py @@ -79,14 +79,14 @@ def update_source(self): def create_subclip(self, mark_in, mark_out): + fps = self.parent().current_asset["video/fps_f"] if mark_in and mark_out and mark_in > mark_out: logging.error("Unable to create subclip. In point must precede out point") return text, ok = QInputDialog.getText( self, - #TODO: use fps - 'Create subclip {} to {}'.format(s2tc(mark_in), s2tc(mark_out)), - 'Enter the subclip name:' + f"Create a subclip", + f"{s2tc(mark_in, fps)} - {s2tc(mark_out, fps)}\n\nEnter the subclip name:" ) if not ok: return @@ -180,9 +180,8 @@ def on_rename_subclip(self): old_name = self.model.object_data[idx]["title"] text, ok = QInputDialog.getText( self, - #TODO: use fps - 'Rename subclip {}'.format(old_name), - 'Enter the subclip name:' + "Rename the subclip", + f"Original name: {old_name}\n\nEnter the subclip name:" ) if not ok: return diff --git a/firefly/modules/jobs_model.py b/firefly/modules/jobs_model.py index bd2b522..49bd216 100644 --- a/firefly/modules/jobs_model.py +++ b/firefly/modules/jobs_model.py @@ -27,7 +27,7 @@ def job_format(data, key): id_service = data["id_service"] service = config["services"].get(id_service, None) if service: - return "{}@{}".format(service["title"], service["host"]) + return f"{service['title']}@{service['host']}" return data["id_service"] elif key == "message": return data["message"] @@ -35,7 +35,7 @@ def job_format(data, key): return str(data["id"]) elif key == "progress": if data["status"] == 1: - return "{:.02f}%".format(data["progress"]) + return f"{data['progress']:.02f}%" else: return({ 0 : "Pending", @@ -103,7 +103,7 @@ def data(self, index, role=Qt.DisplayRole): if role == Qt.DisplayRole: return job_format(obj, key) elif role == Qt.ToolTipRole: - return "{}\n\n{}".format(obj["message"], asset_cache[obj["id_asset"]]) + return f"{obj['message']}\n\n{asset_cache[obj['id_asset']]}" elif role == Qt.ForegroundRole: return colors[obj["status"]] diff --git a/firefly/modules/rundown.py b/firefly/modules/rundown.py index b2276d5..84867e4 100644 --- a/firefly/modules/rundown.py +++ b/firefly/modules/rundown.py @@ -85,7 +85,7 @@ def load(self, **kwargs): self.view.model().object_data[idx.row()].id ]) - do_update_header = False + do_update_header = kwargs.get("do_update_header", False) if "id_channel" in kwargs and kwargs["id_channel"] != self.id_channel: do_update_header = True self.id_channel = kwargs["id_channel"] @@ -152,29 +152,28 @@ def update_header(self): else: s = "" t = t.strftime("%A %Y-%m-%d") - self.parent().setWindowTitle("Rundown {}".format(t)) - self.channel_display.setText("{} - {}".format(s, t, ch)) + self.parent().setWindowTitle(f"Rundown {t}") + self.channel_display.setText(f"{t} - {ch}") + logging.debug(f"[RUNDOWN] Header update ({ch})") # # Actions # def set_channel(self, id_channel): - if self.id_channel != id_channel or self.first_load: + if self.id_channel != id_channel: self.id_channel = id_channel - self.load() - if self.mcr: - can_mcr = has_right("mcr", self.id_channel) - self.plugins.load() - self.mcr.btn_take.setEnabled(can_mcr) - self.mcr.btn_freeze.setEnabled(can_mcr) - self.mcr.btn_retake.setEnabled(can_mcr) - self.mcr.btn_abort.setEnabled(can_mcr) + def on_channel_changed(self): + self.load(do_update_header=True) + self.plugins.load() + + if self.mcr: + self.mcr.on_channel_changed() - can_rundown_edit = has_right("rundown_edit", self.id_channel) - self.main_window.action_rundown_edit.setEnabled(can_rundown_edit) - self.toggle_rundown_edit(can_rundown_edit and self.edit_wanted) + can_rundown_edit = has_right("rundown_edit", self.id_channel) + self.main_window.action_rundown_edit.setEnabled(can_rundown_edit) + self.toggle_rundown_edit(can_rundown_edit and self.edit_wanted) def go_day_prev(self): @@ -260,7 +259,7 @@ def do_find(self, search_string, start_row=-1): selection = QItemSelection() i1 = self.view.model().index(i + start_row, 0, QModelIndex()) i2 = self.view.model().index(i + start_row, len(self.view.model().header_data)-1, QModelIndex()) - self.view.scrollTo(i1 , QAbstractItemView.PositionAtTop ) + self.view.scrollTo(i1 , QAbstractItemView.PositionAtTop) selection.select(i1, i2) self.view.selectionModel().select(selection, QItemSelectionModel.ClearAndSelect) break diff --git a/firefly/modules/rundown_mcr.py b/firefly/modules/rundown_mcr.py index 588af8b..6c68d81 100644 --- a/firefly/modules/rundown_mcr.py +++ b/firefly/modules/rundown_mcr.py @@ -61,21 +61,6 @@ class MCR(QWidget): def __init__(self, parent): super(MCR, self).__init__(parent) - self.pos = 0 - self.dur = 0 - self.current = "(loading)" - self.cued = "(loading)" - self.request_time = 0 - self.paused = False - self.cueing = False - self.local_request_time = time.time() - self.updating = False - self.request_display_resize = False - self.first_update = True - - self.fps = 25.0 - - self.parent().setWindowTitle("On air ctrl") self.progress_bar = QProgressBar(self) self.progress_bar.setTextVisible(False) @@ -90,14 +75,6 @@ def __init__(self, parent): self.btn_cue_backward = MCRButton("<", self, self.on_cue_backward) self.btn_cue_forward = MCRButton(">", self, self.on_cue_forward) - can_mcr = True #TODO: ACL - self.btn_take.setEnabled(can_mcr) - self.btn_freeze.setEnabled(can_mcr) - self.btn_retake.setEnabled(can_mcr) - self.btn_abort.setEnabled(can_mcr) - self.btn_cue_backward.setEnabled(can_mcr) - self.btn_cue_forward.setEnabled(can_mcr) - self.btn_cue_backward.setShortcut('Ctrl+J') self.btn_take.setShortcut('Ctrl+K') self.btn_cue_forward.setShortcut('Ctrl+L') @@ -148,6 +125,8 @@ def __init__(self, parent): layout.addLayout(btns_layout, 0) self.setLayout(layout) + self.on_channel_changed() + self.display_timer = QTimer(self) self.display_timer.timeout.connect(self.update_display) @@ -224,7 +203,7 @@ def seismic_handler(self, data): if self.cued != status["cued_title"] or self.cueing != cueing: self.cued = status["cued_title"] if cueing: - self.display_cued.set_text("{}".format(self.cued)) + self.display_cued.set_text(f"{self.cued}") else: self.display_cued.set_text(self.cued) self.cueing = cueing @@ -240,6 +219,39 @@ def hide(self, *args, **kwargs): self.display_timer.stop() + def on_channel_changed(self): + if hasattr(self, "id_channel"): + can_mcr = has_right("mcr", self.id_channel) + self.btn_take.setEnabled(can_mcr) + self.btn_freeze.setEnabled(can_mcr) + self.btn_retake.setEnabled(can_mcr) + self.btn_abort.setEnabled(can_mcr) + self.btn_loop.setEnabled(can_mcr) + self.btn_cue_backward.setEnabled(can_mcr) + self.btn_cue_forward.setEnabled(can_mcr) + + if hasattr(self, "plugins"): + self.plugins.load() + + self.pos = 0 + self.dur = 0 + self.current = "(loading)" + self.cued = "(loading)" + self.request_time = 0 + self.paused = False + self.cueing = False + self.local_request_time = time.time() + self.updating = False + self.request_display_resize = False + self.first_update = True + self.fps = 25.0 + self.parent().setWindowTitle("On air ctrl") + + self.progress_bar.setValue(0) + self.progress_bar.setMaximum(0) + self.display_current.set_text(f"{self.current}") + self.display_cued.set_text(f"{self.cued}") + def update_display(self): now = time.time() adv = now - self.local_request_time @@ -250,14 +262,15 @@ def update_display(self): if not self.paused: rpos += adv - clock = time.strftime("%H:%M:%S:{:02d}", time.localtime(rtime)).format(int(self.fps*(rtime-math.floor(rtime)))) + frame = int(self.fps*(rtime-math.floor(rtime))) + clock = time.strftime(f"%H:%M:%S:{frame:02d}", time.localtime(rtime)) self.display_clock.set_text(clock) self.display_pos.set_text(s2tc(min(self.dur, rpos), self.fps)) rem = self.dur - rpos t = s2tc(max(0, rem), self.fps) if rem < 10: - self.display_rem.set_text("{}".format(t)) + self.display_rem.set_text(f"{t}") else: self.display_rem.set_text(t) diff --git a/firefly/modules/rundown_plugins.py b/firefly/modules/rundown_plugins.py index 5265298..bd2fc08 100644 --- a/firefly/modules/rundown_plugins.py +++ b/firefly/modules/rundown_plugins.py @@ -62,9 +62,9 @@ def execute(self, name): data=json.dumps(data) ) if response: - logging.info("{} action '{}' executed succesfully.".format(self.title, name)) + logging.info(f"{self.title} action '{name}' executed succesfully.") else: - logging.error("Plugin error {}\n\n{}".format(response.response, response.message)) + logging.error(f"[PLUGINS] Plugin error {response.response}\n\n{response.message}") @@ -81,7 +81,7 @@ def load(self): if not user.has_right("mcr", self.id_channel): return - logging.debug("Loading playout plugins") + logging.debug("[PLUGINS] Loading playout plugins") for idx in reversed(range(self.count())): widget = self.widget(idx) self.removeTab(idx) @@ -89,7 +89,7 @@ def load(self): response = api.playout(action="plugin_list", id_channel=self.id_channel) if not response: - logging.error("Unable to load playout plugins:\n{}".format(response.message)) + logging.error(f"[PLUGINS] Unable to load playout plugins:\n{response.message}") return for plugin in response.data or []: diff --git a/firefly/modules/scheduler.py b/firefly/modules/scheduler.py index 04e85c5..2794434 100644 --- a/firefly/modules/scheduler.py +++ b/firefly/modules/scheduler.py @@ -182,7 +182,7 @@ def load(self, *args, **kwargs): self.calendar.load(*args, **kwargs) date = datetime.date.fromtimestamp(self.calendar.week_start_time) week_no = date.isocalendar()[1] - header = "Week {} - {}".format(week_no, self.playout_config["title"]) + header = f"Week {week_no} - {self.playout_config['title']}" self.channel_display.setText(header) def on_week_prev(self): @@ -208,12 +208,17 @@ def open_rundown(self, ts, event=False): self.main_window.main_widget.switch_tab(self.main_window.main_widget.rundown, perform_on_switch_tab=False) def set_channel(self, id_channel): + #TODO: is this used? may be removed? + pass + + def on_channel_changed(self): + logging.debug(f"[SCHEDULER] setting channel to {self.id_channel}") self.load() def refresh_events(self, events): for id_event in events: if id_event in self.calendar.event_ids: - logging.debug("[SCHEDULER] Event id {} has been changed. Reloading calendar".format(id_event)) + logging.debug(f"[SCHEDULER] Event id {id_event} has been changed. Reloading calendar") self.load() break diff --git a/firefly/modules/scheduler_model.py b/firefly/modules/scheduler_model.py index 954ac0f..45026f4 100644 --- a/firefly/modules/scheduler_model.py +++ b/firefly/modules/scheduler_model.py @@ -134,8 +134,6 @@ def drawWidget(self, qp): self.draw_dragging(qp) - - def drawBlock(self, qp, event, end): if type(self.calendar.dragging) == Event and self.calendar.dragging.id == event.id: if not self.drag_outside: @@ -177,8 +175,6 @@ def drawBlock(self, qp, event, end): qp.drawText(6, base_t + TEXT_SIZE + 9, text) - - def draw_dragging(self, qp): if type(self.calendar.dragging) == Asset: exp_dur = suggested_duration(self.calendar.dragging.duration) @@ -196,10 +192,10 @@ def draw_dragging(self, qp): qp.setBrush(QColor(200,200,200,128)) qp.drawRect(0, base_t, self.width(), base_h) - logging.debug("Start time: {} End time: {}".format( - time.strftime("%H:%M", time.localtime(drop_ts)), - time.strftime("%H:%M", time.localtime(drop_ts + max(300, exp_dur))) - )) + + e_start_time = time.strftime("%H:%M", time.localtime(drop_ts)), + e_end_time = time.strftime("%H:%M", time.localtime(drop_ts + max(300, exp_dur))) + logging.debug(f"Start time: {e_start_time} End time: {e_end_time}") def mouseMoveEvent(self, e): @@ -220,11 +216,7 @@ def mouseMoveEvent(self, e): else: diff = "Over: " + s2tc(diff) - self.setToolTip("{title}
Start: {start}
{diff}".format( - title=event["title"], - start=time.strftime("%H:%M",time.localtime(event["start"])), - diff=diff - )) + self.setToolTip(f"{event['title']}
Start: {format_time(event['start'], '%H:%M')}
{diff}") break self.cursor_event = False else: @@ -341,9 +333,7 @@ def dropEvent(self, evt): if event["duration"]: ret = QMessageBox.question(self, "Overwrite", - "Do you really want to overwrite a non-empty event?\n{}".format( - event - ), + f"Do you really want to overwrite a non-empty event?\n{event}", QMessageBox.Yes | QMessageBox.No ) if ret == QMessageBox.Yes: @@ -356,10 +346,7 @@ def dropEvent(self, evt): if evt.keyboardModifiers() & Qt.AltModifier: - logging.info("Creating event from {} at time {}".format( - self.calendar.dragging, - time.strftime("%Y-%m-%d %H:%M", time.localtime(self.cursor_time)) - )) + logging.info(f"Creating event from {self.calendar.dragging} at time {format_time(self.cursor_time)}") if event_dialog( asset=self.calendar.dragging, id_channel=self.id_channel, @@ -393,11 +380,7 @@ def dropEvent(self, evt): if event.id and abs(event["start"] - drop_ts) > 7200: ret = QMessageBox.question(self, "Move event", - "Do you really want to move {}?\n\nFrom: {}\nTo: {}".format( - self.cursor_event, - format_time(event["start"]), - format_time(drop_ts) - ), + f"Do you really want to move {self.cursor_event}?\n\nFrom: {format_time(event['start'])}\nTo: {format_time(drop_ts)}", QMessageBox.Yes | QMessageBox.No ) if ret == QMessageBox.Yes: @@ -485,7 +468,7 @@ def on_delete_event(self): ret = QMessageBox.question(self, "Delete event", - "Do you really want to delete {}?\nThis operation cannot be undone.".format(cursor_event), + f"Do you really want to delete {cursor_event}?\nThis operation cannot be undone.", QMessageBox.Yes | QMessageBox.No ) if ret == QMessageBox.Yes: @@ -499,7 +482,7 @@ def on_delete_event(self): ) self.calendar.setCursor(Qt.ArrowCursor) if response: - logging.info("{} deleted".format(cursor_event)) + logging.info(f"{cursor_event} deleted") self.calendar.set_data(response.data) else: logging.error(response.message) @@ -552,9 +535,9 @@ def set_time(self, start_time): self.start_time = start_time t = format_time(start_time, "%a %Y-%m-%d") if start_time < time.time() - SECS_PER_DAY: - self.setText("{}".format(t)) + self.setText(f"{t}") elif start_time > time.time(): - self.setText("{}".format(t)) + self.setText(f"{t}") else: self.setText(t) diff --git a/firefly/modules/scheduler_utils.py b/firefly/modules/scheduler_utils.py index 9f4733c..3e601f8 100644 --- a/firefly/modules/scheduler_utils.py +++ b/firefly/modules/scheduler_utils.py @@ -53,18 +53,18 @@ def dump_template(calendar): days[day].append(event) for i, day in enumerate(days): - result += " \n".format(DAY_NAMES[i]) + result += f" \n" result += " \n" for event in day: clock = format_time(event["start"], "%H:%M") - result += " \n".format(clock, event["title"]) + result += f" \n" for key in event.meta: if meta_types[key]["ns"] != "m": continue value = event[key] if type(value) in string_types: value = value.replace("&", "&") - result += " {}\n".format(key, value) + result += f" {value}\n" result += " \n" result += " \n" diff --git a/firefly/version.py b/firefly/version.py index 8ea93b6..ee71fd2 100644 --- a/firefly/version.py +++ b/firefly/version.py @@ -1,3 +1 @@ -FIREFLY_VERSION = 5.32 -#FIREFLY_STATUS = "RC1" -FIREFLY_STATUS = "" +FIREFLY_VERSION = "5.32.2" diff --git a/firefly/view.py b/firefly/view.py index f046899..4065371 100644 --- a/firefly/view.py +++ b/firefly/view.py @@ -30,7 +30,7 @@ def headerData(self, col, orientation=Qt.Horizontal, role=Qt.DisplayRole): return format_header(self.header_data[col]) elif role == Qt.ToolTipRole: desc = format_description(self.header_data[col]) - return "

{}

".format(desc) if desc else None + return f"

{desc}

" if desc else None return None def data(self, index, role=Qt.DisplayRole): diff --git a/firefly/widgets/__init__.py b/firefly/widgets/__init__.py index 907839c..844f589 100644 --- a/firefly/widgets/__init__.py +++ b/firefly/widgets/__init__.py @@ -5,21 +5,17 @@ from firefly.widgets.simple import * from firefly.widgets.combo import * - class ChannelDisplay(QLabel): pass - class ToolBarStretcher(QWidget): def __init__(self, parent): super(ToolBarStretcher, self).__init__(parent) self.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) - class ActionButton(QPushButton): pass - class FireflyNotImplementedEditor(QLabel): def __init__(self, parent, **kwargs): super(FireflyNotImplementedEditor, self).__init__(parent) @@ -36,14 +32,7 @@ def get_value(self): def setReadOnly(self, *args, **kwargs): pass - - - - - - #TODO - class FireflyRegions(FireflyNotImplementedEditor): pass @@ -80,7 +69,6 @@ def __init__(self, parent, keys, **kwargs): layout.setColumnStretch(0, 0) layout.setColumnStretch(1, 3) - i = 0 for key, conf in keys: key_label = meta_types[key].alias() @@ -103,7 +91,7 @@ def __init__(self, parent, keys, **kwargs): label = QLabel(key_label, self) label.setStyleSheet("padding-top:9px;") - label.setToolTip("

{}

".format(key_description)) + label.setToolTip(f"

{key_description}

") if parent.__class__.__name__ == "DetailTabMain": label.setContextMenuPolicy(Qt.CustomContextMenu) label.customContextMenuRequested.connect(functools.partial(self.key_menu, key)) diff --git a/firefly/widgets/combo.py b/firefly/widgets/combo.py index fb2fd9d..6806f8f 100644 --- a/firefly/widgets/combo.py +++ b/firefly/widgets/combo.py @@ -48,7 +48,7 @@ def set_data(self, data): self.cdata.append(row["value"]) self.buttons.append(QPushButton(alias)) - self.buttons[-1].setToolTip("

{}

".format(description)) + self.buttons[-1].setToolTip(f"

{description}

") self.buttons[-1].setCheckable(row["role"] in ["option", "header"]) self.buttons[-1].setAutoExclusive(True) self.buttons[-1].clicked.connect(functools.partial(self.switch, i)) @@ -131,7 +131,7 @@ def set_data(self, data): self.cdata.append(value) self.setItemData(i, indent ,Qt.UserRole) - self.setItemData(i, "

{}

".format(description), Qt.ToolTipRole) + self.setItemData(i, f"

{description}

", Qt.ToolTipRole) if role == "header": self.setItemData(i, fonts["bold"], Qt.FontRole) @@ -201,7 +201,7 @@ def set_data(self, data): self.setItemData(i, indent, Qt.UserRole) - self.setItemData(i, "

{}

".format(description), Qt.ToolTipRole) + self.setItemData(i, f"

{description}

", Qt.ToolTipRole) if row["role"] == "label": item = self.model().item(i) diff --git a/firefly/widgets/comboutils.py b/firefly/widgets/comboutils.py index f0def03..533ea10 100644 --- a/firefly/widgets/comboutils.py +++ b/firefly/widgets/comboutils.py @@ -2,7 +2,6 @@ from firefly.common import * - class ComboMenuDelegate(QAbstractItemDelegate): def isSeparator(self, index): return str(index.data(Qt.AccessibleDescriptionRole)) == "separator" diff --git a/firefly/widgets/simple.py b/firefly/widgets/simple.py index 5fad249..8c2b4b5 100644 --- a/firefly/widgets/simple.py +++ b/firefly/widgets/simple.py @@ -1,11 +1,7 @@ from nx import * from firefly.common import * - - from firefly.dialogs.text_editor import TextEditorDialog - - class FireflyString(QLineEdit): def __init__(self, parent, **kwargs): super(FireflyString, self).__init__(parent) @@ -195,7 +191,7 @@ def get_value(self): def set_value(self, value): self.color = value - self.setStyleSheet("background-color: #{:06x}".format(self.color)) + self.setStyleSheet(f"background-color: #{self.color:06x}") def setReadOnly(self, stat): self.setEnabled(not stat) diff --git a/nx/cellformat.py b/nx/cellformat.py index 4d5736c..70f9b18 100644 --- a/nx/cellformat.py +++ b/nx/cellformat.py @@ -122,7 +122,7 @@ def display(self, obj, **kwargs): elif xfrp == 0: xfr = " (STARTING)" elif xfrp < 100: - xfr = " ({:.01f}%)".format(xfrp) + xfr = f" ({xfrp:.01f}%)" return get_object_state_name(state).upper() + xfr diff --git a/res/splash.svg b/res/splash.svg new file mode 100644 index 0000000..de46380 --- /dev/null +++ b/res/splash.svg @@ -0,0 +1,115 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + Firefly 5 + + + www.nebulabroadcast.com + +