32 Commits
0.1.3 ... 0.2.1

Author SHA1 Message Date
AB
b1e418a633 Cleanup cpu_load_bar func. Using lib. 2020-04-11 16:35:55 +03:00
Alexandr Bogomyakov
8e569078de Minor fixes. 2020-04-10 17:20:02 +03:00
Alexandr Bogomyakov
5d23b7da2d Bump version 2019-12-24 13:17:20 +03:00
Alexandr Bogomyakov
5cf2ab0e40 Add time feature with custom format. 2019-12-24 13:16:23 +03:00
AB
7c7d9070a4 Update separator. 2019-12-19 01:39:22 +03:00
AB
456d423f47 Improve player metadata displaying. 2019-12-19 01:36:57 +03:00
Ultra Desu
cd854d450a Fix files. 2019-12-19 00:03:15 +03:00
Ultra Desu
7a934a063f Fix player metadata shorting. 2019-12-19 00:02:33 +03:00
Alexandr Bogomyakov
08cf01ee81 Add multiplayer support. 2019-12-17 15:10:31 +03:00
Alexandr Bogomyakov
530a753527 Add multiplayer support. 2019-12-17 15:05:24 +03:00
House of Vanity
614185e7f8 Create LICENSE-WTFPL
Add license
2019-12-10 13:38:34 +03:00
Alexandr Bogomyakov
ddd09761f5 Fix workflow. 2019-12-04 15:24:04 +03:00
Alexandr Bogomyakov
b264f3b823 Fix workflow. 2019-12-04 14:52:59 +03:00
Alexandr Bogomyakov
86a95efa49 Fix workflow. 2019-12-04 14:50:59 +03:00
Alexandr Bogomyakov
3e131088e6 Add env to workflow. 2019-12-04 14:30:56 +03:00
Alexandr Bogomyakov
f96467272b Add env to workflow. 2019-12-04 14:30:43 +03:00
Alexandr Bogomyakov
8cdaf4628b Add push action - build. 2019-12-04 14:23:28 +03:00
House of Vanity
23cc78cc0a Update README.md 2019-12-04 13:06:54 +03:00
House of Vanity
dec3226f41 Update README.md
Update readme.
2019-12-04 13:06:32 +03:00
House of Vanity
e085d55dde Create README.md 2019-12-04 12:58:26 +03:00
Alexandr Bogomyakov
17228e3ca8 Add screenshot 2019-12-04 12:57:00 +03:00
Ultra Desu
3b3f77a0e0 Merge colorize with player status 2019-12-03 23:53:21 +03:00
Alexandr Bogomyakov
f655e542fc Add player status icon. 2019-12-03 09:33:26 +03:00
Ultra Desu
5785b15a78 Colorize media player applet. 2019-11-30 16:21:33 +03:00
Ultra Desu
c69a95009d Add media player metadata. 2019-11-30 14:10:05 +03:00
Ultra Desu
b95183fd94 lifetime issue 2019-11-30 03:14:00 +03:00
Alexandr Bogomyakov
a9216ff630 Add dbus support 2019-11-29 18:17:57 +03:00
Alexandr Bogomyakov
b070d50bc7 Merge branch 'master' of github.com:house-of-vanity/tmux_helper 2019-11-28 18:00:10 +03:00
Alexandr Bogomyakov
139e7c7577 Linting 2019-11-28 18:00:00 +03:00
Alexandr Bogomyakov
23730088d5 Add dbus client. 2019-11-28 17:59:24 +03:00
House of Vanity
3a04d115bf Delete rust.yml 2019-11-25 12:31:33 +03:00
House of Vanity
971de9c6f0 Update release-upload.yml 2019-11-25 12:27:06 +03:00
8 changed files with 268 additions and 46 deletions

BIN
.github/prev.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

20
.github/workflows/push-build.yml vendored Normal file
View File

@@ -0,0 +1,20 @@
name: Rust
on: push
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Pre-build
run: sudo apt install -y libdbus-1-dev pkg-config libdbus-1-3
- name: Build
run: cargo build --verbose --release
env:
RUST_BACKTRACE: 1
- name: Strip
run: strip target/release/tmux-helper

View File

@@ -9,8 +9,14 @@ jobs:
steps:
- uses: actions/checkout@v1
- name: Pre-build
run: sudo apt install -y libdbus-1-dev pkg-config libdbus-1-3
- name: Build
run: cargo build --verbose --release
env:
RUST_BACKTRACE: 1
- name: Strip
run: strip target/release/tmux-helper
- name: Upload to release
uses: JasonEtco/upload-to-release@master
with:

View File

@@ -1,15 +0,0 @@
name: Rust
on: [push]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose

View File

@@ -1,8 +1,10 @@
[package]
name = "tmux-helper"
version = "0.1.0"
version = "0.2.0"
authors = ["Ultra Desu <ultradesu@hexor.ru>"]
edition = "2018"
[dependencies]
sys-info = "*"
dbus = "*"
chrono = "*"

13
LICENSE-WTFPL Normal file
View File

@@ -0,0 +1,13 @@
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
Version 2, December 2004
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
Everyone is permitted to copy and distribute verbatim or modified
copies of this license document, and changing it is allowed as long
as the name is changed.
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. You just DO WHAT THE FUCK YOU WANT TO.

16
README.md Normal file
View File

@@ -0,0 +1,16 @@
# Tmux helper
Small app that perform system check and print TMUX friendly output.
![Preview](.github/prev.png)
### Features:
1. `tmux-helper -cb` print cpu load with bar.
2. `tmux-helper -mb` print memory usage with bar.
3. `tmux-helper -p` show current player status using d-bus.
### Building
`cargo build --release`
or get binary on release page
### Customization
Colours are hardcoded but it's easy to change it. It defined as CONST in very beginning of main.rs file.

View File

@@ -1,18 +1,29 @@
use std::fs;
use std::env;
extern crate chrono;
extern crate dbus;
use crate::dbus::blocking::stdintf::org_freedesktop_dbus::Properties;
use chrono::{DateTime, Local, Utc};
use dbus::{arg, blocking::Connection};
use std::{env, time::Duration};
use sys_info;
const LOW: &str = "#[fg=colour186]";
const MID: &str = "#[fg=colour208]";
const HIGH: &str = "#[fg=colour160]";
const END: &str = "#[fg=colour7]";
const LOW: &str = "#[fg=colour119]";
const MID: &str = "#[fg=colour220]";
const HIGH: &str = "#[fg=colour197]";
const END: &str = "#[fg=colour153]";
const TRACK_NAME: &str = "#[fg=colour46]";
const TRACK_ARTIST: &str = "#[fg=colour46]";
const TRACK_TIME: &str = "#[fg=colour153]";
fn read_file(file_path: &str) -> String {
fs::read_to_string(file_path)
.expect("Cant read file.")
#[derive(Debug, Clone)]
struct TrackInfo {
title: String,
artist: String,
position: String,
duration: String,
status: String,
}
fn to_bar(value: i32, max: i32, low: f32, mid: f32) {
let mut bar = "".to_string();
let bar_sym = "".to_string();
@@ -26,7 +37,9 @@ fn to_bar(value: i32, max: i32, low: f32, mid: f32) {
for i in 0..max {
if i < value as i32 {
bar.push_str(&bar_sym);
} else {bar.push_str(" ")}
} else {
bar.push_str(" ")
}
}
bar.push_str(END);
bar.push_str("|");
@@ -39,38 +52,205 @@ fn mem_load_bar(bar_len: i32) {
Err(w) => panic!("{:?}", w),
Ok(mem_data) => memory = mem_data,
}
let len = ((memory.total - memory.avail) as f32 / (memory.total as f32) * bar_len as f32) as i32;
let len =
((memory.total - memory.avail) as f32 / (memory.total as f32) * bar_len as f32) as i32;
to_bar(len, bar_len, 0.7, 0.9);
print!("{:.0} MiB", memory.avail/1024);
print!("{:.0} MiB#[default]", memory.avail / 1024);
}
fn cpu_load_bar(bar_len: i32) {
let load = read_file("/proc/loadavg");
let load_data = load.split_whitespace().collect::<Vec<&str>>();
let _cpu_count = read_file("/proc/cpuinfo");
let cpu_count = _cpu_count.matches("model name").count();
let one: f32 = load_data[0].parse().unwrap();
let len: f32 = one as f32 / cpu_count as f32 * bar_len as f32;
let cpu_count = match sys_info::cpu_num() {
Ok(c) => c,
Err(e) => panic!("{:?}", e),
};
let la_one: f32 = match sys_info::loadavg() {
Ok(l) => l.one as f32,
Err(e) => panic!("{:?}", e),
};
let len: f32 = la_one as f32 / cpu_count as f32 * bar_len as f32;
to_bar(len as i32, bar_len, 0.3, 0.7);
print!("{:.2} LA1", one);
print!("{:.2} LA1#[default]", la_one);
}
fn get_player() -> Result<Vec<String>, Box<dyn std::error::Error>> {
let conn = Connection::new_session()?;
let proxy = conn.with_proxy("org.freedesktop.DBus", "/", Duration::from_millis(5000));
let (names,): (Vec<String>,) = proxy.method_call("org.freedesktop.DBus", "ListNames", ())?;
let mut players: Vec<String> = Vec::new();
for name in names {
if name.contains("org.mpris.MediaPlayer2") {
players.push(name);
}
}
Ok(players)
}
fn player_info(players: Vec<String>) -> Result<TrackInfo, Box<dyn std::error::Error>> {
let mut players_vec: Vec<TrackInfo> = Vec::new();
for player in players {
let mut track_info = TrackInfo {
artist: "".to_string(),
title: "".to_string(),
position: "".to_string(),
duration: "".to_string(),
status: "".to_string(),
};
let conn = Connection::new_session()?;
let proxy = conn.with_proxy(
player,
"/org/mpris/MediaPlayer2",
Duration::from_millis(5000),
);
let metadata: Box<dyn arg::RefArg> =
proxy.get("org.mpris.MediaPlayer2.Player", "Metadata")?;
let mut iter = metadata.as_iter().unwrap();
while let Some(key) = iter.next() {
if key.as_str() == Some("xesam:title") {
if let Some(title) = iter.next().unwrap().as_str() {
track_info.title = title.to_string();
}
}
if key.as_str() == Some("mpris:length") {
if let Some(length) = iter.next().unwrap().as_i64() {
track_info.duration = format_time(length / 1000000);
}
}
if key.as_str() == Some("xesam:artist") {
if let Some(mut artists) = iter.next().unwrap().as_iter() {
while let Some(artist) = artists.next() {
if let Some(mut line) = artist.as_iter() {
track_info.artist = line.next().unwrap().as_str().unwrap().to_string();
}
}
}
}
}
let position: Box<dyn arg::RefArg> =
proxy.get("org.mpris.MediaPlayer2.Player", "Position")?;
track_info.position = format_time(position.as_i64().unwrap() / 1000000);
// ugly
let _status_text_box: Box<dyn arg::RefArg> =
proxy.get("org.mpris.MediaPlayer2.Player", "PlaybackStatus")?;
let _status_text = _status_text_box.as_str().unwrap();
match _status_text.as_ref() {
"Playing" => track_info.status = "".to_string(),
"Paused" => track_info.status = "".to_string(),
_ => track_info.status = "".to_string(),
};
players_vec.push(track_info);
}
for player in &players_vec {
if player.status == "".to_string() {
return Ok(player.clone());
}
}
Ok(players_vec[players_vec.len() - 1].clone())
}
fn format_time(sec: i64) -> String {
let minutes = sec / 60;
let secondes = sec % 60;
let result = format!("{:02}:{:02}", minutes, secondes);
result.to_string()
}
fn get_time(utc: bool, mut format: &str) {
// Format reference: https://docs.rs/chrono/0.4.10/chrono/format/strftime/index.html
if format.len() == 0 {
format = "%H:%M";
}
if utc {
let local_time = Local::now();
let utc_time = DateTime::<Utc>::from_utc(local_time.naive_utc(), Utc);
println!("{}", utc_time.format(format));
} else {
let local_time = Local::now();
println!("{}", local_time.format(format));
}
}
fn main() {
let args: Vec<String> = env::args().collect();
let help_text: &str = "Available commands -mb, -cb";
match args.len() {
let args: Vec<String> = env::args().collect();
let help_text: &str = "Available commands -mb, -cb, -tl <TIME FORMAT>, -tu <TIME FORMAT>, -p";
match args.len() {
1 => {
panic!(help_text);
}
2 => match args[1].as_ref() {
"-cb" => cpu_load_bar(15),
"-mb" => mem_load_bar(15),
"-tl" => get_time(false, ""),
"-tu" => get_time(true, ""),
"-p" => match player_info(get_player().unwrap()) {
Ok(mut track_info) => {
let mut title_len = 30;
let mut artist_len = 30;
let mut separator: String = "".to_string();
let max_shift = 6;
if track_info.artist.chars().count() == 0 {
separator = "".to_string();
title_len += artist_len;
}
if artist_len + max_shift >= track_info.artist.chars().count() {
artist_len = track_info.artist.chars().count()
}
if track_info.artist.len() > artist_len {
let mut artist: String = String::new();
let mut counter = 0;
for ch in track_info.artist.chars() {
if counter == artist_len {
artist.push_str("..");
break;
}
artist.push(ch);
counter += 1;
}
track_info.artist = artist;
}
if title_len + max_shift >= track_info.title.chars().count() {
title_len = track_info.title.chars().count()
}
if track_info.title.len() > title_len {
let mut title: String = String::new();
let mut counter = 0;
for ch in track_info.title.chars() {
if counter == title_len {
title.push_str("..");
break;
}
title.push(ch);
counter += 1;
}
track_info.title = title;
}
println!(
"#[none]#[bold]{}{}{}#[none]{}{}{}{} {}[{}/{}] {} {}#[default]",
TRACK_NAME,
track_info.title,
END,
separator,
TRACK_ARTIST,
track_info.artist,
END,
TRACK_TIME,
track_info.position,
track_info.duration,
track_info.status,
END,
);
}
Err(_e) => println!("No music playing"),
},
_ => panic!(help_text),
},
2 => {
match args[1].as_ref() {
"-cb" => cpu_load_bar(15),
"-mb" => mem_load_bar(15),
_ => panic!(help_text),
}
3 => match args[1].as_ref() {
"-tl" => get_time(false, args[2].as_ref()),
"-tu" => get_time(true, args[2].as_ref()),
_ => panic!(help_text),
},
_ => {
panic!(help_text);
}
}
}
}