Author SHA1 Message Date
benjamyn 50ae536650 Merge branch 'master' into wallhaven
/ release x86_64-unknown-linux-musl (push) Failing after 3s
2025-01-02 16:15:18 +11:00
benjamyn 812d3f9d0c Merge branch 'master' into wallhaven 2024-12-05 16:27:17 +11:00
benjamyn edb02fd8ec Added wallhaven downloading 2024-12-05 16:09:26 +11:00
benjamyn c1a3083fab More wallhaven features
Updated ABI slightly
2024-11-22 14:08:07 +11:00
benjamyn edb48a32a0 Added wallhaven initial support 2024-11-20 21:34:59 +11:00
11 changed files with 1813 additions and 118 deletions

No files matched your search

+11 -14
View File
@@ -7,23 +7,20 @@ on:
jobs:
release:
name: release
runs-on: ubuntu-latest
name: release ${{ matrix.target }}
runs-on: alma9
strategy:
fail-fast: false
matrix:
target: [x86_64-unknown-linux-musl]
steps:
- uses: actions/checkout@master
- name: Install deps
run: apt-get update; apt-get install -y libdbus-1-3 libdbus-1-dev libdbus-1-3 pkg-config
- name: Build binary
uses: houseabsolute/actions-rust-cross@v1
run: apt-get update; apt-get install -y libdbus-1-dev pkg-config
- name: Compile and release
uses: rust-build/rust-build.action@v1.4.5
env:
GITHUB_TOKEN: ${{ secrets.GT_TOKEN }}
with:
command: "build"
target: "x86_64-unknown-linux-gnu"
args: "--locked --release"
strip: true
- name: Publish artifacts and release
uses: houseabsolute/actions-rust-release@v0
with:
executable-name: "mycelium"
target: "x86_64-unknown-linux-gnu"
RUSTTARGET: ${{ matrix.target }}
EXTRA_FILES: README.md
Generated
+1418 -1
View File
File diff suppressed because it is too large. Load diff
+2 -1
View File
@@ -4,8 +4,9 @@ version = "0.1.0"
edition = "2021"
[dependencies]
toml = "0.8"
configparser = "3.0.4"
dbus = "0.9.7"
glob = "0.3.1"
rand = "0.8.5"
untildify = { git = "https://git.lovelynet.net/benjamyn/untildify.git", branch = "main" }
reqwest = { version = "0.12.9", features = ["json", "blocking"] }
+1 -2
View File
@@ -19,5 +19,4 @@ What I want to add
- Wallhaven API support
- Gnome support
- Plugin system for Desktop environments? (Need to think about how I wanna do this)
- Add Hyprpaper support (IPC STUFF YOLO)
- Other things as I think about them
- Other things as I think about them
+52 -74
View File
@@ -1,28 +1,27 @@
use configparser::ini::Ini;
use core::fmt;
use toml::Value;
use crate::enums::*;
use untildify;
#[derive(Debug, Clone)]
pub struct Monitor {
pub monitor_type: MonitorType,
pub ratio: MonitorType,
pub index: i64,
}
impl Monitor {
pub fn new(monitor_type: MonitorType) -> Monitor {
pub fn new(monitor_type: MonitorType, index: i64) -> Monitor {
Monitor {
monitor_type,
ratio: monitor_type,
index: index,
}
}
pub fn display(&self) -> String {
format!("Monitor({:?})", self.monitor_type)
}
}
impl fmt::Display for Monitor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.display())
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
@@ -40,11 +39,8 @@ impl Monitors {
impl fmt::Display for Monitors {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[")?;
for (i, v) in self.monitors.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v.display())?;
for v in &self.monitors {
write!(f, "{}, ", v)?;
}
write!(f, "]")
}
@@ -58,7 +54,7 @@ pub struct WallpaperDir {
}
impl WallpaperDir {
pub fn get(&self, monitor_type: &MonitorType) -> String {
pub fn get(&self, monitor_type: MonitorType) -> String {
match monitor_type {
MonitorType::Ultrawide => self.ultrawide.to_string(),
MonitorType::Horizontal => self.horizontal.to_string(),
@@ -72,8 +68,10 @@ impl fmt::Display for WallpaperDir {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"ultrawide: {}, horizontal {}, vertical {}",
self.ultrawide, self.horizontal, self.vertical
"ultrawide: {ultrawide}, horizontal {horizontal}, vertical {vertical}",
ultrawide = self.ultrawide,
horizontal = self.horizontal,
vertical = self.vertical
)
}
}
@@ -84,88 +82,68 @@ pub struct Config {
pub wallpaper_engine: WallpaperHandler,
pub wallpaper_dir: WallpaperDir,
pub monitors: Monitors,
pub wallhaven_api_key: String,
pub wallhaven_tmp_dir: String,
}
impl Config {
pub fn from_config(file_path: String) -> Config {
let toml_str = std::fs::read_to_string(&file_path).expect("Failed to read config file");
let value: Value = toml::from_str(&toml_str).expect("Failed to parse TOML config");
let mut config = Ini::new();
let map = config.load(file_path).unwrap();
// Get the general section
let general = value
.get("general")
.and_then(|g| g.as_table())
.expect("Missing [general] section in config");
// Get the wallpaper engine and parse it
let wallpaper_engine =
WallpaperHandler::new(config.get("general", "wallpaper_engine").unwrap());
//
// Get the wallpaper engine
let wallpaper_engine_str = general
.get("wallpaper_engine")
.and_then(|v| v.as_str())
.expect("Missing wallpaper_engine in [general] section");
let wallpaper_engine = WallpaperHandler::new(wallpaper_engine_str.to_string());
// Get the wallpaper dirs
let ultrawide_dir = general
.get("ultra_wall_dir")
.and_then(|v| v.as_str())
.map(|s| untildify::untildify(s))
.expect("Missing ultra_wall_dir in [general] section");
let horizontal_dir = general
.get("hor_wall_dir")
.and_then(|v| v.as_str())
.map(|s| untildify::untildify(s))
.expect("Missing hor_wall_dir in [general] section");
let vert_wall_dir = general
.get("vert_wall_dir")
.and_then(|v| v.as_str())
.map(|s| untildify::untildify(s))
.expect("Missing vert_wall_dir in [general] section");
// Get the wallpaper dirs and parse them
let ultrawide_dir =
untildify::untildify(config.get("general", "ultra_wall_dir").unwrap().as_ref());
let horizontal_dir =
untildify::untildify(config.get("general", "hor_wall_dir").unwrap().as_ref());
let vert_wall_dir =
untildify::untildify(config.get("general", "vert_wall_dir").unwrap().as_ref());
let wallpaper_dir = WallpaperDir {
ultrawide: ultrawide_dir,
horizontal: horizontal_dir,
vertical: vert_wall_dir,
};
//
// Get the monitors section
let monitors = general
.get("monitors")
.and_then(|m| m.as_array())
.expect("Missing [[monitors]] section in config");
// Get the Monitors and parse them
let mut monitors_vec = Monitors {
let mut monitors = Monitors {
monitors: Vec::new(),
};
for monitor_value in monitors {
let monitor_type_str = monitor_value
.get("monitor_type")
.and_then(|v| v.as_str())
.expect("Missing monitor_type in monitors array");
let monitor_type = MonitorType::new(monitor_type_str.to_string());
monitors_vec.add(Monitor::new(monitor_type));
for mon in map.get("monitors").iter() {
for key in mon.keys() {
let monitor = mon.get(key).unwrap().as_ref().unwrap();
// Add monitor type to Monitors vector
monitors.add(Monitor::new(
MonitorType::new(monitor.to_string()),
key.parse::<i64>()
.expect("Failed to parse int in motitor config"),
));
}
}
let wallhaven_api_key = config.get("wallhaven", "api_key").unwrap();
let wallhaven_tmp_dir = config.get("wallhaven", "tmp_dir").unwrap();
Config {
x_server: general
.get("x_server")
.and_then(|v| v.as_str())
.unwrap_or("")
.to_string(),
x_server: config.get("general", "x_server").unwrap(),
wallpaper_engine,
wallpaper_dir,
monitors: monitors_vec,
monitors,
wallhaven_api_key,
wallhaven_tmp_dir,
}
}
}
impl fmt::Display for Config {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Mycelium Config: X Server: {}, Wallpaper Engine: {}, Wallpaper Directories: {}, Monitors: {}",
self.x_server, self.wallpaper_engine, self.wallpaper_dir, self.monitors
)
write!(f, "Mycelium Config: X Server: {xserv}, Wallpaper Engine: {wallpaper_engine}, Wallpaper Directories: {wallpaper_dirs}, Monitors: {monitors}", xserv = self.x_server, wallpaper_engine = self.wallpaper_engine, wallpaper_dirs = self.wallpaper_dir, monitors = self.monitors)
}
}
+6
View File
@@ -17,6 +17,12 @@ impl fmt::Display for Wallpaper {
}
}
impl Wallpaper {
pub fn new(path: String) -> Wallpaper {
Wallpaper { path: path }
}
}
#[derive(Debug, Clone)]
pub struct Wallpapers {
ultrawide: Vec<Wallpaper>,
-2
View File
@@ -1,5 +1,3 @@
#![allow(dead_code)]
// This code was autogenerated with `dbus-codegen-rust -g -m None -d org.kde.plasmashell -p /PlasmaShell`, see https://github.com/diwic/dbus-rs
use dbus;
#[allow(unused_imports)]
+1 -1
View File
@@ -7,7 +7,7 @@ pub fn change_wallpapers(wallpapers: &Wallpapers) {
command.arg("--no-fehbg").arg("--bg-fill");
for monitor in wallpapers.config.monitors.monitors.iter() {
command.arg(wallpapers.random_selection(&monitor.monitor_type).to_string());
command.arg(wallpapers.random_selection(&monitor.ratio).to_string());
}
let result = command.output();
+3 -3
View File
@@ -30,7 +30,7 @@ pub fn change_wallpapers(wallpapers: &Wallpapers) {
pub fn generate_js(wallpapers: &Wallpapers) -> String {
let mut javascript = String::new();
javascript.push_str("var allDesktops = desktops();\n");
for (count, monitor) in wallpapers.config.monitors.monitors.iter().enumerate() {
for (_count, monitor) in wallpapers.config.monitors.monitors.iter().enumerate() {
let mut boilerplate = String::new();
let _ = write!(
&mut boilerplate,
@@ -39,8 +39,8 @@ pub fn generate_js(wallpapers: &Wallpapers) -> String {
allDesktops[{0}].currentConfigGroup = Array('Wallpaper', 'org.kde.image', 'General');
allDesktops[{0}].writeConfig('Image', 'file://{1}');
",
count,
wallpapers.random_selection(&monitor.monitor_type)
monitor.index,
wallpapers.random_selection(&monitor.ratio)
);
javascript.push_str(&boilerplate);
}
+44 -20
View File
@@ -1,39 +1,63 @@
mod config;
use std::fmt::format;
use config::*;
mod enums;
mod files;
use enums::WallpaperHandler;
use files::*;
mod handlers;
mod wallhaven;
use wallhaven::*;
use untildify::untildify;
fn main() {
// test_request();
// Load the config file from the default path
let path = untildify("~/.config/wallpaperctl/wallpaperctl.toml");
let path = untildify("~/.config/wallpaperctl/wallpaperctl.ini");
let config = Config::from_config(path);
// Initialise the `Wallpapers` struct with a clone of the config
let mut wallpapers = Wallpapers::new(config.clone());
// Load all wallpapers based on the config file specs
wallpapers.load_all();
let api = WallHavenAPI::new(config.wallhaven_api_key, config.wallhaven_tmp_dir.clone());
// Check the configured Wallpaper engine and run the change_wallpapers() method from the respective handlers
let wallpaper_engine = config.wallpaper_engine;
// println!("{:?}", &wallpapers);
match wallpaper_engine {
WallpaperHandler::Plasma => {
handlers::plasma::change_wallpapers(&wallpapers);
}
WallpaperHandler::Feh => {
handlers::feh::change_wallpapers(&wallpapers);
}
WallpaperHandler::Gnome => {
handlers::gnome::change_wallpapers(&wallpapers);
}
_ => {
println!("Error: Unknown wallpaper engine");
}
}
let search_terms = vec![String::from("tsundere")];
let purities = vec![Purity::Sketchy];
let categories = vec![Category::Anime];
let query = WallHavenQuery::new(
api,
search_terms,
purities,
categories,
Ratio::Horizontal,
1,
);
let results = query.run();
let wall = results.get_random();
println!("{}", results);
// // Load all wallpapers based on the config file specs
// wallpapers.load_all();
// // Check the configured Wallpaper engine and run the change_wallpapers() method from the respective handlers
// let wallpaper_engine = config.wallpaper_engine;
// match wallpaper_engine {
// WallpaperHandler::Plasma => {
// handlers::plasma::change_wallpapers(&wallpapers);
// }
// WallpaperHandler::Feh => {
// handlers::feh::change_wallpapers(&wallpapers);
// }
// WallpaperHandler::Gnome => {
// handlers::gnome::change_wallpapers(&wallpapers);
// }
// _ => {
// println!("Error: Unknown wallpaper engine");
// }
// }
}
+275
View File
@@ -0,0 +1,275 @@
use core::fmt;
use dbus::arg::RefArg;
use reqwest::*;
use serde::Deserialize;
use serde::Serialize;
use std::fs::File;
use std::io;
use urlencoding::encode;
use crate::{Wallpaper, Wallpapers};
#[derive(Serialize, Deserialize)]
struct Thumbs {
large: String,
original: String,
small: String,
}
impl fmt::Display for Thumbs {
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
write!(f, "large: {}\noriginal: {}", &self.large, &self.original)
}
}
#[derive(Serialize, Deserialize)]
struct Meta {
current_page: i32,
last_page: i32,
per_page: String,
total: i32,
query: Option<String>,
seed: Option<String>,
}
impl fmt::Display for Meta {
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
write!(f, "current_page: {}", &self.current_page)
}
}
#[derive(Serialize, Deserialize)]
pub struct WallHavenResponse {
pub data: Vec<WallHavenWallpaper>,
pub meta: Meta,
}
impl fmt::Display for WallHavenResponse {
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
for entry in &self.data {
write! {f, "{}\n", entry}.unwrap();
}
write!(f, "{}", &self.meta).unwrap();
Ok(())
}
}
impl WallHavenResponse {
pub fn get_random(&self) -> &WallHavenWallpaper {
self.data.get(0).unwrap()
}
}
#[derive(Serialize, Deserialize)]
pub struct WallHavenWallpaper {
id: String,
url: String,
short_url: String,
views: i64,
favorites: i64,
source: String,
purity: String,
category: String,
dimension_x: i32,
dimension_y: i32,
resolution: String,
ratio: String,
file_size: i64,
file_type: String,
created_at: String,
colors: Vec<String>,
path: String,
thumbs: Thumbs,
}
impl fmt::Display for WallHavenWallpaper {
fn fmt(&self, f: &mut fmt::Formatter) -> std::fmt::Result {
write!(
f,
"ID: {}\tURL: {} ({})",
&self.id, &self.path, &self.purity
)
}
}
impl WallHavenWallpaper {
pub fn download(self, path: String) -> bool {
let image_resp = blocking::get(self.path).unwrap();
let mut outfile = File::create(path).unwrap();
io::copy(&mut image_resp.text().unwrap().as_bytes(), &mut outfile).unwrap();
true
}
pub fn to_wallpaper(&self) -> Wallpaper {
Wallpaper::new(String::new())
}
}
pub struct WallHavenAPI {
pub api_key: String,
pub tmp_dir: String,
}
impl WallHavenAPI {
pub fn new(api_key: String, tmp_dir: String) -> WallHavenAPI {
WallHavenAPI {
api_key: api_key,
tmp_dir: tmp_dir,
}
}
}
pub enum Ratio {
Ultrawide,
Horizontal,
Vertical,
}
pub enum Purity {
Sfw,
Sketchy,
Nsfw,
}
pub enum Category {
General,
People,
Anime,
}
pub struct WallHavenQuery {
api: WallHavenAPI,
search_terms: Vec<String>,
purity: String,
category: String,
ratio: String,
page: i32,
}
impl WallHavenQuery {
pub fn new(
api: WallHavenAPI,
search_terms: Vec<String>,
purity: Vec<Purity>,
category: Vec<Category>,
ratio: Ratio,
page: i32,
) -> WallHavenQuery {
let mut purity_value = String::from("000");
for p in purity {
match p {
Purity::Nsfw => {
purity_value.replace_range(2..3, "1");
}
Purity::Sfw => {
purity_value.replace_range(0..1, "1");
}
Purity::Sketchy => {
purity_value.replace_range(1..2, "1");
}
}
}
let ratio_value: String;
match ratio {
Ratio::Horizontal => ratio_value = String::from("16x9"),
Ratio::Ultrawide => ratio_value = String::from("21x9"),
Ratio::Vertical => ratio_value = String::from("9x16"),
};
let mut category_value = String::from("000");
for c in category {
match c {
Category::Anime => {
category_value.replace_range(1..2, "1");
}
Category::General => {
category_value.replace_range(0..1, "1");
}
Category::People => {
category_value.replace_range(2..3, "1");
}
}
}
WallHavenQuery {
api: api,
search_terms: search_terms,
purity: purity_value,
category: category_value,
ratio: ratio_value,
page: page,
}
}
pub fn run(&self) -> WallHavenResponse {
// Build the query string, URL encode it then send off the request
let mut query_string = String::from("?q=");
let mut url = String::from("https://wallhaven.cc/api/v1/search");
let mut search_terms = String::new();
for term in &self.search_terms {
search_terms.push_str(term.as_str());
search_terms.push_str("+");
}
query_string.push_str(search_terms.as_str());
query_string.push_str("&ratios=");
query_string.push_str(self.ratio.as_str());
query_string.push_str("&purity=");
query_string.push_str(self.purity.as_str());
query_string.push_str("&categories=");
query_string.push_str(self.category.as_str());
query_string.push_str("&apikey=");
query_string.push_str(self.api.api_key.as_str());
url.push_str(query_string.as_str());
// let encoded = encode(url.as_str()).into_owned();
println!("{}", &url);
serde_json::from_str(blocking::get(url).unwrap().text().unwrap().as_str()).unwrap()
// Return the WallHavenResponse object
}
}
// pub fn test_request() {
// // let body = blocking::get("https://wallhaven.cc/api/v1/search?q=anime+skirt&ratios=21x9");
// // // println!("{}", body.unwrap().json::<HashMap<String, String>>());
// // let data: WallHavenResponse =
// // serde_json::from_str(body.unwrap().text().unwrap().as_str()).unwrap();
// // // for entry in data["data"] {
// // // let v: serde_jwson::Value = serde_json::from_str(entry.as_str().unwrap()).unwrap();
// // // println!("{}", v);
// // // }
// // println!("{}", data);
// // wallhaven.add_tag(String::from("Anime"));
// // wallhaven.add_tag(String::from("Anime3"));
// // wallhaven.add_tag(String::from("Anime2"));
// // wallhaven.add_tag(String::from("Anime4"));
// // wallhaven.add_tag(String::from("Anime5"));
// // println!("{}", wallhaven)
// let mut search_terms = Vec::<String>::new();
// // search_terms.push(String::from("sexy"));
// search_terms.push(String::from("crab"));
// let mut purities = Vec::<Purity>::new();
// purities.push(Purity::Nsfw);
// purities.push(Purity::Sketchy);
// let mut categories = Vec::<Category>::new();
// categories.push(Category::Anime);
// let request = WallHavenQuery::new(
// wallhaven,
// search_terms,
// purities,
// categories,
// Ratio::Horizontal,
// 1,
// );
// let response = request.run();
// print!("{}", response);
// }