netease-cloud-music-gtk
netease-cloud-music-gtk copied to clipboard
通过 Mpris 接口 `Metadata::xesam:asText` 提供歌词
这个 Feature Request 算是此评论的后续。
这样做可以每首歌少读一次文件,而且文件名格式更自由有利于解决 #264 这类问题,也可以加songId之类的精确匹配。
我不会 rust,弄的这个 patch 可能有问题,但可以工作:
lyrics.patch
diff --git a/src/audio/mpris.rs b/src/audio/mpris.rs
index b4cce1a..48e1097 100644
--- a/src/audio/mpris.rs
+++ b/src/audio/mpris.rs
@@ -55,8 +55,9 @@ impl MprisController {
Ok(Self { mpris_player })
}
- pub async fn update_metadata(&self, si: &SongInfo) -> Result<()> {
+ pub async fn update_metadata(&self, si: &SongInfo, lrc: String) -> Result<()> {
let mut metadata = Metadata::new();
+ metadata.set_lyrics(Some(lrc.clone()));
metadata.set_artist(Some(vec![si.singer.clone()]));
metadata.set_title(Some(si.name.clone()));
metadata.set_album(Some(si.album.clone()));
diff --git a/src/gui/player_controls.rs b/src/gui/player_controls.rs
index ba12410..741717b 100644
--- a/src/gui/player_controls.rs
+++ b/src/gui/player_controls.rs
@@ -19,7 +19,7 @@ use once_cell::sync::*;
use crate::{application::Action, audio::*, model::ImageDownloadImpl, path::CACHE};
use std::{
- cell::Cell,
+ cell::{Cell, RefCell},
fs, path,
rc::Rc,
sync::{Arc, Mutex},
@@ -177,11 +177,12 @@ impl PlayerControls {
artist_label.set_label(&song_info.singer);
let volume = self.property("volume");
+ let lyrics: String = self.property("lyrics");
if let Some(mpris) = imp.mpris.get() {
crate::MAINCONTEXT.spawn_local_with_priority(
Priority::LOW,
clone!(@weak mpris => async move {
- if let Err(err) = mpris.update_metadata(&song_info).await {
+ if let Err(err) = mpris.update_metadata(&song_info, lyrics).await {
warn!("设置 MPRIS metadata 失败: {err:?}");
}
if let Err(err) = mpris.set_playback_status(PlaybackStatus::Playing).await {
@@ -324,13 +325,29 @@ impl PlayerControls {
self.set_property("duration", sec);
+ let lyrics: String = self.property("lyrics");
if let Some(mpris) = imp.mpris.get() {
if let Some(mut si) = self.get_current_song() {
si.duration = msec / 1000;
crate::MAINCONTEXT.spawn_local_with_priority(
Priority::LOW,
clone!(@weak mpris => async move {
- mpris.update_metadata(&si).await.ok();
+ mpris.update_metadata(&si, lyrics).await.ok();
+ }),
+ );
+ }
+ }
+ }
+
+ pub fn metadata_lyrics_update(&self, lrc: String) {
+ self.set_property("lyrics", lrc.clone());
+ let imp = self.imp();
+ if let Some(mpris) = imp.mpris.get() {
+ if let Some(mut si) = self.get_current_song() {
+ crate::MAINCONTEXT.spawn_local_with_priority(
+ Priority::LOW,
+ clone!(@weak mpris => async move {
+ mpris.update_metadata(&si, lrc).await.ok();
}),
);
}
@@ -654,7 +671,7 @@ impl PlayerControls {
mod imp {
- use gst::glib::Propagation;
+ use gst::glib::{ParamSpecString, Propagation};
use super::*;
@@ -713,6 +730,7 @@ mod imp {
// 播放条拖动结束时的值
scale_value: Cell<f64>,
+ lyrics: RefCell<String>,
}
#[glib::object_subclass]
@@ -934,6 +952,7 @@ mod imp {
ParamSpecUInt64::builder("duration").build(),
ParamSpecBoolean::builder("like").readwrite().build(),
ParamSpecDouble::builder("scale-value").readwrite().build(),
+ ParamSpecString::builder("lyrics").build(),
]
});
PROPERTIES.as_ref()
@@ -965,6 +984,10 @@ mod imp {
let scale_value = value.get().expect("The value needs to be of type `bool`.");
self.scale_value.replace(scale_value);
}
+ "lyrics" => {
+ let val = value.get().unwrap();
+ self.lyrics.replace(val);
+ }
n => unimplemented!("{}", n),
}
}
@@ -977,6 +1000,7 @@ mod imp {
"duration" => self.duration.get().to_value(),
"like" => self.like.get().to_value(),
"scale-value" => self.scale_value.get().to_value(),
+ "lyrics" => self.lyrics.borrow().to_value(),
n => unimplemented!("{}", n),
}
}
diff --git a/src/ncmapi.rs b/src/ncmapi.rs
index 16b9506..fcccaac 100644
--- a/src/ncmapi.rs
+++ b/src/ncmapi.rs
@@ -169,19 +169,16 @@ impl NcmClient {
pub async fn get_lyrics(&self, si: SongInfo) -> Result<String> {
let mut path = LYRICS.clone();
path.push(format!("{}-{}-{}.lrc", si.name, si.singer, si.album));
- let re = regex::Regex::new(r"\[\d+:\d+.\d+\]").unwrap();
if !path.exists() {
if let Ok(lyr) = self.client.song_lyric(si.id).await {
let lrc = lyr.into_iter().collect::<Vec<String>>().join("\n");
fs::write(&path, &lrc)?;
- let lrc = re.replace_all(&lrc, "").to_string();
Ok(lrc)
} else {
Ok(gettextrs::gettext("No lyrics found!".to_owned()))
}
} else {
let lrc = fs::read_to_string(&path)?;
- let lrc = re.replace_all(&lrc, "").to_string();
Ok(lrc)
}
}
diff --git a/src/window.rs b/src/window.rs
index 58dcd6e..c348c42 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -674,9 +674,12 @@ impl NeteaseCloudMusicGtk4Window {
pub fn update_lyrics(&self, lrc: String) {
let imp = self.imp();
+ let re = regex::Regex::new(r"\[\d+:\d+.\d+\]").unwrap();
+ let txt = re.replace_all(&lrc, "").to_string();
let page = imp.playlist_lyrics_page.get().unwrap();
+ imp.player_controls.get().metadata_lyrics_update(lrc);
if self.page_cur_playlist_lyrics_page() {
- page.update_lyrics(lrc);
+ page.update_lyrics(txt);
}
}
貌似 Github 不支持上传 patch 文件,那直接贴这儿了。
没有使用mpris的歌词接口是为了减少网络请求次数,将歌词缓存在本地,当再次需要时可以直接从本地读取。缓存lrc和使用asText功能上是重复的,如果只实现一个的话缓存lrc性价比会更高一些。
上面的 patch 只是在更新 PlayListLyricsPage 的歌词时顺便更一下 Mpris,获取歌词的方式根本没变。更新 Mpris 也需要网络请求吗?
我的意思是,更新Mpris歌词与缓存歌词文件在目的上是重复的,如果二选一选择mpris的asText传送歌词,会造成每次都要请求网络。
另外如果只在打开播放列表页面时才触发更新mpris歌词,这样该操作是没有意义的。
另外如果只在打开播放列表页面时才触发更新mpris歌词,这样该操作是没有意义的。
在打开判定之前更新的。
这里启用桌面歌词支持时每次都会下载/读取本地歌词,顺便更新一下 Mpris 不好吗?读了不能传,要传不能存(读),苦涩的选择。目前如果本地有文件,扩展那边要多读一次,否则就多一次注定失败的网络请求,也算重复吧。既然如此,你可能要按需读取。
谢谢回答。
另外发现在这每250ms更新进度条时 Mpris 会发Seeked
信号,同时更新Mpris::Position
。这似乎都没有必要。
建议在自动更新进度条时不要发Seeked
,至于Position
之前就建议参考Lollypop按需直接从Gst.Player读取不用更新。
谢谢。
如果不更新 position 你的歌词插件该如何确定当前播放位置? 特别是反复前后拖拉进度条后,会不会有影响。 你说的按需直接从Gst.Player读取我没太明白,这里是谁来读取?
更新 seeked 是为了让别的 mpris 播放插件同步播放条进度。
我上次测试gnome-music 在反复前后拖拉进度条时好像会卡住,具体情况忘了,反正就是做了取舍才弄成现在这样。
最近在忙装修,可以把你的建议先说下,我等闲了再研究研究看怎么弄。
如果不更新 position 你的歌词插件该如何确定当前播放位置? 特别是反复前后拖拉进度条后,会不会有影响。
客户端维护有 timer,获取当前 position 一般为初始化而非同步。拖动进度条结束发一次Seeked
就行,客户端不关心怎么拖的。
你说的按需直接从Gst.Player读取我没太明白,这里是谁来读取?
服务端Position
不像Volume
之类的会同步到客户端,客户端通过 org.freedesktop.DBus.Properties.Get
接口来获取 Position
,所以服务端在实现此接口时,对Position
直接返回 Gst.Player 的位置即可,而非定期set_position
缓存一个随时过期的值待用。
我上次测试gnome-music 在反复前后拖拉进度条时好像会卡住,具体情况忘了,反正就是做了取舍才弄成现在这样。
gnome-music 的实现有问题,表现为拖动过程中 PlaybackStatus
在 Playing
和 Paused
间反复横跳,与客户端无关。
更新 seeked 是为了让别的 mpris 播放插件同步播放条进度。
这听起来有点像某软件每秒截30次屏来实现屏幕共享。尤其结合上文,为某进度条每250ms发一次信号,却不愿为歌词更一次元数据,就显得很有意思:) 总之,这信号据说 indicates that the track position has changed in a way that is inconsistant with the current playing state,用于同步不失为另辟蹊径吧。
最近在忙装修,可以把你的建议先说下,我等闲了再研究研究看怎么弄。
祝好。建议先前有一些了,刚打开播放列表没找到移除歌曲的办法,不确定这是 Feature 还是 Bug。
就这样吧。
你好,我在尝试使用 asText 提供歌词时,desktop-lyrics 没有正常工作,问题如下:
1、播放歌曲时,在使用asText推送歌词的情况下,desktop-lyrics 依然会自动下载歌词,测试应该是没有使用我用asText提供的歌词;
2、当播放下面的歌曲时,没有任何反应,也没有自动下载歌词。
https://music.163.com/song?id=2021437775 歌曲:My Stupid Heart (Kids Version) 歌手:Walk off the Earth
歌词如下(已测试去掉换行符问题依旧):
"[00:00.000] 作曲 : Gianni Luminati Nicassio/Jake Torrey/Lostboy/Michael Matosic/Sarah Blackwood/Tokyo Speirs\n[00:00.112]Hey, everybody\n[00:00.514]My mom 'n dad made this new song called \"My Stupid Heart\"\n[00:03.547]I'm gonna play it with my brothers\n[00:05.187]Let's gooooooo!!\n[00:07.074]\n[00:07.337]My stupid heart\n[00:08.881]Don't know\n[00:09.983]I've tried to let you go\n[00:11.850]So many times before\n[00:13.938]Then wound up at your door\n[00:15.765]My stupid\n[00:16.149]\n[00:16.524]Can't believe that I haven't figured out by now\n[00:19.634]Every time I call you up\n[00:21.842]All you do is let me down (Let's Go!)\n[00:24.487]\n[00:24.833]Shoulda known there was nothing about us I could change\n[00:28.105]Everytime we try to be friends\n[00:30.112]It always ends the same\n[00:32.207]\n[00:32.368]But when I try to remember\n[00:36.376]All the pain that we've been through\n[00:40.596]Something in me says whatever\n[00:44.781]And it brings me back to you\n[00:48.172]\n[00:48.869]My stupid heart\n[00:50.638]Don't know\n[00:51.631]I've tried to let you go\n[00:53.583]So many times before\n[00:55.604]Then wound up at your door\n[00:57.482]My stupid heart\n[00:58.854]Too late\n[00:59.796]Already on my way\n[01:01.920]If we go down in flames\n[01:04.123]Again then you can blame my stupid heart\n[01:09.832](Okay!)\n[01:11.917](Okay!)\n[01:13.068]You can blame my stupid heart\n[01:18.386](Okay!)\n[01:20.658](Okay!)\n[01:21.279]You can blame my stupid heart\n[01:23.518]\n[01:23.737]Every now and then I get inside my head\n[01:26.555]Try to leave your text unread\n[01:28.357]But I wind up here instead\n[01:30.968]\n[01:31.676]I shoulda bit my tongue while we were still ahead\n[01:34.787]Yeah always had to be right\n[01:36.800]Til we had nothing left\n[01:39.079]\n[01:39.238]But when I try to remember\n[01:43.129]All the pain that we've been through\n[01:47.364]Something in me says whatever\n[01:51.615]And it brings me back to you\n[01:55.052]\n[01:55.613]My stupid heart\n[01:57.328]Don't know\n[01:58.440]I've tried to let you go\n[02:00.248]So many times before\n[02:02.418]Then wound up at your door\n[02:04.002]My stupid heart\n[02:05.665]Too late\n[02:06.822]Already on my way\n[02:08.590]If we go down in flames\n[02:10.830]Again then you can blame my stupid heart (Let's Go!)\n[02:16.703](Okay!)\n[02:18.945](Okay!)\n[02:19.437]\n[02:19.607]You can blame my stupid heart\n[02:23.868]I've tried to let you go\n[02:25.389]So many times before\n[02:29.064]You can blame my stupid heart\n[02:30.832]Too late\n[02:32.142]Already on my way\n[02:33.964]If we go down in flames\n[02:35.902]Again then you can blame my stupid heart\n[02:39.253]Too late\n[02:40.317]Already on my way\n[02:42.139]If we go down in flames\n[02:44.276]Again then you can blame my stupid heart"
GNOME45及以后的版本才支持,如果版本没问题看看Metadata
是否提供了歌词:
gdbus call --session --dest org.mpris.MediaPlayer2.NeteaseCloudMusicGtk4 --object-path /org/mpris/MediaPlayer2 --method org.freedesktop.DBus.Properties.Get org.mpris.MediaPlayer2.Player Metadata
另外无asText
字段时会尝试下载,所以初始化暂无歌词时需设置asText
为空(字符串),有歌词再更新即可。
GNOME 版本是 45.3, Metadata 中可以正常获取歌词:
(<{'mpris:artUrl': <'file:///home/gmg/.cache/netease-cloud-music-gtk4/159843058-songlist.jpg'>, 'mpris:length': <int64 168112000>, 'mpris:trackid': <objectpath '/com/gitee/gmg137/NeteaseCloudMusicGtk4/2021437775'>, 'xesam:album': <'My Stupid Heart (Kids Version)'>, 'xesam:artist': <['Walk off the Earth']>, 'xesam:asText': <"[00:00.000] 作词 : Kim Taylor\n[00:00.000] 作曲 : Kim Taylor\n[00:00.000]La di da di da da\n[00:03.860]La di da di da da\n[00:08.110]La di da di da da\n[00:12.000]La da da\n[00:14.760]\n[00:16.510]I am tied by truth like an anchor\n[00:25.440]Anchored to a bottomless sea\n[00:32.340]I am floating freely in the heavens\n[00:41.440]Held in by your heart's gravity\n[00:46.160]\n[00:48.920]All because of love\n[00:53.040]All because of love\n[00:57.410]Even though sometimes\n[00:59.999]You don't know who I am\n[01:03.259]\n[01:05.239]I am you..\n[01:09.290]Everything you do\n[01:13.168]Anything you say,\n[01:17.319]You want me to be\n[01:21.328]You and me..\n[01:25.290]We're charms on a chain\n[01:29.790]Linked eternally one we can't undo\n[01:37.110]And I am you\n[01:40.790]\n[01:41.890]La di da di da da\n[01:45.840]La di da di da da\n[01:49.799]La da da\n[01:52.099]\n[01:53.920]All my senses awaken to the changes yeah\n[02:02.209]And I feel alive inside my own skin\n[02:10.159]All my reasons tell me just how strange it is\n[02:18.409]Coming home to a place I've always been\n[02:24.199]\n[02:26.439]And it's all for love\n[02:30.530]And it's all for love\n[02:34.739]Even though sometimes,\n[02:37.429]I don't know who I am\n[02:40.639]\n[02:42.629]I am you..\n[02:46.699]Everything you do\n[02:50.360]Anything you say,\n[02:54.549]You want me to be\n[02:58.810]You and me..\n[03:02.739]We're charms on a chain\n[03:07.109]Linked eternally one we can't undo, ohhh\n[03:14.829]I am you...\n[03:18.979]La di da di da da\n[03:22.869]La di da di da da (anything you say)\n[03:27.069]La da da (everything you do)\n[03:30.970]La di da di da da (I am you...)\n[03:35.129]La di da di da da\n[03:39.169]La di da di da da (anything you say)\n[03:43.160]La da da (everything you do)\n[03:47.209]I am you... (La di da di da da)\n[03:51.190]I am you... (La di da di da da)\n[03:55.119]I am you... (La di da di da da)\n[03:59.049]I am you... (La di da di da da)\n[04:01.639]\n[04:03.199]I am you... (La di da di da da)\n[04:07.139]I am you... (La di da di da da)\n[04:11.379]Everything you do....\n[04:18.069]\n[04:20.459]Fading away...\n[04:28.379]">, 'xesam:title': <'My Stupid Heart (Kids Version)'>}>,)
下面是我编译好的程序你可以测试下,如果不能运行我再把源代码打包了发你。 netease-cloud-music-gtk4.zip
用此提交(对应扩展网站上的版本19)在GNOME46上测试发现你编译的 grsource 文件路径硬编码为本地,不过我用软连接解决了。 随机播放歌曲均显示为你上方提供的歌词,扩展本身并未下载/保存任何文件:
已经可以正常显示了,不过osdlyrics 貌似不支持 xesam:asText 接口,这样的话还是要保留从歌词文件读取歌词的功能。