Add docs and Help link + Toasts
This commit is contained in:
parent
6c4f43a947
commit
aa21483bd1
10 changed files with 225 additions and 118 deletions
74
docs/HELP.md
Normal file
74
docs/HELP.md
Normal file
|
@ -0,0 +1,74 @@
|
|||
|
||||
<p align="center">
|
||||
<img src="../src/resources/net.jeena.Recoder.svg" width="120" height="120" alt="Recoder logo">
|
||||
</p>
|
||||
|
||||
# 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:
|
||||
|
||||

|
||||
|
||||
### 📂 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:
|
||||
|
||||

|
||||
|
||||
- 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:
|
||||
|
||||

|
||||
|
||||
- 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`
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 💡 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.
|
BIN
docs/screenshot-1.png
Normal file
BIN
docs/screenshot-1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
BIN
docs/screenshot-2.png
Normal file
BIN
docs/screenshot-2.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
BIN
docs/screenshot-3.png
Normal file
BIN
docs/screenshot-3.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
docs/screenshot-4.png
Normal file
BIN
docs/screenshot-4.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 48 KiB |
|
@ -55,6 +55,11 @@ def main():
|
|||
self.add_action(preferences_action)
|
||||
self.set_accels_for_action("app.preferences", ["<Primary>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)
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -4,6 +4,5 @@
|
|||
<file>preferences.ui</file>
|
||||
<file>file_entry_row.ui</file>
|
||||
<file>style.css</file>
|
||||
<file alias="net.jeena.Recoder.svg">../resources/net.jeena.Recoder.svg</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
|
|
@ -8,6 +8,10 @@
|
|||
<attribute name="label">Preferences</attribute>
|
||||
<attribute name="action">app.preferences</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">Help</attribute>
|
||||
<attribute name="action">app.help</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label">About Recoder</attribute>
|
||||
<attribute name="action">app.about</attribute>
|
||||
|
@ -24,143 +28,145 @@
|
|||
<property name="default-height">400</property>
|
||||
|
||||
<child>
|
||||
<object class="AdwToolbarView" id="toolbar_view">
|
||||
<child type="top">
|
||||
<object class="AdwHeaderBar" id="header_bar">
|
||||
<child>
|
||||
<object class="GtkButton" id="btn_transcode">
|
||||
<property name="label">Transcode</property>
|
||||
<property name="sensitive">False</property>
|
||||
<object class="AdwToastOverlay" id="toast_overlay">
|
||||
<child>
|
||||
<object class="AdwToolbarView" id="toolbar_view">
|
||||
<child type="top">
|
||||
<object class="AdwHeaderBar" id="header_bar">
|
||||
<child>
|
||||
<object class="GtkButton" id="btn_transcode">
|
||||
<property name="label">Transcode</property>
|
||||
<property name="sensitive">False</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="title">
|
||||
<object class="GtkLabel" id="folder_label">
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="halign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkMenuButton" id="menu_button">
|
||||
<property name="icon-name">open-menu-symbolic</property>
|
||||
<property name="menu-model">main_menu</property>
|
||||
<property name="tooltip-text">Menu</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkButton" id="btn_clear">
|
||||
<property name="icon-name">edit-clear-symbolic</property>
|
||||
<property name="tooltip-text">Clear</property>
|
||||
<property name="visible">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<style>
|
||||
<class name="flat"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child type="title">
|
||||
<object class="GtkLabel" id="folder_label">
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="hexpand">True</property>
|
||||
<property name="halign">center</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkMenuButton" id="menu_button">
|
||||
<property name="icon-name">open-menu-symbolic</property>
|
||||
<property name="menu-model">main_menu</property>
|
||||
<property name="tooltip-text">Menu</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="end">
|
||||
<object class="GtkButton" id="btn_clear">
|
||||
<property name="icon-name">edit-clear-symbolic</property>
|
||||
<property name="tooltip-text">Clear</property>
|
||||
<property name="visible">False</property>
|
||||
<property name="can-focus">False</property>
|
||||
<style>
|
||||
<class name="flat"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<property name="content">
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="vexpand">true</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkOverlay" id="overlay">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="content">
|
||||
<object class="GtkBox" id="main_box">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">6</property>
|
||||
<property name="vexpand">true</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||
<object class="GtkOverlay" id="overlay">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkListBox" id="listbox">
|
||||
<property name="selection-mode">none</property>
|
||||
<object class="GtkScrolledWindow" id="scrolled_window">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
<property name="show-separators">True</property>
|
||||
<style>
|
||||
<class name="rich-list"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child type="overlay">
|
||||
<object class="GtkBox" id="drop_hint">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="halign">fill</property>
|
||||
<property name="valign">fill</property>
|
||||
<property name="spacing">48</property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
|
||||
<!-- Inner box to center content -->
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="vexpand">true</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="spacing">48</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="spacing">48</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">video-x-generic-symbolic</property>
|
||||
<property name="pixel-size">48</property>
|
||||
<style>
|
||||
<class name="dim-icon"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">folder-symbolic</property>
|
||||
<property name="pixel-size">48</property>
|
||||
<style>
|
||||
<class name="dim-icon"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">Drop video files or folders here to get started</property>
|
||||
<object class="GtkListBox" id="listbox">
|
||||
<property name="selection-mode">none</property>
|
||||
<property name="vexpand">true</property>
|
||||
<property name="show-separators">True</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
<class name="rich-list"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child type="overlay">
|
||||
<object class="GtkBox" id="drop_hint">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="halign">fill</property>
|
||||
<property name="valign">fill</property>
|
||||
<property name="spacing">48</property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="vexpand">true</property>
|
||||
|
||||
<!-- Inner box to center content -->
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="vexpand">true</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">center</property>
|
||||
<property name="spacing">48</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">horizontal</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="spacing">48</property>
|
||||
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">video-x-generic-symbolic</property>
|
||||
<property name="pixel-size">48</property>
|
||||
<style>
|
||||
<class name="dim-icon"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkImage">
|
||||
<property name="icon-name">folder-symbolic</property>
|
||||
<property name="pixel-size">48</property>
|
||||
<style>
|
||||
<class name="dim-icon"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">Drop video files or folders here to get started</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkProgressBar" id="progress_bar">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="visible">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
|
||||
<child>
|
||||
<object class="GtkProgressBar" id="progress_bar">
|
||||
<property name="hexpand">true</property>
|
||||
<property name="visible">False</property>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue