diff --git a/docs/HELP.md b/docs/HELP.md new file mode 100644 index 0000000..c09348c --- /dev/null +++ b/docs/HELP.md @@ -0,0 +1,74 @@ + +

+ Recoder logo +

+ +# Recoder — Help Guide + +Recoder is a minimal, user-friendly tool for batch video transcoding. This quick guide walks you through using the app. + +--- + +## 🚀 Getting Started + +When you open Recoder, you’ll see a prompt inviting you to drop a video file or folder: + +![Initial View](screenshot-1.png) + +### 📂 Dropping Files or Folders + +- You can drop **one video file** or **one folder** containing video files onto the app. +- The folder can have subdirectories, but Recoder will **not** process files recursively. Only files in the dropped folder itself will be processed. +- Non-video files will be ignored. + +### 🔧 Preparing to Transcode + +After you drop a folder into Recoder, it will list all the video files it found: + +![Folder Loaded](./screenshot-2.png) + +- A blue **Transcode** button appears once the files are ready to process. +- The **Clear icon** is always available — click it to cancel everything and reset the app if you're done or need to start over. +- The **menu button** gives access to Preferences and Help. + +--- + +## 🎬 Transcoding + +Click the Transcode button to start processing. While transcoding: + +![Transcoding in Progress](./screenshot-3.png) + +- The blue **Transcode** button is replaced by a **Pause** button, allowing you to temporarily stop the process. +- If paused, the button changes to **Resume**, so you can continue when you're ready. +- The **Clear button** can also be used during transcoding to cancel the process entirely and clear the current session. + +By default: + +- Transcoded files are saved into the same directory as the source, inside a subfolder named `{{source_folder_name}}-transcoded`. +- File names remain the same as the originals but with a `.mov` extension. + +--- + +### ⚙️ Preferences + +In Preferences, you can customize the single **output folder path** where transcoded files will be saved. This path controls both the folder’s location and name. You can use: + +- `{{source_folder_name}}` to reuse the original folder name +- Relative paths like `../done/` +- Absolute paths like `/mnt/Export/` +- `~` to refer to your home directory +- Simple names like `output` to create a folder inside the source folder +- Any combination of the above, e.g. `../{{source_folder_name}}-dnxhd` + +![Preferences](screenshot-4.png) + +--- + +## 💡 Notes + +- Make sure you have enough free space on your drive because both the original and transcoded files are kept, and transcoded files may be larger. + +--- + +If you need more help, check the [GitHub repository](https://github.com/jeena/recoder) or open an issue. \ No newline at end of file diff --git a/docs/screenshot-1.png b/docs/screenshot-1.png new file mode 100644 index 0000000..9e57506 Binary files /dev/null and b/docs/screenshot-1.png differ diff --git a/docs/screenshot-2.png b/docs/screenshot-2.png new file mode 100644 index 0000000..9cb59e5 Binary files /dev/null and b/docs/screenshot-2.png differ diff --git a/docs/screenshot-3.png b/docs/screenshot-3.png new file mode 100644 index 0000000..ae4fe73 Binary files /dev/null and b/docs/screenshot-3.png differ diff --git a/docs/screenshot-4.png b/docs/screenshot-4.png new file mode 100644 index 0000000..4d3b35c Binary files /dev/null and b/docs/screenshot-4.png differ diff --git a/src/recoder/app.py b/src/recoder/app.py index 5436a5a..67f6fc0 100755 --- a/src/recoder/app.py +++ b/src/recoder/app.py @@ -55,6 +55,11 @@ def main(): self.add_action(preferences_action) self.set_accels_for_action("app.preferences", ["comma"]) + help_action = Gio.SimpleAction.new("help", None) + help_action.connect("activate", self.on_help_activated) + self.add_action(help_action) + self.set_accels_for_action("app.help", ["F1"]) + about_action = Gio.SimpleAction.new("about", None) about_action.connect("activate", self.on_about_activate) self.add_action(about_action) @@ -89,6 +94,9 @@ def main(): def on_preferences_close(self, window): window.set_visible(False) + if window.prefs_changed: + window.prefs_changed = False + self.window.toast_overlay.add_toast(Adw.Toast.new("Preferences saved")) # Don't destroy, just hide return True # stops further handlers, prevents default destruction @@ -96,6 +104,16 @@ def main(): self.quit() return False # allow default handler to proceed + def on_help_activated(self, action, param): + uri = "https://github.com/jeena/recoder/blob/master/docs/HELP.md" + try: + Gio.AppInfo.launch_default_for_uri(uri, None) + self.window.toast_overlay.add_toast(Adw.Toast.new("Opening help in browser…")) + except GLib.Error as e: + self.window.toast_overlay.add_toast(Adw.Toast.new(f"Failed to open help: {e.message}")) + + + app = RecoderApp() return app.run(sys.argv) diff --git a/src/recoder/preferences.py b/src/recoder/preferences.py index 01a685e..09ed941 100644 --- a/src/recoder/preferences.py +++ b/src/recoder/preferences.py @@ -13,6 +13,7 @@ class RecoderPreferences(Adw.PreferencesWindow): def __init__(self): super().__init__() + self.prefs_changed = False self.settings = Gio.Settings.new("net.jeena.recoder.preferences") current_value = self.settings.get_string("output-folder-template") @@ -44,13 +45,11 @@ class RecoderPreferences(Adw.PreferencesWindow): if self.validate_template(text): self.settings.set_string("output-folder-template", text) + self.prefs_changed = True 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) diff --git a/src/recoder/window.py b/src/recoder/window.py index f5fbb00..d614acd 100644 --- a/src/recoder/window.py +++ b/src/recoder/window.py @@ -22,6 +22,7 @@ from recoder.app import APP_NAME class RecoderWindow(Adw.ApplicationWindow): __gtype_name__ = "RecoderWindow" + toast_overlay = Gtk.Template.Child() overlay = Gtk.Template.Child() drop_hint = Gtk.Template.Child() listbox = Gtk.Template.Child() @@ -96,6 +97,10 @@ class RecoderWindow(Adw.ApplicationWindow): self.file_items_to_process = file_items self.app_state_manager.state = AppState.FILES_LOADED + + count = len(self.file_items_to_process) + toast = Adw.Toast.new(f"{count} video file{'s' if count != 1 else ''} added") + self.toast_overlay.add_toast(toast) return False def clear_listbox(self): @@ -122,26 +127,30 @@ class RecoderWindow(Adw.ApplicationWindow): self.transcoder.connect("notify::batch-progress", self.on_transcoder_progress) self.transcoder.connect("notify::batch-status", self.on_transcoder_status) self.transcoder.start() - self.app_state_manager.state = AppState.TRANSCODING + self.toast_overlay.add_toast(Adw.Toast.new("Starting transcoding")) def pause_transcoding(self): if self.transcoder: self.transcoder.pause() self.is_paused = True self.app_state_manager.state = AppState.PAUSED + self.toast_overlay.add_toast(Adw.Toast.new("Transcoding paused")) def resume_transcoding(self): if self.transcoder: self.transcoder.resume() self.is_paused = False self.app_state_manager.state = AppState.TRANSCODING + self.toast_overlay.add_toast(Adw.Toast.new("Resuming transcoding")) def on_clear_clicked(self, button): if self.transcoder and self.transcoder.is_processing: self.transcoder.stop() self.transcoder = None + self.clear_listbox() self.app_state_manager.state = AppState.STOPPED + self.toast_overlay.add_toast(Adw.Toast.new("File list cleared")) def on_transcoder_progress(self, transcoder, param): self.progress_bar.set_fraction(transcoder.batch_progress / 100.0) @@ -150,6 +159,7 @@ class RecoderWindow(Adw.ApplicationWindow): if transcoder.batch_status == BatchStatus.DONE: play_complete_sound() notify_done(APP_NAME, "Transcoding finished!") + self.toast_overlay.add_toast(Adw.Toast.new("Transcoding finished!")) self.app_state_manager.state = AppState.DONE elif transcoder.batch_status == BatchStatus.STOPPED: @@ -157,4 +167,5 @@ class RecoderWindow(Adw.ApplicationWindow): elif transcoder.batch_status == BatchStatus.ERROR: notify_done(APP_NAME, "An error occurred during transcoding.") + self.toast_overlay.add_toast(Adw.Toast.new("An error occurred during transcoding")) self.app_state_manager.state = AppState.ERROR diff --git a/src/resources/resources.xml b/src/resources/resources.xml index f4ae0d6..dad3728 100644 --- a/src/resources/resources.xml +++ b/src/resources/resources.xml @@ -4,6 +4,5 @@ preferences.ui file_entry_row.ui style.css - ../resources/net.jeena.Recoder.svg diff --git a/src/resources/window.ui b/src/resources/window.ui index 4a72557..0e4a030 100644 --- a/src/resources/window.ui +++ b/src/resources/window.ui @@ -8,6 +8,10 @@ Preferences app.preferences + + Help + app.help + About Recoder app.about @@ -24,143 +28,145 @@ 400 - - - - - - Transcode - False + + + + + + + + Transcode + False + + + + + end + True + center + + + + + open-menu-symbolic + main_menu + Menu + + + + + edit-clear-symbolic + Clear + False + False + + + - - - end - True - center - - - - - open-menu-symbolic - main_menu - Menu - - - - - edit-clear-symbolic - Clear - False - False - - - - - - - - vertical - 6 - true - - - - true + + + vertical + 6 true - + true true - - none + + true true - True - - - - - - - - - vertical - fill - fill - 48 - true - true - - - - - vertical - true - center - center - 48 - - horizontal - center - 48 - - - - video-x-generic-symbolic - 48 - - - - - - - folder-symbolic - 48 - - - - - - - - - - Drop video files or folders here to get started + + none + true + True + + + + vertical + fill + fill + 48 + true + true + + + + + vertical + true + center + center + 48 + + + + horizontal + center + 48 + + + + video-x-generic-symbolic + 48 + + + + + + + folder-symbolic + 48 + + + + + + + + + Drop video files or folders here to get started + + + + + + + + + + true + False + + - - - - - true - False - - + - +