From 4a6d85f7eadc664af2f7693d0d13b6a4de5e30e2 Mon Sep 17 00:00:00 2001 From: Jeena Date: Thu, 5 Jun 2025 12:37:55 +0900 Subject: [PATCH] Add preferences --- .gitignore | 1 + dev-run.sh | 14 +++++ src/recoder/app.py | 2 +- src/recoder/preferences.py | 58 +++++++++++++++++++ src/recoder/{recoder_window.py => window.py} | 36 +++++++++--- src/resources/net.jeena.recoder.gschema.xml | 36 ++++++++++++ src/resources/preferences.ui | 52 +++++++++++++++++ src/resources/resources.xml | 3 +- .../{recoder_window.ui => window.ui} | 45 ++++++++------ 9 files changed, 219 insertions(+), 28 deletions(-) create mode 100755 dev-run.sh create mode 100644 src/recoder/preferences.py rename src/recoder/{recoder_window.py => window.py} (78%) create mode 100644 src/resources/net.jeena.recoder.gschema.xml create mode 100644 src/resources/preferences.ui rename src/resources/{recoder_window.ui => window.ui} (70%) diff --git a/.gitignore b/.gitignore index b5a6689..1452a03 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ dist/ src/recoder.egg-info/ *.gresource +*.compiled diff --git a/dev-run.sh b/dev-run.sh new file mode 100755 index 0000000..8c2d40e --- /dev/null +++ b/dev-run.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +# Compile resources +glib-compile-resources src/resources/resources.xml \ + --target=src/recoder/resources.gresource \ + --sourcedir=src/resources + +# Compile GSettings schemas (using correct path) +glib-compile-schemas src/resources + +# Run the app with environment variables +GSETTINGS_SCHEMA_DIR=$(pwd)/src/resources \ +PYTHONPATH=$(pwd)/src \ +python -m recoder.app diff --git a/src/recoder/app.py b/src/recoder/app.py index a1bc48a..f464675 100755 --- a/src/recoder/app.py +++ b/src/recoder/app.py @@ -18,7 +18,7 @@ def load_resources(): def main(): load_resources() - from recoder.recoder_window import RecoderWindow # delayed import + from recoder.window import RecoderWindow # delayed import class RecoderApp(Adw.Application): def __init__(self): diff --git a/src/recoder/preferences.py b/src/recoder/preferences.py new file mode 100644 index 0000000..01a685e --- /dev/null +++ b/src/recoder/preferences.py @@ -0,0 +1,58 @@ +import gi +gi.require_version("Gtk", "4.0") +gi.require_version("Adw", "1") +from gi.repository import Gtk, Gio, Adw +import re + +@Gtk.Template(resource_path="/net/jeena/recoder/preferences.ui") +class RecoderPreferences(Adw.PreferencesWindow): + __gtype_name__ = "RecoderPreferences" + + output_folder_entry = Gtk.Template.Child() + + def __init__(self): + super().__init__() + + self.settings = Gio.Settings.new("net.jeena.recoder.preferences") + + current_value = self.settings.get_string("output-folder-template") + self.output_folder_entry.set_text(current_value) + + self.output_folder_entry.connect("changed", self.on_output_folder_changed) + self.settings.connect("changed::output-folder-template", self.on_setting_changed) + + def validate_template(self, text): + allowed_pattern = r'^[\w\s\-./~${}]+$' + if not re.match(allowed_pattern, text): + return False + + if text.count("{{") != text.count("}}"): + return False + + var_pattern = r'\{\{([a-zA-Z0-9_]+)\}\}' + for var in re.findall(r'\{\{.*?\}\}', text): + if not re.match(var_pattern, var): + return False + + if '//' in text.replace('file://', ''): + return False + + return True + + def on_output_folder_changed(self, entry): + text = entry.get_text() + + if self.validate_template(text): + self.settings.set_string("output-folder-template", text) + entry.remove_css_class("error") + else: + entry.add_css_class("error") + + + + + def on_setting_changed(self, settings, key): + if key == "output-folder-template": + new_val = settings.get_string(key) + if self.output_folder_entry.get_text() != new_val: + self.output_folder_entry.set_text(new_val) diff --git a/src/recoder/recoder_window.py b/src/recoder/window.py similarity index 78% rename from src/recoder/recoder_window.py rename to src/recoder/window.py index abcdd6b..52ee2b7 100644 --- a/src/recoder/recoder_window.py +++ b/src/recoder/window.py @@ -14,9 +14,10 @@ from recoder.utils import extract_video_files, notify_done, play_complete_sound from recoder.file_entry_row import FileEntryRow from recoder.drop_handler import DropHandler from recoder.app_state import AppState, AppStateManager, UIStateManager +from recoder.preferences import RecoderPreferences -@Gtk.Template(resource_path="/net/jeena/recoder/recoder_window.ui") +@Gtk.Template(resource_path="/net/jeena/recoder/window.ui") class RecoderWindow(Adw.ApplicationWindow): __gtype_name__ = "RecoderWindow" @@ -28,9 +29,18 @@ class RecoderWindow(Adw.ApplicationWindow): btn_cancel = Gtk.Template.Child() progress_bar = Gtk.Template.Child() folder_label = Gtk.Template.Child() + btn_preferences = Gtk.Template.Child() def __init__(self, application): super().__init__(application=application) + + self.state_settings = Gio.Settings.new("net.jeena.recoder.state") + + # Bind window size and state to your window properties + self.state_settings.bind("width", self, "default-width", Gio.SettingsBindFlags.DEFAULT) + self.state_settings.bind("height", self, "default-height", Gio.SettingsBindFlags.DEFAULT) + self.state_settings.bind("is-maximized", self, "maximized", Gio.SettingsBindFlags.DEFAULT) + self.state_settings.bind("is-fullscreen", self, "fullscreened", Gio.SettingsBindFlags.DEFAULT) self.file_items_to_process = [] self.current_folder_name = None @@ -44,6 +54,7 @@ class RecoderWindow(Adw.ApplicationWindow): self.btn_transcode.connect("clicked", self.on_transcode_clicked) self.btn_cancel.connect("clicked", self.on_cancel_clicked) + self.btn_preferences.connect("clicked", self.on_preferences_clicked) self.app_state_manager.state = AppState.IDLE @@ -57,10 +68,24 @@ class RecoderWindow(Adw.ApplicationWindow): Notify.init("Recoder") - def process_drop_value(self, value): + self.preferences_window = None - # value could be a list of Gio.File or a single Gio.File - # Assuming it's a list: + def on_preferences_clicked(self, button): + if self.preferences_window is None: + self.preferences_window = RecoderPreferences() + self.preferences_window.set_transient_for(self) + self.preferences_window.set_modal(True) + self.preferences_window.connect("close-request", self.on_preferences_window_close) + + self.preferences_window.present() + + def on_preferences_window_close(self, window): + window.hide() + # Don't destroy, just hide + return True # stops further handlers, prevents default destruction + + + def process_drop_value(self, value): folder_file = None if isinstance(value, list) and len(value) > 0: folder_file = value[0] @@ -68,7 +93,6 @@ class RecoderWindow(Adw.ApplicationWindow): folder_file = value if folder_file: - # Set the current folder name for UI self.current_folder_name = folder_file.get_basename() file_items = extract_video_files(value) @@ -107,8 +131,6 @@ class RecoderWindow(Adw.ApplicationWindow): if not self.file_items_to_process: return - # no need to remove drop_controller here - self.transcoder = Transcoder(self.file_items_to_process) self.transcoder.connect("notify::batch-progress", self.on_transcoder_progress) self.transcoder.connect("notify::batch-status", self.on_transcoder_status) diff --git a/src/resources/net.jeena.recoder.gschema.xml b/src/resources/net.jeena.recoder.gschema.xml new file mode 100644 index 0000000..e2ab4b2 --- /dev/null +++ b/src/resources/net.jeena.recoder.gschema.xml @@ -0,0 +1,36 @@ + + + + '{{source_folder_name}}-transcoded' + Template for output folder + + Relative or absolute path template for transcoded files. + Supports {{source_folder_name}} as a variable. + + + + + + + 600 + Window width + Last saved width of the main window. + + + 350 + Window height + Last saved height of the main window. + + + false + Window maximized state + Whether the main window was maximized last time. + + + false + Window fullscreen state + Whether the main window was fullscreen last time. + + + + diff --git a/src/resources/preferences.ui b/src/resources/preferences.ui new file mode 100644 index 0000000..0215a49 --- /dev/null +++ b/src/resources/preferences.ui @@ -0,0 +1,52 @@ + + + + diff --git a/src/resources/resources.xml b/src/resources/resources.xml index 973c271..dad3728 100644 --- a/src/resources/resources.xml +++ b/src/resources/resources.xml @@ -1,6 +1,7 @@ - recoder_window.ui + window.ui + preferences.ui file_entry_row.ui style.css diff --git a/src/resources/recoder_window.ui b/src/resources/window.ui similarity index 70% rename from src/resources/recoder_window.ui rename to src/resources/window.ui index 29de453..5ec4e0d 100644 --- a/src/resources/recoder_window.ui +++ b/src/resources/window.ui @@ -12,25 +12,31 @@ - - - Transcode - False - - - - - end - True - center - - - - - Cancel - False - - + + + Transcode + False + + + + + end + True + center + + + + + emblem-system-symbolic + Preferences + + + + + Cancel + False + + @@ -75,6 +81,7 @@ + true