clementine-player / Clementine

:tangerine: Clementine Music Player
https://www.clementine-player.org/
GNU General Public License v3.0
3.72k stars 671 forks source link

How to get current song data in source code? #7095

Open berkozel1911 opened 2 years ago

berkozel1911 commented 2 years ago

Hi. I'm trying to write a plugin to Clementine. I need to get current playing song data such as song name, title name, album name etc... I tried accessing using app_->player()->GetCurrentItem()->Metadata() at src/core/player.cpp's PlayPause() function. Can't get any information with that, just causes to a segmentation fault.

jbroadus commented 2 years ago

I'm not sure where you are within PlayPause, but the current item may be null, so you'll need to check that.

When you say "plugin", do you mean that you're doing something with the qt plugin interface?

berkozel1911 commented 2 years ago

I'm not sure where you are within PlayPause, but the current item may be null, so you'll need to check that.

When you say "plugin", do you mean that you're doing something with the qt plugin interface?

I evaluated if the item is null with an if statement, the statement passes OK for "item" is not null. I guess my code trying to access with a wrong function, but i couldn't find any other helpful thing.

The plugin should communicate with the core music player interface. If i do qt plugin interface only, my plugin should work fine, but since Clementine can be controlled through tools such as mpris, playerctl, then my plugin may won't work properly. Btw the song data i need is title, album name, artist and duration only.

jbroadus commented 2 years ago

I'm still not clear where your plugin is hooking in, but I have done some experimentation with qt plugins in the past. Maybe this could be helpful: https://github.com/jbroadus/Clementine/tree/plugin-architecture

berkozel1911 commented 2 years ago

I'm still not clear where your plugin is hooking in, but I have done some experimentation with qt plugins in the past. Maybe this could be helpful: https://github.com/jbroadus/Clementine/tree/plugin-architecture

The plugin (actually more like a feature) should be hooked to Clementine's core play/pause/stop/next/back functions. Clementine's source should have these functions that all other controllers (GUI interface of Clementine's main window / playerctl - mpris) implement them.

I thought, if i hook my code to Qt plugin only then the hooks will work when user press to buttons of Clementine's main window buttons (play pause next etc...) only, but won't work if user wants to control Clementine's current playing status (playerctl, GNOME/KDE's media controller menus). Please correct me if i'm thinking wrong of this.

This is why i tried to play with functions at src/core/player.cpp.

berkozel1911 commented 2 years ago

Btw i found where the core controllers are, but the question is how can i extract current playing song info.

neoh4x0r commented 2 years ago

@frogwine What happens if you try the following ?

Playlist* playlist = app_->playlist_manager()->active();
if ( playlist ) {
    int row = playlist->current_row();
    PlaylistItemPtr itemptr = playlist->item_at ( row );
    if ( itemptr ) {
        Song song = itemptr->Metadata();
        QString album = song.album();
        QString title = song.title();
    }
}

Checking if PlaylistItemPtr is not null before attempting to access the song metadata is probably not very useful.

Due to multi-threading is possible for the PlaylistItemPtr to be valid when checked, but be invalid when the call to Metadata() is made.

There doesn't (currently) seem to be a way to check/verify that the Metadata is valid, that the shared_ptr is still valid. The only way (that I know of) to do that is use mutexes or other thread synchronization techniques to ensure that the shared_ptr is accessed correctly. (Which is not being done in the code currently).

std::shared_ptr is only a smart pointer that ensures the memory is deallocated when the last holder releases its hold on the ptr. It doesn't provide any sort of thread-safety

I believe this is what is causing your code to generate a Segmentation Fault (SIGSEGV).


Using the below as a template is probably better than manually accessing the playlist song information, through the shared_ptr:

You can look at: src/core/mpris2.h,src/core/mpris2.cpp: https://github.com/clementine-player/Clementine/blob/master/src/core/mpris2.h#L202 https://github.com/clementine-player/Clementine/blob/master/src/core/mpris2.cpp#L109

Pay close attention to this part in src/core/mpris2.cpp (line 109 and 336)

You might be able to create your custom class (by refactoring this code) to grab the song information.

Mpris2::Mpris2(Application* app, QObject* parent) : QObject(parent), app_(app) {

 connect(app_->playlist_manager(),
  SIGNAL(CurrentSongChanged(Song)),
  SLOT(CurrentSongChanged(Song))
 );

}

void Mpris2::CurrentSongChanged(const Song& song) {
 // you can 
 QString album = song.album();
 QString title = song.title();
 // do something with the song information
}

PS: the benefit of using SIGNAL/SLOT is that the song information is most likely being accessed on the correct thread (eg. may avoid generating a SIGSEGV due to accessing the shared_ptr / metadata in an invalid context).