mirror of
https://github.com/2e3s/awatcher.git
synced 2025-07-21 17:40:08 +00:00
Add a bundle feature
This commit is contained in:
parent
856a7afee6
commit
216b2a1334
1
.github/workflows/verify.yml
vendored
1
.github/workflows/verify.yml
vendored
@ -21,6 +21,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
|
- run: sudo apt-get install -y libdbus-1-dev
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
- uses: dtolnay/rust-toolchain@nightly
|
||||||
with:
|
with:
|
||||||
components: clippy
|
components: clippy
|
||||||
|
1955
Cargo.lock
generated
1955
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -24,7 +24,16 @@ fern = { version = "0.6.2", features = ["colored"] }
|
|||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
|
|
||||||
|
ksni = {version = "0.2.0", optional = true}
|
||||||
|
aw-server = { git = "https://github.com/ActivityWatch/aw-server-rust.git", optional = true }
|
||||||
|
aw-datastore = { git = "https://github.com/ActivityWatch/aw-server-rust.git", optional = true }
|
||||||
|
smol = {version = "1.3.0", optional = true }
|
||||||
|
async-compat = { version = "0.2.1", optional = true }
|
||||||
|
webbrowser = { version = "0.8.9", optional = true }
|
||||||
|
zip-extract = { version = "0.1.2", default-features = false, features = ["deflate"], optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["gnome", "kwin_window"]
|
default = ["gnome", "kwin_window"]
|
||||||
gnome = ["watchers/gnome"]
|
gnome = ["watchers/gnome"]
|
||||||
kwin_window = ["watchers/kwin_window"]
|
kwin_window = ["watchers/kwin_window"]
|
||||||
|
bundle = ["ksni", "smol", "async-compat", "aw-server", "aw-datastore", "webbrowser", "zip-extract"]
|
||||||
|
13
README.md
13
README.md
@ -24,6 +24,19 @@ add `--features=?` ("gnome" or "kwin_window") on top of that if you want to enab
|
|||||||
To track your activities in browsers install the plugin for your browser from
|
To track your activities in browsers install the plugin for your browser from
|
||||||
[here](https://github.com/ActivityWatch/aw-watcher-web) (Firefox, Chrome etc).
|
[here](https://github.com/ActivityWatch/aw-watcher-web) (Firefox, Chrome etc).
|
||||||
|
|
||||||
|
#### Compile with bundle
|
||||||
|
|
||||||
|
The executable can be bundled with a tray icon, ActivityWatch server and, optionally, Web UI (if steps 1-2 are done):
|
||||||
|
|
||||||
|
1. Clone and follow the instruction in [ActivityWatch/aw-webui](https://github.com/ActivityWatch/aw-webui)
|
||||||
|
to build the "dist" folder,
|
||||||
|
1. Then zip it with `zip -r dist.zip aw-webui/dist`.
|
||||||
|
2. Build the executable with `--features=bundle`.
|
||||||
|
|
||||||
|
This should be compiled on nightly. The complete bundled version is also built and released.
|
||||||
|
|
||||||
|
Gnome needs [the extension](https://extensions.gnome.org/extension/615/appindicator-support/) to support StatusNotifierItem specification.
|
||||||
|
|
||||||
## Supported environments
|
## Supported environments
|
||||||
|
|
||||||
ActivityWatch server should be run before `awatcher` is running.
|
ActivityWatch server should be run before `awatcher` is running.
|
||||||
|
21
src/bundle.rs
Normal file
21
src/bundle.rs
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
mod menu;
|
||||||
|
mod server;
|
||||||
|
mod site_data;
|
||||||
|
|
||||||
|
pub use menu::Tray;
|
||||||
|
use site_data::unpack_data;
|
||||||
|
use watchers::config::Config;
|
||||||
|
|
||||||
|
pub fn run(config: &Config) -> anyhow::Result<()> {
|
||||||
|
let service = ksni::TrayService::new(Tray {
|
||||||
|
server_host: config.host.clone(),
|
||||||
|
server_port: config.port,
|
||||||
|
});
|
||||||
|
service.spawn();
|
||||||
|
|
||||||
|
let port = config.port;
|
||||||
|
let data_dir = unpack_data()?;
|
||||||
|
server::run(data_dir, port);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
BIN
src/bundle/dist.zip
Normal file
BIN
src/bundle/dist.zip
Normal file
Binary file not shown.
41
src/bundle/menu.rs
Normal file
41
src/bundle/menu.rs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Tray {
|
||||||
|
pub server_host: String,
|
||||||
|
pub server_port: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ksni::Tray for Tray {
|
||||||
|
fn icon_name(&self) -> String {
|
||||||
|
// Taken from https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
|
||||||
|
"appointment-new".into()
|
||||||
|
}
|
||||||
|
fn title(&self) -> String {
|
||||||
|
"Awatcher".into()
|
||||||
|
}
|
||||||
|
fn menu(&self) -> Vec<ksni::MenuItem<Self>> {
|
||||||
|
vec![
|
||||||
|
ksni::menu::StandardItem {
|
||||||
|
label: "Open".into(),
|
||||||
|
icon_name: "document-properties".into(),
|
||||||
|
activate: {
|
||||||
|
let url = format!("http://{}:{}", self.server_host, self.server_port);
|
||||||
|
|
||||||
|
Box::new(move |_| {
|
||||||
|
webbrowser::open(&url).unwrap();
|
||||||
|
})
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
ksni::menu::StandardItem {
|
||||||
|
label: "Exit".into(),
|
||||||
|
icon_name: "application-exit".into(),
|
||||||
|
activate: Box::new(|_| {
|
||||||
|
std::process::exit(0);
|
||||||
|
}),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.into(),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
29
src/bundle/server.rs
Normal file
29
src/bundle/server.rs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
use anyhow::anyhow;
|
||||||
|
use async_compat::Compat;
|
||||||
|
use aw_server::endpoints::build_rocket;
|
||||||
|
use std::{path::PathBuf, sync::Mutex};
|
||||||
|
|
||||||
|
pub fn run(asset_path: PathBuf, port: u32) {
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let db_path = aw_server::dirs::db_path(false)
|
||||||
|
.map_err(|_| anyhow!("DB path is not found: {}", asset_path.display()))
|
||||||
|
.unwrap()
|
||||||
|
.to_str()
|
||||||
|
.unwrap()
|
||||||
|
.to_string();
|
||||||
|
let device_id = aw_server::device_id::get_device_id();
|
||||||
|
let mut config = aw_server::config::create_config(false);
|
||||||
|
config.address = "127.0.0.1".to_string();
|
||||||
|
config.port = u16::try_from(port).unwrap();
|
||||||
|
|
||||||
|
let legacy_import = false;
|
||||||
|
let server_state = aw_server::endpoints::ServerState {
|
||||||
|
datastore: Mutex::new(aw_datastore::Datastore::new(db_path, legacy_import)),
|
||||||
|
asset_path: asset_path.join("dist"),
|
||||||
|
device_id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let future = build_rocket(server_state, config).launch();
|
||||||
|
smol::block_on(Compat::new(future)).unwrap();
|
||||||
|
});
|
||||||
|
}
|
14
src/bundle/site_data.rs
Normal file
14
src/bundle/site_data.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
use std::{fs, io::Cursor, path::PathBuf};
|
||||||
|
|
||||||
|
const SITE_DATA: &[u8] = include_bytes!("./dist.zip");
|
||||||
|
|
||||||
|
pub fn unpack_data() -> anyhow::Result<PathBuf> {
|
||||||
|
let target_dir = std::env::temp_dir().join("awatcher");
|
||||||
|
|
||||||
|
if target_dir.exists() {
|
||||||
|
fs::remove_dir_all(&target_dir)?;
|
||||||
|
}
|
||||||
|
zip_extract::extract(Cursor::new(SITE_DATA), &target_dir, false)?;
|
||||||
|
|
||||||
|
Ok(target_dir)
|
||||||
|
}
|
@ -6,15 +6,10 @@ use clap::{arg, value_parser, Arg, ArgAction, ArgMatches, Command};
|
|||||||
use fern::colors::{Color, ColoredLevelConfig};
|
use fern::colors::{Color, ColoredLevelConfig};
|
||||||
use log::LevelFilter;
|
use log::LevelFilter;
|
||||||
use watchers::config::defaults;
|
use watchers::config::defaults;
|
||||||
use watchers::config::Config as WatchersConfig;
|
use watchers::config::Config;
|
||||||
use watchers::config::FileConfig;
|
use watchers::config::FileConfig;
|
||||||
|
|
||||||
pub struct Config {
|
pub fn setup_logger(verbosity: LevelFilter) -> Result<(), fern::InitError> {
|
||||||
pub watchers_config: WatchersConfig,
|
|
||||||
verbosity: LevelFilter,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn setup_logger(config: &Config) -> Result<(), fern::InitError> {
|
|
||||||
fern::Dispatch::new()
|
fern::Dispatch::new()
|
||||||
.format(|out, message, record| {
|
.format(|out, message, record| {
|
||||||
let colors = ColoredLevelConfig::new()
|
let colors = ColoredLevelConfig::new()
|
||||||
@ -29,9 +24,9 @@ pub fn setup_logger(config: &Config) -> Result<(), fern::InitError> {
|
|||||||
message
|
message
|
||||||
));
|
));
|
||||||
})
|
})
|
||||||
.level(log::LevelFilter::Error)
|
.level(log::LevelFilter::Warn)
|
||||||
.level_for("watchers", config.verbosity)
|
.level_for("watchers", verbosity)
|
||||||
.level_for("awatcher", config.verbosity)
|
.level_for("awatcher", verbosity)
|
||||||
.chain(std::io::stdout())
|
.chain(std::io::stdout())
|
||||||
.apply()?;
|
.apply()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -77,18 +72,16 @@ pub fn from_cli() -> anyhow::Result<Config> {
|
|||||||
3 => LevelFilter::Debug,
|
3 => LevelFilter::Debug,
|
||||||
_ => LevelFilter::Trace,
|
_ => LevelFilter::Trace,
|
||||||
};
|
};
|
||||||
|
setup_logger(verbosity)?;
|
||||||
|
|
||||||
Ok(Config {
|
Ok(Config {
|
||||||
watchers_config: WatchersConfig {
|
port: config.server.port,
|
||||||
port: config.server.port,
|
host: config.server.host,
|
||||||
host: config.server.host,
|
idle_timeout: config.client.get_idle_timeout(),
|
||||||
idle_timeout: config.client.get_idle_timeout(),
|
poll_time_idle: config.client.get_poll_time_idle(),
|
||||||
poll_time_idle: config.client.get_poll_time_idle(),
|
poll_time_window: config.client.get_poll_time_window(),
|
||||||
poll_time_window: config.client.get_poll_time_window(),
|
filters: config.client.filters,
|
||||||
filters: config.client.filters,
|
no_server: *matches.get_one("no-server").unwrap(),
|
||||||
no_server: *matches.get_one("no-server").unwrap(),
|
|
||||||
},
|
|
||||||
verbosity,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
33
src/main.rs
33
src/main.rs
@ -3,6 +3,8 @@
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate log;
|
extern crate log;
|
||||||
|
|
||||||
|
#[cfg(feature = "bundle")]
|
||||||
|
mod bundle;
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@ -11,35 +13,28 @@ use watchers::ReportClient;
|
|||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
let config = config::from_cli()?;
|
let config = config::from_cli()?;
|
||||||
config::setup_logger(&config)?;
|
|
||||||
|
|
||||||
let client = ReportClient::new(config.watchers_config)?;
|
if config.no_server {
|
||||||
let client = Arc::new(client);
|
warn!("Not sending to server {}:{}", config.host, config.port);
|
||||||
|
|
||||||
if client.config.no_server {
|
|
||||||
warn!(
|
|
||||||
"Not sending to server {}:{}",
|
|
||||||
client.config.host, client.config.port
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
info!(
|
info!("Sending to server {}:{}", config.host, config.port);
|
||||||
"Sending to server {}:{}",
|
|
||||||
client.config.host, client.config.port
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
info!(
|
info!("Idle timeout: {} seconds", config.idle_timeout.as_secs());
|
||||||
"Idle timeout: {} seconds",
|
|
||||||
client.config.idle_timeout.as_secs()
|
|
||||||
);
|
|
||||||
info!(
|
info!(
|
||||||
"Idle polling period: {} seconds",
|
"Idle polling period: {} seconds",
|
||||||
client.config.poll_time_idle.as_secs()
|
config.poll_time_idle.as_secs()
|
||||||
);
|
);
|
||||||
info!(
|
info!(
|
||||||
"Window polling period: {} seconds",
|
"Window polling period: {} seconds",
|
||||||
client.config.poll_time_window.as_secs()
|
config.poll_time_window.as_secs()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(feature = "bundle")]
|
||||||
|
bundle::run(&config)?;
|
||||||
|
|
||||||
|
let client = ReportClient::new(config)?;
|
||||||
|
let client = Arc::new(client);
|
||||||
|
|
||||||
let mut thread_handlers = Vec::new();
|
let mut thread_handlers = Vec::new();
|
||||||
|
|
||||||
if let Some(idle_handler) = watchers::IDLE.run_first_supported(&client) {
|
if let Some(idle_handler) = watchers::IDLE.run_first_supported(&client) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user