├── src ├── web_routes.rs ├── web_routes │ ├── test_page.rs │ └── config.rs ├── lyric_providers │ ├── mpris2_text.rs │ ├── feeluown_netease.rs │ ├── yesplaymusic.rs │ ├── netease_trackid.rs │ ├── file.rs │ └── netease.rs ├── config.rs ├── main.rs ├── lyric_providers.rs ├── lyric_parser.rs ├── websocket.rs └── player.rs ├── assets └── lyrica.png ├── .gitignore ├── .idea └── vcs.xml ├── plasmoid ├── contents │ ├── config │ │ ├── config.qml │ │ └── main.xml │ └── ui │ │ ├── configFrontend.qml │ │ ├── configBackend.qml │ │ └── main.qml └── metadata.json ├── docs ├── LYRIC_PROVIDERS.zh.md ├── LYRIC_PROVIDERS.md └── BUILD.md ├── Cargo.toml ├── README.zh.md ├── LICENSE ├── README.md ├── examples └── lyrica_obs_plugin.py └── Cargo.lock /src/web_routes.rs: -------------------------------------------------------------------------------- 1 | pub mod test_page; 2 | pub mod config; 3 | -------------------------------------------------------------------------------- /assets/lyrica.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiyuki0325/lyrica/HEAD/assets/lyrica.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | plasma-desktop-lyrics-daemon 2 | .idea/ 3 | /.idea/ 4 | build-**/ 5 | target/ 6 | **/*.kate-swp 7 | package/ 8 | *.plasmoid 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /plasmoid/contents/config/config.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.0 2 | import QtQml 2.2 3 | 4 | import org.kde.plasma.configuration 2.0 5 | 6 | ConfigModel { 7 | id: configModel 8 | 9 | ConfigCategory { 10 | name: i18n("Frontend") 11 | icon: "preferences-desktop-color" 12 | source: "configFrontend.qml" 13 | } 14 | 15 | ConfigCategory { 16 | name: i18n("Backend") 17 | icon: "configure" 18 | source: "configBackend.qml" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /docs/LYRIC_PROVIDERS.zh.md: -------------------------------------------------------------------------------- 1 | # 歌词源 2 | 3 | Lyrica 使用以下歌词源来获取播放器的歌词。 4 | 5 | 可以在 Lyrica 小部件的设置中禁用不需要的歌词源。 6 | 7 | ## `mpris2_text` 8 | 9 | 如果你的播放器支持在 mpris2 元数据的 asText 中提供带时间轴的歌词,那么这个歌词源将被使用。 10 | 11 | ## `file` 12 | 13 | 这个歌词源从音乐文件的标签或 LRC 文件中读取歌词,当播放本地音乐时使用。 14 | 15 | 支持的文件格式:mp3、flac、lrc。 16 | 17 | ## `yesplaymusic` 18 | 19 | YesPlayMusic 是网易云音乐的第三方客户端。当使用此播放器时,Lyrica 可以从 YesPlayMusic 提供的 API 获取歌词。 20 | 21 | ## `netease_trackid` 22 | 23 | 这个歌词源适用于支持在 mpris2 元数据中提供 track ID 的第三方网易云客户端,如 ElectronNCM 和 NetEase Cloud Music GTK4。 24 | 25 | ## `feeluown_netease` 26 | 27 | FeelUOwn 是一个用 Python 编写的本地 / 在线音乐播放器。当使用 FeelUOwn 的网易源听歌时,可以使用此歌词源。 28 | 29 | ## `netease` 30 | 31 | `netease` 歌词源会在网易云音乐中查找对应的歌曲并获取歌词。当其他歌词源不可用时,可以作为备用选项使用。 32 | -------------------------------------------------------------------------------- /src/web_routes/test_page.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{HttpResponse, Responder}; 2 | 3 | pub(crate) async fn test_page() -> impl Responder { 4 | HttpResponse::Ok() 5 | .append_header(("Content-Type", "text/html; charset=utf-8")) 6 | .body( 7 | r#" 8 | 9 | 10 | 11 | 20 | 21 | 22 | "#, 23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /plasmoid/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "KPlugin": { 3 | "Authors": [ 4 | { 5 | "Email": "me@chyk.ink", 6 | "Name": "Kirikaze Chiyuki", 7 | "Name[zh_CN]": "斩风千雪", 8 | "Name[zh_TW]": "斬風千雪" 9 | } 10 | ], 11 | "Category": "Multimedia", 12 | "Description": "Display real-time lyrics for music players", 13 | "Description[zh_CN]": "显示音乐播放器的实时歌词", 14 | "Description[zh_TW]": "顯示音樂播放器的實時歌詞", 15 | "Icon": "music-amarok", 16 | "Id": "ink.chyk.LyricaPlasmoid", 17 | "Name": "Lyrica", 18 | "Version": "0.14", 19 | "Website": "https://github.com/chiyuki0325/lyrica" 20 | }, 21 | "X-Plasma-API-Minimum-Version": "6.0", 22 | "X-Plasma-MainScript": "ui/main.qml", 23 | "X-Plasma-RemoteLocation": "", 24 | "KPackageStructure": "Plasma/Applet" 25 | } 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lyrica" 3 | version = "0.14.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | tokio = { version = "1.40.0", features = ["full"] } 8 | actix = "0.13.5" 9 | actix-web = "4.9.0" 10 | actix-web-actors = "4.3.0" 11 | serde = { version = "1.0.210", features = ["derive"] } 12 | serde_json = "1.0.128" 13 | mpris = "2.0.1" 14 | # mpris-async = "0.1.0" 15 | # async-std = "1.12.0" 16 | url = "2.5.2" 17 | #flac = "0.5.0" 18 | id3 = "1.14.0" 19 | # async-trait = "0.1.80" 20 | # ncm-api = { git = "https://github.com/gmg137/netease-cloud-music-api.git", branch = "dev", package = "netease-cloud-music-api" } 21 | ncm-api = { git = "https://github.com/chiyuki0325/netease-cloud-music-api.git", tag = "1.5.1-fix1", package = "netease-cloud-music-api" } 22 | lazy_static = "1.5.0" 23 | metaflac = "0.2.7" 24 | isahc = { version = "1.7.2", features = ["json"] } 25 | -------------------------------------------------------------------------------- /src/lyric_providers/mpris2_text.rs: -------------------------------------------------------------------------------- 1 | use mpris::Metadata; 2 | use crate::lyric_parser::{ 3 | LyricLine, 4 | parse_lyrics, 5 | }; 6 | 7 | #[derive(Clone)] 8 | pub struct Mpris2TextProvider {} 9 | 10 | impl Mpris2TextProvider { 11 | pub async fn get_lyric_by_metadata(&self, metadata: &Metadata) -> (Vec, bool, bool) { 12 | if let Some(mpris2_text) = metadata.get("xesam:asText") { 13 | let mpris2_text = mpris2_text.as_string().unwrap().clone(); 14 | if mpris2_text.lines().all(|line| line.starts_with('[')) { 15 | (parse_lyrics(mpris2_text), true, false) 16 | } else { 17 | (Vec::new(), false, true) 18 | } 19 | } else { 20 | (Vec::new(), false, true) 21 | } 22 | } 23 | 24 | pub fn is_available_by_metadata(&self, metadata: &Metadata) -> bool { 25 | metadata.iter().any(|(k, _)| k == "xesam:asText") 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /README.zh.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

Lyrica

5 |
6 | 7 | [English](/README.md) 8 | 9 | Lyrica 是一个简洁轻量的 Linux 桌面歌词显示软件。 10 | 不同于类似的项目,比如 [Waylyrics](https://github.com/waylyrics/waylyrics),Lyrica 通过桌面挂件 / 拓展与桌面环境集成,将歌词直接显示在面板上。 11 | 目前项目正在开发中,但目前的版本已经可以使用。 12 | 13 | 项目的名字来源于东方 Project 的角色莉莉卡·普莉兹姆利巴。 14 | 15 | --- 16 | 17 | ### 开发进度 18 | 19 | - [x] LRC 解析器 20 | - [x] 实时歌词 21 | - [x] 文件歌词源 22 | - [x] 网易云音乐歌词源 23 | - [x] 不同搜索模式(歌名 / 歌名 + 歌手) 24 | - [ ] 缓存下载的歌词 25 | - [x] 播放器 API 歌词源 26 | - [x] Mpris2 asText 27 | - [x] 网易云音乐歌曲 ID 28 | - [x] YesPlayMusic 29 | - [x] KDE Plasma 挂件前端 30 | - [ ] ~~GNOME Shell 拓展前端~~ 31 | 由于已经有类似的项目,比如 desktop-lyrics 和 osdlyrics,GNOME Shell 拓展暂时不会优先考虑。 32 | - [ ] 文档 33 | 34 | ### 使用方法 35 | 36 | 查看 [构建说明](/docs/BUILD.md) 来构建并安装 Lyrica。 37 | 38 | --- 39 | 40 | 本项目的 Logo 来自于音乐游戏《舞萌 DX》中的東方 Project Revival 区域。 41 | 版权归原作者所有。 42 | -------------------------------------------------------------------------------- /src/lyric_providers/feeluown_netease.rs: -------------------------------------------------------------------------------- 1 | use crate::lyric_parser::{ 2 | LyricLine, 3 | parse_netease_lyrics, 4 | }; 5 | 6 | #[derive(Clone)] 7 | pub struct FeelUOwnNeteaseLyricProvider {} 8 | 9 | 10 | impl FeelUOwnNeteaseLyricProvider { 11 | pub async fn get_lyric(&self, url: &str) -> (Vec, bool, bool) { 12 | let ncm_api = ncm_api::MusicApi::new(0); 13 | let music_id = url.strip_prefix("fuo://netease/songs/").unwrap().parse::().unwrap(); 14 | let lyric_result = ncm_api.song_lyric(music_id).await; 15 | if let Ok(lyric_result) = lyric_result { 16 | let lyric_lines = lyric_result.lyric; 17 | let tlyric_lines = lyric_result.tlyric; 18 | return ( 19 | parse_netease_lyrics(lyric_lines, tlyric_lines), 20 | true, false 21 | ); 22 | } 23 | (Vec::new(), false, true) 24 | } 25 | 26 | pub fn is_available(&self, url: &str) -> bool { 27 | url.starts_with("fuo://netease/") 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/web_routes/config.rs: -------------------------------------------------------------------------------- 1 | use crate::config::{Config, SharedConfig}; 2 | use actix_web::{web, HttpResponse}; 3 | 4 | pub(crate) async fn update_config( 5 | config_req: web::Json, 6 | config: web::Data, 7 | ) -> HttpResponse { 8 | { 9 | // acquire read lock 10 | if config.read().await.verbose { 11 | println!("Updating config: {:?}", config_req.0); 12 | } 13 | // drop read lock 14 | } 15 | // acquire write lock 16 | let mut config = config.write().await; 17 | *config = config_req.0; 18 | // check if lyric_search_folder exists 19 | config.alt_folder_exists = if tokio::fs::metadata(&config.lyric_search_folder).await.is_err() { 20 | false 21 | } else { 22 | true 23 | }; 24 | HttpResponse::Ok() 25 | .content_type("application/json") 26 | .body(r#"{"status": "ok"}"#) 27 | // all locks dropped now 28 | } 29 | 30 | pub(crate) async fn get_config(config: web::Data) -> HttpResponse { 31 | let config = config.read().await; 32 | HttpResponse::Ok().json(&*config) 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Kirikaze Chiyuki 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/LYRIC_PROVIDERS.md: -------------------------------------------------------------------------------- 1 | # Lyric Providers 2 | 3 | [简体中文](LYRIC_PROVIDERS.zh.md) 4 | 5 | The lyric providers below are used to fetch lyrics from players. 6 | 7 | You can disable unnecessary lyric providers in the settings of the Lyrica widget. 8 | 9 | ## `mpris2_text` 10 | 11 | If your player supports providing lyrics with timeline in asText of mpris2 metadata, then this lyric provider will be used. 12 | 13 | ## `file` 14 | 15 | This lyrics provider reads lyrics from the music file's tags or LRC files when playing local music. 16 | 17 | Supports the following file formats: mp3, flac, lrc. 18 | 19 | ## `yesplaymusic` 20 | 21 | YesPlayMusic is a third-party player for NetEase Cloud Music. Lyrica can get lyrics from the API provided by YesPlayMusic when using this player. 22 | 23 | ## `netease_trackid` 24 | 25 | This lyric provider is available to third-party NetEase Cloud Music players that expose music track IDs like ElectronNCM and NetEase Cloud Music GTK4. 26 | 27 | ## `feeluown_netease` 28 | 29 | FeelUOwn is a local & online music player written in Python. This lyrics provider is available when using FeelUOwn to listen to songs on NetEase Cloud Music. 30 | 31 | ## `netease` 32 | 33 | The `netease` lyrics provider finds the corresponding song and obtains the lyrics from NetEase Cloud Music. It can be used as a fallback option when other lyrics providers are unavailable. 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |

Lyrica

5 |
6 | 7 | [简体中文](/README.zh.md) 8 | 9 | Lyrica is a simple software to show desktop lyrics on Linux focused on simplicity and integration. 10 | Unlike similar projects like [Waylyrics](https://github.com/waylyrics/waylyrics), it integrates with desktop environments and shows lyrics on the desktop's panels. 11 | Currently the project is in development, but the current version is already usable. 12 | 13 | The project's name comes from the Touhou Project character Lyrica Prismriver. 14 | 15 | --- 16 | 17 | ### Project status 18 | 19 | - [x] LRC parser 20 | - [x] Real-time lyrics 21 | - [x] File lyric provider 22 | - [x] NetEase Cloud Music lyric provider 23 | - [x] Different search patterns 24 | - [ ] Cache online lyrics 25 | - [x] Player API lyric provider 26 | - [x] Mpris2 asText 27 | - [x] NetEase Track ID 28 | - [x] YesPlayMusic 29 | - [x] KDE Plasma Plasmoid frontend 30 | - [ ] ~~GNOME shell extension frontend~~ 31 | There are also similar projects like desktop-lyrics and osdlyrics, so GNOME shell extension is not a priority. 32 | - [ ] User documents 33 | 34 | 35 | ### Usage 36 | 37 | Check the [build instructions](/docs/BUILD.md) to build and install Lyrica. 38 | 39 | --- 40 | 41 | The logo of this project comes from the "東方 Project リバイバルちほー" of the rhythm game "maimai でらっくす". 42 | Copyright belongs to the original author. 43 | -------------------------------------------------------------------------------- /plasmoid/contents/ui/configFrontend.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls 3 | import QtQuick.Layouts 1.12 4 | import org.kde.kirigami as Kirigami 5 | 6 | Kirigami.FormLayout { 7 | id: page 8 | 9 | property alias cfg_characterLimit: characterLimit.text 10 | property alias cfg_shouldUseDefaultThemeFontSize: shouldUseDefaultThemeFontSize.checked 11 | property alias cfg_configuredFontSize: configuredFontSize.text 12 | property alias cfg_layoutHeight: layoutHeight.text 13 | property alias cfg_showReconnectingText: showReconnectingText.checked 14 | 15 | TextField { 16 | id: characterLimit 17 | Kirigami.FormData.label: i18n("Character Limit:") 18 | placeholderText: i18n("") 19 | validator: IntValidator {bottom: 0; top: 9999} 20 | } 21 | 22 | CheckBox { 23 | id: shouldUseDefaultThemeFontSize 24 | text: i18n("Ignore the setting below and use theme default size") 25 | } 26 | 27 | TextField { 28 | id: configuredFontSize 29 | Kirigami.FormData.label: i18n("Custom font size:") 30 | placeholderText: i18n("") 31 | validator: IntValidator {bottom: 0; top: 9999} 32 | } 33 | 34 | TextField { 35 | id: layoutHeight 36 | Kirigami.FormData.label: i18n("Layout Height:") 37 | placeholderText: i18n("") 38 | validator: IntValidator {bottom: 0; top: 9999} 39 | } 40 | 41 | CheckBox { 42 | id: showReconnectingText 43 | text: i18n("Show [Reconnecting...] text when connection lost") 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /docs/BUILD.md: -------------------------------------------------------------------------------- 1 | # Build Instructions 2 | 3 | ### KDE Plasma 4 | 5 | Lyrica only works with Plasma version 6.0 or later. 6 | 7 | Because the project uses relative path to run the backend, so desktops installed by Flatpak or Snap and distros that use non-standard home directory like NixOS are not supported. 8 | 9 | #### Fetch source code 10 | 11 | ```bash 12 | git clone https://github.com/chiyuki0325/lyrica 13 | cd lyrica 14 | git checkout $(git tag --list "v*" | tail -1) 15 | ``` 16 | 17 | #### Install dependencies 18 | 19 | Debian/Ubuntu: 20 | 21 | ```bash 22 | sudo apt install rustup jq qt6-declarative-dev qt6-websockets-dev qml6-module-qtwebsockets libdbus-1-dev 23 | rustup toolchain install stable 24 | ``` 25 | 26 | Arch Linux: 27 | 28 | ```bash 29 | sudo pacman -S --needed rustup qt6-declarative qt6-websockets jq 30 | rustup toolchain install stable 31 | ```` 32 | 33 | Fedora: 34 | 35 | ```bash 36 | sudo dnf install rustup jq qt6-qtdeclarative qt6-qtdeclarative-devel qt6-qtwebsockets qt6-qtwebsockets-devel dbus-devel 37 | rustup toolchain install stable 38 | ``` 39 | 40 | openSUSE: 41 | 42 | ```bash 43 | sudo zypper install rustup jq qt6-declarative qt6-websockets qt6-websockets-imports dbus-1-devel 44 | rustup toolchain install stable 45 | ```` 46 | 47 | #### Build 48 | 49 | ```bash 50 | bash build_plasmoid.sh 51 | ``` 52 | 53 | #### Install 54 | ```bash 55 | kpackagetool6 -i lyrica-plasmoid---.plasmoid -t Plasma/Applet 56 | ``` 57 | 58 | #### Upgrade 59 | ```bash 60 | kpackagetool6 -u lyrica-plasmoid---.plasmoid -t Plasma/Applet 61 | ``` 62 | -------------------------------------------------------------------------------- /src/config.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::sync::Arc; 3 | use tokio::sync::RwLock; 4 | 5 | // source: https://stackoverflow.com/questions/53866508 6 | macro_rules! pub_struct { 7 | ($name:ident {$($field:ident: $t:ty,)*}) => { 8 | #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] // ewww 9 | pub struct $name { 10 | $(pub $field: $t),* 11 | } 12 | } 13 | } 14 | 15 | pub_struct!(Config { 16 | verbose: bool, 17 | tlyric_mode: u8, 18 | disabled_players: Vec, 19 | enabled_lyric_providers: Vec, 20 | online_search_pattern: u8, 21 | disabled_folders: Vec, 22 | online_search_timeout: u64, 23 | online_search_retry: bool, 24 | max_retries: u8, 25 | lyric_search_folder: String, 26 | alt_folder_exists: bool, 27 | }); 28 | 29 | pub type SharedConfig = Arc>; 30 | 31 | pub fn initialize_config() -> SharedConfig { 32 | let config = Config { 33 | verbose: true, 34 | tlyric_mode: 1, 35 | // 0: always use original lyric 36 | // 1: show tlyric instead of lyric if available 37 | // 2: Lyric | TLyric 38 | // 3: TLyric | Lyric 39 | disabled_players: vec![], 40 | enabled_lyric_providers: vec![0, 1, 2, 3, 4, 5], 41 | online_search_pattern: 0, 42 | // 0: Title + Artist 43 | // 1: Title only 44 | disabled_folders: vec![], 45 | online_search_timeout: 10, 46 | online_search_retry: true, 47 | max_retries: 3, 48 | lyric_search_folder: "~/Music/lrc".to_string(), 49 | alt_folder_exists: false, 50 | }; 51 | Arc::new(RwLock::new(config)) 52 | } 53 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | mod websocket; 2 | mod lyric_parser; 3 | mod player; 4 | mod config; 5 | mod web_routes; 6 | mod lyric_providers; 7 | 8 | use actix_web::{web, App, HttpServer}; 9 | use tokio::sync::broadcast; 10 | use tokio::task::LocalSet; 11 | use crate::config::{initialize_config}; 12 | 13 | #[derive(Debug, Clone)] 14 | enum ChannelMessage { 15 | UpdateLyricLine(u128, String), // time, lyric 16 | UpdateMusicInfo(String, String), // title, artist 17 | } 18 | 19 | 20 | #[tokio::main] 21 | async fn main() -> std::io::Result<()> { 22 | let local_set = LocalSet::new(); 23 | // 在单线程环境中运行 24 | local_set 25 | .run_until(async move { 26 | let (tx, _rx) = broadcast::channel(6); 27 | // 最多同时有 3 个客户端 28 | let tx1 = tx.clone(); 29 | 30 | let config = initialize_config(); 31 | let config_clone = config.clone(); 32 | let web_data_config = web::Data::new(config); 33 | 34 | 35 | tokio::task::spawn_local(async { 36 | player::mpris_loop(tx1, config_clone).await; 37 | }); 38 | 39 | println!("Lyrica is running at port 15649"); 40 | 41 | // Start the actix-web server 42 | HttpServer::new(move || { 43 | App::new() 44 | .route("/test", web::get().to(web_routes::test_page::test_page)) 45 | .route("/config", web::get().to(web_routes::config::get_config)) 46 | .route("/config/update", web::post().to(web_routes::config::update_config)) 47 | .app_data(web::Data::new(tx.clone())) 48 | .app_data(web_data_config.clone()) 49 | .route("/ws", web::get().to(websocket::ws_index)) 50 | }) 51 | .bind("127.0.0.1:15649")? 52 | .run() 53 | .await 54 | }) 55 | .await 56 | } 57 | -------------------------------------------------------------------------------- /src/lyric_providers/yesplaymusic.rs: -------------------------------------------------------------------------------- 1 | use mpris::Metadata; 2 | use crate::lyric_parser::{ 3 | LyricLine, 4 | parse_lyrics, 5 | }; 6 | use isahc::{Request, AsyncReadResponseExt}; 7 | use isahc::HttpClient; 8 | use serde_json::Value as JsonValue; 9 | 10 | #[derive(Clone)] 11 | pub struct YesPlayMusicLyricProvider { 12 | client: HttpClient, 13 | } 14 | 15 | impl YesPlayMusicLyricProvider { 16 | pub fn new() -> Self { 17 | Self { 18 | client: HttpClient::builder() 19 | .build() 20 | .expect("No network connection") 21 | } 22 | } 23 | 24 | pub async fn get_lyric(&self, music_url: &str) -> (Vec, bool, bool) { 25 | let req = Request::get( 26 | format!("http://localhost:10754/lyric?id={}", music_url.strip_prefix("/trackid/").unwrap()) 27 | ).body(()).unwrap(); 28 | 29 | let res = self.client.send_async(req).await; 30 | 31 | if let Ok(mut res) = res { 32 | if res.status().is_success() { 33 | let lyric: JsonValue = res.json().await.unwrap(); 34 | let lyric_str; 35 | let tlyric_str; 36 | if let Some(lyric) = lyric.get("lrc").or(lyric.get("lyric")) { 37 | lyric_str = lyric.get("lyric").unwrap().as_str().unwrap(); 38 | } else { return (Vec::new(), false, true); }; 39 | if let Some(tlyric) = lyric.get("tlyric") { 40 | tlyric_str = tlyric.get("lyric").unwrap().as_str().unwrap(); 41 | (parse_lyrics(format!("{}\n{}", lyric_str, tlyric_str)), true, false) 42 | } else { 43 | (parse_lyrics(lyric_str.to_string()), true, false) 44 | } 45 | } else { (Vec::new(), false, true) } 46 | } else { (Vec::new(), false, true) } 47 | } 48 | 49 | pub fn is_available(&self, url: &str, metadata: &Metadata) -> bool { 50 | if url.starts_with("/trackid/") { 51 | return if let Some(track_id) = metadata.track_id() { 52 | track_id.as_str().starts_with("/org/node/mediaplayer/yesplaymusic") 53 | } else { false }; 54 | } 55 | false 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /plasmoid/contents/config/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 60 11 | 12 | 13 | true 14 | 15 | 16 | 18 17 | 18 | 19 | 28 20 | 21 | 22 | true 23 | 24 | 25 | 26 | 27 | 28 | false 29 | 30 | 31 | 0 32 | 33 | 34 | 35 | 36 | 37 | 38 | firefox,chromium,plasma-browser-integration,kdeconnect 39 | 40 | 41 | mpris2_text,file,yesplaymusic,netease_trackid,feeluown_netease,netease 42 | 43 | 44 | 45 | 46 | 47 | 0 48 | 49 | 50 | 51 | 52 | 10 53 | 54 | 55 | true 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/lyric_providers/netease_trackid.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use isahc::HttpClient; 3 | use isahc::prelude::Configurable; 4 | use mpris::Metadata; 5 | use crate::lyric_parser::{ 6 | LyricLine, 7 | parse_netease_lyrics, 8 | }; 9 | 10 | #[derive(Clone)] 11 | pub struct NeteaseTrackIDLyricProvider {} 12 | 13 | 14 | impl NeteaseTrackIDLyricProvider { 15 | pub async fn get_lyric_by_metadata( 16 | &self, 17 | metadata: &Metadata, 18 | config: crate::config::SharedConfig, 19 | ) -> (Vec, bool, bool) { 20 | let client = HttpClient::builder() 21 | .timeout(Duration::from_secs( 22 | config.read().await.online_search_timeout 23 | )) 24 | .cookies() 25 | .build() 26 | .expect("初始化网络请求失败!"); 27 | let ncm_api = ncm_api::MusicApi::from_client(client); 28 | if let Some(track_id) = metadata.track_id() { 29 | // let music_id = track_id.rsplit("/").next().unwrap().parse::().unwrap(); 30 | if let Ok(music_id) = track_id.as_str().rsplit("/").next().unwrap().parse::() { 31 | let mut success = !config.read().await.online_search_retry; 32 | let mut try_count = 0; 33 | let max_retries = config.read().await.max_retries; 34 | 35 | #[allow(unused_assignments)] 36 | while !success && try_count < max_retries { 37 | println!("Trying to get lyric for track_id: {}", music_id); 38 | let lyric_result = ncm_api.song_lyric(music_id).await; 39 | if let Ok(lyric_result) = lyric_result { 40 | success = true; 41 | let lyric_lines = lyric_result.lyric; 42 | let tlyric_lines = lyric_result.tlyric; 43 | return ( 44 | parse_netease_lyrics(lyric_lines, tlyric_lines), 45 | true, false 46 | ); 47 | } else { 48 | try_count += 1; 49 | } 50 | } 51 | return (Vec::new(), false, true); // 达到最大重试次数 52 | } 53 | } 54 | (Vec::new(), false, true) 55 | } 56 | 57 | pub fn is_available_by_metadata(&self, metadata: &Metadata) -> bool { 58 | metadata.track_id().is_some() && metadata.track_id().unwrap().as_str().rsplit("/").next().unwrap().parse::().is_ok() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/lyric_providers.rs: -------------------------------------------------------------------------------- 1 | use mpris::Metadata; 2 | use lazy_static::lazy_static; 3 | 4 | pub mod file; 5 | pub mod netease; 6 | pub mod mpris2_text; 7 | pub mod yesplaymusic; 8 | pub mod feeluown_netease; 9 | pub mod netease_trackid; 10 | 11 | use crate::lyric_parser::LyricLine; 12 | 13 | pub enum LyricProvider { 14 | File(file::FileLyricProvider), 15 | Netease(netease::NeteaseLyricProvider), 16 | Mpris2Text(mpris2_text::Mpris2TextProvider), 17 | YesPlayMusic(yesplaymusic::YesPlayMusicLyricProvider), 18 | FeelUOwnNetease(feeluown_netease::FeelUOwnNeteaseLyricProvider), 19 | NeteaseTrackID(netease_trackid::NeteaseTrackIDLyricProvider), 20 | } 21 | 22 | impl LyricProvider { 23 | pub async fn get_lyric( 24 | &self, 25 | music_url: &str, 26 | metadata: &Metadata, 27 | config: crate::config::SharedConfig, 28 | ) -> (Vec, bool, bool) { 29 | match self { 30 | LyricProvider::File(provider) => provider.get_lyric(music_url, config).await, 31 | LyricProvider::Netease(provider) => provider.get_lyric_by_metadata(metadata, config).await, 32 | LyricProvider::Mpris2Text(provider) => provider.get_lyric_by_metadata(metadata).await, 33 | LyricProvider::YesPlayMusic(provider) => provider.get_lyric(music_url).await, 34 | LyricProvider::FeelUOwnNetease(provider) => provider.get_lyric(music_url).await, 35 | LyricProvider::NeteaseTrackID(provider) => provider.get_lyric_by_metadata(metadata, config).await, 36 | } 37 | } 38 | 39 | pub fn is_available(&self, music_url: &str, metadata: &Metadata) -> bool { 40 | match self { 41 | LyricProvider::File(provider) => provider.is_available(music_url), 42 | LyricProvider::Netease(_) => true, 43 | LyricProvider::Mpris2Text(provider) => provider.is_available_by_metadata(metadata), 44 | LyricProvider::YesPlayMusic(provider) => provider.is_available(music_url, metadata), 45 | LyricProvider::FeelUOwnNetease(provider) => provider.is_available(music_url), 46 | LyricProvider::NeteaseTrackID(provider) => provider.is_available_by_metadata(metadata), 47 | } 48 | } 49 | 50 | pub fn get_name(&self) -> &str { 51 | match self { 52 | LyricProvider::File(_) => "File", 53 | LyricProvider::Netease(_) => "Netease", 54 | LyricProvider::Mpris2Text(_) => "Mpris2Text", 55 | LyricProvider::YesPlayMusic(_) => "YesPlayMusic", 56 | LyricProvider::FeelUOwnNetease(_) => "FeelUOwnNetease", 57 | LyricProvider::NeteaseTrackID(_) => "NeteaseTrackID", 58 | } 59 | } 60 | } 61 | 62 | 63 | lazy_static! { 64 | pub static ref LYRIC_PROVIDERS: Vec = vec![ 65 | LyricProvider::Mpris2Text(mpris2_text::Mpris2TextProvider {}), // 0 66 | LyricProvider::File(file::FileLyricProvider {}), // 1 67 | LyricProvider::YesPlayMusic(yesplaymusic::YesPlayMusicLyricProvider::new()), // 2 68 | LyricProvider::NeteaseTrackID(netease_trackid::NeteaseTrackIDLyricProvider {}), // 3 69 | LyricProvider::FeelUOwnNetease(feeluown_netease::FeelUOwnNeteaseLyricProvider {}), // 4 70 | LyricProvider::Netease(netease::NeteaseLyricProvider {}), // 5 71 | ]; 72 | } 73 | -------------------------------------------------------------------------------- /src/lyric_parser.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | pub struct LyricLine { 3 | pub time: u128, 4 | pub lyric: String, 5 | pub tlyric: Option, 6 | } 7 | 8 | 9 | fn parse_single_line(line: String) -> Result<(u128, String), ()> { 10 | // 解析一行歌词 11 | let line = line.trim(); 12 | let line_parts: Vec<&str> = line.split(']').collect(); 13 | 14 | if line_parts.len() > 1 { 15 | // 解析时间 16 | let time_str = line_parts[0].trim_start_matches('['); 17 | let time_parts: Vec<&str> = time_str.split(':').collect(); 18 | if time_parts.len() != 2 { 19 | // 时间格式错误 20 | return Err(()); 21 | } 22 | let minute: i64 = time_parts[0].parse().unwrap_or(0); 23 | let second: f64 = time_parts[1].parse().unwrap_or(0.0); 24 | 25 | // 解析歌词 26 | let lyric_str = line_parts[1].trim().to_string(); 27 | let time = (minute * 60000000) as u128 + (second * 1000000.0) as u128; 28 | return Ok((time, lyric_str)); 29 | } 30 | Err(()) 31 | } 32 | 33 | pub(crate) fn parse_lyrics(lyric_string: String) -> Vec { 34 | let lyric_lines = lyric_string.lines(); 35 | let mut lyrics: Vec = Vec::new(); 36 | 37 | for line in lyric_lines { 38 | if let Ok((time, lyric_str)) = parse_single_line(String::from(line)) { 39 | 40 | // println!("line {}: {}", time, lyric_str); 41 | let mut idx = 0; 42 | loop { 43 | if let Some(lyric_line) = lyrics.get(idx) { 44 | if lyric_line.time == time { 45 | // 这句歌词是该歌词的翻译 46 | // println!("TLYRIC {}: {}", time, lyric_str); 47 | lyrics[idx].tlyric = Some(lyric_str); 48 | break; 49 | } else if time < lyric_line.time { 50 | // 是新的一句歌词 51 | // println!("APPEND {}: {}", time, lyric_str); 52 | lyrics.push(LyricLine { 53 | time, 54 | lyric: lyric_str, 55 | tlyric: None, 56 | }); 57 | break; 58 | } else { 59 | // println!("SKIPPING {}: {}", lyric_line.time, lyric_line.lyric); 60 | idx += 1; 61 | } 62 | } else { 63 | // println!("APPEND {}: {}", time, lyric_str); 64 | lyrics.push(LyricLine { 65 | time, 66 | lyric: lyric_str.clone(), 67 | tlyric: None, 68 | }); 69 | break; 70 | } 71 | } 72 | } 73 | } 74 | 75 | lyrics 76 | } 77 | 78 | pub fn merge_lyrics(mut lyrics: Vec, tlyrics: Vec) -> Vec { 79 | let mut j = 0; 80 | 81 | for i in 0..lyrics.len() { 82 | while j < tlyrics.len() && tlyrics[j].time < lyrics[i].time { 83 | j += 1; 84 | } 85 | if j < tlyrics.len() && tlyrics[j].time == lyrics[i].time { 86 | lyrics[i].tlyric = Some(tlyrics[j].lyric.clone()); 87 | } 88 | } 89 | 90 | lyrics 91 | } 92 | 93 | pub(crate) fn parse_netease_lyrics( 94 | lyric_lines: Vec, 95 | tlyric_lines: Vec, 96 | ) -> Vec { 97 | if tlyric_lines.len() > 0 { 98 | let lyrics = parse_lyrics(lyric_lines.join("\n")); 99 | let tlyrics = parse_lyrics(tlyric_lines.join("\n")); 100 | merge_lyrics(lyrics, tlyrics) 101 | } else { 102 | parse_lyrics(lyric_lines.join("\n")) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/websocket.rs: -------------------------------------------------------------------------------- 1 | use actix::{Actor, AsyncContext, StreamHandler, Handler}; 2 | use actix_web::{web, HttpRequest, HttpResponse, Error}; 3 | use actix_web_actors::ws; 4 | use tokio::sync::broadcast; 5 | use serde::Serialize; 6 | 7 | use crate::ChannelMessage; 8 | use crate::config::SharedConfig; 9 | 10 | #[derive(Debug, Clone, Serialize)] 11 | pub(crate) struct WebSocketPacket { 12 | pub id: u8, 13 | pub data: WebSocketPacketData, 14 | } 15 | 16 | #[derive(Debug, Clone, Serialize)] 17 | #[serde(rename_all = "snake_case")] 18 | pub(crate) enum WebSocketPacketData { 19 | LyricLine(UpdateLyricLinePacket), 20 | MusicInfo(UpdateMusicInfoPacket), 21 | } 22 | 23 | #[derive(Debug, Clone, Serialize)] 24 | pub(crate) struct UpdateLyricLinePacket { 25 | lyric: String, 26 | time: u128, 27 | } 28 | 29 | #[derive(Debug, Clone, Serialize)] 30 | pub(crate) struct UpdateMusicInfoPacket { 31 | title: String, 32 | artist: String, 33 | } 34 | 35 | pub(crate) struct LyricaSocket { 36 | rx: broadcast::Receiver, 37 | config: SharedConfig, 38 | } 39 | 40 | impl Actor for LyricaSocket { 41 | type Context = ws::WebsocketContext; 42 | fn started(&mut self, ctx: &mut Self::Context) { 43 | println!("WebSocket connection established"); 44 | 45 | let mut rx = self.rx.resubscribe(); 46 | let ctx_address = ctx.address(); 47 | 48 | 49 | ctx_address.do_send(ChannelMessage::UpdateMusicInfo( 50 | String::new(), 51 | String::new(), 52 | )); 53 | 54 | let fut = async move { 55 | while let Ok(msg) = rx.recv().await { 56 | ctx_address.do_send(msg); 57 | } 58 | }; 59 | ctx.spawn(actix::fut::wrap_future(fut)); 60 | } 61 | } 62 | 63 | impl actix::Message for ChannelMessage { 64 | type Result = (); 65 | } 66 | 67 | impl Handler for LyricaSocket { 68 | type Result = (); 69 | 70 | fn handle(&mut self, msg: ChannelMessage, ctx: &mut Self::Context) { 71 | match msg { 72 | ChannelMessage::UpdateLyricLine(time, lyric) => { 73 | /* 74 | if self.config.read().await.verbose { 75 | println!("[{time}] {lyric}"); 76 | } 77 | */ 78 | 79 | let packet = WebSocketPacket { 80 | id: 1, 81 | data: WebSocketPacketData::LyricLine(UpdateLyricLinePacket { 82 | lyric, time, 83 | }), 84 | }; 85 | ctx.text(serde_json::to_string(&packet).unwrap()); 86 | } 87 | ChannelMessage::UpdateMusicInfo(title, artist) => { 88 | /* 89 | if self.config.read().unwrap().verbose { 90 | println!("[{title} - {artist}]"); 91 | } 92 | */ 93 | 94 | let packet = WebSocketPacket { 95 | id: 0, 96 | data: WebSocketPacketData::MusicInfo(UpdateMusicInfoPacket { 97 | title, artist, 98 | }), 99 | }; 100 | ctx.text(serde_json::to_string(&packet).unwrap()); 101 | } 102 | } 103 | } 104 | } 105 | 106 | /// Handler for ws::Message message 107 | impl StreamHandler> for LyricaSocket { 108 | fn handle(&mut self, msg: Result, ctx: &mut Self::Context) { 109 | match msg { 110 | Ok(ws::Message::Ping(msg)) => ctx.pong(&msg), 111 | Ok(ws::Message::Text(text)) => ctx.text(text), 112 | Ok(ws::Message::Binary(bin)) => ctx.binary(bin), 113 | _ => (), 114 | } 115 | } 116 | } 117 | 118 | pub(crate) async fn ws_index( 119 | req: HttpRequest, 120 | stream: web::Payload, 121 | tx: web::Data>, 122 | config: web::Data, 123 | ) -> Result { 124 | ws::start(LyricaSocket { 125 | rx: tx.subscribe(), 126 | config: config.get_ref().clone(), 127 | }, &req, stream) 128 | } 129 | -------------------------------------------------------------------------------- /examples/lyrica_obs_plugin.py: -------------------------------------------------------------------------------- 1 | # Lyrica OBS Plugin example. 2 | # Reference: https://obsproject.com/forum/threads/help-updating-text-file-read-rate-to-be-faster.171431/ 3 | 4 | import obspython as obs 5 | from typing import Optional 6 | import threading 7 | import asyncio 8 | import websockets 9 | import json 10 | 11 | 12 | _LOOP: Optional[asyncio.AbstractEventLoop] = None 13 | _THREAD: Optional[threading.Thread] = None 14 | 15 | 16 | interval: str = 50 17 | source_name: str = "" 18 | 19 | metadata: str = "" 20 | lyric_line: str = "" 21 | 22 | # ------------------------------------------------------------ 23 | 24 | def update_text(): 25 | global source_name 26 | 27 | source = obs.obs_get_source_by_name(source_name) 28 | if source is not None: 29 | 30 | settings = obs.obs_data_create() 31 | obs.obs_data_set_string(settings, "text", metadata + "\n" + lyric_line) 32 | obs.obs_source_update(source, settings) 33 | obs.obs_data_release(settings) 34 | 35 | obs.obs_source_release(source) 36 | 37 | def refresh_pressed(props, prop): 38 | update_text() 39 | 40 | # ------------------------------------------------------------ 41 | 42 | def script_description(): 43 | return "Lyrica OBS Plugin." 44 | 45 | def lyrica_thread(): 46 | global metadata, lyric_line 47 | async def listen(): 48 | global metadata, lyric_line 49 | async with websockets.connect("ws://127.0.0.1:15649/ws") as websocket: 50 | while True: 51 | message = json.loads(await websocket.recv()) 52 | match message["id"]: 53 | case 0: 54 | metadata = "Now Playing: " + message["data"]["music_info"]["artist"] + " - " + message["data"]["music_info"]["title"] 55 | lyric_line = "" 56 | case 1: 57 | lyric_line = message["data"]["lyric_line"]["lyric"] 58 | _LOOP = asyncio.new_event_loop() 59 | asyncio.set_event_loop(_LOOP) 60 | 61 | # Start anything that needs the async loop. The call to run_forever may 62 | # not be needed, depends on the way the loop dependent components are 63 | # started. 64 | _LOOP.run_until_complete(listen()) 65 | 66 | # Stop anything that is running on the loop before closing. Most likely 67 | # using the loop run_until_complete function 68 | _LOOP.close() 69 | _LOOP = None 70 | 71 | 72 | 73 | def script_load(settings): 74 | _THREAD = threading.Thread(None, lyrica_thread, daemon=True) 75 | _THREAD.start() 76 | 77 | def script_unload(): 78 | if _LOOP is not None: 79 | _LOOP.call_soon_threadsafe(lambda l: l.stop(), _LOOP) 80 | 81 | if _THREAD is not None: 82 | # Wait for 5 seconds, if it doesn't exit just move on not to block 83 | # OBS main thread. Logging something about the failure to properly exit 84 | # is advised. 85 | _THREAD.join(timeout=5) 86 | _THREAD = None 87 | 88 | def script_update(settings): 89 | global interval 90 | global source_name 91 | 92 | interval = obs.obs_data_get_int(settings, "interval") 93 | source_name = obs.obs_data_get_string(settings, "source") 94 | 95 | if source_name != "": 96 | obs.timer_add(update_text, interval) 97 | 98 | def script_defaults(settings): 99 | obs.obs_data_set_default_int(settings, "interval", 1000) 100 | 101 | def script_properties(): 102 | props = obs.obs_properties_create() 103 | 104 | obs.obs_properties_add_int(props, "interval", "Update Interval (Milliseconds)", 1, 3600, 1) 105 | 106 | p = obs.obs_properties_add_list(props, "source", "Text Source", obs.OBS_COMBO_TYPE_EDITABLE, obs.OBS_COMBO_FORMAT_STRING) 107 | sources = obs.obs_enum_sources() 108 | if sources is not None: 109 | for source in sources: 110 | source_id = obs.obs_source_get_unversioned_id(source) 111 | if source_id in ["text_gdiplus", "text_ft2_source"]: 112 | name = obs.obs_source_get_name(source) 113 | obs.obs_property_list_add_string(p, name, name) 114 | 115 | obs.source_list_release(sources) 116 | 117 | obs.obs_properties_add_button(props, "button", "Refresh", refresh_pressed) 118 | return props 119 | -------------------------------------------------------------------------------- /src/lyric_providers/file.rs: -------------------------------------------------------------------------------- 1 | use url::Url; 2 | use std::path::Path; 3 | use id3::Tag as ID3Tag; 4 | use crate::lyric_parser::{ 5 | LyricLine, 6 | parse_lyrics, 7 | }; 8 | use metaflac::Tag as FLACTag; 9 | 10 | #[derive(Clone)] 11 | pub struct FileLyricProvider {} 12 | 13 | impl FileLyricProvider { 14 | pub async fn get_lyric( 15 | &self, 16 | music_url: &str, 17 | config: crate::config::SharedConfig 18 | ) -> (Vec, bool, bool) { 19 | match Self::parse_file_url(music_url) { 20 | Ok(path) => { 21 | // 此时得到了音乐文件的路径 22 | if config.read().await.disabled_folders.contains(&path) { 23 | // 文件夹被禁用 24 | return (Vec::new(), false, false); 25 | } 26 | if let Ok(lyric) = Self::read_tag_lyric(&path) { 27 | // 读取 tag 成功 28 | (parse_lyrics(lyric), true, false) 29 | } else { 30 | // 音乐没有 tag,直接读取 lrc 31 | if let Ok(lrc) = Self::read_lrc_file( 32 | &path, 33 | config.read().await.lyric_search_folder.clone(), 34 | ) { 35 | // lrc 文件存在 36 | (parse_lyrics(lrc), true, false) 37 | } else { 38 | // lrc 文件也不存在 39 | (Vec::new(), false, true) 40 | } 41 | } 42 | } 43 | Err(e) => { 44 | eprintln!("Failed to parse file URL: {}", e); 45 | (Vec::new(), false, true) 46 | } 47 | } 48 | } 49 | 50 | pub fn is_available(&self, music_url: &str) -> bool { 51 | if music_url.starts_with("file://") { 52 | true 53 | } else { 54 | false 55 | } 56 | } 57 | } 58 | 59 | impl FileLyricProvider { 60 | fn parse_file_url(url: &str) -> Result { 61 | if url.starts_with("file://") { 62 | let url = Url::parse(url).map_err(|e| format!("Failed to parse URL: {}", e))?; 63 | let path = url.to_file_path().map_err(|_| "Failed to convert URL to file path".to_string())?; 64 | path.to_str() 65 | .map(|s| s.to_string()) 66 | .ok_or_else(|| "Failed to convert path to string".to_string()) 67 | } else { 68 | Ok(url.to_string()) 69 | } 70 | } 71 | 72 | fn read_lrc_file( 73 | path: &str, 74 | search_folder: String, 75 | ) -> Result { 76 | let path = Path::new(path).with_extension("lrc"); 77 | if !path.exists() { 78 | // search in alternative folder 79 | let path = Path::new(&search_folder).join(path.file_name().unwrap()); 80 | if !path.exists() { 81 | return Err("Lrc file not found".to_string()); 82 | } 83 | } 84 | std::fs::read_to_string(path.to_str().unwrap()) 85 | .map_err(|e| format!("Failed to read file: {}", e)) 86 | } 87 | 88 | fn read_tag_lyric(path: &str) -> Result { 89 | match Path::new(path).extension() { 90 | Some(ext) => { 91 | let lower_ext = ext.to_str().unwrap().to_ascii_lowercase(); 92 | if lower_ext == "mp3" { 93 | Self::read_id3_tag_lyric(path) 94 | } else if lower_ext == "flac" { 95 | Self::read_flac_tag_lyric(path) 96 | } else { 97 | Err("Unsupported file type".to_string()) 98 | } 99 | } 100 | None => Err("Failed to get file extension".to_string()) 101 | } 102 | } 103 | 104 | fn read_id3_tag_lyric(path: &str) -> Result { 105 | let tag = ID3Tag::read_from_path(path).map_err(|e| format!("Failed to read tag: {}", e))?; 106 | for lyric_tag in tag.lyrics() { 107 | return Ok(lyric_tag.text.clone()); 108 | } 109 | Err("Lyric tag not found".to_string()) 110 | } 111 | 112 | fn read_flac_tag_lyric(path: &str) -> Result { 113 | if let Ok(tag) = FLACTag::read_from_path(path) { 114 | if let Some(lyric_tags) = tag.get_vorbis("LYRICS") { 115 | Ok(lyric_tags.collect::>().join("\n")) 116 | } else { 117 | Err("Lyrics tag not found".to_string()) 118 | } 119 | } else { 120 | Err("FLAC tag not found".to_string()) 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/lyric_providers/netease.rs: -------------------------------------------------------------------------------- 1 | use mpris::Metadata; 2 | use serde_json::{ 3 | Value, 4 | from_str as from_json_str, 5 | }; 6 | use std::time::Duration; 7 | use isahc::HttpClient; 8 | use isahc::prelude::Configurable; 9 | use crate::lyric_parser::{ 10 | LyricLine, 11 | parse_netease_lyrics, 12 | }; 13 | 14 | #[derive(Clone)] 15 | pub struct NeteaseLyricProvider {} 16 | 17 | 18 | impl NeteaseLyricProvider { 19 | pub async fn get_lyric_by_metadata( 20 | &self, 21 | metadata: &Metadata, 22 | config: crate::config::SharedConfig, 23 | ) -> (Vec, bool, bool) { 24 | let _config = config.read().await; 25 | let client = HttpClient::builder() 26 | .timeout(Duration::from_secs( 27 | _config.online_search_timeout 28 | )) 29 | .cookies() 30 | .build() 31 | .expect("初始化网络请求失败!"); 32 | let ncm_api = ncm_api::MusicApi::from_client(client); 33 | let title = metadata.title().unwrap_or_default().to_string(); 34 | let artist = metadata.artists().unwrap_or_default().get(0).unwrap_or(&"").to_string(); 35 | let search_result = ncm_api.search( 36 | match _config.online_search_pattern { 37 | 0 => title + " " + &artist, 38 | 1 => title, 39 | _ => String::new(), 40 | }, 41 | 1, // 单曲 42 | 0, 43 | 5, 44 | ).await; 45 | 46 | if let Ok(search_result) = search_result { 47 | // 搜索有结果 48 | let search_result = from_json_str(&search_result); 49 | if search_result.is_err() { 50 | return (Vec::new(), false, true); 51 | } 52 | let search_result: Value = search_result.unwrap(); 53 | for song in search_result["result"]["songs"].as_array().unwrap_or(&Vec::new()) { 54 | if let Some(name) = song.get("name") { 55 | let lower_searched_name = name.as_str().unwrap_or_default().to_ascii_lowercase(); 56 | let lower_metadata_name = metadata.title().unwrap_or_default().to_ascii_lowercase(); 57 | // 此比较方法可以使带(翻唱版)等后缀的歌曲也匹配成功 58 | if lower_searched_name.starts_with(&lower_metadata_name) || lower_metadata_name.starts_with(&lower_searched_name) { 59 | let searched_length = Duration::from_millis(song["duration"].as_u64().unwrap()); 60 | let music_length = metadata.length().unwrap_or_default(); 61 | if music_length.checked_sub(searched_length).unwrap_or_default() < Duration::from_secs(6) { 62 | // 相差不超过 6 秒 63 | 64 | let mut success = !_config.online_search_retry; 65 | let mut try_count = 0; 66 | let max_retries = _config.max_retries; 67 | 68 | #[allow(unused_assignments)] 69 | while !success && try_count < max_retries { 70 | let lyric_result = ncm_api.song_lyric(song["id"].as_u64().unwrap()).await; 71 | if let Ok(lyric_result) = lyric_result { 72 | success = true; 73 | let lyric_lines = lyric_result.lyric; 74 | if (lyric_lines.is_empty()) || ( 75 | lyric_lines.len() == 1 && lyric_lines[0].ends_with("纯音乐,欣赏") 76 | // 纯音乐 77 | ) { 78 | // 没有歌词 79 | return (Vec::new(), false, true); 80 | } 81 | let tlyric_lines = lyric_result.tlyric; 82 | return ( 83 | parse_netease_lyrics(lyric_lines, tlyric_lines), 84 | true, false 85 | ); 86 | } else { 87 | try_count += 1; 88 | } 89 | } 90 | return (Vec::new(), false, true); // 达到最大重试次数 91 | } 92 | } 93 | } 94 | } 95 | (Vec::new(), false, true) 96 | } else { 97 | // 搜索没结果 98 | (Vec::new(), false, true) 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /plasmoid/contents/ui/configBackend.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2 | import QtQuick.Controls 3 | import QtQuick.Layouts 1.12 4 | import org.kde.kirigami as Kirigami 5 | 6 | Kirigami.FormLayout { 7 | id: page 8 | 9 | property int cfg_tlyricMode: 0 10 | property int cfg_onlineSearchPattern: 0 11 | property alias cfg_verbose: verbose.checked 12 | property alias cfg_disabledPlayers: disabledPlayers.text 13 | property alias cfg_enabledLyricProviders: enabledLyricProviders.text 14 | property alias cfg_disabledFolders: disabledFolders.text 15 | property alias cfg_lyricSearchFolder: lyricSearchFolder.text 16 | 17 | property alias cfg_onlineSearchTimeout: onlineSearchTimeout.text 18 | property alias cfg_onlineSearchRetry: onlineSearchRetry.checked 19 | 20 | Label { 21 | text: i18n('Note that the backend settings will share among all the Lyrica widgets.\nUsing only one widget is recommended.') 22 | font.bold: true 23 | } 24 | Label { 25 | text: i18n('After saving settings, right-click the widget\nthen select "Reload" to make the changes take effect.\n') 26 | font.bold: true 27 | } 28 | 29 | Label { 30 | text: i18n('Lyric translation mode:') 31 | } 32 | 33 | ComboBox { 34 | id: tlyricMode 35 | textRole: 'label' 36 | model: [ 37 | { 38 | 'label': i18n('Show original lyric only'), 39 | 'value': 0 40 | }, 41 | { 42 | 'label': i18n('Show translation only'), 43 | 'value': 1 44 | }, 45 | { 46 | 'label': i18n('Original lyric | translation'), 47 | 'value': 2 48 | }, 49 | { 50 | 'label': i18n('Translation | original lyric'), 51 | 'value': 3 52 | } 53 | ] 54 | onCurrentIndexChanged: cfg_tlyricMode = model[currentIndex]['value'] 55 | 56 | Component.onCompleted: { 57 | for (var i = 0; i < model.length; i++) { 58 | if (model[i]['value'] == plasmoid.configuration.tlyricMode) { 59 | tlyricMode.currentIndex = i 60 | } 61 | } 62 | } 63 | 64 | property string currentVal: model[currentIndex]['value'] 65 | } 66 | 67 | CheckBox { 68 | id: verbose 69 | text: i18n("Show detailed logs in the journal") 70 | } 71 | 72 | TextField { 73 | id: disabledPlayers 74 | Kirigami.FormData.label: i18n("Disabled players (comma separated):") 75 | placeholderText: i18n("firefox,chromium,plasma-browser-integration,kdeconnect") 76 | } 77 | 78 | TextField { 79 | id: enabledLyricProviders 80 | Kirigami.FormData.label: i18n("Enabled lyric providers (comma separated):") 81 | placeholderText: i18n("mpris2_text,file,yesplaymusic,feeluown_netease,netease") 82 | } 83 | 84 | Label { 85 | text: i18n('(For available providers, see the project\'s GitHub page.)') 86 | onLinkActivated: Qt.openUrlExternally(link) 87 | } 88 | 89 | Label { 90 | text: i18n('Online lyric search pattern:') 91 | } 92 | 93 | ComboBox { 94 | id: onlineSearchPattern 95 | textRole: 'label' 96 | model: [ 97 | { 98 | 'label': i18n('Title + Artist'), 99 | 'value': 0 100 | }, 101 | { 102 | 'label': i18n('Title only (may not accurate)'), 103 | 'value': 1 104 | } 105 | ] 106 | onCurrentIndexChanged: cfg_onlineSearchPattern = model[currentIndex]['value'] 107 | 108 | Component.onCompleted: { 109 | for (var i = 0; i < model.length; i++) { 110 | if (model[i]['value'] == plasmoid.configuration.onlineSearchPattern) { 111 | onlineSearchPattern.currentIndex = i 112 | } 113 | } 114 | } 115 | 116 | property string currentVal: model[currentIndex]['value'] 117 | } 118 | 119 | TextField { 120 | id: onlineSearchTimeout 121 | Kirigami.FormData.label: i18n("Online search timeout (seconds):") 122 | placeholderText: i18n("10") 123 | validator: IntValidator {bottom: 0; top: 500} 124 | } 125 | 126 | CheckBox { 127 | id: onlineSearchRetry 128 | text: i18n("Retry online search if failed") 129 | } 130 | 131 | TextArea { 132 | id: disabledFolders 133 | Kirigami.FormData.label: i18n("Disabled folders (one per line):\nMusics in these folders will be treated as instrumental and won't be searched for lyrics.") 134 | placeholderText: i18n("/home/user/Music/lyric\n/home/user/Music/lyric2") 135 | } 136 | 137 | TextArea { 138 | id: lyricSearchFolder 139 | Kirigami.FormData.label: i18n("Alternative folder to search for .lrc files:") 140 | placeholderText: i18n("/home/user/Music/lrc") 141 | } 142 | 143 | } 144 | -------------------------------------------------------------------------------- /plasmoid/contents/ui/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.15 2 | import QtQuick.Layouts 1.1 3 | import org.kde.plasma.plasmoid 4 | import org.kde.plasma.core as PlasmaCore 5 | import org.kde.plasma.plasma5support as Plasma5Support 6 | import QtWebSockets 7 | 8 | PlasmoidItem { 9 | id: root 10 | preferredRepresentation: fullRepresentation 11 | fullRepresentation: Item { 12 | id: oneLineLayout 13 | anchors.fill: parent 14 | Layout.minimumWidth: text.contentWidth 15 | Layout.minimumHeight: plasmoid.configuration.layoutHeight 16 | Layout.preferredWidth: Layout.minimumWidth 17 | 18 | 19 | function updateLayoutSize() { 20 | Layout.minimumWidth = text.contentWidth 21 | } 22 | 23 | Timer { 24 | id: timer 25 | } 26 | 27 | function delay(delayTime,cb) { 28 | timer.interval = delayTime 29 | timer.repeat = false 30 | timer.triggered.connect(cb) 31 | timer.start() 32 | } 33 | 34 | WebSocket { 35 | id: socket 36 | url: "ws://127.0.0.1:15649/ws" 37 | onTextMessageReceived: (message) => { 38 | message = JSON.parse(message) 39 | switch (message.id) { 40 | case 0: 41 | // Update music metadata 42 | text.text = "" 43 | updateLayoutSize() 44 | break 45 | case 1: 46 | // Update lyric line 47 | let lyric = message.data.lyric_line.lyric || "" 48 | if (lyric.length > plasmoid.configuration.characterLimit) { 49 | lyric = lyric.slice(0, plasmoid.configuration.characterLimit) + "..." 50 | } 51 | text.text = lyric 52 | updateLayoutSize() 53 | break 54 | } 55 | } 56 | onStatusChanged: (status) => { 57 | if (status == WebSocket.Closed || status == WebSocket.Error) { 58 | if (plasmoid.configuration.showReconnectingText) { 59 | text.text = "[" + i18n("Reconnecting") + "...]" 60 | } else { 61 | text.text = "" 62 | } 63 | updateLayoutSize() 64 | socket.active = false 65 | delay(500, () => { 66 | if (socket.active == false) { 67 | socket.active = true 68 | } 69 | }) 70 | } else if (status == WebSocket.Open) { 71 | // Send config 72 | const providerMap = Object.assign({ 73 | "mpris2_text": 0, 74 | "file": 1, 75 | "yesplaymusic": 2, 76 | "netease_trackid": 3, 77 | "feeluown_netease": 4, 78 | "netease": 5 79 | }) 80 | const configString = JSON.stringify({ 81 | verbose: plasmoid.configuration.verbose, 82 | tlyric_mode: plasmoid.configuration.tlyricMode, 83 | disabled_players: plasmoid.configuration.disabledPlayers.split(","), 84 | enabled_lyric_providers: plasmoid.configuration.enabledLyricProviders.split(",").map(p => providerMap[p]), 85 | online_search_pattern: plasmoid.configuration.onlineSearchPattern, 86 | disabled_folders: plasmoid.configuration.disabledFolders.split("\n"), 87 | online_search_timeout: plasmoid.configuration.onlineSearchTimeout, 88 | online_search_retry: plasmoid.configuration.onlineSearchRetry, 89 | max_retries: 3, // Not configurable 90 | lyric_search_folder: plasmoid.configuration.lyricSearchFolder || "~/User/Music/lrc", 91 | alt_folder_exists: false, 92 | }) 93 | const xhr = new XMLHttpRequest() 94 | console.log("[lyrica] Updating config") 95 | xhr.open("POST", "http://127.0.0.1:15649/config/update", true) 96 | xhr.setRequestHeader("Content-Type", "application/json") 97 | xhr.onreadystatechange = () => { 98 | if (xhr.readyState == 4) { 99 | console.log("[lyrica]" + xhr.responseText) 100 | } 101 | } 102 | xhr.send(configString) 103 | } 104 | } 105 | active: false 106 | } 107 | 108 | Item { 109 | id: offsetItem 110 | width: 0 111 | height: parent.height 112 | x: 0 113 | y: 0 114 | } 115 | 116 | Text { 117 | property int fontSize: { 118 | return (plasmoid.configuration.shouldUseDefaultThemeFontSize) 119 | ? PlasmaCore.Theme.defaultFont.pixelSize 120 | : plasmoid.configuration.configuredFontSize 121 | } 122 | id: text 123 | text: "" 124 | height: plasmoid.configuration.layoutHeight 125 | verticalAlignment: Text.AlignVCenter 126 | font.pixelSize: fontSize 127 | color: PlasmaCore.Theme.textColor 128 | } 129 | 130 | Plasmoid.contextualActions: [ 131 | PlasmaCore.Action { 132 | text: i18n("Reload configuration") 133 | icon.name: "view-refresh-symbolic" 134 | priority: PlasmaCore.Action.LowPriority 135 | onTriggered: { 136 | socket.active = false 137 | delay(200, () => { 138 | socket.active = true 139 | }) 140 | } 141 | }, 142 | 143 | PlasmaCore.Action { 144 | text: i18n("Restart Lyrica") 145 | icon.name: "collapse-all-symbolic" 146 | priority: PlasmaCore.Action.LowPriority 147 | onTriggered: { 148 | backendExecutable.disconnectSource(backendExecutable.command) 149 | commandLine.connectSource("bash -c 'killall lyrica --signal=SIGKILL'") 150 | delay(100, () => { 151 | backendExecutable.connectSource(backendExecutable.command) 152 | }) 153 | } 154 | } 155 | ] 156 | 157 | 158 | Plasma5Support.DataSource { 159 | id: backendExecutable 160 | readonly property string command: "bash -c '$HOME/.local/share/plasma/plasmoids/ink.chyk.LyricaPlasmoid/contents/bin/lyrica'" 161 | engine: "executable" 162 | connectedSources: [] 163 | onSourceConnected: { 164 | socket.active = true 165 | } 166 | } 167 | 168 | Plasma5Support.DataSource { 169 | id: commandLine 170 | engine: "executable" 171 | connectedSources: [] 172 | } 173 | 174 | Component.onCompleted: { 175 | backendExecutable.connectSource(backendExecutable.command) 176 | // TODO: use relative path 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /src/player.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | use tokio::sync::broadcast; 3 | use tokio::time::sleep; 4 | use crate::{ChannelMessage, lyric_providers}; 5 | use crate::config::SharedConfig; 6 | use crate::lyric_parser::{ 7 | LyricLine, 8 | }; 9 | 10 | struct MprisInfo { 11 | url: String, 12 | is_lyric: bool, 13 | player_running: bool, 14 | } 15 | 16 | // type MprisCache = Arc>; 17 | 18 | pub async fn mpris_loop( 19 | tx: broadcast::Sender, 20 | config: SharedConfig, 21 | ) { 22 | let mut cache = MprisInfo { 23 | url: String::new(), 24 | is_lyric: false, 25 | player_running: false, 26 | }; 27 | let player_finder = mpris::PlayerFinder::new().expect("Failed to create player finder"); 28 | 29 | loop { 30 | for player in player_finder.iter_players().expect("Failed to connect to D-Bus") { 31 | 32 | if player.is_err() { 33 | // 播放器已经关闭(此代码块可能会被执行多次,此时 player 一直是 DBusError) 34 | if cache.player_running { 35 | if config.read().await.verbose { 36 | println!("Player closed, exiting loop..."); 37 | } 38 | tx.send(ChannelMessage::UpdateMusicInfo("".to_string(), "".to_string())).unwrap(); 39 | // 跳出 loop 块,继续等待下一个播放器 40 | cache.player_running = false; 41 | } 42 | continue; 43 | } 44 | 45 | let player = player.unwrap(); 46 | 47 | // 得到播放器,进入循环 48 | if config.read().await.verbose { 49 | println!("New player connected: {:?}", player.bus_name()); 50 | } 51 | let player_name = String::from(player.bus_name()); 52 | let player_name = player_name.strip_prefix("org.mpris.MediaPlayer2.").unwrap(); 53 | 54 | let mut is_disabled = false; 55 | for disabled_player in config.read().await.disabled_players.iter() { 56 | if player_name.starts_with(disabled_player) { 57 | if config.read().await.verbose { 58 | println!("Player {} detected, but disabled in the config.", player_name); 59 | } 60 | is_disabled = true; 61 | break; 62 | } 63 | } 64 | if is_disabled { 65 | continue; 66 | } 67 | 68 | cache.player_running = true; 69 | 70 | let mut idx = 0; 71 | let mut last_time: u128 = 0; 72 | // 这个变量是循环上一次运行时的时间,用于判断进度条是否往左拉了 73 | let mut lyric: Vec = Vec::new(); 74 | let mut tlyric_mode; 75 | 76 | loop { 77 | // 主循环,此时 player 已被移动到此大括号中 78 | match player.get_metadata() { 79 | Ok(metadata) => { 80 | // 更新设置 81 | tlyric_mode = config.read().await.tlyric_mode; 82 | 83 | // 判断歌曲是否更改 84 | 85 | let url = if let Some(_url) = metadata.url() { 86 | _url.to_string() 87 | } else { 88 | let art_url = metadata.art_url().unwrap_or_default().to_string(); 89 | if let Some(_track_id) = metadata.track_id() { 90 | let track_id = _track_id.to_string(); 91 | if track_id.contains("org/mpris") || track_id.contains("MediaPlayer2") { 92 | // KDE Connect quirk 93 | art_url 94 | } else { 95 | track_id 96 | } 97 | } else { 98 | art_url 99 | } 100 | }; 101 | 102 | if cache.url != url { 103 | // 歌曲更改 104 | if config.read().await.verbose { 105 | println!("New song detected: {}", url); 106 | } 107 | cache.url = url.clone(); 108 | 109 | 110 | let title = metadata.title().unwrap_or_default().to_string(); 111 | let artist = metadata.artists().unwrap_or_default().get(0).unwrap_or(&"").to_string(); 112 | // 这个 mpris 库可能抓不到歌手,需要额外做处理 113 | tx.send(ChannelMessage::UpdateMusicInfo( 114 | title.clone(), 115 | artist, 116 | )).unwrap(); 117 | 118 | if title.is_empty() { 119 | // 没有歌曲名,不尝试获取歌词 120 | cache.is_lyric = false; 121 | continue; 122 | } 123 | 124 | // 尝试获取歌词 125 | lyric = Vec::new(); 126 | cache.is_lyric = false; 127 | for provider_id in config.read().await.enabled_lyric_providers.iter() { 128 | if let Some(provider) = lyric_providers::LYRIC_PROVIDERS.get(provider_id.clone()) { 129 | 130 | if config.read().await.verbose { 131 | println!("Trying provider: {}", provider.get_name()); 132 | } 133 | // 由于现在使用 provider ID,因此注释掉 134 | 135 | // 这个 provider 可用 136 | if provider.is_available(&url, &metadata) { 137 | // 这个 provider 可以处理这个 URL 138 | let success; 139 | let try_next; 140 | (lyric, success, try_next) = provider.get_lyric( 141 | &url, 142 | &metadata, 143 | config.clone(), 144 | ).await; 145 | if success { 146 | // 成功获取歌词 147 | if config.read().await.verbose { 148 | println!("Got lyric from provider {}", provider.get_name()); 149 | } 150 | // 解析歌词并且存入 lyric 151 | cache.is_lyric = true; 152 | idx = 0; 153 | last_time = 0; 154 | break; 155 | } else if !try_next { 156 | // 无法获取歌词,但是不需要尝试下一个 provider 157 | // 目前此分支只会在音乐文件在 disabled_folders 中时触发 158 | break; 159 | } 160 | } 161 | } 162 | } 163 | // println!("{:?} {:?}", lyric, cache.is_lyric); 164 | } 165 | } 166 | Err(_) => { 167 | // 播放器已经关闭。 168 | if config.read().await.verbose { 169 | println!("Player closed, exiting loop..."); 170 | } 171 | tx.send(ChannelMessage::UpdateMusicInfo("".to_string(), "".to_string())).unwrap(); 172 | // 跳出 loop 块,继续等待下一个播放器 173 | cache.player_running = false; 174 | break; 175 | } 176 | } 177 | // 歌词是否变化? 178 | 179 | // 获取当前时间 180 | if cache.is_lyric { 181 | let current_time = player.get_position().unwrap_or_default().as_micros(); 182 | 183 | if current_time < last_time { 184 | // 进度条往左拉了,重置 idx 185 | idx = 0; 186 | while idx < lyric.len() && current_time >= lyric[idx].time { 187 | idx += 1; 188 | } 189 | } 190 | 191 | let line = lyric.get(idx); 192 | if let Some(line) = line { 193 | if current_time >= line.time { 194 | // 歌词变化 195 | let line_lyric = if line.tlyric.is_some() { 196 | // 有翻译 197 | let tlyric_clone = line.tlyric.clone().unwrap(); 198 | if tlyric_clone.is_empty() || tlyric_clone == line.lyric { 199 | line.lyric.clone() 200 | } else { 201 | match tlyric_mode { 202 | 1 => tlyric_clone, 203 | 2 => format!("{} | {}", line.lyric, tlyric_clone), 204 | 3 => format!("{} | {}", tlyric_clone, line.lyric), 205 | _ => line.lyric.clone(), // 0 206 | } 207 | } 208 | } else { 209 | // 没有翻译 210 | line.lyric.clone() 211 | }; 212 | 213 | tx.send(ChannelMessage::UpdateLyricLine(line.time, line_lyric)).unwrap(); 214 | while idx < lyric.len() && current_time >= lyric[idx].time { 215 | idx += 1; 216 | } 217 | } 218 | } 219 | 220 | last_time = current_time; 221 | } 222 | 223 | sleep(Duration::from_millis(100)).await; 224 | } 225 | } 226 | tokio::time::sleep(Duration::from_secs(1)).await; 227 | } 228 | } 229 | 230 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "actix" 7 | version = "0.13.5" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "de7fa236829ba0841304542f7614c42b80fca007455315c45c785ccfa873a85b" 10 | dependencies = [ 11 | "actix-macros", 12 | "actix-rt", 13 | "actix_derive", 14 | "bitflags 2.5.0", 15 | "bytes", 16 | "crossbeam-channel", 17 | "futures-core", 18 | "futures-sink", 19 | "futures-task", 20 | "futures-util", 21 | "log", 22 | "once_cell", 23 | "parking_lot", 24 | "pin-project-lite", 25 | "smallvec", 26 | "tokio", 27 | "tokio-util", 28 | ] 29 | 30 | [[package]] 31 | name = "actix-codec" 32 | version = "0.5.2" 33 | source = "registry+https://github.com/rust-lang/crates.io-index" 34 | checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" 35 | dependencies = [ 36 | "bitflags 2.5.0", 37 | "bytes", 38 | "futures-core", 39 | "futures-sink", 40 | "memchr", 41 | "pin-project-lite", 42 | "tokio", 43 | "tokio-util", 44 | "tracing", 45 | ] 46 | 47 | [[package]] 48 | name = "actix-http" 49 | version = "3.7.0" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | checksum = "4eb9843d84c775696c37d9a418bbb01b932629d01870722c0f13eb3f95e2536d" 52 | dependencies = [ 53 | "actix-codec", 54 | "actix-rt", 55 | "actix-service", 56 | "actix-utils", 57 | "ahash", 58 | "base64", 59 | "bitflags 2.5.0", 60 | "brotli", 61 | "bytes", 62 | "bytestring", 63 | "derive_more", 64 | "encoding_rs", 65 | "flate2", 66 | "futures-core", 67 | "h2", 68 | "http", 69 | "httparse", 70 | "httpdate", 71 | "itoa", 72 | "language-tags", 73 | "local-channel", 74 | "mime", 75 | "percent-encoding", 76 | "pin-project-lite", 77 | "rand 0.8.5", 78 | "sha1", 79 | "smallvec", 80 | "tokio", 81 | "tokio-util", 82 | "tracing", 83 | "zstd", 84 | ] 85 | 86 | [[package]] 87 | name = "actix-macros" 88 | version = "0.2.4" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" 91 | dependencies = [ 92 | "quote 1.0.36", 93 | "syn 2.0.66", 94 | ] 95 | 96 | [[package]] 97 | name = "actix-router" 98 | version = "0.5.3" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" 101 | dependencies = [ 102 | "bytestring", 103 | "cfg-if", 104 | "http", 105 | "regex", 106 | "regex-lite", 107 | "serde", 108 | "tracing", 109 | ] 110 | 111 | [[package]] 112 | name = "actix-rt" 113 | version = "2.9.0" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" 116 | dependencies = [ 117 | "futures-core", 118 | "tokio", 119 | ] 120 | 121 | [[package]] 122 | name = "actix-server" 123 | version = "2.3.0" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4" 126 | dependencies = [ 127 | "actix-rt", 128 | "actix-service", 129 | "actix-utils", 130 | "futures-core", 131 | "futures-util", 132 | "mio 0.8.11", 133 | "socket2", 134 | "tokio", 135 | "tracing", 136 | ] 137 | 138 | [[package]] 139 | name = "actix-service" 140 | version = "2.0.2" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" 143 | dependencies = [ 144 | "futures-core", 145 | "paste", 146 | "pin-project-lite", 147 | ] 148 | 149 | [[package]] 150 | name = "actix-utils" 151 | version = "3.0.1" 152 | source = "registry+https://github.com/rust-lang/crates.io-index" 153 | checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" 154 | dependencies = [ 155 | "local-waker", 156 | "pin-project-lite", 157 | ] 158 | 159 | [[package]] 160 | name = "actix-web" 161 | version = "4.9.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "9180d76e5cc7ccbc4d60a506f2c727730b154010262df5b910eb17dbe4b8cb38" 164 | dependencies = [ 165 | "actix-codec", 166 | "actix-http", 167 | "actix-macros", 168 | "actix-router", 169 | "actix-rt", 170 | "actix-server", 171 | "actix-service", 172 | "actix-utils", 173 | "actix-web-codegen", 174 | "ahash", 175 | "bytes", 176 | "bytestring", 177 | "cfg-if", 178 | "cookie", 179 | "derive_more", 180 | "encoding_rs", 181 | "futures-core", 182 | "futures-util", 183 | "impl-more", 184 | "itoa", 185 | "language-tags", 186 | "log", 187 | "mime", 188 | "once_cell", 189 | "pin-project-lite", 190 | "regex", 191 | "regex-lite", 192 | "serde", 193 | "serde_json", 194 | "serde_urlencoded", 195 | "smallvec", 196 | "socket2", 197 | "time", 198 | "url", 199 | ] 200 | 201 | [[package]] 202 | name = "actix-web-actors" 203 | version = "4.3.0" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "420b001bb709d8510c3e2659dae046e54509ff9528018d09c78381e765a1f9fa" 206 | dependencies = [ 207 | "actix", 208 | "actix-codec", 209 | "actix-http", 210 | "actix-web", 211 | "bytes", 212 | "bytestring", 213 | "futures-core", 214 | "pin-project-lite", 215 | "tokio", 216 | "tokio-util", 217 | ] 218 | 219 | [[package]] 220 | name = "actix-web-codegen" 221 | version = "4.3.0" 222 | source = "registry+https://github.com/rust-lang/crates.io-index" 223 | checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" 224 | dependencies = [ 225 | "actix-router", 226 | "proc-macro2", 227 | "quote 1.0.36", 228 | "syn 2.0.66", 229 | ] 230 | 231 | [[package]] 232 | name = "actix_derive" 233 | version = "0.6.1" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "7c7db3d5a9718568e4cf4a537cfd7070e6e6ff7481510d0237fb529ac850f6d3" 236 | dependencies = [ 237 | "proc-macro2", 238 | "quote 1.0.36", 239 | "syn 2.0.66", 240 | ] 241 | 242 | [[package]] 243 | name = "addr2line" 244 | version = "0.21.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 247 | dependencies = [ 248 | "gimli", 249 | ] 250 | 251 | [[package]] 252 | name = "adler" 253 | version = "1.0.2" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 256 | 257 | [[package]] 258 | name = "ahash" 259 | version = "0.8.11" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 262 | dependencies = [ 263 | "cfg-if", 264 | "getrandom 0.2.15", 265 | "once_cell", 266 | "version_check", 267 | "zerocopy", 268 | ] 269 | 270 | [[package]] 271 | name = "aho-corasick" 272 | version = "1.1.3" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 275 | dependencies = [ 276 | "memchr", 277 | ] 278 | 279 | [[package]] 280 | name = "alloc-no-stdlib" 281 | version = "2.0.4" 282 | source = "registry+https://github.com/rust-lang/crates.io-index" 283 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 284 | 285 | [[package]] 286 | name = "alloc-stdlib" 287 | version = "0.2.2" 288 | source = "registry+https://github.com/rust-lang/crates.io-index" 289 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 290 | dependencies = [ 291 | "alloc-no-stdlib", 292 | ] 293 | 294 | [[package]] 295 | name = "anyhow" 296 | version = "1.0.86" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" 299 | 300 | [[package]] 301 | name = "async-channel" 302 | version = "1.9.0" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" 305 | dependencies = [ 306 | "concurrent-queue", 307 | "event-listener", 308 | "futures-core", 309 | ] 310 | 311 | [[package]] 312 | name = "autocfg" 313 | version = "1.3.0" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" 316 | 317 | [[package]] 318 | name = "backtrace" 319 | version = "0.3.71" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" 322 | dependencies = [ 323 | "addr2line", 324 | "cc", 325 | "cfg-if", 326 | "libc", 327 | "miniz_oxide", 328 | "object", 329 | "rustc-demangle", 330 | ] 331 | 332 | [[package]] 333 | name = "base64" 334 | version = "0.22.1" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 337 | 338 | [[package]] 339 | name = "bitflags" 340 | version = "1.3.2" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 343 | 344 | [[package]] 345 | name = "bitflags" 346 | version = "2.5.0" 347 | source = "registry+https://github.com/rust-lang/crates.io-index" 348 | checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" 349 | 350 | [[package]] 351 | name = "block-buffer" 352 | version = "0.10.4" 353 | source = "registry+https://github.com/rust-lang/crates.io-index" 354 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 355 | dependencies = [ 356 | "generic-array", 357 | ] 358 | 359 | [[package]] 360 | name = "brotli" 361 | version = "6.0.0" 362 | source = "registry+https://github.com/rust-lang/crates.io-index" 363 | checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" 364 | dependencies = [ 365 | "alloc-no-stdlib", 366 | "alloc-stdlib", 367 | "brotli-decompressor", 368 | ] 369 | 370 | [[package]] 371 | name = "brotli-decompressor" 372 | version = "4.0.0" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "e6221fe77a248b9117d431ad93761222e1cf8ff282d9d1d5d9f53d6299a1cf76" 375 | dependencies = [ 376 | "alloc-no-stdlib", 377 | "alloc-stdlib", 378 | ] 379 | 380 | [[package]] 381 | name = "byteorder" 382 | version = "1.5.0" 383 | source = "registry+https://github.com/rust-lang/crates.io-index" 384 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 385 | 386 | [[package]] 387 | name = "bytes" 388 | version = "1.6.0" 389 | source = "registry+https://github.com/rust-lang/crates.io-index" 390 | checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" 391 | 392 | [[package]] 393 | name = "bytestring" 394 | version = "1.3.1" 395 | source = "registry+https://github.com/rust-lang/crates.io-index" 396 | checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" 397 | dependencies = [ 398 | "bytes", 399 | ] 400 | 401 | [[package]] 402 | name = "castaway" 403 | version = "0.1.2" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "a2698f953def977c68f935bb0dfa959375ad4638570e969e2f1e9f433cbf1af6" 406 | 407 | [[package]] 408 | name = "cc" 409 | version = "1.0.98" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" 412 | dependencies = [ 413 | "jobserver", 414 | "libc", 415 | "once_cell", 416 | ] 417 | 418 | [[package]] 419 | name = "cfg-if" 420 | version = "1.0.0" 421 | source = "registry+https://github.com/rust-lang/crates.io-index" 422 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 423 | 424 | [[package]] 425 | name = "concurrent-queue" 426 | version = "2.5.0" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" 429 | dependencies = [ 430 | "crossbeam-utils", 431 | ] 432 | 433 | [[package]] 434 | name = "convert_case" 435 | version = "0.4.0" 436 | source = "registry+https://github.com/rust-lang/crates.io-index" 437 | checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" 438 | 439 | [[package]] 440 | name = "cookie" 441 | version = "0.16.2" 442 | source = "registry+https://github.com/rust-lang/crates.io-index" 443 | checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" 444 | dependencies = [ 445 | "percent-encoding", 446 | "time", 447 | "version_check", 448 | ] 449 | 450 | [[package]] 451 | name = "cpufeatures" 452 | version = "0.2.12" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" 455 | dependencies = [ 456 | "libc", 457 | ] 458 | 459 | [[package]] 460 | name = "crc32fast" 461 | version = "1.4.2" 462 | source = "registry+https://github.com/rust-lang/crates.io-index" 463 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 464 | dependencies = [ 465 | "cfg-if", 466 | ] 467 | 468 | [[package]] 469 | name = "crossbeam-channel" 470 | version = "0.5.13" 471 | source = "registry+https://github.com/rust-lang/crates.io-index" 472 | checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" 473 | dependencies = [ 474 | "crossbeam-utils", 475 | ] 476 | 477 | [[package]] 478 | name = "crossbeam-utils" 479 | version = "0.8.20" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 482 | 483 | [[package]] 484 | name = "crypto-common" 485 | version = "0.1.6" 486 | source = "registry+https://github.com/rust-lang/crates.io-index" 487 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 488 | dependencies = [ 489 | "generic-array", 490 | "typenum", 491 | ] 492 | 493 | [[package]] 494 | name = "curl" 495 | version = "0.4.46" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "1e2161dd6eba090ff1594084e95fd67aeccf04382ffea77999ea94ed42ec67b6" 498 | dependencies = [ 499 | "curl-sys", 500 | "libc", 501 | "openssl-probe", 502 | "openssl-sys", 503 | "schannel", 504 | "socket2", 505 | "windows-sys 0.52.0", 506 | ] 507 | 508 | [[package]] 509 | name = "curl-sys" 510 | version = "0.4.73+curl-8.8.0" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "450ab250ecf17227c39afb9a2dd9261dc0035cb80f2612472fc0c4aac2dcb84d" 513 | dependencies = [ 514 | "cc", 515 | "libc", 516 | "libnghttp2-sys", 517 | "libz-sys", 518 | "openssl-sys", 519 | "pkg-config", 520 | "vcpkg", 521 | "windows-sys 0.52.0", 522 | ] 523 | 524 | [[package]] 525 | name = "darling" 526 | version = "0.14.4" 527 | source = "registry+https://github.com/rust-lang/crates.io-index" 528 | checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" 529 | dependencies = [ 530 | "darling_core", 531 | "darling_macro", 532 | ] 533 | 534 | [[package]] 535 | name = "darling_core" 536 | version = "0.14.4" 537 | source = "registry+https://github.com/rust-lang/crates.io-index" 538 | checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" 539 | dependencies = [ 540 | "fnv", 541 | "ident_case", 542 | "proc-macro2", 543 | "quote 1.0.36", 544 | "strsim", 545 | "syn 1.0.109", 546 | ] 547 | 548 | [[package]] 549 | name = "darling_macro" 550 | version = "0.14.4" 551 | source = "registry+https://github.com/rust-lang/crates.io-index" 552 | checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" 553 | dependencies = [ 554 | "darling_core", 555 | "quote 1.0.36", 556 | "syn 1.0.109", 557 | ] 558 | 559 | [[package]] 560 | name = "dbus" 561 | version = "0.9.7" 562 | source = "registry+https://github.com/rust-lang/crates.io-index" 563 | checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" 564 | dependencies = [ 565 | "libc", 566 | "libdbus-sys", 567 | "winapi", 568 | ] 569 | 570 | [[package]] 571 | name = "deranged" 572 | version = "0.3.11" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" 575 | dependencies = [ 576 | "powerfmt", 577 | ] 578 | 579 | [[package]] 580 | name = "derive_is_enum_variant" 581 | version = "0.1.1" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "d0ac8859845146979953797f03cc5b282fb4396891807cdb3d04929a88418197" 584 | dependencies = [ 585 | "heck", 586 | "quote 0.3.15", 587 | "syn 0.11.11", 588 | ] 589 | 590 | [[package]] 591 | name = "derive_more" 592 | version = "0.99.17" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" 595 | dependencies = [ 596 | "convert_case", 597 | "proc-macro2", 598 | "quote 1.0.36", 599 | "rustc_version", 600 | "syn 1.0.109", 601 | ] 602 | 603 | [[package]] 604 | name = "digest" 605 | version = "0.10.7" 606 | source = "registry+https://github.com/rust-lang/crates.io-index" 607 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 608 | dependencies = [ 609 | "block-buffer", 610 | "crypto-common", 611 | ] 612 | 613 | [[package]] 614 | name = "encoding_rs" 615 | version = "0.8.34" 616 | source = "registry+https://github.com/rust-lang/crates.io-index" 617 | checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" 618 | dependencies = [ 619 | "cfg-if", 620 | ] 621 | 622 | [[package]] 623 | name = "enum-kinds" 624 | version = "0.5.1" 625 | source = "registry+https://github.com/rust-lang/crates.io-index" 626 | checksum = "4e40a16955681d469ab3da85aaa6b42ff656b3c67b52e1d8d3dd36afe97fd462" 627 | dependencies = [ 628 | "proc-macro2", 629 | "quote 1.0.36", 630 | "syn 1.0.109", 631 | ] 632 | 633 | [[package]] 634 | name = "equivalent" 635 | version = "1.0.1" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 638 | 639 | [[package]] 640 | name = "event-listener" 641 | version = "2.5.3" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" 644 | 645 | [[package]] 646 | name = "fastrand" 647 | version = "1.9.0" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 650 | dependencies = [ 651 | "instant", 652 | ] 653 | 654 | [[package]] 655 | name = "flate2" 656 | version = "1.0.30" 657 | source = "registry+https://github.com/rust-lang/crates.io-index" 658 | checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" 659 | dependencies = [ 660 | "crc32fast", 661 | "miniz_oxide", 662 | ] 663 | 664 | [[package]] 665 | name = "fnv" 666 | version = "1.0.7" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 669 | 670 | [[package]] 671 | name = "foreign-types" 672 | version = "0.3.2" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" 675 | dependencies = [ 676 | "foreign-types-shared", 677 | ] 678 | 679 | [[package]] 680 | name = "foreign-types-shared" 681 | version = "0.1.1" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" 684 | 685 | [[package]] 686 | name = "form_urlencoded" 687 | version = "1.2.1" 688 | source = "registry+https://github.com/rust-lang/crates.io-index" 689 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 690 | dependencies = [ 691 | "percent-encoding", 692 | ] 693 | 694 | [[package]] 695 | name = "from_variants" 696 | version = "1.0.2" 697 | source = "registry+https://github.com/rust-lang/crates.io-index" 698 | checksum = "4e859c8f2057687618905dbe99fc76e836e0a69738865ef90e46fc214a41bbf2" 699 | dependencies = [ 700 | "from_variants_impl", 701 | ] 702 | 703 | [[package]] 704 | name = "from_variants_impl" 705 | version = "1.0.2" 706 | source = "registry+https://github.com/rust-lang/crates.io-index" 707 | checksum = "55a5e644a80e6d96b2b4910fa7993301d7b7926c045b475b62202b20a36ce69e" 708 | dependencies = [ 709 | "darling", 710 | "proc-macro2", 711 | "quote 1.0.36", 712 | "syn 1.0.109", 713 | ] 714 | 715 | [[package]] 716 | name = "futures-core" 717 | version = "0.3.30" 718 | source = "registry+https://github.com/rust-lang/crates.io-index" 719 | checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" 720 | 721 | [[package]] 722 | name = "futures-io" 723 | version = "0.3.30" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" 726 | 727 | [[package]] 728 | name = "futures-lite" 729 | version = "1.13.0" 730 | source = "registry+https://github.com/rust-lang/crates.io-index" 731 | checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" 732 | dependencies = [ 733 | "fastrand", 734 | "futures-core", 735 | "futures-io", 736 | "memchr", 737 | "parking", 738 | "pin-project-lite", 739 | "waker-fn", 740 | ] 741 | 742 | [[package]] 743 | name = "futures-sink" 744 | version = "0.3.30" 745 | source = "registry+https://github.com/rust-lang/crates.io-index" 746 | checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" 747 | 748 | [[package]] 749 | name = "futures-task" 750 | version = "0.3.30" 751 | source = "registry+https://github.com/rust-lang/crates.io-index" 752 | checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" 753 | 754 | [[package]] 755 | name = "futures-util" 756 | version = "0.3.30" 757 | source = "registry+https://github.com/rust-lang/crates.io-index" 758 | checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" 759 | dependencies = [ 760 | "futures-core", 761 | "futures-task", 762 | "pin-project-lite", 763 | "pin-utils", 764 | ] 765 | 766 | [[package]] 767 | name = "generic-array" 768 | version = "0.14.7" 769 | source = "registry+https://github.com/rust-lang/crates.io-index" 770 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 771 | dependencies = [ 772 | "typenum", 773 | "version_check", 774 | ] 775 | 776 | [[package]] 777 | name = "getrandom" 778 | version = "0.2.15" 779 | source = "registry+https://github.com/rust-lang/crates.io-index" 780 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 781 | dependencies = [ 782 | "cfg-if", 783 | "libc", 784 | "wasi 0.11.0+wasi-snapshot-preview1", 785 | ] 786 | 787 | [[package]] 788 | name = "getrandom" 789 | version = "0.3.3" 790 | source = "registry+https://github.com/rust-lang/crates.io-index" 791 | checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" 792 | dependencies = [ 793 | "cfg-if", 794 | "libc", 795 | "r-efi", 796 | "wasi 0.14.2+wasi-0.2.4", 797 | ] 798 | 799 | [[package]] 800 | name = "gimli" 801 | version = "0.28.1" 802 | source = "registry+https://github.com/rust-lang/crates.io-index" 803 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 804 | 805 | [[package]] 806 | name = "h2" 807 | version = "0.3.26" 808 | source = "registry+https://github.com/rust-lang/crates.io-index" 809 | checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" 810 | dependencies = [ 811 | "bytes", 812 | "fnv", 813 | "futures-core", 814 | "futures-sink", 815 | "futures-util", 816 | "http", 817 | "indexmap", 818 | "slab", 819 | "tokio", 820 | "tokio-util", 821 | "tracing", 822 | ] 823 | 824 | [[package]] 825 | name = "hashbrown" 826 | version = "0.14.5" 827 | source = "registry+https://github.com/rust-lang/crates.io-index" 828 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 829 | 830 | [[package]] 831 | name = "heck" 832 | version = "0.3.3" 833 | source = "registry+https://github.com/rust-lang/crates.io-index" 834 | checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" 835 | dependencies = [ 836 | "unicode-segmentation", 837 | ] 838 | 839 | [[package]] 840 | name = "hermit-abi" 841 | version = "0.3.9" 842 | source = "registry+https://github.com/rust-lang/crates.io-index" 843 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 844 | 845 | [[package]] 846 | name = "hex" 847 | version = "0.4.3" 848 | source = "registry+https://github.com/rust-lang/crates.io-index" 849 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 850 | 851 | [[package]] 852 | name = "http" 853 | version = "0.2.12" 854 | source = "registry+https://github.com/rust-lang/crates.io-index" 855 | checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" 856 | dependencies = [ 857 | "bytes", 858 | "fnv", 859 | "itoa", 860 | ] 861 | 862 | [[package]] 863 | name = "httparse" 864 | version = "1.8.0" 865 | source = "registry+https://github.com/rust-lang/crates.io-index" 866 | checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" 867 | 868 | [[package]] 869 | name = "httpdate" 870 | version = "1.0.3" 871 | source = "registry+https://github.com/rust-lang/crates.io-index" 872 | checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" 873 | 874 | [[package]] 875 | name = "id3" 876 | version = "1.14.0" 877 | source = "registry+https://github.com/rust-lang/crates.io-index" 878 | checksum = "55f4e785f2c700217ee82a1c727c720449421742abd5fcb2f1df04e1244760e9" 879 | dependencies = [ 880 | "bitflags 2.5.0", 881 | "byteorder", 882 | "flate2", 883 | ] 884 | 885 | [[package]] 886 | name = "ident_case" 887 | version = "1.0.1" 888 | source = "registry+https://github.com/rust-lang/crates.io-index" 889 | checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" 890 | 891 | [[package]] 892 | name = "idna" 893 | version = "0.5.0" 894 | source = "registry+https://github.com/rust-lang/crates.io-index" 895 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 896 | dependencies = [ 897 | "unicode-bidi", 898 | "unicode-normalization", 899 | ] 900 | 901 | [[package]] 902 | name = "impl-more" 903 | version = "0.1.6" 904 | source = "registry+https://github.com/rust-lang/crates.io-index" 905 | checksum = "206ca75c9c03ba3d4ace2460e57b189f39f43de612c2f85836e65c929701bb2d" 906 | 907 | [[package]] 908 | name = "indexmap" 909 | version = "2.2.6" 910 | source = "registry+https://github.com/rust-lang/crates.io-index" 911 | checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" 912 | dependencies = [ 913 | "equivalent", 914 | "hashbrown", 915 | ] 916 | 917 | [[package]] 918 | name = "instant" 919 | version = "0.1.13" 920 | source = "registry+https://github.com/rust-lang/crates.io-index" 921 | checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 922 | dependencies = [ 923 | "cfg-if", 924 | ] 925 | 926 | [[package]] 927 | name = "isahc" 928 | version = "1.7.2" 929 | source = "registry+https://github.com/rust-lang/crates.io-index" 930 | checksum = "334e04b4d781f436dc315cb1e7515bd96826426345d498149e4bde36b67f8ee9" 931 | dependencies = [ 932 | "async-channel", 933 | "castaway", 934 | "crossbeam-utils", 935 | "curl", 936 | "curl-sys", 937 | "encoding_rs", 938 | "event-listener", 939 | "futures-lite", 940 | "http", 941 | "httpdate", 942 | "log", 943 | "mime", 944 | "once_cell", 945 | "polling", 946 | "serde", 947 | "serde_json", 948 | "slab", 949 | "sluice", 950 | "tracing", 951 | "tracing-futures", 952 | "url", 953 | "waker-fn", 954 | ] 955 | 956 | [[package]] 957 | name = "itoa" 958 | version = "1.0.11" 959 | source = "registry+https://github.com/rust-lang/crates.io-index" 960 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 961 | 962 | [[package]] 963 | name = "jobserver" 964 | version = "0.1.31" 965 | source = "registry+https://github.com/rust-lang/crates.io-index" 966 | checksum = "d2b099aaa34a9751c5bf0878add70444e1ed2dd73f347be99003d4577277de6e" 967 | dependencies = [ 968 | "libc", 969 | ] 970 | 971 | [[package]] 972 | name = "language-tags" 973 | version = "0.3.2" 974 | source = "registry+https://github.com/rust-lang/crates.io-index" 975 | checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" 976 | 977 | [[package]] 978 | name = "lazy_static" 979 | version = "1.5.0" 980 | source = "registry+https://github.com/rust-lang/crates.io-index" 981 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 982 | 983 | [[package]] 984 | name = "libc" 985 | version = "0.2.155" 986 | source = "registry+https://github.com/rust-lang/crates.io-index" 987 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 988 | 989 | [[package]] 990 | name = "libdbus-sys" 991 | version = "0.2.5" 992 | source = "registry+https://github.com/rust-lang/crates.io-index" 993 | checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" 994 | dependencies = [ 995 | "pkg-config", 996 | ] 997 | 998 | [[package]] 999 | name = "libnghttp2-sys" 1000 | version = "0.1.10+1.61.0" 1001 | source = "registry+https://github.com/rust-lang/crates.io-index" 1002 | checksum = "959c25552127d2e1fa72f0e52548ec04fc386e827ba71a7bd01db46a447dc135" 1003 | dependencies = [ 1004 | "cc", 1005 | "libc", 1006 | ] 1007 | 1008 | [[package]] 1009 | name = "libz-sys" 1010 | version = "1.1.18" 1011 | source = "registry+https://github.com/rust-lang/crates.io-index" 1012 | checksum = "c15da26e5af7e25c90b37a2d75cdbf940cf4a55316de9d84c679c9b8bfabf82e" 1013 | dependencies = [ 1014 | "cc", 1015 | "libc", 1016 | "pkg-config", 1017 | "vcpkg", 1018 | ] 1019 | 1020 | [[package]] 1021 | name = "local-channel" 1022 | version = "0.1.5" 1023 | source = "registry+https://github.com/rust-lang/crates.io-index" 1024 | checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" 1025 | dependencies = [ 1026 | "futures-core", 1027 | "futures-sink", 1028 | "local-waker", 1029 | ] 1030 | 1031 | [[package]] 1032 | name = "local-waker" 1033 | version = "0.1.4" 1034 | source = "registry+https://github.com/rust-lang/crates.io-index" 1035 | checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" 1036 | 1037 | [[package]] 1038 | name = "lock_api" 1039 | version = "0.4.12" 1040 | source = "registry+https://github.com/rust-lang/crates.io-index" 1041 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1042 | dependencies = [ 1043 | "autocfg", 1044 | "scopeguard", 1045 | ] 1046 | 1047 | [[package]] 1048 | name = "log" 1049 | version = "0.4.21" 1050 | source = "registry+https://github.com/rust-lang/crates.io-index" 1051 | checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" 1052 | 1053 | [[package]] 1054 | name = "lyrica" 1055 | version = "0.14.0" 1056 | dependencies = [ 1057 | "actix", 1058 | "actix-web", 1059 | "actix-web-actors", 1060 | "id3", 1061 | "isahc", 1062 | "lazy_static", 1063 | "metaflac", 1064 | "mpris", 1065 | "netease-cloud-music-api", 1066 | "serde", 1067 | "serde_json", 1068 | "tokio", 1069 | "url", 1070 | ] 1071 | 1072 | [[package]] 1073 | name = "memchr" 1074 | version = "2.7.2" 1075 | source = "registry+https://github.com/rust-lang/crates.io-index" 1076 | checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" 1077 | 1078 | [[package]] 1079 | name = "metaflac" 1080 | version = "0.2.7" 1081 | source = "registry+https://github.com/rust-lang/crates.io-index" 1082 | checksum = "d0f083edae4a21f5acb1fda8220d1c14fa31f725bfd4e21005a14c2d8944db9b" 1083 | dependencies = [ 1084 | "byteorder", 1085 | "hex", 1086 | ] 1087 | 1088 | [[package]] 1089 | name = "mime" 1090 | version = "0.3.17" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 1093 | 1094 | [[package]] 1095 | name = "miniz_oxide" 1096 | version = "0.7.3" 1097 | source = "registry+https://github.com/rust-lang/crates.io-index" 1098 | checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" 1099 | dependencies = [ 1100 | "adler", 1101 | ] 1102 | 1103 | [[package]] 1104 | name = "mio" 1105 | version = "0.8.11" 1106 | source = "registry+https://github.com/rust-lang/crates.io-index" 1107 | checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" 1108 | dependencies = [ 1109 | "libc", 1110 | "log", 1111 | "wasi 0.11.0+wasi-snapshot-preview1", 1112 | "windows-sys 0.48.0", 1113 | ] 1114 | 1115 | [[package]] 1116 | name = "mio" 1117 | version = "1.0.2" 1118 | source = "registry+https://github.com/rust-lang/crates.io-index" 1119 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 1120 | dependencies = [ 1121 | "hermit-abi", 1122 | "libc", 1123 | "wasi 0.11.0+wasi-snapshot-preview1", 1124 | "windows-sys 0.52.0", 1125 | ] 1126 | 1127 | [[package]] 1128 | name = "mpris" 1129 | version = "2.0.1" 1130 | source = "registry+https://github.com/rust-lang/crates.io-index" 1131 | checksum = "55cef955a7826b1e00e901a3652e7a895abd221fb4ab61547e7d0e4c235d7feb" 1132 | dependencies = [ 1133 | "dbus", 1134 | "derive_is_enum_variant", 1135 | "enum-kinds", 1136 | "from_variants", 1137 | "thiserror", 1138 | ] 1139 | 1140 | [[package]] 1141 | name = "netease-cloud-music-api" 1142 | version = "1.5.1" 1143 | source = "git+https://github.com/chiyuki0325/netease-cloud-music-api.git?tag=1.5.1-fix1#4644b29151fbeacaa8fe227e4c6d1bb7cf1cfc1c" 1144 | dependencies = [ 1145 | "anyhow", 1146 | "base64", 1147 | "hex", 1148 | "isahc", 1149 | "lazy_static", 1150 | "openssl", 1151 | "rand 0.9.1", 1152 | "regex", 1153 | "serde", 1154 | "serde_json", 1155 | "urlqstring", 1156 | ] 1157 | 1158 | [[package]] 1159 | name = "num-conv" 1160 | version = "0.1.0" 1161 | source = "registry+https://github.com/rust-lang/crates.io-index" 1162 | checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" 1163 | 1164 | [[package]] 1165 | name = "object" 1166 | version = "0.32.2" 1167 | source = "registry+https://github.com/rust-lang/crates.io-index" 1168 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 1169 | dependencies = [ 1170 | "memchr", 1171 | ] 1172 | 1173 | [[package]] 1174 | name = "once_cell" 1175 | version = "1.19.0" 1176 | source = "registry+https://github.com/rust-lang/crates.io-index" 1177 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 1178 | 1179 | [[package]] 1180 | name = "openssl" 1181 | version = "0.10.64" 1182 | source = "registry+https://github.com/rust-lang/crates.io-index" 1183 | checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" 1184 | dependencies = [ 1185 | "bitflags 2.5.0", 1186 | "cfg-if", 1187 | "foreign-types", 1188 | "libc", 1189 | "once_cell", 1190 | "openssl-macros", 1191 | "openssl-sys", 1192 | ] 1193 | 1194 | [[package]] 1195 | name = "openssl-macros" 1196 | version = "0.1.1" 1197 | source = "registry+https://github.com/rust-lang/crates.io-index" 1198 | checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" 1199 | dependencies = [ 1200 | "proc-macro2", 1201 | "quote 1.0.36", 1202 | "syn 2.0.66", 1203 | ] 1204 | 1205 | [[package]] 1206 | name = "openssl-probe" 1207 | version = "0.1.5" 1208 | source = "registry+https://github.com/rust-lang/crates.io-index" 1209 | checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" 1210 | 1211 | [[package]] 1212 | name = "openssl-sys" 1213 | version = "0.9.102" 1214 | source = "registry+https://github.com/rust-lang/crates.io-index" 1215 | checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" 1216 | dependencies = [ 1217 | "cc", 1218 | "libc", 1219 | "pkg-config", 1220 | "vcpkg", 1221 | ] 1222 | 1223 | [[package]] 1224 | name = "parking" 1225 | version = "2.2.0" 1226 | source = "registry+https://github.com/rust-lang/crates.io-index" 1227 | checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" 1228 | 1229 | [[package]] 1230 | name = "parking_lot" 1231 | version = "0.12.3" 1232 | source = "registry+https://github.com/rust-lang/crates.io-index" 1233 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1234 | dependencies = [ 1235 | "lock_api", 1236 | "parking_lot_core", 1237 | ] 1238 | 1239 | [[package]] 1240 | name = "parking_lot_core" 1241 | version = "0.9.10" 1242 | source = "registry+https://github.com/rust-lang/crates.io-index" 1243 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1244 | dependencies = [ 1245 | "cfg-if", 1246 | "libc", 1247 | "redox_syscall", 1248 | "smallvec", 1249 | "windows-targets 0.52.5", 1250 | ] 1251 | 1252 | [[package]] 1253 | name = "paste" 1254 | version = "1.0.15" 1255 | source = "registry+https://github.com/rust-lang/crates.io-index" 1256 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1257 | 1258 | [[package]] 1259 | name = "percent-encoding" 1260 | version = "2.3.1" 1261 | source = "registry+https://github.com/rust-lang/crates.io-index" 1262 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1263 | 1264 | [[package]] 1265 | name = "pin-project" 1266 | version = "1.1.5" 1267 | source = "registry+https://github.com/rust-lang/crates.io-index" 1268 | checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" 1269 | dependencies = [ 1270 | "pin-project-internal", 1271 | ] 1272 | 1273 | [[package]] 1274 | name = "pin-project-internal" 1275 | version = "1.1.5" 1276 | source = "registry+https://github.com/rust-lang/crates.io-index" 1277 | checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" 1278 | dependencies = [ 1279 | "proc-macro2", 1280 | "quote 1.0.36", 1281 | "syn 2.0.66", 1282 | ] 1283 | 1284 | [[package]] 1285 | name = "pin-project-lite" 1286 | version = "0.2.14" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 1289 | 1290 | [[package]] 1291 | name = "pin-utils" 1292 | version = "0.1.0" 1293 | source = "registry+https://github.com/rust-lang/crates.io-index" 1294 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1295 | 1296 | [[package]] 1297 | name = "pkg-config" 1298 | version = "0.3.30" 1299 | source = "registry+https://github.com/rust-lang/crates.io-index" 1300 | checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" 1301 | 1302 | [[package]] 1303 | name = "polling" 1304 | version = "2.8.0" 1305 | source = "registry+https://github.com/rust-lang/crates.io-index" 1306 | checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" 1307 | dependencies = [ 1308 | "autocfg", 1309 | "bitflags 1.3.2", 1310 | "cfg-if", 1311 | "concurrent-queue", 1312 | "libc", 1313 | "log", 1314 | "pin-project-lite", 1315 | "windows-sys 0.48.0", 1316 | ] 1317 | 1318 | [[package]] 1319 | name = "powerfmt" 1320 | version = "0.2.0" 1321 | source = "registry+https://github.com/rust-lang/crates.io-index" 1322 | checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" 1323 | 1324 | [[package]] 1325 | name = "ppv-lite86" 1326 | version = "0.2.17" 1327 | source = "registry+https://github.com/rust-lang/crates.io-index" 1328 | checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" 1329 | 1330 | [[package]] 1331 | name = "proc-macro2" 1332 | version = "1.0.84" 1333 | source = "registry+https://github.com/rust-lang/crates.io-index" 1334 | checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" 1335 | dependencies = [ 1336 | "unicode-ident", 1337 | ] 1338 | 1339 | [[package]] 1340 | name = "quote" 1341 | version = "0.3.15" 1342 | source = "registry+https://github.com/rust-lang/crates.io-index" 1343 | checksum = "7a6e920b65c65f10b2ae65c831a81a073a89edd28c7cce89475bff467ab4167a" 1344 | 1345 | [[package]] 1346 | name = "quote" 1347 | version = "1.0.36" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 1350 | dependencies = [ 1351 | "proc-macro2", 1352 | ] 1353 | 1354 | [[package]] 1355 | name = "r-efi" 1356 | version = "5.2.0" 1357 | source = "registry+https://github.com/rust-lang/crates.io-index" 1358 | checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" 1359 | 1360 | [[package]] 1361 | name = "rand" 1362 | version = "0.8.5" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1365 | dependencies = [ 1366 | "libc", 1367 | "rand_chacha 0.3.1", 1368 | "rand_core 0.6.4", 1369 | ] 1370 | 1371 | [[package]] 1372 | name = "rand" 1373 | version = "0.9.1" 1374 | source = "registry+https://github.com/rust-lang/crates.io-index" 1375 | checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" 1376 | dependencies = [ 1377 | "rand_chacha 0.9.0", 1378 | "rand_core 0.9.3", 1379 | ] 1380 | 1381 | [[package]] 1382 | name = "rand_chacha" 1383 | version = "0.3.1" 1384 | source = "registry+https://github.com/rust-lang/crates.io-index" 1385 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1386 | dependencies = [ 1387 | "ppv-lite86", 1388 | "rand_core 0.6.4", 1389 | ] 1390 | 1391 | [[package]] 1392 | name = "rand_chacha" 1393 | version = "0.9.0" 1394 | source = "registry+https://github.com/rust-lang/crates.io-index" 1395 | checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 1396 | dependencies = [ 1397 | "ppv-lite86", 1398 | "rand_core 0.9.3", 1399 | ] 1400 | 1401 | [[package]] 1402 | name = "rand_core" 1403 | version = "0.6.4" 1404 | source = "registry+https://github.com/rust-lang/crates.io-index" 1405 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1406 | dependencies = [ 1407 | "getrandom 0.2.15", 1408 | ] 1409 | 1410 | [[package]] 1411 | name = "rand_core" 1412 | version = "0.9.3" 1413 | source = "registry+https://github.com/rust-lang/crates.io-index" 1414 | checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 1415 | dependencies = [ 1416 | "getrandom 0.3.3", 1417 | ] 1418 | 1419 | [[package]] 1420 | name = "redox_syscall" 1421 | version = "0.5.1" 1422 | source = "registry+https://github.com/rust-lang/crates.io-index" 1423 | checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" 1424 | dependencies = [ 1425 | "bitflags 2.5.0", 1426 | ] 1427 | 1428 | [[package]] 1429 | name = "regex" 1430 | version = "1.10.4" 1431 | source = "registry+https://github.com/rust-lang/crates.io-index" 1432 | checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" 1433 | dependencies = [ 1434 | "aho-corasick", 1435 | "memchr", 1436 | "regex-automata", 1437 | "regex-syntax", 1438 | ] 1439 | 1440 | [[package]] 1441 | name = "regex-automata" 1442 | version = "0.4.6" 1443 | source = "registry+https://github.com/rust-lang/crates.io-index" 1444 | checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" 1445 | dependencies = [ 1446 | "aho-corasick", 1447 | "memchr", 1448 | "regex-syntax", 1449 | ] 1450 | 1451 | [[package]] 1452 | name = "regex-lite" 1453 | version = "0.1.5" 1454 | source = "registry+https://github.com/rust-lang/crates.io-index" 1455 | checksum = "30b661b2f27137bdbc16f00eda72866a92bb28af1753ffbd56744fb6e2e9cd8e" 1456 | 1457 | [[package]] 1458 | name = "regex-syntax" 1459 | version = "0.8.3" 1460 | source = "registry+https://github.com/rust-lang/crates.io-index" 1461 | checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" 1462 | 1463 | [[package]] 1464 | name = "rustc-demangle" 1465 | version = "0.1.24" 1466 | source = "registry+https://github.com/rust-lang/crates.io-index" 1467 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1468 | 1469 | [[package]] 1470 | name = "rustc_version" 1471 | version = "0.4.0" 1472 | source = "registry+https://github.com/rust-lang/crates.io-index" 1473 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 1474 | dependencies = [ 1475 | "semver", 1476 | ] 1477 | 1478 | [[package]] 1479 | name = "ryu" 1480 | version = "1.0.18" 1481 | source = "registry+https://github.com/rust-lang/crates.io-index" 1482 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1483 | 1484 | [[package]] 1485 | name = "schannel" 1486 | version = "0.1.23" 1487 | source = "registry+https://github.com/rust-lang/crates.io-index" 1488 | checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" 1489 | dependencies = [ 1490 | "windows-sys 0.52.0", 1491 | ] 1492 | 1493 | [[package]] 1494 | name = "scopeguard" 1495 | version = "1.2.0" 1496 | source = "registry+https://github.com/rust-lang/crates.io-index" 1497 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1498 | 1499 | [[package]] 1500 | name = "semver" 1501 | version = "1.0.23" 1502 | source = "registry+https://github.com/rust-lang/crates.io-index" 1503 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 1504 | 1505 | [[package]] 1506 | name = "serde" 1507 | version = "1.0.210" 1508 | source = "registry+https://github.com/rust-lang/crates.io-index" 1509 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 1510 | dependencies = [ 1511 | "serde_derive", 1512 | ] 1513 | 1514 | [[package]] 1515 | name = "serde_derive" 1516 | version = "1.0.210" 1517 | source = "registry+https://github.com/rust-lang/crates.io-index" 1518 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 1519 | dependencies = [ 1520 | "proc-macro2", 1521 | "quote 1.0.36", 1522 | "syn 2.0.66", 1523 | ] 1524 | 1525 | [[package]] 1526 | name = "serde_json" 1527 | version = "1.0.128" 1528 | source = "registry+https://github.com/rust-lang/crates.io-index" 1529 | checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" 1530 | dependencies = [ 1531 | "itoa", 1532 | "memchr", 1533 | "ryu", 1534 | "serde", 1535 | ] 1536 | 1537 | [[package]] 1538 | name = "serde_urlencoded" 1539 | version = "0.7.1" 1540 | source = "registry+https://github.com/rust-lang/crates.io-index" 1541 | checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" 1542 | dependencies = [ 1543 | "form_urlencoded", 1544 | "itoa", 1545 | "ryu", 1546 | "serde", 1547 | ] 1548 | 1549 | [[package]] 1550 | name = "sha1" 1551 | version = "0.10.6" 1552 | source = "registry+https://github.com/rust-lang/crates.io-index" 1553 | checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" 1554 | dependencies = [ 1555 | "cfg-if", 1556 | "cpufeatures", 1557 | "digest", 1558 | ] 1559 | 1560 | [[package]] 1561 | name = "signal-hook-registry" 1562 | version = "1.4.2" 1563 | source = "registry+https://github.com/rust-lang/crates.io-index" 1564 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 1565 | dependencies = [ 1566 | "libc", 1567 | ] 1568 | 1569 | [[package]] 1570 | name = "slab" 1571 | version = "0.4.9" 1572 | source = "registry+https://github.com/rust-lang/crates.io-index" 1573 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 1574 | dependencies = [ 1575 | "autocfg", 1576 | ] 1577 | 1578 | [[package]] 1579 | name = "sluice" 1580 | version = "0.5.5" 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" 1582 | checksum = "6d7400c0eff44aa2fcb5e31a5f24ba9716ed90138769e4977a2ba6014ae63eb5" 1583 | dependencies = [ 1584 | "async-channel", 1585 | "futures-core", 1586 | "futures-io", 1587 | ] 1588 | 1589 | [[package]] 1590 | name = "smallvec" 1591 | version = "1.13.2" 1592 | source = "registry+https://github.com/rust-lang/crates.io-index" 1593 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 1594 | 1595 | [[package]] 1596 | name = "socket2" 1597 | version = "0.5.7" 1598 | source = "registry+https://github.com/rust-lang/crates.io-index" 1599 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 1600 | dependencies = [ 1601 | "libc", 1602 | "windows-sys 0.52.0", 1603 | ] 1604 | 1605 | [[package]] 1606 | name = "strsim" 1607 | version = "0.10.0" 1608 | source = "registry+https://github.com/rust-lang/crates.io-index" 1609 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 1610 | 1611 | [[package]] 1612 | name = "syn" 1613 | version = "0.11.11" 1614 | source = "registry+https://github.com/rust-lang/crates.io-index" 1615 | checksum = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" 1616 | dependencies = [ 1617 | "quote 0.3.15", 1618 | "synom", 1619 | "unicode-xid", 1620 | ] 1621 | 1622 | [[package]] 1623 | name = "syn" 1624 | version = "1.0.109" 1625 | source = "registry+https://github.com/rust-lang/crates.io-index" 1626 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 1627 | dependencies = [ 1628 | "proc-macro2", 1629 | "quote 1.0.36", 1630 | "unicode-ident", 1631 | ] 1632 | 1633 | [[package]] 1634 | name = "syn" 1635 | version = "2.0.66" 1636 | source = "registry+https://github.com/rust-lang/crates.io-index" 1637 | checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" 1638 | dependencies = [ 1639 | "proc-macro2", 1640 | "quote 1.0.36", 1641 | "unicode-ident", 1642 | ] 1643 | 1644 | [[package]] 1645 | name = "synom" 1646 | version = "0.11.3" 1647 | source = "registry+https://github.com/rust-lang/crates.io-index" 1648 | checksum = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" 1649 | dependencies = [ 1650 | "unicode-xid", 1651 | ] 1652 | 1653 | [[package]] 1654 | name = "thiserror" 1655 | version = "1.0.61" 1656 | source = "registry+https://github.com/rust-lang/crates.io-index" 1657 | checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" 1658 | dependencies = [ 1659 | "thiserror-impl", 1660 | ] 1661 | 1662 | [[package]] 1663 | name = "thiserror-impl" 1664 | version = "1.0.61" 1665 | source = "registry+https://github.com/rust-lang/crates.io-index" 1666 | checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" 1667 | dependencies = [ 1668 | "proc-macro2", 1669 | "quote 1.0.36", 1670 | "syn 2.0.66", 1671 | ] 1672 | 1673 | [[package]] 1674 | name = "time" 1675 | version = "0.3.36" 1676 | source = "registry+https://github.com/rust-lang/crates.io-index" 1677 | checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" 1678 | dependencies = [ 1679 | "deranged", 1680 | "itoa", 1681 | "num-conv", 1682 | "powerfmt", 1683 | "serde", 1684 | "time-core", 1685 | "time-macros", 1686 | ] 1687 | 1688 | [[package]] 1689 | name = "time-core" 1690 | version = "0.1.2" 1691 | source = "registry+https://github.com/rust-lang/crates.io-index" 1692 | checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" 1693 | 1694 | [[package]] 1695 | name = "time-macros" 1696 | version = "0.2.18" 1697 | source = "registry+https://github.com/rust-lang/crates.io-index" 1698 | checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" 1699 | dependencies = [ 1700 | "num-conv", 1701 | "time-core", 1702 | ] 1703 | 1704 | [[package]] 1705 | name = "tinyvec" 1706 | version = "1.6.0" 1707 | source = "registry+https://github.com/rust-lang/crates.io-index" 1708 | checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" 1709 | dependencies = [ 1710 | "tinyvec_macros", 1711 | ] 1712 | 1713 | [[package]] 1714 | name = "tinyvec_macros" 1715 | version = "0.1.1" 1716 | source = "registry+https://github.com/rust-lang/crates.io-index" 1717 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 1718 | 1719 | [[package]] 1720 | name = "tokio" 1721 | version = "1.40.0" 1722 | source = "registry+https://github.com/rust-lang/crates.io-index" 1723 | checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" 1724 | dependencies = [ 1725 | "backtrace", 1726 | "bytes", 1727 | "libc", 1728 | "mio 1.0.2", 1729 | "parking_lot", 1730 | "pin-project-lite", 1731 | "signal-hook-registry", 1732 | "socket2", 1733 | "tokio-macros", 1734 | "windows-sys 0.52.0", 1735 | ] 1736 | 1737 | [[package]] 1738 | name = "tokio-macros" 1739 | version = "2.4.0" 1740 | source = "registry+https://github.com/rust-lang/crates.io-index" 1741 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 1742 | dependencies = [ 1743 | "proc-macro2", 1744 | "quote 1.0.36", 1745 | "syn 2.0.66", 1746 | ] 1747 | 1748 | [[package]] 1749 | name = "tokio-util" 1750 | version = "0.7.11" 1751 | source = "registry+https://github.com/rust-lang/crates.io-index" 1752 | checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" 1753 | dependencies = [ 1754 | "bytes", 1755 | "futures-core", 1756 | "futures-sink", 1757 | "pin-project-lite", 1758 | "tokio", 1759 | ] 1760 | 1761 | [[package]] 1762 | name = "tracing" 1763 | version = "0.1.40" 1764 | source = "registry+https://github.com/rust-lang/crates.io-index" 1765 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 1766 | dependencies = [ 1767 | "log", 1768 | "pin-project-lite", 1769 | "tracing-attributes", 1770 | "tracing-core", 1771 | ] 1772 | 1773 | [[package]] 1774 | name = "tracing-attributes" 1775 | version = "0.1.27" 1776 | source = "registry+https://github.com/rust-lang/crates.io-index" 1777 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 1778 | dependencies = [ 1779 | "proc-macro2", 1780 | "quote 1.0.36", 1781 | "syn 2.0.66", 1782 | ] 1783 | 1784 | [[package]] 1785 | name = "tracing-core" 1786 | version = "0.1.32" 1787 | source = "registry+https://github.com/rust-lang/crates.io-index" 1788 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 1789 | dependencies = [ 1790 | "once_cell", 1791 | ] 1792 | 1793 | [[package]] 1794 | name = "tracing-futures" 1795 | version = "0.2.5" 1796 | source = "registry+https://github.com/rust-lang/crates.io-index" 1797 | checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" 1798 | dependencies = [ 1799 | "pin-project", 1800 | "tracing", 1801 | ] 1802 | 1803 | [[package]] 1804 | name = "typenum" 1805 | version = "1.17.0" 1806 | source = "registry+https://github.com/rust-lang/crates.io-index" 1807 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 1808 | 1809 | [[package]] 1810 | name = "unicode-bidi" 1811 | version = "0.3.15" 1812 | source = "registry+https://github.com/rust-lang/crates.io-index" 1813 | checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" 1814 | 1815 | [[package]] 1816 | name = "unicode-ident" 1817 | version = "1.0.12" 1818 | source = "registry+https://github.com/rust-lang/crates.io-index" 1819 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 1820 | 1821 | [[package]] 1822 | name = "unicode-normalization" 1823 | version = "0.1.23" 1824 | source = "registry+https://github.com/rust-lang/crates.io-index" 1825 | checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" 1826 | dependencies = [ 1827 | "tinyvec", 1828 | ] 1829 | 1830 | [[package]] 1831 | name = "unicode-segmentation" 1832 | version = "1.11.0" 1833 | source = "registry+https://github.com/rust-lang/crates.io-index" 1834 | checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" 1835 | 1836 | [[package]] 1837 | name = "unicode-xid" 1838 | version = "0.0.4" 1839 | source = "registry+https://github.com/rust-lang/crates.io-index" 1840 | checksum = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" 1841 | 1842 | [[package]] 1843 | name = "url" 1844 | version = "2.5.2" 1845 | source = "registry+https://github.com/rust-lang/crates.io-index" 1846 | checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 1847 | dependencies = [ 1848 | "form_urlencoded", 1849 | "idna", 1850 | "percent-encoding", 1851 | ] 1852 | 1853 | [[package]] 1854 | name = "urlqstring" 1855 | version = "0.3.5" 1856 | source = "registry+https://github.com/rust-lang/crates.io-index" 1857 | checksum = "25ef3473a06a065718d8ec7cd7acc6a35fc20f836dee7661ad3b64ea3cc2e0cc" 1858 | 1859 | [[package]] 1860 | name = "vcpkg" 1861 | version = "0.2.15" 1862 | source = "registry+https://github.com/rust-lang/crates.io-index" 1863 | checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" 1864 | 1865 | [[package]] 1866 | name = "version_check" 1867 | version = "0.9.4" 1868 | source = "registry+https://github.com/rust-lang/crates.io-index" 1869 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 1870 | 1871 | [[package]] 1872 | name = "waker-fn" 1873 | version = "1.2.0" 1874 | source = "registry+https://github.com/rust-lang/crates.io-index" 1875 | checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" 1876 | 1877 | [[package]] 1878 | name = "wasi" 1879 | version = "0.11.0+wasi-snapshot-preview1" 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" 1881 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 1882 | 1883 | [[package]] 1884 | name = "wasi" 1885 | version = "0.14.2+wasi-0.2.4" 1886 | source = "registry+https://github.com/rust-lang/crates.io-index" 1887 | checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" 1888 | dependencies = [ 1889 | "wit-bindgen-rt", 1890 | ] 1891 | 1892 | [[package]] 1893 | name = "winapi" 1894 | version = "0.3.9" 1895 | source = "registry+https://github.com/rust-lang/crates.io-index" 1896 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 1897 | dependencies = [ 1898 | "winapi-i686-pc-windows-gnu", 1899 | "winapi-x86_64-pc-windows-gnu", 1900 | ] 1901 | 1902 | [[package]] 1903 | name = "winapi-i686-pc-windows-gnu" 1904 | version = "0.4.0" 1905 | source = "registry+https://github.com/rust-lang/crates.io-index" 1906 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 1907 | 1908 | [[package]] 1909 | name = "winapi-x86_64-pc-windows-gnu" 1910 | version = "0.4.0" 1911 | source = "registry+https://github.com/rust-lang/crates.io-index" 1912 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 1913 | 1914 | [[package]] 1915 | name = "windows-sys" 1916 | version = "0.48.0" 1917 | source = "registry+https://github.com/rust-lang/crates.io-index" 1918 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 1919 | dependencies = [ 1920 | "windows-targets 0.48.5", 1921 | ] 1922 | 1923 | [[package]] 1924 | name = "windows-sys" 1925 | version = "0.52.0" 1926 | source = "registry+https://github.com/rust-lang/crates.io-index" 1927 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 1928 | dependencies = [ 1929 | "windows-targets 0.52.5", 1930 | ] 1931 | 1932 | [[package]] 1933 | name = "windows-targets" 1934 | version = "0.48.5" 1935 | source = "registry+https://github.com/rust-lang/crates.io-index" 1936 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 1937 | dependencies = [ 1938 | "windows_aarch64_gnullvm 0.48.5", 1939 | "windows_aarch64_msvc 0.48.5", 1940 | "windows_i686_gnu 0.48.5", 1941 | "windows_i686_msvc 0.48.5", 1942 | "windows_x86_64_gnu 0.48.5", 1943 | "windows_x86_64_gnullvm 0.48.5", 1944 | "windows_x86_64_msvc 0.48.5", 1945 | ] 1946 | 1947 | [[package]] 1948 | name = "windows-targets" 1949 | version = "0.52.5" 1950 | source = "registry+https://github.com/rust-lang/crates.io-index" 1951 | checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" 1952 | dependencies = [ 1953 | "windows_aarch64_gnullvm 0.52.5", 1954 | "windows_aarch64_msvc 0.52.5", 1955 | "windows_i686_gnu 0.52.5", 1956 | "windows_i686_gnullvm", 1957 | "windows_i686_msvc 0.52.5", 1958 | "windows_x86_64_gnu 0.52.5", 1959 | "windows_x86_64_gnullvm 0.52.5", 1960 | "windows_x86_64_msvc 0.52.5", 1961 | ] 1962 | 1963 | [[package]] 1964 | name = "windows_aarch64_gnullvm" 1965 | version = "0.48.5" 1966 | source = "registry+https://github.com/rust-lang/crates.io-index" 1967 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 1968 | 1969 | [[package]] 1970 | name = "windows_aarch64_gnullvm" 1971 | version = "0.52.5" 1972 | source = "registry+https://github.com/rust-lang/crates.io-index" 1973 | checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" 1974 | 1975 | [[package]] 1976 | name = "windows_aarch64_msvc" 1977 | version = "0.48.5" 1978 | source = "registry+https://github.com/rust-lang/crates.io-index" 1979 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 1980 | 1981 | [[package]] 1982 | name = "windows_aarch64_msvc" 1983 | version = "0.52.5" 1984 | source = "registry+https://github.com/rust-lang/crates.io-index" 1985 | checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" 1986 | 1987 | [[package]] 1988 | name = "windows_i686_gnu" 1989 | version = "0.48.5" 1990 | source = "registry+https://github.com/rust-lang/crates.io-index" 1991 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 1992 | 1993 | [[package]] 1994 | name = "windows_i686_gnu" 1995 | version = "0.52.5" 1996 | source = "registry+https://github.com/rust-lang/crates.io-index" 1997 | checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" 1998 | 1999 | [[package]] 2000 | name = "windows_i686_gnullvm" 2001 | version = "0.52.5" 2002 | source = "registry+https://github.com/rust-lang/crates.io-index" 2003 | checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" 2004 | 2005 | [[package]] 2006 | name = "windows_i686_msvc" 2007 | version = "0.48.5" 2008 | source = "registry+https://github.com/rust-lang/crates.io-index" 2009 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 2010 | 2011 | [[package]] 2012 | name = "windows_i686_msvc" 2013 | version = "0.52.5" 2014 | source = "registry+https://github.com/rust-lang/crates.io-index" 2015 | checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" 2016 | 2017 | [[package]] 2018 | name = "windows_x86_64_gnu" 2019 | version = "0.48.5" 2020 | source = "registry+https://github.com/rust-lang/crates.io-index" 2021 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 2022 | 2023 | [[package]] 2024 | name = "windows_x86_64_gnu" 2025 | version = "0.52.5" 2026 | source = "registry+https://github.com/rust-lang/crates.io-index" 2027 | checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" 2028 | 2029 | [[package]] 2030 | name = "windows_x86_64_gnullvm" 2031 | version = "0.48.5" 2032 | source = "registry+https://github.com/rust-lang/crates.io-index" 2033 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 2034 | 2035 | [[package]] 2036 | name = "windows_x86_64_gnullvm" 2037 | version = "0.52.5" 2038 | source = "registry+https://github.com/rust-lang/crates.io-index" 2039 | checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" 2040 | 2041 | [[package]] 2042 | name = "windows_x86_64_msvc" 2043 | version = "0.48.5" 2044 | source = "registry+https://github.com/rust-lang/crates.io-index" 2045 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 2046 | 2047 | [[package]] 2048 | name = "windows_x86_64_msvc" 2049 | version = "0.52.5" 2050 | source = "registry+https://github.com/rust-lang/crates.io-index" 2051 | checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" 2052 | 2053 | [[package]] 2054 | name = "wit-bindgen-rt" 2055 | version = "0.39.0" 2056 | source = "registry+https://github.com/rust-lang/crates.io-index" 2057 | checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" 2058 | dependencies = [ 2059 | "bitflags 2.5.0", 2060 | ] 2061 | 2062 | [[package]] 2063 | name = "zerocopy" 2064 | version = "0.7.34" 2065 | source = "registry+https://github.com/rust-lang/crates.io-index" 2066 | checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" 2067 | dependencies = [ 2068 | "zerocopy-derive", 2069 | ] 2070 | 2071 | [[package]] 2072 | name = "zerocopy-derive" 2073 | version = "0.7.34" 2074 | source = "registry+https://github.com/rust-lang/crates.io-index" 2075 | checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" 2076 | dependencies = [ 2077 | "proc-macro2", 2078 | "quote 1.0.36", 2079 | "syn 2.0.66", 2080 | ] 2081 | 2082 | [[package]] 2083 | name = "zstd" 2084 | version = "0.13.1" 2085 | source = "registry+https://github.com/rust-lang/crates.io-index" 2086 | checksum = "2d789b1514203a1120ad2429eae43a7bd32b90976a7bb8a05f7ec02fa88cc23a" 2087 | dependencies = [ 2088 | "zstd-safe", 2089 | ] 2090 | 2091 | [[package]] 2092 | name = "zstd-safe" 2093 | version = "7.1.0" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "1cd99b45c6bc03a018c8b8a86025678c87e55526064e38f9df301989dce7ec0a" 2096 | dependencies = [ 2097 | "zstd-sys", 2098 | ] 2099 | 2100 | [[package]] 2101 | name = "zstd-sys" 2102 | version = "2.0.10+zstd.1.5.6" 2103 | source = "registry+https://github.com/rust-lang/crates.io-index" 2104 | checksum = "c253a4914af5bafc8fa8c86ee400827e83cf6ec01195ec1f1ed8441bf00d65aa" 2105 | dependencies = [ 2106 | "cc", 2107 | "pkg-config", 2108 | ] 2109 | --------------------------------------------------------------------------------