mirror of
https://codeberg.org/icewind/galton.git
synced 2026-06-03 10:24:07 +02:00
add notify option
This commit is contained in:
parent
b423323473
commit
ea24479757
7 changed files with 878 additions and 19 deletions
817
Cargo.lock
generated
817
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -21,6 +21,8 @@ ctrlc = "3.5.0"
|
|||
sha2 = "0.11.0-rc.2"
|
||||
hex = "0.4.3"
|
||||
xrust = "1.3.0"
|
||||
notify-rust = "4.11.7"
|
||||
open = "5.3.2"
|
||||
|
||||
[dev-dependencies]
|
||||
maplit = "1.0.2"
|
||||
|
|
@ -115,3 +115,10 @@ delete the newly download file if a duplicate is found.
|
|||
[watch]
|
||||
remove-duplicates = true
|
||||
```
|
||||
|
||||
## Notifications
|
||||
|
||||
Since having the downloaded file being moved or deleted right after downloading
|
||||
makes the default desktop notifications less useful (e.g. you can't open the
|
||||
newly download file trough it). Galton supports sending it's own notifications
|
||||
whenever it performs an action. Providing an easy way to interact with the file.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
[watch]
|
||||
symlink = "~/Downloads/last"
|
||||
remove-duplicates = true
|
||||
notify = true
|
||||
|
||||
[[rule]]
|
||||
name = "\\.(csv|CSV)"
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ with lib; let
|
|||
removeNulls = filterAttrs (_: val: val != null);
|
||||
configFile = format.generate "galton.toml" {
|
||||
watch = removeNulls {
|
||||
inherit (cfg) symlink;
|
||||
inherit (cfg) symlink notify;
|
||||
remove-duplicates = cfg.removeDuplicates;
|
||||
};
|
||||
rule = map removeNulls cfg.rules;
|
||||
|
|
@ -37,6 +37,12 @@ in {
|
|||
description = "Remove duplicate downloads";
|
||||
};
|
||||
|
||||
notify = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = "Show notifications for moved downloads";
|
||||
};
|
||||
|
||||
rules = mkOption {
|
||||
default = [];
|
||||
type = types.listOf (types.submodule {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ pub struct WatchConfig {
|
|||
symlink: Option<String>,
|
||||
#[serde(rename = "remove-duplicates", default)]
|
||||
pub remove_duplicates: bool,
|
||||
#[serde(default)]
|
||||
pub notify: bool,
|
||||
}
|
||||
|
||||
impl WatchConfig {
|
||||
|
|
|
|||
60
src/main.rs
60
src/main.rs
|
|
@ -8,12 +8,13 @@ use main_error::MainResult;
|
|||
use notify_debouncer_full::notify::event::{AccessKind, AccessMode, ModifyKind, RenameMode};
|
||||
use notify_debouncer_full::notify::{EventKind, RecursiveMode};
|
||||
use notify_debouncer_full::{new_debouncer, DebounceEventResult};
|
||||
use notify_rust::{Hint, Notification};
|
||||
use std::fs::{copy, create_dir_all, read_dir, remove_file, rename};
|
||||
use std::io::ErrorKind;
|
||||
use std::os::unix::fs::symlink;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::mpsc::channel;
|
||||
use std::thread::sleep;
|
||||
use std::thread::{sleep, spawn};
|
||||
use std::time::Duration;
|
||||
use tracing::{debug, error, info, instrument};
|
||||
|
||||
|
|
@ -94,6 +95,7 @@ fn main() -> MainResult {
|
|||
&rules,
|
||||
symlink.as_deref(),
|
||||
config.watch.remove_duplicates,
|
||||
config.watch.notify,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -107,6 +109,7 @@ fn handle_watch_event(
|
|||
rules: &[Rule],
|
||||
link_target: Option<&str>,
|
||||
remove_duplicates: bool,
|
||||
notify: bool,
|
||||
) {
|
||||
let handle_path = |path: &Path| {
|
||||
// give originfox time to set xattr
|
||||
|
|
@ -117,10 +120,14 @@ fn handle_watch_event(
|
|||
}
|
||||
|
||||
match FileInfo::load(path) {
|
||||
Ok(file) => maybe_link(
|
||||
handle_file(&file, rules, remove_duplicates).as_deref(),
|
||||
link_target,
|
||||
),
|
||||
Ok(file) => {
|
||||
if let Some(new_path) = handle_file(&file, rules, remove_duplicates) {
|
||||
maybe_link_target(&new_path, link_target);
|
||||
if notify {
|
||||
show_notification(new_path);
|
||||
}
|
||||
};
|
||||
}
|
||||
Err(error) => {
|
||||
error!(%error, "failed to load file info");
|
||||
}
|
||||
|
|
@ -166,8 +173,47 @@ fn is_part(path: &Path) -> bool {
|
|||
path.extension().and_then(|ext| ext.to_str()) == Some("part")
|
||||
}
|
||||
|
||||
fn maybe_link(source: Option<&Path>, target: Option<&str>) {
|
||||
if let (Some(source), Some(target)) = (source, target) {
|
||||
fn show_notification(source: PathBuf) {
|
||||
debug!(file = %source.display(), "showing notification for file");
|
||||
match Notification::new()
|
||||
.summary("Download moved")
|
||||
.appname("Galton")
|
||||
.body(&format!(
|
||||
"<a href=\"{}\">{}</a>",
|
||||
source.display(),
|
||||
source.file_name().unwrap().to_string_lossy()
|
||||
))
|
||||
.hint(Hint::ActionIcons(true))
|
||||
.hint(Hint::Category("transfer.complete".into()))
|
||||
.action("document-open", "Open")
|
||||
.action("folder-open", "Open Containing folder")
|
||||
.show()
|
||||
{
|
||||
Ok(notification) => {
|
||||
spawn(move || {
|
||||
notification.wait_for_action(|action| match action {
|
||||
"document-open" => {
|
||||
if let Err(error) = open::that(&source) {
|
||||
error!(%error, file = %source.display(), "failed to open file from notification");
|
||||
}
|
||||
}
|
||||
"folder-open" => {
|
||||
if let Err(error) = open::that(source.parent().unwrap()) {
|
||||
error!(%error, file = %source.display(), "failed to open parent folder from notification");
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
});
|
||||
}
|
||||
Err(error) => {
|
||||
error!(%error, "Failed to show notification");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_link_target(source: &Path, target: Option<&str>) {
|
||||
if let Some(target) = target {
|
||||
if Path::new(target).is_symlink() {
|
||||
if let Err(error) = remove_file(target) {
|
||||
error!(%error, "failed to remove link target");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue