Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package netease-cloud-music-gtk for 
openSUSE:Factory checked in at 2021-01-18 11:27:56
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/netease-cloud-music-gtk (Old)
 and      /work/SRC/openSUSE:Factory/.netease-cloud-music-gtk.new.28504 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "netease-cloud-music-gtk"

Mon Jan 18 11:27:56 2021 rev:5 rq:862795 version:1.2.0

Changes:
--------
--- 
/work/SRC/openSUSE:Factory/netease-cloud-music-gtk/netease-cloud-music-gtk.changes
  2020-11-17 21:22:01.361152699 +0100
+++ 
/work/SRC/openSUSE:Factory/.netease-cloud-music-gtk.new.28504/netease-cloud-music-gtk.changes
       2021-01-18 11:31:04.340608811 +0100
@@ -1,0 +2,8 @@
+Wed Jan 13 05:09:51 UTC 2021 - gmg 137 <[email protected]>
+
+- Updata to version 1.2.0:
+ + Add a playlist.
+ + Search page automatically loads more.
+ + Fix bug.
+
+-------------------------------------------------------------------

Old:
----
  netease-cloud-music-gtk-1.1.3.tar.xz

New:
----
  netease-cloud-music-gtk-1.2.0.tar.xz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ netease-cloud-music-gtk.spec ++++++
--- /var/tmp/diff_new_pack.inQF30/_old  2021-01-18 11:31:29.848633940 +0100
+++ /var/tmp/diff_new_pack.inQF30/_new  2021-01-18 11:31:29.852633945 +0100
@@ -1,7 +1,7 @@
 #
 # spec file for package netease-cloud-music-gtk
 #
-# Copyright (c) 2020 SUSE LLC
+# Copyright (c) 2021 SUSE LLC
 # Copyright (c) specCURRENT_YEAR SUSE LINUX GmbH, Nuernberg, Germany.
 #
 # All modifications and additions to the file contributed by third parties
@@ -18,7 +18,7 @@
 
 
 Name:           netease-cloud-music-gtk
-Version:        1.1.3
+Version:        1.2.0
 Release:        0
 Summary:        Linux ??????????????? Rust + GTK 
?????????????????????????????????
 License:        GPL-3.0-or-later

++++++ netease-cloud-music-gtk-1.1.3.tar.xz -> 
netease-cloud-music-gtk-1.2.0.tar.xz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/Cargo.toml 
new/netease-cloud-music-gtk-1.2.0/Cargo.toml
--- old/netease-cloud-music-gtk-1.1.3/Cargo.toml        2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/Cargo.toml        2021-01-13 
05:37:02.000000000 +0100
@@ -1,7 +1,7 @@
 [package]
 name = "netease-cloud-music-gtk"
 description = "Linux ??????????????? Rust + GTK 
?????????????????????????????????"
-version = "1.1.3"
+version = "1.2.0"
 authors = ["gmg137 <[email protected]>"]
 license = "GPL-v3"
 edition = "2018"
@@ -36,7 +36,7 @@
 xdg = "*"
 futures = "^0.3"
 async-std = "^1.6.1"
-isahc = { version = "*", features = ["cookies"] }
+isahc = { version = "^1.0.1", features = ["cookies"] }
 custom_error = "*"
 
 log = "*"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/README.md 
new/netease-cloud-music-gtk-1.2.0/README.md
--- old/netease-cloud-music-gtk-1.1.3/README.md 2020-11-06 03:47:40.000000000 
+0100
+++ new/netease-cloud-music-gtk-1.2.0/README.md 2021-01-13 05:37:02.000000000 
+0100
@@ -21,6 +21,7 @@
 - ????????????(????????? [OSDLyrics](https://github.com/osdlyrics/osdlyrics))
 - ????????????
 - ????????????
+- Mpris ??????
 
 ## ????????????
 > openssl, gstreamer, gstreamer-plugins-base, gstreamer-plugins-good, 
 > gstreamer-plugins-bad, gstreamer-plugins-ugly
@@ -30,6 +31,13 @@
 ```bash
 sudo zypper in netease-cloud-music-gtk
 ```
+### openSUSE Leap
+```bash
+// ?????????
+sudo zypper ar -f obs://multimedia:apps multimedia
+// ??????
+sudo zypper in netease-cloud-music-gtk
+```
 ### Arch Linux
 
[????????????](https://www.archlinuxcn.org/archlinux-cn-repo-and-mirror/)?????? 
/etc/pacman.conf 
?????????????????????????????????[????????????????????????](https://github.com/archlinuxcn/mirrorlist-repo)??????
 ```
@@ -55,7 +63,7 @@
 
 sudo apt install netease-cloud-music-gtk
 ```
-### openSUSE Leap / Ubuntu
+### Fedora / Ubuntu
 - ?????? 
[RPM/DEB](https://gitee.com/gmg137/netease-cloud-music-gtk/releases)????????????
 
 ### ???????????????
@@ -95,12 +103,32 @@
 cargo install cargo-deb
 cargo deb
 ```
+### FAQ
+1. ???????????????????????????????????????????
+> ?????? GTK3 
??????????????????????????????????????????????????????????????????<br>
+> **????????????:**
+> - Mpris ??????: GNOME ?????? [Mpris Indicator 
Button](https://extensions.gnome.org/extension/1379/mpris-indicator-button/)??????????????????????????????
 Mpris ?????????
+> - ????????????????????????????????????????????????
+2. ???????????????????????????????????????????
+> ???????????????????????????????????????????????????????????? glib 
???????????????????????????????????????(ubuntu18.04, 
leap15)????????????????????????
+3. ?????????????????????????????????????????????????
+> 
??????????????????????????????????????????????????????????????????????????????????????????????????????????????????
+4. ?????????????????????????????????????
+> ???????????????????????? ```--debug``` 
?????????????????????????????????????????????????????????????????????
+5. ??????????????????????????????????
+> ???????????????????????? ```--debug``` ?????????????????????, 
?????????????????????
+6. ?????? [OSDLyrics](https://github.com/osdlyrics/osdlyrics) 
?????????????????????????????????
+> ?????? [OSDLyrics](https://github.com/osdlyrics/osdlyrics) 
???????????????????????????????????????????????????????????????????????????????????????????????????????????????
+> ?????????????????????????????????????????????????????????
 
 ## ??????
 
![home](https://user-images.githubusercontent.com/6460323/74423902-fa996900-4e8b-11ea-915f-a4ec40bd2982.jpg)
 
![found](https://user-images.githubusercontent.com/6460323/74421939-c8d2d300-4e88-11ea-9b93-962ae80f5a11.png)
 
![mine](https://user-images.githubusercontent.com/6460323/74424004-29afda80-4e8c-11ea-9c16-af3f25525c9c.jpeg)
 
+## License
+This project's source code and documentation is licensed under the  [GNU 
General Public License](LICENSE) (GPL v3).
+
 ## ??????
 - [podcasts](https://gitlab.gnome.org/World/podcasts)
 - [gnome-music](https://gitlab.gnome.org/GNOME/gnome-music)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/src/app.rs 
new/netease-cloud-music-gtk-1.2.0/src/app.rs
--- old/netease-cloud-music-gtk-1.1.3/src/app.rs        2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/src/app.rs        2021-01-13 
05:37:02.000000000 +0100
@@ -64,12 +64,17 @@
     RefreshMineFmPlayerList,
     RefreshMineFmPlay,
     RefreshMineFmPause,
+    RefreshMineFmImage(String),
     CancelCollection,
     Search(String),
+    AppendSearch,
+    AppendSubLowView(Vec<SongInfo>),
     PlayerInit(SongInfo, PlayerTypes),
     PlayerTypes(PlayerTypes),
     ReadyPlayer(SongInfo),
     RefreshLyricsText(String),
+    RefreshPlaylist(PlayerListData),
+    PlaylistSong(i32),
     Player(SongInfo),
     PlayerOne,
     RefreshPlayerImage(String),
@@ -255,6 +260,7 @@
             }
             Action::RefreshMineFmPlay => self.view.switch_fm_play(),
             Action::RefreshMineFmPause => self.view.switch_fm_pause(),
+            Action::RefreshMineFmImage(path) => self.view.set_fm_image(path),
             Action::PlayerFm => self.view.play_fm(),
             Action::PauseFm => self.player.pause(),
             Action::FmLike => self.view.like_fm(),
@@ -264,14 +270,22 @@
             }
             Action::CancelCollection => self.view.cancel_collection(),
             Action::Search(text) => self.view.switch_stack_search(text),
+            Action::AppendSearch => {
+                if let Some((text, num)) = self.view.get_sub_page_data() {
+                    self.view.append_search(text, num);
+                }
+            }
+            Action::AppendSubLowView(song_list) => 
self.view.append_sub_low_view(song_list),
             Action::Login(name, pass) => self.header.login(name, pass),
             Action::Logout => self.header.logout(),
             Action::DailyTask => self.header.daily_task(),
             Action::PlayerInit(info, pt) => 
self.player.initialize_player(info, pt, self.configs.borrow().lyrics),
             Action::PlayerTypes(pt) => self.player.set_player_typers(pt),
+            Action::PlaylistSong(index) => self.player.playlist_song(index),
             Action::Player(info) => self.player.player(info),
             Action::ReadyPlayer(info) => self.player.ready_player(info, 
self.configs.borrow().lyrics),
             Action::RefreshLyricsText(lrc) => 
self.player.update_lyrics_text(lrc),
+            Action::RefreshPlaylist(pl) => self.player.update_playlist(pl),
             Action::ShowNotice(text) => {
                 let notif = mark_all_notif(text);
                 let old = self.notice.replace(Some(notif));
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/src/musicapi/mod.rs 
new/netease-cloud-music-gtk-1.2.0/src/musicapi/mod.rs
--- old/netease-cloud-music-gtk-1.1.3/src/musicapi/mod.rs       2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/src/musicapi/mod.rs       2021-01-13 
05:37:02.000000000 +0100
@@ -7,7 +7,7 @@
 pub(crate) mod model;
 use crate::model::{Errors, NCMResult};
 use encrypt::Crypto;
-use isahc::prelude::*;
+use isahc::{prelude::*, *};
 use lazy_static::lazy_static;
 use model::*;
 use regex::Regex;
@@ -128,14 +128,14 @@
                         }
                     }
                 }
-                response.text_async().await.map_err(|_| Errors::NoneError)
+                response.text().await.map_err(|_| Errors::NoneError)
             }
             Method::GET => self
                 .client
                 .get_async(&url)
                 .await
                 .map_err(|_| Errors::NoneError)?
-                .text_async()
+                .text()
                 .await
                 .map_err(|_| Errors::NoneError),
         }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/src/task.rs 
new/netease-cloud-music-gtk-1.2.0/src/task.rs
--- old/netease-cloud-music-gtk-1.1.3/src/task.rs       2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/src/task.rs       2021-01-13 
05:37:02.000000000 +0100
@@ -18,6 +18,7 @@
         width: u32,
         high: u32,
         timeout: u64,
+        fm: bool,
     },
     DownloadMusic {
         url: String,
@@ -42,8 +43,12 @@
                 width,
                 high,
                 timeout,
+                fm,
             } => {
                 download_img(&url, &path, width, high, timeout).await.ok();
+                if fm {
+                    
sender.send(Action::RefreshMineFmImage(path.clone())).unwrap();
+                }
                 sender.send(Action::RefreshPlayerImage(path)).unwrap();
             }
             Task::DownloadMineRecommendImage(rr) => {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/src/utils.rs 
new/netease-cloud-music-gtk-1.2.0/src/utils.rs
--- old/netease-cloud-music-gtk-1.1.3/src/utils.rs      2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/src/utils.rs      2021-01-13 
05:37:02.000000000 +0100
@@ -12,12 +12,15 @@
     musicapi::model::SongInfo,
     widgets::player::LoopsState,
 };
-use async_std::fs;
+use async_std::{
+    fs,
+    sync::{Arc, Mutex},
+};
 use cairo::{Context, ImageSurface};
 use gdk::{pixbuf_get_from_surface, prelude::GdkContextExt};
 use gdk_pixbuf::Pixbuf;
 use glib::Sender;
-use isahc::{prelude::*, ResponseExt};
+use isahc::{prelude::*, *};
 use rand::{seq::SliceRandom, thread_rng};
 use serde::{Deserialize, Serialize};
 use std::{io, io::Error, time::Duration};
@@ -40,7 +43,9 @@
         let mut response = client.get_async(music_url).await?;
         if response.status().is_success() {
             let tmp_path = format!("{}.tmp", path);
-            response.copy_to_file(&tmp_path)?;
+            let mut buf = vec![];
+            response.copy_to(&mut buf).await?;
+            fs::write(&tmp_path, buf).await?;
             fs::rename(&tmp_path, path).await?;
         }
     }
@@ -66,7 +71,9 @@
         let client = 
HttpClient::builder().timeout(Duration::from_millis(timeout)).build()?;
         let mut response = client.get_async(image_url).await?;
         if response.status().is_success() {
-            response.copy_to_file(path)?;
+            let mut buf = vec![];
+            response.copy_to(&mut buf).await?;
+            fs::write(&path, buf).await?;
         }
     }
     Ok(())
@@ -74,13 +81,13 @@
 
 // ??????????????????
 #[derive(Debug, Deserialize, Serialize)]
-struct PlayerListData {
+pub(crate) struct PlayerListData {
     // ????????????: (????????????,????????????)
-    player_list: Vec<(SongInfo, bool)>,
+    pub player_list: Vec<(SongInfo, bool)>,
     // ??????????????????????????????
     shuffle_list: Vec<i32>,
     // ???????????????????????????
-    index: i32,
+    pub index: i32,
 }
 
 // ??????????????????
@@ -253,7 +260,7 @@
 
 // ??????????????????
 #[allow(unused)]
-pub(crate) async fn update_player_list(sender: Sender<Action>) -> 
NCMResult<()> {
+pub(crate) async fn update_player_list(sender: Sender<Action>, music_data: 
Arc<Mutex<MusicData>>) -> NCMResult<()> {
     let path = format!("{}player_list.db", NCM_DATA.to_string_lossy());
     let buffer = fs::read(&path).await?;
     // ????????????????????????
@@ -264,7 +271,7 @@
     } = bincode::deserialize(&buffer).map_err(|_| Errors::NoneError)?;
     // ???????????? id ??????
     let song_id_list = player_list.iter().map(|(si, _)| 
si.id).collect::<Vec<u64>>();
-    let mut api = MusicData::new().await;
+    let mut api = music_data.lock().await;
     // ?????????????????? URL
     if let Ok(v) = api.songs_url(&song_id_list, 320).await {
         // ?????????????????????
@@ -483,3 +490,34 @@
     let lrc = fs::read_to_string(&path).await?;
     Ok(lrc)
 }
+
+// ??????????????????
+#[allow(unused)]
+pub(crate) async fn get_playlist() -> NCMResult<PlayerListData> {
+    // ??????????????????
+    let path = format!("{}player_list.db", NCM_DATA.to_string_lossy());
+    let buffer = fs::read(&path).await?;
+    // ????????????????????????
+    Ok(bincode::deserialize(&buffer).map_err(|_| Errors::NoneError)?)
+}
+
+// ?????????????????????????????????
+// index: ????????????
+#[allow(unused)]
+pub(crate) async fn get_playlist_song_by_index(index_new: i32, sender: 
Sender<Action>) -> NCMResult<()> {
+    // ??????????????????
+    let path = format!("{}player_list.db", NCM_DATA.to_string_lossy());
+    let buffer = fs::read(&path).await?;
+    // ????????????????????????
+    let mut pld: PlayerListData = bincode::deserialize(&buffer).map_err(|_| 
Errors::NoneError)?;
+    // ???????????????????????????
+    if !pld.player_list.is_empty() {
+        pld.index = index_new;
+        fs::write(&path, bincode::serialize(&pld).map_err(|_| 
Errors::NoneError)?).await?;
+        if let Some((si, _)) = pld.player_list.get(index_new as usize) {
+            sender.send(Action::ReadyPlayer(si.to_owned())).unwrap();
+            return Ok(());
+        }
+    }
+    Err(Errors::NoneError)
+}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/src/view/mine/fm.rs 
new/netease-cloud-music-gtk-1.2.0/src/view/mine/fm.rs
--- old/netease-cloud-music-gtk-1.1.3/src/view/mine/fm.rs       2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/src/view/mine/fm.rs       2021-01-13 
05:37:02.000000000 +0100
@@ -242,4 +242,11 @@
         self.play.hide();
         self.pause.show();
     }
+
+    pub(crate) fn set_fm_image(&self, image_path: String) {
+        if let Ok(image) = Pixbuf::from_file(&image_path) {
+            let image = image.scale_simple(140, 140, InterpType::Bilinear);
+            self.image.set_from_pixbuf(image.as_ref());
+        };
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/src/view/mod.rs 
new/netease-cloud-music-gtk-1.2.0/src/view/mod.rs
--- old/netease-cloud-music-gtk-1.1.3/src/view/mod.rs   2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/src/view/mod.rs   2021-01-13 
05:37:02.000000000 +0100
@@ -224,11 +224,12 @@
         let text_clone = text.clone();
         task::spawn(async move {
             let mut data = MusicData::new().await;
-            if let Ok(json) = data.search(text_clone, 1, 0, 50).await {
+            if let Ok(json) = data.search(text_clone.to_owned(), 1, 0, 
50).await {
                 if let Ok(song_list) = 
serde_json::from_str::<Vec<SongInfo>>(&json) {
+                    let search_text = format!("search:{}", text_clone);
                     // ????????????????????????, ??????????????????????????????
                     if sender
-                        .send(Action::RefreshSubUpView(0, String::new(), 
String::new()))
+                        .send(Action::RefreshSubUpView(0, search_text, 
String::new()))
                         .is_ok()
                     {
                         // ??????????????????
@@ -243,6 +244,26 @@
         self.sender.send(Action::SwitchHeaderBar(text)).unwrap_or(());
     }
 
+    // ????????????
+    pub(crate) fn append_search(&self, text: String, num: usize) {
+        let sender = self.sender.clone();
+        task::spawn(async move {
+            let mut data = MusicData::new().await;
+            if let Ok(json) = data.search(text.to_owned(), 1, num as u16, 
50).await {
+                if let Ok(song_list) = 
serde_json::from_str::<Vec<SongInfo>>(&json) {
+                    // ??????????????????
+                    
sender.send(Action::AppendSubLowView(song_list)).unwrap_or(());
+                }
+            } else {
+                
sender.send(Action::ShowNotice("????????????????????????!".to_owned())).unwrap();
+            }
+        });
+    }
+
+    pub(crate) fn append_sub_low_view(&self, song_list: Vec<SongInfo>) {
+        self.subpages.borrow_mut().append_low_view(song_list);
+    }
+
     pub(crate) fn update_home_view(&self, tsl: Arc<Vec<SongList>>, rr: 
Arc<Vec<SongList>>) {
         self.home.borrow_mut().update(tsl, rr);
     }
@@ -294,6 +315,10 @@
         self.subpages.borrow_mut().update_low_view(song_list);
     }
 
+    pub(crate) fn get_sub_page_data(&self) -> Option<(String, usize)> {
+        self.subpages.borrow_mut().get_search_data()
+    }
+
     pub(crate) fn update_home(&self) {
         let sender = self.sender.clone();
         let mut sender_task = self.sender_task.clone();
@@ -627,6 +652,10 @@
         self.mine.borrow_mut().fmview.switch_pause();
     }
 
+    pub(crate) fn set_fm_image(&self, path: String) {
+        self.mine.borrow_mut().fmview.set_fm_image(path);
+    }
+
     pub(crate) fn cancel_collection(&self) {
         if let Some(id) = self.mine.borrow_mut().listview.get_song_id() {
             let sender = self.sender.clone();
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/src/view/subpages.rs 
new/netease-cloud-music-gtk-1.2.0/src/view/subpages.rs
--- old/netease-cloud-music-gtk-1.1.3/src/view/subpages.rs      2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/src/view/subpages.rs      2021-01-13 
06:58:06.000000000 +0100
@@ -12,12 +12,16 @@
 use async_std::task;
 use gdk_pixbuf::{InterpType, Pixbuf};
 use glib::Sender;
-use gtk::{prelude::*, Builder, Button, CellRendererText, Grid, Image, Label, 
ListStore, TreeView, TreeViewColumn};
+use gtk::{
+    prelude::*, Builder, Button, CellRendererText, Grid, Image, Label, 
ListStore, ScrolledWindow, TreeView,
+    TreeViewColumn,
+};
 use pango::EllipsizeMode;
 
 #[derive(Clone)]
 pub(crate) struct Subpages {
     overview: Overview,
+    scrolled: ScrolledWindow,
     tree: TreeView,
     store: ListStore,
     song_list: Vec<SongInfo>,
@@ -53,6 +57,9 @@
         let play: Button = builder
             .get_object("subpages_play_button")
             .expect("???????????? subpages_play_button .");
+        let scrolled: ScrolledWindow = builder
+            .get_object("subpages_scrolled_window")
+            .expect("???????????? subpages_scrolled_window.");
         let overview = Overview {
             grid,
             pic,
@@ -74,6 +81,7 @@
         ]);
         let s = Subpages {
             overview,
+            scrolled,
             tree,
             store,
             song_list: vec![],
@@ -144,6 +152,14 @@
         s.overview.like.connect_clicked(move |_| {
             sender.send(Action::LikeSongList).unwrap_or(());
         });
+
+        // ?????????????????????????????????
+        let sender = s.sender.clone();
+        s.scrolled.connect_edge_overshot(move |_, position| {
+            if position == gtk::PositionType::Bottom {
+                sender.send(Action::AppendSearch).unwrap_or(());
+            }
+        });
     }
 
     pub(crate) fn update_up_view(&mut self, id: u64, name: String, image_path: 
String) {
@@ -158,12 +174,19 @@
             self.overview.grid.hide();
             return;
         }
+        if name.starts_with("search:") {
+            self.overview.grid.hide();
+        }
         if let Ok(image) = Pixbuf::from_file(&image_path) {
             let image = image.scale_simple(140, 140, InterpType::Bilinear);
             self.overview.pic.set_from_pixbuf(image.as_ref());
         };
         self.overview.album.set_label(&name);
         self.overview.num.set_label("0 ???");
+        // ?????????????????????????????????, ?????????????????????????????????
+        //if let Some(adj) = self.scrolled.get_vadjustment() {
+        //adj.set_value(0.0);
+        //}
     }
 
     pub(crate) fn update_low_view(&mut self, song_list: Vec<SongInfo>) {
@@ -234,6 +257,27 @@
         });
     }
 
+    pub(crate) fn append_low_view(&mut self, song_list: Vec<SongInfo>) {
+        let mut song_list_old = self.song_list.to_owned();
+        let mut song_list_new = song_list.to_owned();
+        song_list_old.append(&mut song_list_new);
+        self.song_list = song_list_old;
+        song_list.iter().for_each(|song| {
+            self.store.insert_with_values(
+                None,
+                &[0, 1, 2, 3, 4, 5],
+                &[
+                    &song.id,
+                    &song.name,
+                    &song.duration,
+                    &song.singer,
+                    &song.album,
+                    &song.pic_url,
+                ],
+            );
+        });
+    }
+
     pub(crate) fn play_all(&self) {
         let song_list = self.song_list.clone();
         let sender = self.sender.clone();
@@ -258,4 +302,16 @@
     pub(crate) fn get_song_list_id(&self) -> u64 {
         self.song_list_id
     }
+
+    // ??????????????????
+    // return: (???????????????,??????????????????)
+    pub(crate) fn get_search_data(&self) -> Option<(String, usize)> {
+        let text = self.overview.album.get_text().to_string();
+        let num = self.song_list.len();
+        if text.starts_with("search:") {
+            Some((text.replace("search:", ""), num))
+        } else {
+            None
+        }
+    }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/src/widgets/player.rs 
new/netease-cloud-music-gtk-1.2.0/src/widgets/player.rs
--- old/netease-cloud-music-gtk-1.1.3/src/widgets/player.rs     2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/src/widgets/player.rs     2021-01-13 
05:37:02.000000000 +0100
@@ -12,10 +12,11 @@
 use glib::{clone, Sender, SignalHandlerId, WeakRef};
 use gst::ClockTime;
 use gtk::{
-    prelude::*, AccelGroup, ActionBar, Builder, Button, Image, Label, 
MenuButton, RadioButton, Scale, TextView,
-    VolumeButton,
+    prelude::*, AccelGroup, ActionBar, Builder, Button, CellRendererText, 
Image, Label, ListStore, MenuButton, Popover,
+    RadioButton, Scale, TextView, TreeView, TreeViewColumn, VolumeButton,
 };
 use mpris_player::{LoopStatus, Metadata, MprisPlayer, 
OrgMprisMediaPlayer2Player, PlaybackStatus};
+use pango::EllipsizeMode;
 use serde::{Deserialize, Serialize};
 use std::{cell::RefCell, ops::Deref, path::Path, rc::Rc, sync::Arc};
 
@@ -27,7 +28,10 @@
     forward: Button,
     like: Button,
     volume: VolumeButton,
-    lyrics: MenuButton,
+    more: MenuButton,
+    more_popover: Popover,
+    tree: TreeView,
+    store: ListStore,
     lyrics_text: TextView,
 }
 
@@ -201,8 +205,19 @@
         volume.set_value(volume_value);
         player.set_volume(volume_value);
         mpris.set_volume(volume_value).ok();
-        let lyrics: MenuButton = builder.get_object("lyrics_button").unwrap();
+        let more: MenuButton = builder.get_object("more_button").unwrap();
+        let more_popover: Popover = 
builder.get_object("more_popover").unwrap();
         let lyrics_text: TextView = 
builder.get_object("lyrics_text_view").unwrap();
+        let tree: TreeView = builder
+            .get_object("playlist_tree_view")
+            .expect("???????????? playlist_tree_view .");
+        let store: ListStore = ListStore::new(&[
+            glib::Type::U64,
+            String::static_type(),
+            String::static_type(),
+            String::static_type(),
+            String::static_type(),
+        ]);
 
         let controls = PlayerControls {
             play,
@@ -211,7 +226,10 @@
             backward,
             like,
             volume,
-            lyrics,
+            more,
+            more_popover,
+            tree,
+            store,
             lyrics_text,
         };
 
@@ -337,6 +355,7 @@
                             width: 34,
                             high: 34,
                             timeout: 1000,
+                            fm: false,
                         })
                         .await
                         .ok();
@@ -365,6 +384,15 @@
     pub(crate) fn ready_player(&self, song_info: SongInfo, lyrics: bool) {
         let sender = self.sender.clone();
         let mut sender_task = self.sender_task.clone();
+        // ???????????? FM ????????????
+        let mut fm = false;
+        let mut width = 34;
+        let mut high = 34;
+        if let PlayerTypes::Fm = *self.player_types.borrow() {
+            fm = true;
+            width = 140;
+            high = 140;
+        }
         task::spawn(async move {
             // ????????????
             if lyrics {
@@ -380,9 +408,10 @@
                 .send(Task::DownloadPlayerImg {
                     url: song_info.pic_url.to_owned(),
                     path: image_path.to_owned(),
-                    width: 34,
-                    high: 34,
+                    width,
+                    high,
                     timeout: 1000,
+                    fm,
                 })
                 .await
                 .ok();
@@ -414,6 +443,11 @@
             self.player.set_uri(&music_url);
         }
         self.play();
+        // ????????????????????????????????????????????????
+        if self.controls.more_popover.is_visible() {
+            self.get_lyrics_text();
+            self.get_playlist();
+        }
     }
 
     fn connect_update_slider(slider: &Scale, player: 
WeakRef<gst_player::Player>) -> SignalHandlerId {
@@ -560,6 +594,16 @@
         });
     }
 
+    fn get_playlist(&self) {
+        let sender = self.sender.clone();
+        task::spawn(async move {
+            // ??????????????????
+            if let Ok(playlist) = get_playlist().await {
+                sender.send(Action::RefreshPlaylist(playlist)).unwrap();
+            }
+        });
+    }
+
     // ??? Mpris2 ??????????????????
     fn set_loops(&self, loops_status: LoopStatus) {
         match loops_status {
@@ -599,10 +643,76 @@
         }
     }
 
+    pub(crate) fn update_playlist(&self, pl: PlayerListData) {
+        self.controls.store.clear();
+        for c in self.controls.tree.get_columns().iter() {
+            self.controls.tree.remove_column(c);
+        }
+        self.controls.tree.set_model(Some(&self.controls.store));
+
+        let column = TreeViewColumn::new();
+        column.set_visible(false);
+        column.set_sizing(gtk::TreeViewColumnSizing::Fixed);
+        let id = CellRendererText::new();
+        column.pack_start(&id, true);
+        column.add_attribute(&id, "text", 0);
+        self.controls.tree.append_column(&column);
+
+        let column = TreeViewColumn::new();
+        column.set_sizing(gtk::TreeViewColumnSizing::Fixed);
+        let play = CellRendererText::new();
+        play.set_property_xpad(18);
+        play.set_property_xalign(0.0);
+        play.set_property_yalign(0.5);
+        play.set_property_height(37);
+        column.pack_start(&play, true);
+        column.add_attribute(&play, "text", 1);
+        self.controls.tree.append_column(&column);
+
+        let column = TreeViewColumn::new();
+        column.set_sizing(gtk::TreeViewColumnSizing::Fixed);
+        let title = CellRendererText::new();
+        play.set_property_xpad(18);
+        play.set_property_xalign(0.0);
+        title.set_property_ellipsize(EllipsizeMode::End);
+        column.pack_start(&title, true);
+        column.add_attribute(&title, "text", 2);
+
+        let duration = CellRendererText::new();
+        duration.set_property_xpad(32);
+        duration.set_property_xalign(0.0);
+        column.pack_start(&duration, true);
+        column.add_attribute(&duration, "text", 3);
+
+        let singer = CellRendererText::new();
+        singer.set_property_xpad(22);
+        singer.set_property_xalign(0.0);
+        singer.set_property_ellipsize(EllipsizeMode::End);
+        column.pack_start(&singer, true);
+        column.add_attribute(&singer, "text", 4);
+        self.controls.tree.append_column(&column);
+
+        let song_id = *self.info.song_id.borrow();
+        pl.player_list.iter().for_each(|(song, _)| {
+            let play_icon = if Some(song.id).eq(&song_id) { "???" } else { "" 
};
+            self.controls.store.insert_with_values(
+                None,
+                &[0, 1, 2, 3, 4],
+                &[&song.id, &play_icon, &song.name, &song.duration, 
&song.singer],
+            );
+        });
+    }
+
     pub(crate) fn set_player_typers(&self, player_types: PlayerTypes) {
         *self.player_types.borrow_mut() = player_types;
     }
 
+    pub(crate) fn playlist_song(&self, index: i32) {
+        if task::block_on(get_playlist_song_by_index(index, 
self.sender.clone())).is_err() {
+            
self.sender.send(Action::ShowNotice("????????????!".to_owned())).unwrap();
+        }
+    }
+
     pub(crate) fn set_cover_image(&self, image_path: String) {
         if let Ok(image) = Pixbuf::from_file(&image_path) {
             let image = image.scale_simple(38, 38, InterpType::Bilinear);
@@ -659,12 +769,26 @@
     }
 
     fn init(&self, sender: &Sender<Action>) {
+        self.connect_control_tree();
         self.connect_control_buttons();
         self.connect_loops_buttons();
         self.connect_mpris_buttons();
         self.connect_gst_signals(sender);
     }
 
+    fn connect_control_tree(&self) {
+        let sender = self.sender.clone();
+        self.controls.tree.connect_button_press_event(move |tree, event| {
+            if event.get_event_type() == gdk::EventType::DoubleButtonPress {
+                if let Some(path) = 
tree.get_selection().get_selected_rows().0.get(0) {
+                    let index = path.get_indices()[0];
+                    sender.send(Action::PlaylistSong(index)).unwrap_or(());
+                }
+            }
+            Inhibit(false)
+        });
+    }
+
     /// Connect the `PlayerControls` buttons to the `PlayerExt` methods.
     fn connect_control_buttons(&self) {
         let weak = Rc::clone(self);
@@ -697,8 +821,9 @@
                 weak.set_volume(value,false);
             }));
 
-        self.controls.lyrics.connect_clicked(clone!(@weak weak => move |_| {
+        self.controls.more.connect_clicked(clone!(@weak weak => move |_| {
             weak.get_lyrics_text();
+            weak.get_playlist();
         }));
     }
 
@@ -708,15 +833,17 @@
             .connect_warning(move |_, warn| warn!("gst warning: {}", warn));
 
         let sender_clone = sender.clone();
+        let data = self.music_data.clone();
         // Log gst errors.
         self.player.connect_error(move |_, _| {
             sender_clone
                 .send(Action::ShowNotice("??????????????????!".to_owned()))
                 .unwrap();
             let sender_clone = sender_clone.clone();
+            let data = data.clone();
             // ??????????????????
             task::spawn(async move {
-                update_player_list(sender_clone).await.ok();
+                update_player_list(sender_clone, data).await.ok();
             });
         });
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/ui/subpages.ui 
new/netease-cloud-music-gtk-1.2.0/ui/subpages.ui
--- old/netease-cloud-music-gtk-1.1.3/ui/subpages.ui    2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/ui/subpages.ui    2021-01-13 
05:37:02.000000000 +0100
@@ -139,7 +139,7 @@
           </packing>
         </child>
         <child>
-          <object class="GtkScrolledWindow">
+          <object class="GtkScrolledWindow" id="subpages_scrolled_window">
             <property name="visible">True</property>
             <property name="can_focus">True</property>
             <property name="hscrollbar_policy">never</property>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/netease-cloud-music-gtk-1.1.3/ui/window.ui 
new/netease-cloud-music-gtk-1.2.0/ui/window.ui
--- old/netease-cloud-music-gtk-1.1.3/ui/window.ui      2020-11-06 
03:47:40.000000000 +0100
+++ new/netease-cloud-music-gtk-1.2.0/ui/window.ui      2021-01-13 
05:37:02.000000000 +0100
@@ -639,7 +639,7 @@
               </packing>
             </child>
             <child>
-              <object class="GtkMenuButton" id="lyrics_button">
+              <object class="GtkMenuButton" id="more_button">
                 <property name="visible">True</property>
                 <property name="can_focus">True</property>
                 <property name="receives_default">False</property>
@@ -647,18 +647,12 @@
                 <property name="valign">center</property>
                 <property name="relief">none</property>
                 <property name="direction">up</property>
-                <property name="popover">lyrics_popover</property>
+                <property name="popover">more_popover</property>
                 <child>
-                  <object class="GtkLabel">
+                  <object class="GtkImage" id="more_image">
                     <property name="visible">True</property>
-                    <property name="can_focus">False</property>
-                    <property name="halign">center</property>
-                    <property name="valign">center</property>
-                    <property name="label" translatable="yes">???</property>
-                    <property name="justify">center</property>
-                    <attributes>
-                      <attribute name="weight" value="bold"/>
-                    </attributes>
+                    <property name="can-focus">False</property>
+                    <property name="icon-name">view-more-symbolic</property>
                   </object>
                 </child>
               </object>
@@ -941,34 +935,128 @@
       </object>
     </child>
   </object>
-  <object class="GtkPopover" id="lyrics_popover">
-    <property name="can_focus">False</property>
-    <property name="relative_to">lyrics_button</property>
+  <object class="GtkPopover" id="more_popover">
+    <property name="can-focus">False</property>
+    <property name="hexpand">True</property>
+    <property name="relative-to">more_button</property>
     <child>
-      <object class="GtkScrolledWindow">
-        <property name="width_request">350</property>
-        <property name="height_request">220</property>
+      <object class="GtkBox">
         <property name="visible">True</property>
-        <property name="can_focus">True</property>
-        <property name="margin_top">8</property>
-        <property name="margin_bottom">8</property>
-        <property name="shadow_type">in</property>
+        <property name="can-focus">False</property>
         <child>
-          <object class="GtkTextView" id="lyrics_text_view">
+          <object class="GtkBox">
+            <property name="width-request">550</property>
             <property name="visible">True</property>
-            <property name="can_focus">True</property>
-            <property name="hscroll_policy">natural</property>
-            <property name="vscroll_policy">natural</property>
-            <property name="pixels_below_lines">1</property>
-            <property name="editable">False</property>
-            <property name="justification">center</property>
-            <property name="left_margin">8</property>
-            <property name="right_margin">8</property>
-            <property name="top_margin">18</property>
-            <property name="bottom_margin">18</property>
-            <property name="cursor_visible">False</property>
-            <property name="accepts_tab">False</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="halign">start</property>
+                <property name="label" 
translatable="yes">????????????</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="shadow-type">in</property>
+                <property name="margin-top">8</property>
+                <property name="margin-bottom">8</property>
+                <child>
+                  <object class="GtkTreeView" id="playlist_tree_view">
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="valign">start</property>
+                    <property name="headers_visible">False</property>
+                    <property name="enable_search">False</property>
+                    <property name="fixed_height_mode">True</property>
+                    <property name="show_expanders">False</property>
+                    <property name="enable_grid_lines">horizontal</property>
+                    <property name="activate_on_single_click">True</property>
+                    <child internal-child="selection">
+                      <object class="GtkTreeSelection"/>
+                    </child>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">True</property>
+            <property name="fill">True</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkBox">
+            <property name="visible">True</property>
+            <property name="can-focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="visible">True</property>
+                <property name="can-focus">False</property>
+                <property name="halign">start</property>
+                <property name="label" translatable="yes">??????</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkScrolledWindow">
+                <property name="width-request">350</property>
+                <property name="height-request">220</property>
+                <property name="visible">True</property>
+                <property name="can-focus">True</property>
+                <property name="halign">end</property>
+                <property name="margin-top">8</property>
+                <property name="margin-bottom">8</property>
+                <property name="shadow-type">in</property>
+                <child>
+                  <object class="GtkTextView" id="lyrics_text_view">
+                    <property name="visible">True</property>
+                    <property name="can-focus">True</property>
+                    <property name="hscroll-policy">natural</property>
+                    <property name="vscroll-policy">natural</property>
+                    <property name="pixels-below-lines">1</property>
+                    <property name="editable">False</property>
+                    <property name="justification">center</property>
+                    <property name="left-margin">8</property>
+                    <property name="right-margin">8</property>
+                    <property name="top-margin">18</property>
+                    <property name="bottom_margin">18</property>
+                    <property name="cursor-visible">False</property>
+                    <property name="accepts-tab">False</property>
+                  </object>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
           </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="fill">True</property>
+            <property name="position">1</property>
+          </packing>
         </child>
       </object>
     </child>

++++++ vendor.tar.xz ++++++
/work/SRC/openSUSE:Factory/netease-cloud-music-gtk/vendor.tar.xz 
/work/SRC/openSUSE:Factory/.netease-cloud-music-gtk.new.28504/vendor.tar.xz 
differ: char 25, line 1

Reply via email to