├── 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 |
--------------------------------------------------------------------------------