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.add_action(preferences_action)
|
||||||
self.set_accels_for_action("app.preferences", ["<Primary>comma"])
|
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 = Gio.SimpleAction.new("about", None)
|
||||||
about_action.connect("activate", self.on_about_activate)
|
about_action.connect("activate", self.on_about_activate)
|
||||||
self.add_action(about_action)
|
self.add_action(about_action)
|
||||||
|
@ -89,6 +94,9 @@ def main():
|
||||||
|
|
||||||
def on_preferences_close(self, window):
|
def on_preferences_close(self, window):
|
||||||
window.set_visible(False)
|
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
|
# Don't destroy, just hide
|
||||||
return True # stops further handlers, prevents default destruction
|
return True # stops further handlers, prevents default destruction
|
||||||
|
|
||||||
|
@ -96,6 +104,16 @@ def main():
|
||||||
self.quit()
|
self.quit()
|
||||||
return False # allow default handler to proceed
|
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()
|
app = RecoderApp()
|
||||||
return app.run(sys.argv)
|
return app.run(sys.argv)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ class RecoderPreferences(Adw.PreferencesWindow):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
|
|
||||||
|
self.prefs_changed = False
|
||||||
self.settings = Gio.Settings.new("net.jeena.recoder.preferences")
|
self.settings = Gio.Settings.new("net.jeena.recoder.preferences")
|
||||||
|
|
||||||
current_value = self.settings.get_string("output-folder-template")
|
current_value = self.settings.get_string("output-folder-template")
|
||||||
|
@ -44,13 +45,11 @@ class RecoderPreferences(Adw.PreferencesWindow):
|
||||||
|
|
||||||
if self.validate_template(text):
|
if self.validate_template(text):
|
||||||
self.settings.set_string("output-folder-template", text)
|
self.settings.set_string("output-folder-template", text)
|
||||||
|
self.prefs_changed = True
|
||||||
entry.remove_css_class("error")
|
entry.remove_css_class("error")
|
||||||
else:
|
else:
|
||||||
entry.add_css_class("error")
|
entry.add_css_class("error")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def on_setting_changed(self, settings, key):
|
def on_setting_changed(self, settings, key):
|
||||||
if key == "output-folder-template":
|
if key == "output-folder-template":
|
||||||
new_val = settings.get_string(key)
|
new_val = settings.get_string(key)
|
||||||
|
|
|
@ -22,6 +22,7 @@ from recoder.app import APP_NAME
|
||||||
class RecoderWindow(Adw.ApplicationWindow):
|
class RecoderWindow(Adw.ApplicationWindow):
|
||||||
__gtype_name__ = "RecoderWindow"
|
__gtype_name__ = "RecoderWindow"
|
||||||
|
|
||||||
|
toast_overlay = Gtk.Template.Child()
|
||||||
overlay = Gtk.Template.Child()
|
overlay = Gtk.Template.Child()
|
||||||
drop_hint = Gtk.Template.Child()
|
drop_hint = Gtk.Template.Child()
|
||||||
listbox = Gtk.Template.Child()
|
listbox = Gtk.Template.Child()
|
||||||
|
@ -96,6 +97,10 @@ class RecoderWindow(Adw.ApplicationWindow):
|
||||||
|
|
||||||
self.file_items_to_process = file_items
|
self.file_items_to_process = file_items
|
||||||
self.app_state_manager.state = AppState.FILES_LOADED
|
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
|
return False
|
||||||
|
|
||||||
def clear_listbox(self):
|
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-progress", self.on_transcoder_progress)
|
||||||
self.transcoder.connect("notify::batch-status", self.on_transcoder_status)
|
self.transcoder.connect("notify::batch-status", self.on_transcoder_status)
|
||||||
self.transcoder.start()
|
self.transcoder.start()
|
||||||
|
|
||||||
self.app_state_manager.state = AppState.TRANSCODING
|
self.app_state_manager.state = AppState.TRANSCODING
|
||||||
|
self.toast_overlay.add_toast(Adw.Toast.new("Starting transcoding"))
|
||||||
|
|
||||||
def pause_transcoding(self):
|
def pause_transcoding(self):
|
||||||
if self.transcoder:
|
if self.transcoder:
|
||||||
self.transcoder.pause()
|
self.transcoder.pause()
|
||||||
self.is_paused = True
|
self.is_paused = True
|
||||||
self.app_state_manager.state = AppState.PAUSED
|
self.app_state_manager.state = AppState.PAUSED
|
||||||
|
self.toast_overlay.add_toast(Adw.Toast.new("Transcoding paused"))
|
||||||
|
|
||||||
def resume_transcoding(self):
|
def resume_transcoding(self):
|
||||||
if self.transcoder:
|
if self.transcoder:
|
||||||
self.transcoder.resume()
|
self.transcoder.resume()
|
||||||
self.is_paused = False
|
self.is_paused = False
|
||||||
self.app_state_manager.state = AppState.TRANSCODING
|
self.app_state_manager.state = AppState.TRANSCODING
|
||||||
|
self.toast_overlay.add_toast(Adw.Toast.new("Resuming transcoding"))
|
||||||
|
|
||||||
def on_clear_clicked(self, button):
|
def on_clear_clicked(self, button):
|
||||||
if self.transcoder and self.transcoder.is_processing:
|
if self.transcoder and self.transcoder.is_processing:
|
||||||
self.transcoder.stop()
|
self.transcoder.stop()
|
||||||
self.transcoder = None
|
self.transcoder = None
|
||||||
|
self.clear_listbox()
|
||||||
self.app_state_manager.state = AppState.STOPPED
|
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):
|
def on_transcoder_progress(self, transcoder, param):
|
||||||
self.progress_bar.set_fraction(transcoder.batch_progress / 100.0)
|
self.progress_bar.set_fraction(transcoder.batch_progress / 100.0)
|
||||||
|
@ -150,6 +159,7 @@ class RecoderWindow(Adw.ApplicationWindow):
|
||||||
if transcoder.batch_status == BatchStatus.DONE:
|
if transcoder.batch_status == BatchStatus.DONE:
|
||||||
play_complete_sound()
|
play_complete_sound()
|
||||||
notify_done(APP_NAME, "Transcoding finished!")
|
notify_done(APP_NAME, "Transcoding finished!")
|
||||||
|
self.toast_overlay.add_toast(Adw.Toast.new("Transcoding finished!"))
|
||||||
self.app_state_manager.state = AppState.DONE
|
self.app_state_manager.state = AppState.DONE
|
||||||
|
|
||||||
elif transcoder.batch_status == BatchStatus.STOPPED:
|
elif transcoder.batch_status == BatchStatus.STOPPED:
|
||||||
|
@ -157,4 +167,5 @@ class RecoderWindow(Adw.ApplicationWindow):
|
||||||
|
|
||||||
elif transcoder.batch_status == BatchStatus.ERROR:
|
elif transcoder.batch_status == BatchStatus.ERROR:
|
||||||
notify_done(APP_NAME, "An error occurred during transcoding.")
|
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
|
self.app_state_manager.state = AppState.ERROR
|
||||||
|
|
|
@ -4,6 +4,5 @@
|
||||||
<file>preferences.ui</file>
|
<file>preferences.ui</file>
|
||||||
<file>file_entry_row.ui</file>
|
<file>file_entry_row.ui</file>
|
||||||
<file>style.css</file>
|
<file>style.css</file>
|
||||||
<file alias="net.jeena.Recoder.svg">../resources/net.jeena.Recoder.svg</file>
|
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
<attribute name="label">Preferences</attribute>
|
<attribute name="label">Preferences</attribute>
|
||||||
<attribute name="action">app.preferences</attribute>
|
<attribute name="action">app.preferences</attribute>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<attribute name="label">Help</attribute>
|
||||||
|
<attribute name="action">app.help</attribute>
|
||||||
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<attribute name="label">About Recoder</attribute>
|
<attribute name="label">About Recoder</attribute>
|
||||||
<attribute name="action">app.about</attribute>
|
<attribute name="action">app.about</attribute>
|
||||||
|
@ -23,6 +27,8 @@
|
||||||
<property name="default-width">700</property>
|
<property name="default-width">700</property>
|
||||||
<property name="default-height">400</property>
|
<property name="default-height">400</property>
|
||||||
|
|
||||||
|
<child>
|
||||||
|
<object class="AdwToastOverlay" id="toast_overlay">
|
||||||
<child>
|
<child>
|
||||||
<object class="AdwToolbarView" id="toolbar_view">
|
<object class="AdwToolbarView" id="toolbar_view">
|
||||||
<child type="top">
|
<child type="top">
|
||||||
|
@ -133,7 +139,6 @@
|
||||||
</style>
|
</style>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
|
@ -149,7 +154,6 @@
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
|
@ -163,5 +167,7 @@
|
||||||
</property>
|
</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</template>
|
</template>
|
||||||
</interface>
|
</interface>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue