Improve contextview and engine code

This commit is contained in:
Jonas Kvinge 2018-09-22 23:13:56 +02:00
parent 15a2ccc21e
commit 121a186160
22 changed files with 294 additions and 213 deletions

View File

@ -82,7 +82,8 @@ ContextView::ContextView(QWidget *parent) :
timeline_fade_(new QTimeLine(1000, this)), timeline_fade_(new QTimeLine(1000, this)),
image_strawberry_(":/pictures/strawberry.png"), image_strawberry_(":/pictures/strawberry.png"),
active_(false), active_(false),
downloading_covers_(false) downloading_covers_(false),
lyrics_id_(-1)
{ {
ui_->setupUi(this); ui_->setupUi(this);
@ -173,10 +174,10 @@ void ContextView::Playing() {}
void ContextView::Stopped() { void ContextView::Stopped() {
active_ = false; active_ = false;
song_playing_ = song_empty_; song_playing_ = Song();
song_ = song_empty_; song_ = Song();
downloading_covers_ = false; downloading_covers_ = false;
prev_artist_ = QString(); song_prev_ = Song();
lyrics_ = QString(); lyrics_ = QString();
SetImage(image_strawberry_); SetImage(image_strawberry_);
@ -190,21 +191,40 @@ void ContextView::UpdateNoSong() {
void ContextView::SongChanged(const Song &song) { void ContextView::SongChanged(const Song &song) {
image_previous_ = image_original_; if (song_playing_.is_valid() && song.id() == song_playing_.id() && song.url() == song_playing_.url()) {
prev_artist_ = song_playing_.artist(); UpdateSong(song);
lyrics_ = song.lyrics(); }
song_playing_ = song; else {
song_ = song; song_prev_ = song_playing_;
UpdateSong(); lyrics_ = song.lyrics();
update(); lyrics_id_ = -1;
if (action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song.artist(), song.album(), song.title()); song_playing_ = song;
song_ = song;
SetSong(song);
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) {
lyrics_fetcher_->Clear();
lyrics_id_ = lyrics_fetcher_->Search(song.artist(), song.album(), song.title());
}
}
} }
void ContextView::SetText(QLabel *label, int value, const QString &suffix, const QString &def) { void ContextView::SetLabelEnabled(QLabel *label) {
label->setEnabled(true);
label->setVisible(true);
label->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
}
void ContextView::SetLabelText(QLabel *label, int value, const QString &suffix, const QString &def) {
label->setText(value <= 0 ? def : (QString::number(value) + " " + suffix)); label->setText(value <= 0 ? def : (QString::number(value) + " " + suffix));
} }
void ContextView::SetLabelDisabled(QLabel *label) {
label->setEnabled(false);
label->setVisible(false);
label->setMaximumSize(0, 0);
}
void ContextView::NoSong() { void ContextView::NoSong() {
ui_->label_stop_top->setStyleSheet( ui_->label_stop_top->setStyleSheet(
@ -232,7 +252,7 @@ void ContextView::NoSong() {
} }
void ContextView::UpdateSong() { void ContextView::SetSong(const Song &song) {
QList <QLabel *> labels_play_data; QList <QLabel *> labels_play_data;
@ -251,85 +271,58 @@ void ContextView::UpdateSong() {
"font: 11pt;" "font: 11pt;"
"font-weight: regular;" "font-weight: regular;"
); );
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song_.PrettyTitle().toHtmlEscaped(), song_.artist().toHtmlEscaped(), song_.album().toHtmlEscaped())); ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song.PrettyTitle().toHtmlEscaped(), song.artist().toHtmlEscaped(), song.album().toHtmlEscaped()));
if (action_show_data_->isChecked()) { if (action_show_data_->isChecked()) {
for (QLabel *l : labels_play_data) {
l->setEnabled(true);
l->setVisible(true);
l->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
}
ui_->layout_play_data->setEnabled(true); ui_->layout_play_data->setEnabled(true);
ui_->filetype->setText(song_.TextForFiletype()); SetLabelEnabled(ui_->label_filetype);
ui_->length->setText(Utilities::PrettyTimeNanosec(song_.length_nanosec())); SetLabelEnabled(ui_->filetype);
if (song_.samplerate() <= 0) { SetLabelEnabled(ui_->label_length);
ui_->label_samplerate->setEnabled(false); SetLabelEnabled(ui_->length);
ui_->label_samplerate->setVisible(false); ui_->filetype->setText(song.TextForFiletype());
ui_->label_samplerate->setMaximumSize(0, 0); ui_->length->setText(Utilities::PrettyTimeNanosec(song.length_nanosec()));
ui_->samplerate->setEnabled(false); if (song.samplerate() <= 0) {
ui_->samplerate->setVisible(false); SetLabelDisabled(ui_->label_samplerate);
ui_->samplerate->setMaximumSize(0, 0); SetLabelDisabled(ui_->samplerate);
ui_->samplerate->clear(); ui_->samplerate->clear();
} }
else { else {
ui_->label_samplerate->setEnabled(true); SetLabelEnabled(ui_->label_samplerate);
ui_->label_samplerate->setVisible(true); SetLabelEnabled(ui_->samplerate);
ui_->label_samplerate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); SetLabelText(ui_->samplerate, song.samplerate(), "Hz");
ui_->samplerate->setEnabled(true);
ui_->samplerate->setVisible(true);
ui_->samplerate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
SetText(ui_->samplerate, song_.samplerate(), "Hz");
} }
if (song_.bitdepth() <= 0) { if (song.bitdepth() <= 0) {
ui_->label_bitdepth->setEnabled(false); SetLabelDisabled(ui_->label_bitdepth);
ui_->label_bitdepth->setVisible(false); SetLabelDisabled(ui_->bitdepth);
ui_->label_bitdepth->setMaximumSize(0, 0);
ui_->bitdepth->setEnabled(false);
ui_->bitdepth->setVisible(false);
ui_->bitdepth->setMaximumSize(0, 0);
ui_->bitdepth->clear(); ui_->bitdepth->clear();
} }
else { else {
ui_->label_bitdepth->setEnabled(true); SetLabelEnabled(ui_->label_bitdepth);
ui_->label_bitdepth->setVisible(true); SetLabelEnabled(ui_->bitdepth);
ui_->label_bitdepth->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); SetLabelText(ui_->bitdepth, song.bitdepth(), "Bit");
ui_->bitdepth->setEnabled(true);
ui_->bitdepth->setVisible(true);
ui_->bitdepth->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
SetText(ui_->bitdepth, song_.bitdepth(), "Bit");
} }
if (song_.bitrate() <= 0) { if (song.bitrate() <= 0) {
ui_->label_bitrate->setEnabled(false); SetLabelDisabled(ui_->label_bitrate);
ui_->label_bitrate->setVisible(false); SetLabelDisabled(ui_->bitrate);
ui_->label_bitrate->setMaximumSize(0, 0);
ui_->bitrate->setEnabled(false);
ui_->bitrate->setVisible(false);
ui_->bitrate->setMaximumSize(0, 0);
ui_->bitrate->clear(); ui_->bitrate->clear();
} }
else { else {
ui_->label_bitrate->setEnabled(true); SetLabelEnabled(ui_->label_bitrate);
ui_->label_bitrate->setVisible(true); SetLabelEnabled(ui_->bitrate);
ui_->label_bitrate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX); SetLabelText(ui_->bitrate, song.bitrate(), tr("kbps"));
ui_->bitrate->setEnabled(true);
ui_->bitrate->setVisible(true);
ui_->bitrate->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
SetText(ui_->bitrate, song_.bitrate(), tr("kbps"));
} }
ui_->spacer_play_data->changeSize(20, 20, QSizePolicy::Fixed); ui_->spacer_play_data->changeSize(20, 20, QSizePolicy::Fixed);
} }
else { else {
for (QLabel *l : labels_play_data) {
l->setEnabled(false);
l->setVisible(false);
l->setMaximumSize(0, 0);
}
ui_->layout_play_data->setEnabled(false);
ui_->filetype->clear(); ui_->filetype->clear();
ui_->length->clear(); ui_->length->clear();
ui_->samplerate->clear(); ui_->samplerate->clear();
ui_->bitdepth->clear(); ui_->bitdepth->clear();
ui_->bitrate->clear(); ui_->bitrate->clear();
for (QLabel *l : labels_play_data) {
SetLabelDisabled(l);
}
ui_->layout_play_data->setEnabled(false);
ui_->spacer_play_data->changeSize(0, 0, QSizePolicy::Fixed); ui_->spacer_play_data->changeSize(0, 0, QSizePolicy::Fixed);
} }
@ -403,18 +396,18 @@ void ContextView::UpdateSong() {
ui_->device->setMaximumSize(0, 0); ui_->device->setMaximumSize(0, 0);
} }
if (action_show_albums_->isChecked() && prev_artist_ != song_.artist()) { if (action_show_albums_->isChecked() && song_prev_.artist() != song.artist()) {
const QueryOptions opt; const QueryOptions opt;
CollectionBackend::AlbumList albumlist; CollectionBackend::AlbumList albumlist;
ui_->widget_play_albums->albums_model()->Reset(); ui_->widget_play_albums->albums_model()->Reset();
albumlist = app_->collection_backend()->GetAlbumsByArtist(song_.artist(), opt); albumlist = app_->collection_backend()->GetAlbumsByArtist(song.artist(), opt);
if (albumlist.count() > 1) { if (albumlist.count() > 1) {
ui_->label_play_albums->setVisible(true); ui_->label_play_albums->setVisible(true);
ui_->label_play_albums->setMinimumSize(0, 20); ui_->label_play_albums->setMinimumSize(0, 20);
ui_->label_play_albums->setText(QString("<b>Albums by %1</b>").arg( song_.artist().toHtmlEscaped())); ui_->label_play_albums->setText(QString("<b>Albums by %1</b>").arg( song.artist().toHtmlEscaped()));
ui_->label_play_albums->setStyleSheet("background-color: #3DADE8; color: rgb(255, 255, 255); font: 11pt;"); ui_->label_play_albums->setStyleSheet("background-color: #3DADE8; color: rgb(255, 255, 255); font: 11pt;");
for (CollectionBackend::Album album : albumlist) { for (CollectionBackend::Album album : albumlist) {
SongList songs = app_->collection_backend()->GetSongs(song_.artist(), album.album_name, opt); SongList songs = app_->collection_backend()->GetSongs(song.artist(), album.album_name, opt);
ui_->widget_play_albums->albums_model()->AddSongs(songs); ui_->widget_play_albums->albums_model()->AddSongs(songs);
} }
ui_->widget_play_albums->setEnabled(true); ui_->widget_play_albums->setEnabled(true);
@ -454,9 +447,62 @@ void ContextView::UpdateSong() {
} }
void ContextView::UpdateSong(const Song &song) {
if (song.artist() != song_playing_.artist() || song.album() != song_playing_.album() || song.title() != song_playing_.title()) {
ui_->label_play_top->setText( QString("<b>%1 - %2</b><br/>%3").arg(song.PrettyTitle().toHtmlEscaped(), song.artist().toHtmlEscaped(), song.album().toHtmlEscaped()));
}
if (action_show_data_->isChecked()) {
if (song.filetype() != song_playing_.filetype()) ui_->filetype->setText(song.TextForFiletype());
if (song.length_nanosec() != song_playing_.length_nanosec()) ui_->label_length->setText(Utilities::PrettyTimeNanosec(song.length_nanosec()));
if (song.samplerate() != song_playing_.samplerate()) {
if (song.samplerate() <= 0) {
SetLabelDisabled(ui_->label_samplerate);
SetLabelDisabled(ui_->samplerate);
ui_->samplerate->clear();
}
else {
SetLabelEnabled(ui_->label_samplerate);
SetLabelEnabled(ui_->samplerate);
SetLabelText(ui_->samplerate, song.samplerate(), "Hz");
}
}
if (song.bitdepth() != song_playing_.bitdepth()) {
if (song.bitdepth() <= 0) {
SetLabelDisabled(ui_->label_bitdepth);
SetLabelDisabled(ui_->bitdepth);
ui_->bitdepth->clear();
}
else {
SetLabelEnabled(ui_->label_bitdepth);
SetLabelEnabled(ui_->bitdepth);
SetLabelText(ui_->bitdepth, song.bitdepth(), "Bit");
}
}
if (song.bitrate() != song_playing_.bitrate()) {
if (song.bitrate() <= 0) {
SetLabelDisabled(ui_->label_bitrate);
SetLabelDisabled(ui_->bitrate);
ui_->bitrate->clear();
}
else {
SetLabelEnabled(ui_->label_bitrate);
SetLabelEnabled(ui_->bitrate);
SetLabelText(ui_->bitrate, song.bitrate(), tr("kbps"));
}
}
}
song_playing_ = song;
song_ = song;
}
void ContextView::UpdateLyrics(quint64 id, const QString lyrics) { void ContextView::UpdateLyrics(quint64 id, const QString lyrics) {
if (id != lyrics_id_) return;
lyrics_ = lyrics; lyrics_ = lyrics;
lyrics_id_ = -1;
if (action_show_lyrics_->isChecked()) { if (action_show_lyrics_->isChecked()) {
ui_->label_play_lyrics->setText(lyrics); ui_->label_play_lyrics->setText(lyrics);
} }
@ -551,6 +597,7 @@ void ContextView::ScaleCover() {
void ContextView::AlbumArtLoaded(const Song &song, const QString&, const QImage &image) { void ContextView::AlbumArtLoaded(const Song &song, const QString&, const QImage &image) {
if (song.id() != song_playing_.id() || song.url() != song_playing_.url()) return;
if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return; if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return;
if (image == image_original_) return; if (image == image_original_) return;
@ -573,6 +620,7 @@ void ContextView::SetImage(const QImage &image) {
DrawImage(&p); DrawImage(&p);
p.end(); p.end();
image_previous_ = image_original_;
image_original_ = image; image_original_ = image;
ScaleCover(); ScaleCover();
@ -624,7 +672,7 @@ void ContextView::ActionShowData() {
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("show_data", action_show_data_->isChecked()); s.setValue("show_data", action_show_data_->isChecked());
s.endGroup(); s.endGroup();
UpdateSong(); SetSong(song_);
} }
void ContextView::ActionShowOutput() { void ContextView::ActionShowOutput() {
@ -632,7 +680,7 @@ void ContextView::ActionShowOutput() {
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("show_output", action_show_output_->isChecked()); s.setValue("show_output", action_show_output_->isChecked());
s.endGroup(); s.endGroup();
UpdateSong(); SetSong(song_);
} }
void ContextView::ActionShowAlbums() { void ContextView::ActionShowAlbums() {
@ -640,8 +688,8 @@ void ContextView::ActionShowAlbums() {
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("show_albums", action_show_albums_->isChecked()); s.setValue("show_albums", action_show_albums_->isChecked());
s.endGroup(); s.endGroup();
prev_artist_ = QString(); song_prev_ = Song();
UpdateSong(); SetSong(song_);
} }
void ContextView::ActionShowLyrics() { void ContextView::ActionShowLyrics() {
@ -649,8 +697,11 @@ void ContextView::ActionShowLyrics() {
s.beginGroup(kSettingsGroup); s.beginGroup(kSettingsGroup);
s.setValue("show_lyrics", action_show_lyrics_->isChecked()); s.setValue("show_lyrics", action_show_lyrics_->isChecked());
s.endGroup(); s.endGroup();
UpdateSong(); SetSong(song_);
if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) lyrics_fetcher_->Search(song_.artist(), song_.album(), song_.title()); if (lyrics_.isEmpty() && action_show_lyrics_->isChecked()) {
lyrics_fetcher_->Clear();
lyrics_id_ = lyrics_fetcher_->Search(song_.artist(), song_.album(), song_.title());
}
} }
void ContextView::SearchCoverAutomatically() { void ContextView::SearchCoverAutomatically() {

View File

@ -94,21 +94,23 @@ class ContextView : public QWidget {
AlbumCoverLoaderOptions cover_loader_options_; AlbumCoverLoaderOptions cover_loader_options_;
Song song_; Song song_;
Song song_playing_; Song song_playing_;
Song song_empty_; Song song_prev_;
QImage image_original_; QImage image_original_;
QImage image_previous_; QImage image_previous_;
QPixmap pixmap_current_; QPixmap pixmap_current_;
QPixmap pixmap_previous_; QPixmap pixmap_previous_;
qreal pixmap_previous_opacity_; qreal pixmap_previous_opacity_;
std::unique_ptr<QMovie> spinner_animation_; std::unique_ptr<QMovie> spinner_animation_;
qint64 lyrics_id_;
QString prev_artist_;
QString lyrics_; QString lyrics_;
void AddActions(); void AddActions();
void SetText(QLabel *label, int value, const QString &suffix, const QString &def = QString()); void SetLabelEnabled(QLabel *label);
void SetLabelDisabled(QLabel *label);
void SetLabelText(QLabel *label, int value, const QString &suffix, const QString &def = QString());
void NoSong(); void NoSong();
void UpdateSong(); void SetSong(const Song &song);
void UpdateSong(const Song &song);
void SetImage(const QImage &image); void SetImage(const QImage &image);
void DrawImage(QPainter *p); void DrawImage(QPainter *p);
void ScaleCover(); void ScaleCover();

View File

@ -831,8 +831,8 @@ void MainWindow::MediaStopped() {
tray_icon_->SetProgress(0); tray_icon_->SetProgress(0);
tray_icon_->SetStopped(); tray_icon_->SetStopped();
song_playing_ = song_empty_; song_playing_ = Song();
song_ = song_empty_; song_ = Song();
image_original_ = QImage(); image_original_ = QImage();
} }

View File

@ -389,7 +389,6 @@ signals:
Song song_; Song song_;
Song song_playing_; Song song_playing_;
Song song_empty_;
QImage image_original_; QImage image_original_;
}; };

View File

@ -246,7 +246,7 @@ void Player::HandleLoadResult(const UrlHandler::LoadResult &result) {
item->SetTemporaryMetadata(song); item->SetTemporaryMetadata(song);
app_->playlist_manager()->active()->InformOfCurrentSongChange(); app_->playlist_manager()->active()->InformOfCurrentSongChange();
} }
engine_->Play(result.media_url_, stream_change_type_, item->Metadata().has_cue(), item->Metadata().beginning_nanosec(), item->Metadata().end_nanosec()); engine_->Play(result.media_url_, song.url(), stream_change_type_, item->Metadata().has_cue(), item->Metadata().beginning_nanosec(), item->Metadata().end_nanosec());
current_item_ = item; current_item_ = item;
loading_async_ = QUrl(); loading_async_ = QUrl();
@ -507,8 +507,7 @@ void Player::PlayAt(int index, Engine::TrackChangeFlags change, bool reshuffle)
} }
else { else {
loading_async_ = QUrl(); loading_async_ = QUrl();
engine_->Play(current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->Metadata().beginning_nanosec(), current_item_->Metadata().end_nanosec()); engine_->Play(current_item_->Url(), current_item_->Url(), change, current_item_->Metadata().has_cue(), current_item_->Metadata().beginning_nanosec(), current_item_->Metadata().end_nanosec());
} }
} }
@ -550,7 +549,7 @@ void Player::EngineMetadataReceived(const Engine::SimpleMetaBundle &bundle) {
PlaylistItemPtr item = app_->playlist_manager()->active()->current_item(); PlaylistItemPtr item = app_->playlist_manager()->active()->current_item();
if (!item) return; if (!item) return;
if (item->Metadata().url() != bundle.url) return; if (bundle.url != item->Metadata().url()) return;
Engine::SimpleMetaBundle bundle_copy = bundle; Engine::SimpleMetaBundle bundle_copy = bundle;
@ -627,6 +626,8 @@ void Player::TogglePrettyOSD() {
void Player::TrackAboutToEnd() { void Player::TrackAboutToEnd() {
qLog(Debug) << __PRETTY_FUNCTION__;
// If the current track was from a URL handler then it might have special behaviour to queue up a subsequent track. // If the current track was from a URL handler then it might have special behaviour to queue up a subsequent track.
// We don't want to preload (and scrobble) the next item in the playlist if it's just going to be stopped again immediately after. // We don't want to preload (and scrobble) the next item in the playlist if it's just going to be stopped again immediately after.
if (app_->playlist_manager()->active()->current_item()) { if (app_->playlist_manager()->active()->current_item()) {
@ -679,7 +680,7 @@ void Player::TrackAboutToEnd() {
break; break;
} }
} }
engine_->StartPreloading(url, next_item->Metadata().has_cue(), next_item->Metadata().beginning_nanosec(), next_item->Metadata().end_nanosec()); engine_->StartPreloading(url, next_item->Url(), next_item->Metadata().has_cue(), next_item->Metadata().beginning_nanosec(), next_item->Metadata().end_nanosec());
} }

View File

@ -996,12 +996,12 @@ void Song::MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle) {
if (!bundle.album.isEmpty()) d->album_ = bundle.album; if (!bundle.album.isEmpty()) d->album_ = bundle.album;
if (!bundle.comment.isEmpty()) d->comment_ = bundle.comment; if (!bundle.comment.isEmpty()) d->comment_ = bundle.comment;
if (!bundle.genre.isEmpty()) d->genre_ = bundle.genre; if (!bundle.genre.isEmpty()) d->genre_ = bundle.genre;
if (!bundle.bitrate.isEmpty()) d->bitrate_ = bundle.bitrate.toInt(); if (bundle.bitrate > 0) d->bitrate_ = bundle.bitrate;
if (!bundle.samplerate.isEmpty()) d->samplerate_ = bundle.samplerate.toInt(); if (bundle.samplerate > 0) d->samplerate_ = bundle.samplerate;
if (!bundle.bitdepth.isEmpty()) d->samplerate_ = bundle.bitdepth.toInt(); if (bundle.bitdepth > 0) d->samplerate_ = bundle.bitdepth;
if (!bundle.length.isEmpty()) set_length_nanosec(bundle.length.toLongLong()); if (bundle.length > 0) set_length_nanosec(bundle.length);
if (!bundle.year.isEmpty()) d->year_ = bundle.year.toInt(); if (bundle.year > 0) d->year_ = bundle.year;
if (!bundle.tracknr.isEmpty()) d->track_ = bundle.tracknr.toInt(); if (bundle.tracknr > 0) d->track_ = bundle.tracknr;
} }

View File

@ -308,7 +308,6 @@ class Song {
void set_image(const QImage &i); void set_image(const QImage &i);
// Comparison functions // Comparison functions
bool IsMetadataEqual(const Song &other) const; bool IsMetadataEqual(const Song &other) const;
bool IsOnSameAlbum(const Song &other) const; bool IsOnSameAlbum(const Song &other) const;

View File

@ -60,11 +60,12 @@ Engine::Base::Base()
Engine::Base::~Base() {} Engine::Base::~Base() {}
bool Engine::Base::Load(const QUrl &url, TrackChangeFlags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool Engine::Base::Load(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
Q_UNUSED(force_stop_at_end); Q_UNUSED(force_stop_at_end);
url_ = url; media_url_ = media_url;
original_url_ = original_url;
beginning_nanosec_ = beginning_nanosec; beginning_nanosec_ = beginning_nanosec;
end_nanosec_ = end_nanosec; end_nanosec_ = end_nanosec;
@ -73,12 +74,13 @@ bool Engine::Base::Load(const QUrl &url, TrackChangeFlags, bool force_stop_at_en
} }
bool Engine::Base::Play(const QUrl &url, TrackChangeFlags flags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool Engine::Base::Play(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags flags, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
if (!Load(url, flags, force_stop_at_end, beginning_nanosec, end_nanosec)) if (!Load(media_url, original_url, flags, force_stop_at_end, beginning_nanosec, end_nanosec))
return false; return false;
return Play(0); return Play(0);
} }
void Engine::Base::SetVolume(uint value) { void Engine::Base::SetVolume(uint value) {

View File

@ -68,8 +68,8 @@ public:
virtual bool Init() = 0; virtual bool Init() = 0;
virtual State state() const = 0; virtual State state() const = 0;
virtual void StartPreloading(const QUrl&, bool, qint64, qint64) {} virtual void StartPreloading(const QUrl &media_url, const QUrl &original_url, bool, qint64, qint64) {}
virtual bool Load(const QUrl &url, TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); virtual bool Load(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
virtual bool Play(quint64 offset_nanosec) = 0; virtual bool Play(quint64 offset_nanosec) = 0;
virtual void Stop(bool stop_after = false) = 0; virtual void Stop(bool stop_after = false) = 0;
virtual void Pause() = 0; virtual void Pause() = 0;
@ -97,7 +97,7 @@ public:
// Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length). // Plays a media stream represented with the URL 'u' from the given 'beginning' to the given 'end' (usually from 0 to a song's length).
// Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown. // Both markers should be passed in nanoseconds. 'end' can be negative, indicating that the real length of 'u' stream is unknown.
bool Play(const QUrl &u, TrackChangeFlags c, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Play(const QUrl &media_url, const QUrl &original_url, TrackChangeFlags c, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
void SetVolume(uint value); void SetVolume(uint value);
static uint MakeVolumeLogarithmic(uint volume); static uint MakeVolumeLogarithmic(uint volume);
@ -164,7 +164,8 @@ protected:
uint volume_; uint volume_;
quint64 beginning_nanosec_; quint64 beginning_nanosec_;
qint64 end_nanosec_; qint64 end_nanosec_;
QUrl url_; QUrl media_url_;
QUrl original_url_;
Scope scope_; Scope scope_;
bool buffering_; bool buffering_;
bool equalizer_enabled_; bool equalizer_enabled_;
@ -209,12 +210,13 @@ struct SimpleMetaBundle {
QString album; QString album;
QString comment; QString comment;
QString genre; QString genre;
QString bitrate; qlonglong length;
QString samplerate; int year;
QString bitdepth; int tracknr;
QString length; int samplerate;
QString year; int bitdepth;
QString tracknr; qlonglong bitrate;
QString lyrics;
}; };
} // namespace } // namespace

View File

@ -137,7 +137,7 @@ bool GstEngine::Init() {
Engine::State GstEngine::state() const { Engine::State GstEngine::state() const {
if (!current_pipeline_) return url_.isEmpty() ? Engine::Empty : Engine::Idle; if (!current_pipeline_) return media_url_.isEmpty() ? Engine::Empty : Engine::Idle;
switch (current_pipeline_->state()) { switch (current_pipeline_->state()) {
case GST_STATE_NULL: case GST_STATE_NULL:
@ -154,32 +154,32 @@ Engine::State GstEngine::state() const {
} }
void GstEngine::StartPreloading(const QUrl &url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec) { void GstEngine::StartPreloading(const QUrl &media_url, const QUrl &original_url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec) {
EnsureInitialised(); EnsureInitialised();
QByteArray gst_url = FixupUrl(url); QByteArray gst_url = FixupUrl(media_url);
// No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully) // No crossfading, so we can just queue the new URL in the existing pipeline and get gapless playback (hopefully)
if (current_pipeline_) if (current_pipeline_)
current_pipeline_->SetNextUrl(gst_url, beginning_nanosec, force_stop_at_end ? end_nanosec : 0); current_pipeline_->SetNextUrl(gst_url, original_url, beginning_nanosec, force_stop_at_end ? end_nanosec : 0);
} }
bool GstEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool GstEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
EnsureInitialised(); EnsureInitialised();
Engine::Base::Load(url, change, force_stop_at_end, beginning_nanosec, end_nanosec); Engine::Base::Load(media_url, original_url, change, force_stop_at_end, beginning_nanosec, end_nanosec);
QByteArray gst_url = FixupUrl(url); QByteArray gst_url = FixupUrl(media_url);
bool crossfade = current_pipeline_ && ((crossfade_enabled_ && change & Engine::Manual) || (autocrossfade_enabled_ && change & Engine::Auto) || ((crossfade_enabled_ || autocrossfade_enabled_) && change & Engine::Intro)); bool crossfade = current_pipeline_ && ((crossfade_enabled_ && change & Engine::Manual) || (autocrossfade_enabled_ && change & Engine::Auto) || ((crossfade_enabled_ || autocrossfade_enabled_) && change & Engine::Intro));
if (change & Engine::Auto && change & Engine::SameAlbum && !crossfade_same_album_) if (change & Engine::Auto && change & Engine::SameAlbum && !crossfade_same_album_)
crossfade = false; crossfade = false;
if (!crossfade && current_pipeline_ && current_pipeline_->url() == gst_url && change & Engine::Auto) { if (!crossfade && current_pipeline_ && current_pipeline_->media_url() == gst_url && change & Engine::Auto) {
// We're not crossfading, and the pipeline is already playing the URI we want, so just do nothing. // We're not crossfading, and the pipeline is already playing the URI we want, so just do nothing.
return true; return true;
} }
@ -188,7 +188,7 @@ bool GstEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool forc
//SetEqualizerParameters(equalizer_preamp_, equalizer_gains_); //SetEqualizerParameters(equalizer_preamp_, equalizer_gains_);
//SetStereoBalance(stereo_balance_); //SetStereoBalance(stereo_balance_);
shared_ptr<GstEnginePipeline> pipeline = CreatePipeline(gst_url, force_stop_at_end ? end_nanosec : 0); shared_ptr<GstEnginePipeline> pipeline = CreatePipeline(gst_url, original_url, force_stop_at_end ? end_nanosec : 0);
if (!pipeline) return false; if (!pipeline) return false;
if (crossfade) StartFadeout(); if (crossfade) StartFadeout();
@ -229,7 +229,8 @@ void GstEngine::Stop(bool stop_after) {
StopTimers(); StopTimers();
url_ = QUrl(); // To ensure we return Empty from state() media_url_ = QUrl(); // To ensure we return Empty from state()
original_url_ = QUrl();
beginning_nanosec_ = end_nanosec_ = 0; beginning_nanosec_ = end_nanosec_ = 0;
// Check if we started a fade out. If it isn't finished yet and the user pressed stop, we cancel the fader and just stop the playback. // Check if we started a fade out. If it isn't finished yet and the user pressed stop, we cancel the fader and just stop the playback.
@ -575,7 +576,7 @@ void GstEngine::HandlePipelineError(int pipeline_id, const QString &message, int
BufferingFinished(); BufferingFinished();
emit StateChanged(Engine::Error); emit StateChanged(Engine::Error);
// unable to play media stream with this url // unable to play media stream with this url
emit InvalidSongRequested(url_); emit InvalidSongRequested(media_url_);
emit Error(message); emit Error(message);
@ -649,9 +650,9 @@ void GstEngine::PlayDone(QFuture<GstStateChangeReturn> future, const quint64 off
if (ret == GST_STATE_CHANGE_FAILURE) { if (ret == GST_STATE_CHANGE_FAILURE) {
// Failure, but we got a redirection URL - try loading that instead // Failure, but we got a redirection URL - try loading that instead
QByteArray redirect_url = current_pipeline_->redirect_url(); QByteArray redirect_url = current_pipeline_->redirect_url();
if (!redirect_url.isEmpty() && redirect_url != current_pipeline_->url()) { if (!redirect_url.isEmpty() && redirect_url != current_pipeline_->media_url()) {
qLog(Info) << "Redirecting to" << redirect_url; qLog(Info) << "Redirecting to" << redirect_url;
current_pipeline_ = CreatePipeline(redirect_url, end_nanosec_); current_pipeline_ = CreatePipeline(redirect_url, current_pipeline_->original_url(), end_nanosec_);
Play(offset_nanosec); Play(offset_nanosec);
return; return;
} }
@ -665,14 +666,14 @@ void GstEngine::PlayDone(QFuture<GstStateChangeReturn> future, const quint64 off
StartTimers(); StartTimers();
// initial offset // Initial offset
if (offset_nanosec != 0 || beginning_nanosec_ != 0) { if (offset_nanosec != 0 || beginning_nanosec_ != 0) {
Seek(offset_nanosec); Seek(offset_nanosec);
} }
emit StateChanged(Engine::Playing); emit StateChanged(Engine::Playing);
// we've successfully started playing a media stream with this url // We've successfully started playing a media stream with this url
emit ValidSongRequested(url_); emit ValidSongRequested(media_url_);
} }
@ -823,25 +824,14 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
connect(ret.get(), SIGNAL(BufferingProgress(int)), SLOT(BufferingProgress(int))); connect(ret.get(), SIGNAL(BufferingProgress(int)), SLOT(BufferingProgress(int)));
connect(ret.get(), SIGNAL(BufferingFinished()), SLOT(BufferingFinished())); connect(ret.get(), SIGNAL(BufferingFinished()), SLOT(BufferingFinished()));
return ret;
}
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QUrl &url, qint64 end_nanosec) {
shared_ptr<GstEnginePipeline> ret = CreatePipeline();
if (!ret->InitFromUrl(url.toEncoded(), end_nanosec)) ret.reset();
return ret; return ret;
} }
shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QByteArray &url, qint64 end_nanosec) { shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline(const QByteArray &gst_url, const QUrl &original_url, qint64 end_nanosec) {
shared_ptr<GstEnginePipeline> ret = CreatePipeline(); shared_ptr<GstEnginePipeline> ret = CreatePipeline();
if (!ret->InitFromUrl(gst_url, original_url, end_nanosec)) ret.reset();
if (!ret->InitFromUrl(url, end_nanosec)) ret.reset();
return ret; return ret;
} }

View File

@ -68,8 +68,8 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
bool Init(); bool Init();
Engine::State state() const; Engine::State state() const;
void StartPreloading(const QUrl &url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec); void StartPreloading(const QUrl &media_url, const QUrl &original_url, bool force_stop_at_end, qint64 beginning_nanosec, qint64 end_nanosec);
bool Load(const QUrl &, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec); bool Play(quint64 offset_nanosec);
void Stop(bool stop_after = false); void Stop(bool stop_after = false);
void Pause(); void Pause();
@ -157,8 +157,7 @@ class GstEngine : public Engine::Base, public GstBufferConsumer {
void StopTimers(); void StopTimers();
std::shared_ptr<GstEnginePipeline> CreatePipeline(); std::shared_ptr<GstEnginePipeline> CreatePipeline();
std::shared_ptr<GstEnginePipeline> CreatePipeline(const QUrl &url, qint64 end_nanosec); std::shared_ptr<GstEnginePipeline> CreatePipeline(const QByteArray &gst_url, const QUrl &original_url, qint64 end_nanosec);
std::shared_ptr<GstEnginePipeline> CreatePipeline(const QByteArray &url, qint64 end_nanosec);
void UpdateScope(int chunk_length); void UpdateScope(int chunk_length);

View File

@ -371,15 +371,16 @@ bool GstEnginePipeline::InitFromString(const QString &pipeline) {
} }
bool GstEnginePipeline::InitFromUrl(const QByteArray &url, qint64 end_nanosec) { bool GstEnginePipeline::InitFromUrl(const QByteArray &media_url, const QUrl original_url, qint64 end_nanosec) {
url_ = url; media_url_ = media_url;
original_url_ = original_url;
end_offset_nanosec_ = end_nanosec; end_offset_nanosec_ = end_nanosec;
pipeline_ = engine_->CreateElement("playbin"); pipeline_ = engine_->CreateElement("playbin");
if (pipeline_ == nullptr) return false; if (pipeline_ == nullptr) return false;
g_object_set(G_OBJECT(pipeline_), "uri", url.constData(), nullptr); g_object_set(G_OBJECT(pipeline_), "uri", media_url.constData(), nullptr);
CHECKED_GCONNECT(G_OBJECT(pipeline_), "about-to-finish", &AboutToFinishCallback, this); CHECKED_GCONNECT(G_OBJECT(pipeline_), "about-to-finish", &AboutToFinishCallback, this);
CHECKED_GCONNECT(G_OBJECT(pipeline_), "pad-added", &NewPadCallback, this); CHECKED_GCONNECT(G_OBJECT(pipeline_), "pad-added", &NewPadCallback, this);
@ -501,9 +502,11 @@ void GstEnginePipeline::StreamStartMessageReceived() {
if (next_uri_set_) { if (next_uri_set_) {
next_uri_set_ = false; next_uri_set_ = false;
url_ = next_url_; media_url_ = next_media_url_;
original_url_ = next_original_url_;
end_offset_nanosec_ = next_end_offset_nanosec_; end_offset_nanosec_ = next_end_offset_nanosec_;
next_url_ = QByteArray(); next_media_url_ = QByteArray();
next_original_url_ = QUrl();
next_beginning_offset_nanosec_ = 0; next_beginning_offset_nanosec_ = 0;
next_end_offset_nanosec_ = 0; next_end_offset_nanosec_ = 0;
@ -580,11 +583,18 @@ void GstEnginePipeline::TagMessageReceived(GstMessage *msg) {
gst_message_parse_tag(msg, &taglist); gst_message_parse_tag(msg, &taglist);
Engine::SimpleMetaBundle bundle; Engine::SimpleMetaBundle bundle;
bundle.url = QUrl(QString(url_)); bundle.url = original_url_;
bundle.title = ParseTag(taglist, GST_TAG_TITLE); bundle.title = ParseStrTag(taglist, GST_TAG_TITLE);
bundle.artist = ParseTag(taglist, GST_TAG_ARTIST); bundle.artist = ParseStrTag(taglist, GST_TAG_ARTIST);
bundle.comment = ParseTag(taglist, GST_TAG_COMMENT); bundle.comment = ParseStrTag(taglist, GST_TAG_COMMENT);
bundle.album = ParseTag(taglist, GST_TAG_ALBUM); bundle.album = ParseStrTag(taglist, GST_TAG_ALBUM);
bundle.length = 0;
bundle.year = 0;
bundle.tracknr = 0;
bundle.samplerate = 0;
bundle.bitdepth = 0;
bundle.bitrate = ParseUIntTag(taglist, GST_TAG_BITRATE) / 1000;
bundle.lyrics = ParseStrTag(taglist, GST_TAG_LYRICS);
gst_tag_list_free(taglist); gst_tag_list_free(taglist);
@ -595,7 +605,7 @@ void GstEnginePipeline::TagMessageReceived(GstMessage *msg) {
} }
QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const { QString GstEnginePipeline::ParseStrTag(GstTagList *list, const char *tag) const {
gchar *data = nullptr; gchar *data = nullptr;
bool success = gst_tag_list_get_string(list, tag, &data); bool success = gst_tag_list_get_string(list, tag, &data);
@ -609,6 +619,17 @@ QString GstEnginePipeline::ParseTag(GstTagList *list, const char *tag) const {
} }
guint GstEnginePipeline::ParseUIntTag(GstTagList *list, const char *tag) const {
guint data;
bool success = gst_tag_list_get_uint(list, tag, &data);
guint ret = 0;
if (success && data) ret = data;
return ret;
}
void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) { void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
if (msg->src != GST_OBJECT(pipeline_)) { if (msg->src != GST_OBJECT(pipeline_)) {
@ -632,7 +653,7 @@ void GstEnginePipeline::StateChangedMessageReceived(GstMessage *msg) {
if (next_uri_set_ && new_state == GST_STATE_READY) { if (next_uri_set_ && new_state == GST_STATE_READY) {
// Revert uri and go back to PLAY state again // Revert uri and go back to PLAY state again
next_uri_set_ = false; next_uri_set_ = false;
g_object_set(G_OBJECT(pipeline_), "uri", url_.constData(), nullptr); g_object_set(G_OBJECT(pipeline_), "uri", media_url_.constData(), nullptr);
SetState(GST_STATE_PLAYING); SetState(GST_STATE_PLAYING);
} }
} }
@ -760,10 +781,11 @@ GstPadProbeReturn GstEnginePipeline::HandoffCallback(GstPad*, GstPadProbeInfo *i
quint64 end_time = start_time + duration; quint64 end_time = start_time + duration;
if (end_time > instance->end_offset_nanosec_) { if (end_time > instance->end_offset_nanosec_) {
if (instance->has_next_valid_url() && instance->next_url_ == instance->url_ && instance->next_beginning_offset_nanosec_ == instance->end_offset_nanosec_) { if (instance->has_next_valid_url() && instance->next_media_url_ == instance->media_url_ && instance->next_beginning_offset_nanosec_ == instance->end_offset_nanosec_) {
// The "next" song is actually the next segment of this file - so cheat and keep on playing, but just tell the Engine we've moved on. // The "next" song is actually the next segment of this file - so cheat and keep on playing, but just tell the Engine we've moved on.
instance->end_offset_nanosec_ = instance->next_end_offset_nanosec_; instance->end_offset_nanosec_ = instance->next_end_offset_nanosec_;
instance->next_url_ = QByteArray(); instance->next_media_url_ = QByteArray();
instance->next_original_url_ = QUrl();
instance->next_beginning_offset_nanosec_ = 0; instance->next_beginning_offset_nanosec_ = 0;
instance->next_end_offset_nanosec_ = 0; instance->next_end_offset_nanosec_ = 0;
@ -816,7 +838,7 @@ void GstEnginePipeline::AboutToFinishCallback(GstPlayBin *bin, gpointer self) {
// Set the next uri. When the current song ends it will be played automatically and a STREAM_START message is send to the bus. // Set the next uri. When the current song ends it will be played automatically and a STREAM_START message is send to the bus.
// When the next uri is not playable an error message is send when the pipeline goes to PLAY (or PAUSE) state or immediately if it is currently in PLAY state. // When the next uri is not playable an error message is send when the pipeline goes to PLAY (or PAUSE) state or immediately if it is currently in PLAY state.
instance->next_uri_set_ = true; instance->next_uri_set_ = true;
g_object_set(G_OBJECT(instance->pipeline_), "uri", instance->next_url_.constData(), nullptr); g_object_set(G_OBJECT(instance->pipeline_), "uri", instance->next_media_url_.constData(), nullptr);
} }
} }
@ -1059,9 +1081,10 @@ void GstEnginePipeline::RemoveAllBufferConsumers() {
buffer_consumers_.clear(); buffer_consumers_.clear();
} }
void GstEnginePipeline::SetNextUrl(const QByteArray &url, qint64 beginning_nanosec, qint64 end_nanosec) { void GstEnginePipeline::SetNextUrl(const QByteArray &media_url, const QUrl &original_url, qint64 beginning_nanosec, qint64 end_nanosec) {
next_url_ = url; next_media_url_ = media_url;
next_original_url_ = original_url;
next_beginning_offset_nanosec_ = beginning_nanosec; next_beginning_offset_nanosec_ = beginning_nanosec;
next_end_offset_nanosec_ = end_nanosec; next_end_offset_nanosec_ = end_nanosec;

View File

@ -71,7 +71,7 @@ class GstEnginePipeline : public QObject {
void set_mono_playback(bool enabled); void set_mono_playback(bool enabled);
// Creates the pipeline, returns false on error // Creates the pipeline, returns false on error
bool InitFromUrl(const QByteArray &url, qint64 end_nanosec); bool InitFromUrl(const QByteArray &media_url, const QUrl original_url, qint64 end_nanosec);
bool InitFromString(const QString &pipeline); bool InitFromString(const QString &pipeline);
// GstBufferConsumers get fed audio data. Thread-safe. // GstBufferConsumers get fed audio data. Thread-safe.
@ -89,13 +89,14 @@ class GstEnginePipeline : public QObject {
void StartFader(qint64 duration_nanosec, QTimeLine::Direction direction = QTimeLine::Forward, QTimeLine::CurveShape shape = QTimeLine::LinearCurve, bool use_fudge_timer = true); void StartFader(qint64 duration_nanosec, QTimeLine::Direction direction = QTimeLine::Forward, QTimeLine::CurveShape shape = QTimeLine::LinearCurve, bool use_fudge_timer = true);
// If this is set then it will be loaded automatically when playback finishes for gapless playback // If this is set then it will be loaded automatically when playback finishes for gapless playback
void SetNextUrl(const QByteArray &url, qint64 beginning_nanosec, qint64 end_nanosec); void SetNextUrl(const QByteArray &media_url, const QUrl &original_url, qint64 beginning_nanosec, qint64 end_nanosec);
bool has_next_valid_url() const { return !next_url_.isNull() && !next_url_.isEmpty(); } bool has_next_valid_url() const { return !next_media_url_.isNull() && !next_media_url_.isEmpty(); }
void SetSourceDevice(QString device) { source_device_ = device; } void SetSourceDevice(QString device) { source_device_ = device; }
// Get information about the music playback // Get information about the music playback
QByteArray url() const { return url_; } QByteArray media_url() const { return media_url_; }
QUrl original_url() const { return original_url_; }
bool is_valid() const { return valid_; } bool is_valid() const { return valid_; }
// Please note that this method (unlike GstEngine's.position()) is multiple-section media unaware. // Please note that this method (unlike GstEngine's.position()) is multiple-section media unaware.
qint64 position() const; qint64 position() const;
@ -150,7 +151,8 @@ signals:
void StreamStatusMessageReceived(GstMessage*); void StreamStatusMessageReceived(GstMessage*);
void StreamStartMessageReceived(); void StreamStartMessageReceived();
QString ParseTag(GstTagList *list, const char *tag) const; QString ParseStrTag(GstTagList *list, const char *tag) const;
guint ParseUIntTag(GstTagList *list, const char *tag) const;
bool InitDecodeBin(GstElement* new_bin); bool InitDecodeBin(GstElement* new_bin);
bool InitAudioBin(); bool InitAudioBin();
@ -213,8 +215,10 @@ signals:
bool segment_start_received_; bool segment_start_received_;
// The URL that is currently playing, and the URL that is to be preloaded when the current track is close to finishing. // The URL that is currently playing, and the URL that is to be preloaded when the current track is close to finishing.
QByteArray url_; QByteArray media_url_;
QByteArray next_url_; QUrl original_url_;
QByteArray next_media_url_;
QUrl next_original_url_;
// If this is > 0 then the pipeline will be forced to stop when playback goes past this position. // If this is > 0 then the pipeline will be forced to stop when playback goes past this position.
qint64 end_offset_nanosec_; qint64 end_offset_nanosec_;

View File

@ -64,8 +64,8 @@ bool PhononEngine::CanDecode(const QUrl &url) {
return true; return true;
} }
bool PhononEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool PhononEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
media_object_->setCurrentSource(Phonon::MediaSource(url)); media_object_->setCurrentSource(Phonon::MediaSource(media_url));
return true; return true;
} }

View File

@ -48,7 +48,7 @@ class PhononEngine : public Engine::Base {
bool CanDecode(const QUrl &url); bool CanDecode(const QUrl &url);
bool Load(const QUrl &, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec); bool Play(quint64 offset_nanosec);
void Stop(bool stop_after = false); void Stop(bool stop_after = false);
void Pause(); void Pause();

View File

@ -100,12 +100,12 @@ bool VLCEngine::Initialised() const {
} }
bool VLCEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool VLCEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
if (!Initialised()) return false; if (!Initialised()) return false;
// Create the media object // Create the media object
VlcScopedRef<libvlc_media_t> media(libvlc_media_new_location(instance_, url.toEncoded().constData())); VlcScopedRef<libvlc_media_t> media(libvlc_media_new_location(instance_, media_url.toEncoded().constData()));
libvlc_media_player_set_media(player_, media); libvlc_media_player_set_media(player_, media);

View File

@ -48,7 +48,7 @@ class VLCEngine : public Engine::Base {
bool Init(); bool Init();
Engine::State state() const { return state_; } Engine::State state() const { return state_; }
bool Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec); bool Play(quint64 offset_nanosec);
void Stop(bool stop_after = false); void Stop(bool stop_after = false);
void Pause(); void Pause();

View File

@ -180,20 +180,20 @@ Engine::State XineEngine::state() const {
return Engine::Empty; return Engine::Empty;
case XINE_STATUS_STOP: case XINE_STATUS_STOP:
default: default:
return url_.isEmpty() ? Engine::Empty : Engine::Idle; return media_url_.isEmpty() ? Engine::Empty : Engine::Idle;
} }
} }
bool XineEngine::Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) { bool XineEngine::Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec) {
if (!EnsureStream()) return false; if (!EnsureStream()) return false;
Engine::Base::Load(url, change, force_stop_at_end, beginning_nanosec, end_nanosec); Engine::Base::Load(media_url_, original_url_, change, force_stop_at_end, beginning_nanosec, end_nanosec);
xine_close(stream_); xine_close(stream_);
//int result = xine_open(stream_, url.path().toUtf8()); //int result = xine_open(stream_, url.path().toUtf8());
int result = xine_open(stream_, url.toString().toUtf8()); int result = xine_open(stream_, media_url.toString().toUtf8());
if (result) { if (result) {
#ifndef XINE_SAFE_MODE #ifndef XINE_SAFE_MODE
@ -375,7 +375,7 @@ uint XineEngine::length() const {
// Xine often delivers nonsense values for VBR files and such, so we only use the length for remote files // Xine often delivers nonsense values for VBR files and such, so we only use the length for remote files
if (url_.scheme().toLower() == "file") return 0; if (media_url_.scheme().toLower() == "file") return 0;
else { else {
int pos = 0, time = 0, length = 0; int pos = 0, time = 0, length = 0;
@ -477,10 +477,9 @@ bool XineEngine::MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b) {
b.artist = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ARTIST)); b.artist = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ARTIST));
b.album = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ALBUM)); b.album = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_ALBUM));
b.genre = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_GENRE)); b.genre = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_GENRE));
b.year = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR)); b.year = atoi(xine_get_meta_info(tmpstream, XINE_META_INFO_YEAR));
b.tracknr = QString::fromUtf8(xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER)); b.tracknr = atoi(xine_get_meta_info(tmpstream, XINE_META_INFO_TRACK_NUMBER));
if (b.tracknr.isEmpty()) //if (b.tracknr <= 0) b.tracknr = QFileInfo(url.path()).fileName();
b.tracknr = QFileInfo(url.path()).fileName();
} }
else { else {
b.title = QString("Track %1").arg(QFileInfo(url.path()).fileName()); b.title = QString("Track %1").arg(QFileInfo(url.path()).fileName());
@ -490,18 +489,19 @@ bool XineEngine::MetaDataForUrl(const QUrl &url, Engine::SimpleMetaBundle &b) {
if (audioCodec == "CDDA" || audioCodec == "WAV") { if (audioCodec == "CDDA" || audioCodec == "WAV") {
result = true; result = true;
b.url = url;
int samplerate = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_SAMPLERATE); int samplerate = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_SAMPLERATE);
int bitdepth = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_BITS);
int channels = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_CHANNELS);
// Xine would provide a XINE_STREAM_INFO_AUDIO_BITRATE, but unfortunately not for CDDA or WAV so we calculate the bitrate by our own // Xine would provide a XINE_STREAM_INFO_AUDIO_BITRATE, but unfortunately not for CDDA or WAV so we calculate the bitrate by our own
int bitsPerSample = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_BITS); int bitrate = (samplerate * bitdepth * channels) / 1000;
int nbrChannels = xine_get_stream_info(tmpstream, XINE_STREAM_INFO_AUDIO_CHANNELS);
int bitrate = (samplerate * bitsPerSample * nbrChannels) / 1000;
b.bitrate = QString::number(bitrate); b.samplerate = samplerate;
b.samplerate = QString::number(samplerate); b.bitdepth = bitdepth;
b.bitrate = bitrate;
int pos, time, length = 0; int pos, time, length = 0;
xine_get_pos_length(tmpstream, &pos, &time, &length); xine_get_pos_length(tmpstream, &pos, &time, &length);
b.length = QString::number(length / 1000); b.length = length / 1000;
} }
xine_close(tmpstream); xine_close(tmpstream);
} }
@ -750,7 +750,7 @@ bool XineEngine::event(QEvent *e) {
return true; return true;
case XineEvent::InfoMessage: case XineEvent::InfoMessage:
emit InfoMessage((*message).arg(url_.toString())); emit InfoMessage((*message).arg(media_url_.toString()));
delete message; delete message;
return true; return true;
@ -771,7 +771,7 @@ bool XineEngine::event(QEvent *e) {
case XineEvent::Redirecting: case XineEvent::Redirecting:
emit StatusText(QString("Redirecting to: ").arg(*message)); emit StatusText(QString("Redirecting to: ").arg(*message));
Load(QUrl(*message), Engine::Auto, false, 0, 0); Load(QUrl(*message), original_url_, Engine::Auto, false, 0, 0);
Play(0); Play(0);
delete message; delete message;
return true; return true;
@ -787,15 +787,18 @@ bool XineEngine::event(QEvent *e) {
Engine::SimpleMetaBundle XineEngine::fetchMetaData() const { Engine::SimpleMetaBundle XineEngine::fetchMetaData() const {
Engine::SimpleMetaBundle bundle; Engine::SimpleMetaBundle bundle;
bundle.url = original_url;
bundle.title = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_TITLE)); bundle.title = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_TITLE));
bundle.artist = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ARTIST)); bundle.artist = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ARTIST));
bundle.album = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ALBUM)); bundle.album = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_ALBUM));
bundle.comment = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_COMMENT)); bundle.comment = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_COMMENT));
bundle.genre = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_GENRE)); bundle.genre = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_GENRE));
bundle.bitrate = QString::number(xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_BITRATE) / 1000); bundle.length = 0;
bundle.samplerate = QString::number(xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_SAMPLERATE)); bundle.year = atoi(xine_get_meta_info(stream_, XINE_META_INFO_YEAR));
bundle.year = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_YEAR)); bundle.tracknr = atoi(xine_get_meta_info(stream_, XINE_META_INFO_TRACK_NUMBER));
bundle.tracknr = QString::fromUtf8(xine_get_meta_info(stream_, XINE_META_INFO_TRACK_NUMBER)); bundle.samplerate = xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_SAMPLERATE);
bundle.bitdepth = xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_BITS);
bundle.bitrate = xine_get_stream_info(stream_, XINE_STREAM_INFO_AUDIO_BITRATE) / 1000;
return bundle; return bundle;
@ -899,7 +902,7 @@ void XineEngine::DetermineAndShowErrorMessage() {
// xine can read the plugin but it didn't find any codec // xine can read the plugin but it didn't find any codec
// THUS xine=daft for telling us it could handle the format in canDecode! // THUS xine=daft for telling us it could handle the format in canDecode!
body = "There is no available decoder."; body = "There is no available decoder.";
QString const ext = QFileInfo(url_.path()).completeSuffix(); QString const ext = QFileInfo(media_url_.path()).completeSuffix();
// TODO: // TODO:
// if (ext == "mp3" && EngineController::installDistroCodec("xine-engine")) // if (ext == "mp3" && EngineController::installDistroCodec("xine-engine"))
// return; // return;

View File

@ -70,7 +70,7 @@ class XineEngine : public Engine::Base {
bool Init(); bool Init();
Engine::State state() const; Engine::State state() const;
bool Load(const QUrl &url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec); bool Load(const QUrl &media_url, const QUrl &original_url, Engine::TrackChangeFlags change, bool force_stop_at_end, quint64 beginning_nanosec, qint64 end_nanosec);
bool Play(quint64 offset_nanosec); bool Play(quint64 offset_nanosec);
void Stop(bool stop_after = false); void Stop(bool stop_after = false);
void Pause(); void Pause();
@ -116,7 +116,8 @@ class XineEngine : public Engine::Base {
float preamp_; float preamp_;
std::unique_ptr<PruneScopeThread> prune_; std::unique_ptr<PruneScopeThread> prune_;
QUrl url_; QUrl media_url_;
QUrl original_url_;
static int last_error_; static int last_error_;
static time_t last_error_time_; static time_t last_error_time_;

View File

@ -1460,9 +1460,14 @@ void Playlist::SetStreamMetadata(const QUrl &url, const Song &song) {
if (current_item()->Url() != url) return; if (current_item()->Url() != url) return;
// Don't update the metadata if it's only a minor change from before // Don't update the metadata if it's only a minor change from before
if (current_item()->Metadata().artist() == song.artist() && current_item()->Metadata().title() == song.title()) return; if (current_item()->Metadata().artist() == song.artist() && current_item()->Metadata().title() == song.title() && current_item()->Metadata().album() == song.album()) return;
//qLog(Debug) << "Setting metadata for" << url << "to" << song.artist() << song.title(); // TODO: Update context & playlist if changed, but don't show popup.
//(song.bitrate() <= 0 || current_item()->Metadata().bitrate() == song.bitrate())
//(song.samplerate() <= 0 || current_item()->Metadata().samplerate() == song.samplerate())
//(song.bitdepth() <= 0 || current_item()->Metadata().bitdepth() == song.bitdepth())
qLog(Debug) << "Setting metadata for" << url << "to" << song.artist() << song.title();
current_item()->SetTemporaryMetadata(song); current_item()->SetTemporaryMetadata(song);

View File

@ -594,12 +594,13 @@ void TidalService::SearchFinished(QNetworkReply *reply, int id) {
QString artist = json_artist["name"].toString(); QString artist = json_artist["name"].toString();
QString quality = json_obj["audioQuality"].toString(); QString quality = json_obj["audioQuality"].toString();
QString copyright = json_obj["copyright"].toString();
//qLog(Debug) << "Tidal:" << artist << album << quality; //qLog(Debug) << "Tidal:" << artist << album << quality << copyright;
QString artist_album(QString("%1-%2").arg(artist).arg(album)); QString artist_album(QString("%1-%2").arg(artist).arg(album));
if (albums.contains(artist_album)) { if (albums.contains(artist_album)) {
qLog(Debug) << "Tidal: Skipping duplicate album" << artist << album << quality; qLog(Debug) << "Tidal: Skipping duplicate album" << artist << album << quality << copyright;
continue; continue;
} }
albums.insert(0, artist_album); albums.insert(0, artist_album);

View File

@ -273,9 +273,8 @@ void PlayingWidget::SongChanged(const Song &song) {
void PlayingWidget::AlbumArtLoaded(const Song &song, const QString &, const QImage &image) { void PlayingWidget::AlbumArtLoaded(const Song &song, const QString &, const QImage &image) {
if (!playing_) return; if (!playing_ || song.id() != song_playing_.id() || song.url() != song_playing_.url() || song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return;
if (song.effective_albumartist() != song_playing_.effective_albumartist() || song.effective_album() != song_playing_.effective_album() || song.title() != song_playing_.title()) return; if (timeline_fade_->state() == QTimeLine::Running && image == image_original_) return;
if (timeline_fade_->state() == QTimeLine::Running && image == image_original_ && song.effective_albumartist() == song_.effective_albumartist() && song.effective_album() == song_.effective_album() && song.title() == song_.title()) return;
active_ = true; active_ = true;
downloading_covers_ = false; downloading_covers_ = false;