commit 9a5f1427ad1e0a4b8b0bd7bdc958d9a85067aebe Author: Rawleenc Date: Wed Oct 9 09:08:17 2024 +0000 feat: initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ce2d6d2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# End of .gitignore \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0cc5027 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bing-wallpaper-downloader" +version = "1.0.0" +edition = "2021" + +[dependencies] +reqwest = { version = "0.12", features = ["json"] } +tokio = { version = "1", features = ["full"] } +serde = "1.0" +serde_json = "1.0" +serde_derive = "1.0" +winapi = { version = "0.3", features = ["winuser", "winnls"] } +log = "0.4" +flexi_logger = "0.28" +regex = "1.10" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f0d7d2e --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# Bing Wallpaper Downloader + +Ce projet est une application Rust permettant de télécharger le wallpaper Bing du jour en Ultra HD : + +- `src/main.rs` : Ce fichier est le point d'entrée de l'application Rust. Il contient le code principal de l'application. + +- `src/data.rs` : Ce fichier contient les structures de données utiles à l'extraction de l'url de téléchargement du wallpaper. + +- `Cargo.toml` : Ce fichier est le fichier de configuration de Cargo, le gestionnaire de paquets et de construction de Rust. Il spécifie les dépendances du projet et les paramètres de construction. + +## Installation + +Pour installer et exécuter cette application, vous devez avoir Rust et Cargo installés sur votre système. Vous pouvez les installer en suivant les instructions sur le site officiel de Rust (https://www.rust-lang.org/). + +Une fois que vous avez installé Rust et Cargo, vous pouvez vous rendre dans le répertoire du projet et exécuter la commande suivante pour construire et exécuter l'application : + +``` +cargo run +``` \ No newline at end of file diff --git a/src/data.rs b/src/data.rs new file mode 100644 index 0000000..a94b91b --- /dev/null +++ b/src/data.rs @@ -0,0 +1,12 @@ +use serde_derive::Deserialize; + +#[derive(Deserialize, Debug)] +pub struct Image { + pub startdate: String, + pub url: String, +} + +#[derive(Deserialize, Debug)] +pub struct Data { + pub images: Vec, +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..6faed55 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,132 @@ +#![windows_subsystem = "windows"] + +use flexi_logger::{Cleanup, Criterion, FileSpec, Logger, Naming, WriteMode}; +use log::{error, info}; +use regex::Regex; +use reqwest::{self, Url}; +use serde_json; +use std::ffi::OsString; +use std::os::windows::ffi::OsStringExt; +use std::{ + env, error::Error, ffi::OsStr, fs::File, io::copy, iter::once, os::windows::ffi::OsStrExt, + path::PathBuf, +}; +use winapi::um::winnls::GetUserDefaultLocaleName; +use winapi::um::winuser::{SystemParametersInfoW, SPI_SETDESKWALLPAPER}; + +mod data; +use data::Data; + +/** + * Get the locale of the Windows user + */ +fn get_locale() -> String { + let mut buffer = [0u16; 85]; + unsafe { + GetUserDefaultLocaleName(buffer.as_mut_ptr(), buffer.len() as i32); + } + let os_string = OsString::from_wide(&buffer); + let locale: String = os_string.into_string().unwrap(); + let locale = locale.trim_end_matches('\0'); + info!("Locale: {}", locale); + locale.to_string() +} + +/** + * Get the image URL from Bing.com + */ +async fn get_image_url(locale: &str) -> Result<(Url, String), Box> { + let base_url = "https://www.bing.com"; + let today_image_url = format!( + "{}/HPImageArchive.aspx?format=js&idx=0&n=1&mkt={}", + base_url, locale + ); + + let data: Data = serde_json::from_str(&reqwest::get(&today_image_url).await?.text().await?)?; + let url = Url::parse(&format!( + "{}{}", + base_url, + data.images[0].url.replace("_1920x1080", "_UHD") + ))?; + + info!("Download url: {}", url); + + Ok((url, data.images[0].startdate.clone())) +} + +/** + * Download the image from Bing.com + */ +async fn dowload_image( + url: Url, + startdate: &str, + absolute_path: &PathBuf, +) -> Result> { + let file_name = url + .query_pairs() + .find(|(key, _)| key == "id") + .map(|(_, value)| value.into_owned()) + .unwrap() + .replace("OHR.", &format!("{}_", startdate)); + + let re = Regex::new(r"_[A-Z]{2}-[A-Z]{2}\d+")?; + let image_absolute_path = absolute_path.join(re.replace_all(&file_name, "").to_string()); + + info!("Downloading image to: {}", image_absolute_path.display()); + + let mut out = File::create(&image_absolute_path)?; + let content = reqwest::get(url).await?.bytes().await?; + + copy(&mut content.as_ref(), &mut out)?; + + Ok(image_absolute_path) +} + +/** + * Set the image as the wallpaper + */ +fn set_windows_wallpaper(image_absolute_path: &PathBuf) { + let path_wide: Vec = OsStr::new(&image_absolute_path) + .encode_wide() + .chain(once(0)) + .collect(); + unsafe { + SystemParametersInfoW(SPI_SETDESKWALLPAPER, 0, path_wide.as_ptr() as *mut _, 0); + } +} + +/** + * Download the image and set it as the wallpaper + */ +async fn download_and_set(absolute_path: PathBuf) -> Result<(), Box> { + let locale = get_locale(); + let (url, startdate) = get_image_url(&locale).await?; + let image_absolute_path = dowload_image(url, &startdate, &absolute_path).await?; + set_windows_wallpaper(&image_absolute_path); + Ok(()) +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let absolute_path: PathBuf = match env::current_exe() { + Ok(path) => path.parent().unwrap().to_path_buf(), + Err(_) => PathBuf::from("C:\\Users\\Public\\Pictures"), + }; + let _logger = Logger::try_with_str("info")? + .log_to_file(FileSpec::default().directory(&absolute_path)) + .rotate( + Criterion::Size(u64::MAX), + Naming::Timestamps, + Cleanup::KeepLogFiles(3), + ) + .write_mode(WriteMode::BufferAndFlush) + .start()?; + + match download_and_set(absolute_path).await { + Ok(_) => log::debug!("Program completed successfully"), + Err(e) => { + error!("{}", e); + } + } + Ok(()) +}