Clementine now skips songs that don't exist anymore (updates issue #250)
This commit is contained in:
parent
d8cd7ae80a
commit
8722e00103
@ -51,6 +51,11 @@ Player::Player(PlaylistManagerInterface* playlists, LastFMService* lastfm,
|
||||
SetVolume(settings_.value("volume", 50).toInt());
|
||||
|
||||
connect(engine_.get(), SIGNAL(Error(QString)), SIGNAL(Error(QString)));
|
||||
|
||||
connect(engine_.get(), SIGNAL(ValidSongRequested(QUrl)), SLOT(ValidSongRequested(QUrl)));
|
||||
connect(engine_.get(), SIGNAL(InvalidSongRequested(QUrl)), SLOT(InvalidSongRequested(QUrl)));
|
||||
|
||||
connect(playlists, SIGNAL(CurrentSongChanged(Song)), SLOT(CurrentMetadataChanged(Song)));
|
||||
}
|
||||
|
||||
Player::~Player() {
|
||||
@ -272,6 +277,11 @@ void Player::PlayAt(int index, Engine::TrackChangeType change, bool reshuffle) {
|
||||
}
|
||||
|
||||
void Player::CurrentMetadataChanged(const Song& metadata) {
|
||||
// those things might have changed (especially when a previously invalid
|
||||
// song was reloaded) so we push the latest version into Engine
|
||||
engine_->Load(metadata.url(), Engine::Auto,
|
||||
metadata.beginning_nanosec(), metadata.end_nanosec());
|
||||
|
||||
#ifdef HAVE_LIBLASTFM
|
||||
lastfm_->NowPlaying(metadata);
|
||||
#endif
|
||||
@ -441,3 +451,15 @@ void Player::TrackAboutToEnd() {
|
||||
next_item_->Metadata().end_nanosec());
|
||||
}
|
||||
}
|
||||
|
||||
void Player::ValidSongRequested(const QUrl& url) {
|
||||
emit SongChangeRequestProcessed(url, true);
|
||||
}
|
||||
|
||||
void Player::InvalidSongRequested(const QUrl& url) {
|
||||
// first send the notification to others...
|
||||
emit SongChangeRequestProcessed(url, false);
|
||||
// ... and now when our listeners have completed their processing of the
|
||||
// current item we can change the current item by skipping to the next song
|
||||
NextItem(Engine::Auto);
|
||||
}
|
||||
|
@ -92,6 +92,10 @@ signals:
|
||||
// Emitted when there's a manual change to the current's track position.
|
||||
void Seeked(qlonglong microseconds);
|
||||
|
||||
// Emitted when Player has processed a request to play another song. This contains
|
||||
// the URL of the song and a flag saying whether it was able to play the song.
|
||||
void SongChangeRequestProcessed(const QUrl& url, bool valid);
|
||||
|
||||
void ForceShowOSD(Song);
|
||||
};
|
||||
|
||||
@ -147,6 +151,9 @@ public slots:
|
||||
|
||||
void NextInternal(Engine::TrackChangeType);
|
||||
|
||||
void ValidSongRequested(const QUrl&);
|
||||
void InvalidSongRequested(const QUrl&);
|
||||
|
||||
private:
|
||||
PlaylistManagerInterface* playlists_;
|
||||
LastFMService* lastfm_;
|
||||
|
@ -263,19 +263,22 @@ void Song::InitFromFile(const QString& filename, int directory_id) {
|
||||
if (qApp->thread() == QThread::currentThread())
|
||||
qWarning() << Q_FUNC_INFO << "on GUI thread!";
|
||||
#endif
|
||||
|
||||
d->init_from_file_ = true;
|
||||
|
||||
d->filename_ = filename;
|
||||
d->directory_id_ = directory_id;
|
||||
|
||||
QFileInfo info(filename);
|
||||
d->basefilename_ = info.fileName();
|
||||
|
||||
QMutexLocker l(&taglib_mutex_);
|
||||
scoped_ptr<TagLib::FileRef> fileref(factory_->GetFileRef(filename));
|
||||
|
||||
if(fileref->isNull())
|
||||
if(fileref->isNull()) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->init_from_file_ = true;
|
||||
|
||||
QFileInfo info(filename);
|
||||
d->basefilename_ = info.fileName();
|
||||
d->filesize_ = info.size();
|
||||
d->mtime_ = info.lastModified().toTime_t();
|
||||
d->ctime_ = info.created().toTime_t();
|
||||
@ -928,13 +931,13 @@ void Song::InitFromLastFM(const lastfm::Track& track) {
|
||||
#endif // Q_OS_WIN32
|
||||
|
||||
void Song::MergeFromSimpleMetaBundle(const Engine::SimpleMetaBundle &bundle) {
|
||||
d->valid_ = true;
|
||||
|
||||
if (d->init_from_file_) {
|
||||
// This Song was already loaded using taglib. Our tags are probably better than the engine's.
|
||||
return;
|
||||
}
|
||||
|
||||
d->valid_ = true;
|
||||
|
||||
UniversalEncodingHandler detector(NS_FILTER_NON_CJK);
|
||||
QTextCodec* codec = detector.Guess(bundle);
|
||||
|
||||
@ -1028,6 +1031,12 @@ void Song::ToLastFM(lastfm::Track* track) const {
|
||||
}
|
||||
#endif // HAVE_LIBLASTFM
|
||||
|
||||
QUrl Song::url() const {
|
||||
return QFile::exists(filename())
|
||||
? QUrl::fromLocalFile(filename())
|
||||
: filename();
|
||||
}
|
||||
|
||||
QString Song::PrettyTitle() const {
|
||||
QString title(d->title_);
|
||||
|
||||
|
@ -197,6 +197,9 @@ class Song {
|
||||
|
||||
int directory_id() const { return d->directory_id_; }
|
||||
const QString& filename() const { return d->filename_; }
|
||||
// Returns this Song's URL which may point either to a file or to another type
|
||||
// of stream.
|
||||
QUrl url() const;
|
||||
const QString& basefilename() const { return d->basefilename_; }
|
||||
uint mtime() const { return d->mtime_; }
|
||||
uint ctime() const { return d->ctime_; }
|
||||
|
@ -102,6 +102,12 @@ class Base : public QObject, boost::noncopyable {
|
||||
void StatusText(const QString&);
|
||||
void Error(const QString&);
|
||||
|
||||
// Emitted when Engine was unable to play a song with the given QUrl.
|
||||
void InvalidSongRequested(const QUrl&);
|
||||
// Emitted when Engine successfully started playing a song with the
|
||||
// given QUrl.
|
||||
void ValidSongRequested(const QUrl&);
|
||||
|
||||
void MetaData(const Engine::SimpleMetaBundle&);
|
||||
|
||||
// Signals that the engine's state has changed (a stream was stopped for example).
|
||||
|
@ -64,8 +64,6 @@ const char* GstEngine::kHypnotoadPipeline =
|
||||
"band0=-24 band1=-3 band2=7.5 band3=12 band4=8 "
|
||||
"band5=6 band6=5 band7=6 band8=0 band9=-24";
|
||||
|
||||
// TODO: weird analyzer problems with .cues
|
||||
|
||||
GstEngine::GstEngine()
|
||||
: Engine::Base(),
|
||||
delayq_(g_queue_new()),
|
||||
@ -530,8 +528,9 @@ void GstEngine::PlayDone() {
|
||||
GstStateChangeReturn ret = watcher->result();
|
||||
quint64 offset_nanosec = watcher->data();
|
||||
|
||||
if (!current_pipeline_)
|
||||
if (!current_pipeline_) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (ret == GST_STATE_CHANGE_FAILURE) {
|
||||
// Failure, but we got a redirection URL - try loading that instead
|
||||
@ -559,6 +558,8 @@ void GstEngine::PlayDone() {
|
||||
}
|
||||
|
||||
emit StateChanged(Engine::Playing);
|
||||
// we've successfully started playing a media stream with this url
|
||||
emit ValidSongRequested(url_);
|
||||
}
|
||||
|
||||
|
||||
@ -692,14 +693,20 @@ void GstEngine::timerEvent(QTimerEvent* e) {
|
||||
}
|
||||
}
|
||||
|
||||
void GstEngine::HandlePipelineError(const QString& message) {
|
||||
void GstEngine::HandlePipelineError(const QString& message, int domain, int error_code) {
|
||||
qWarning() << "Gstreamer error:" << message;
|
||||
|
||||
current_pipeline_.reset();
|
||||
emit Error(message);
|
||||
emit StateChanged(Engine::Empty);
|
||||
}
|
||||
|
||||
emit StateChanged(Engine::Empty);
|
||||
// unable to play media stream with this url
|
||||
emit InvalidSongRequested(url_);
|
||||
|
||||
// no user error message when the error is 'no such URI'
|
||||
if(!(domain == GST_RESOURCE_ERROR && error_code == GST_RESOURCE_ERROR_NOT_FOUND)) {
|
||||
emit Error(message);
|
||||
}
|
||||
}
|
||||
|
||||
void GstEngine::EndOfStreamReached(bool has_next_track) {
|
||||
GstEnginePipeline* pipeline_sender = qobject_cast<GstEnginePipeline*>(sender());
|
||||
@ -774,11 +781,12 @@ shared_ptr<GstEnginePipeline> GstEngine::CreatePipeline() {
|
||||
ret->set_buffer_duration_nanosec(buffer_duration_nanosec_);
|
||||
|
||||
ret->AddBufferConsumer(this);
|
||||
foreach (BufferConsumer* consumer, buffer_consumers_)
|
||||
foreach (BufferConsumer* consumer, buffer_consumers_) {
|
||||
ret->AddBufferConsumer(consumer);
|
||||
}
|
||||
|
||||
connect(ret.get(), SIGNAL(EndOfStreamReached(bool)), SLOT(EndOfStreamReached(bool)));
|
||||
connect(ret.get(), SIGNAL(Error(QString)), SLOT(HandlePipelineError(QString)));
|
||||
connect(ret.get(), SIGNAL(Error(QString,int,int)), SLOT(HandlePipelineError(QString,int,int)));
|
||||
connect(ret.get(), SIGNAL(MetadataFound(Engine::SimpleMetaBundle)),
|
||||
SLOT(NewMetaData(Engine::SimpleMetaBundle)));
|
||||
connect(ret.get(), SIGNAL(destroyed()), SLOT(ClearScopeBuffers()));
|
||||
|
@ -114,7 +114,7 @@ class GstEngine : public Engine::Base, public BufferConsumer {
|
||||
|
||||
private slots:
|
||||
void EndOfStreamReached(bool has_next_track);
|
||||
void HandlePipelineError(const QString& message);
|
||||
void HandlePipelineError(const QString& message, int domain, int error_code);
|
||||
void NewMetaData(const Engine::SimpleMetaBundle& bundle);
|
||||
void ClearScopeBuffers();
|
||||
void AddBufferToScope(GstBuffer* buf, GstEnginePipeline* pipeline);
|
||||
|
@ -56,6 +56,7 @@ GstEnginePipeline::GstEnginePipeline(GstEngine* engine)
|
||||
ignore_tags_(false),
|
||||
pipeline_is_initialised_(false),
|
||||
pipeline_is_connected_(false),
|
||||
pipeline_error_(PipelineError()),
|
||||
pending_seek_nanosec_(-1),
|
||||
volume_percent_(100),
|
||||
volume_modifier_(1.0),
|
||||
@ -131,16 +132,20 @@ bool GstEnginePipeline::ReplaceDecodeBin(const QUrl& url) {
|
||||
GstElement* GstEnginePipeline::CreateDecodeBinFromString(const char* pipeline) {
|
||||
GError* error = NULL;
|
||||
GstElement* bin = gst_parse_bin_from_description(pipeline, TRUE, &error);
|
||||
|
||||
if (error) {
|
||||
QString message = QString::fromLocal8Bit(error->message);
|
||||
int domain = error->domain;
|
||||
int code = error->code;
|
||||
g_error_free(error);
|
||||
|
||||
qWarning() << message;
|
||||
emit Error(message);
|
||||
return NULL;
|
||||
}
|
||||
emit Error(message, domain, code);
|
||||
|
||||
return bin;
|
||||
return NULL;
|
||||
} else {
|
||||
return bin;
|
||||
}
|
||||
}
|
||||
|
||||
bool GstEnginePipeline::Init() {
|
||||
@ -264,6 +269,11 @@ GstEnginePipeline::~GstEnginePipeline() {
|
||||
gst_element_set_state(pipeline_, GST_STATE_NULL);
|
||||
gst_object_unref(GST_OBJECT(pipeline_));
|
||||
}
|
||||
|
||||
if(!pipeline_error_.message.isEmpty()) {
|
||||
emit Error(pipeline_error_.message, pipeline_error_.domain,
|
||||
pipeline_error_.error_code);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -293,6 +303,7 @@ gboolean GstEnginePipeline::BusCallback(GstBus*, GstMessage* msg, gpointer self)
|
||||
|
||||
GstBusSyncReply GstEnginePipeline::BusCallbackSync(GstBus*, GstMessage* msg, gpointer self) {
|
||||
GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self);
|
||||
|
||||
switch (GST_MESSAGE_TYPE(msg)) {
|
||||
case GST_MESSAGE_EOS:
|
||||
emit instance->EndOfStreamReached(false);
|
||||
@ -317,6 +328,7 @@ GstBusSyncReply GstEnginePipeline::BusCallbackSync(GstBus*, GstMessage* msg, gpo
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return GST_BUS_PASS;
|
||||
}
|
||||
|
||||
@ -340,7 +352,8 @@ void GstEnginePipeline::ErrorMessageReceived(GstMessage* msg) {
|
||||
gst_message_parse_error(msg, &error, &debugs);
|
||||
QString message = QString::fromLocal8Bit(error->message);
|
||||
QString debugstr = QString::fromLocal8Bit(debugs);
|
||||
|
||||
int domain = error->domain;
|
||||
int code = error->code;
|
||||
g_error_free(error);
|
||||
free(debugs);
|
||||
|
||||
@ -352,8 +365,13 @@ void GstEnginePipeline::ErrorMessageReceived(GstMessage* msg) {
|
||||
return;
|
||||
}
|
||||
|
||||
// we'll send the error later, when pipeline is done with it's state changes
|
||||
pipeline_error_ = PipelineError();
|
||||
pipeline_error_.message = message;
|
||||
pipeline_error_.domain = domain;
|
||||
pipeline_error_.error_code = code;
|
||||
|
||||
qDebug() << debugstr;
|
||||
emit Error(message);
|
||||
}
|
||||
|
||||
void GstEnginePipeline::TagMessageReceived(GstMessage* msg) {
|
||||
@ -410,7 +428,6 @@ void GstEnginePipeline::StateChangedMessageReceived(GstMessage* msg) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GstEnginePipeline::NewPadCallback(GstElement*, GstPad* pad, gpointer self) {
|
||||
GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self);
|
||||
GstPad* const audiopad = gst_element_get_pad(instance->audiobin_, "sink");
|
||||
@ -431,7 +448,6 @@ void GstEnginePipeline::NewPadCallback(GstElement*, GstPad* pad, gpointer self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool GstEnginePipeline::HandoffCallback(GstPad*, GstBuffer* buf, gpointer self) {
|
||||
GstEnginePipeline* instance = reinterpret_cast<GstEnginePipeline*>(self);
|
||||
|
||||
@ -538,7 +554,6 @@ qint64 GstEnginePipeline::position() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
qint64 GstEnginePipeline::length() const {
|
||||
GstFormat fmt = GST_FORMAT_TIME;
|
||||
gint64 value = 0;
|
||||
@ -547,7 +562,6 @@ qint64 GstEnginePipeline::length() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
GstState GstEnginePipeline::state() const {
|
||||
GstState s, sp;
|
||||
if (gst_element_get_state(pipeline_, &s, &sp, kGstStateTimeoutNanosecs) ==
|
||||
@ -582,7 +596,6 @@ void GstEnginePipeline::SetEqualizerEnabled(bool enabled) {
|
||||
UpdateEqualizer();
|
||||
}
|
||||
|
||||
|
||||
void GstEnginePipeline::SetEqualizerParams(int preamp, const QList<int>& band_gains) {
|
||||
eq_preamp_ = preamp;
|
||||
eq_band_gains_ = band_gains;
|
||||
|
@ -40,6 +40,12 @@ class GstEnginePipeline : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct PipelineError {
|
||||
QString message;
|
||||
int domain;
|
||||
int error_code;
|
||||
};
|
||||
|
||||
GstEnginePipeline(GstEngine* engine);
|
||||
~GstEnginePipeline();
|
||||
|
||||
@ -92,7 +98,9 @@ class GstEnginePipeline : public QObject {
|
||||
signals:
|
||||
void EndOfStreamReached(bool has_next_track);
|
||||
void MetadataFound(const Engine::SimpleMetaBundle& bundle);
|
||||
void Error(const QString& message);
|
||||
// This indicates an error, delegated from GStreamer, in the pipeline.
|
||||
// The message, domain and error_code are related to GStreamer's GError.
|
||||
void Error(const QString& message, int domain, int error_code);
|
||||
void FaderFinished();
|
||||
|
||||
protected:
|
||||
@ -107,10 +115,12 @@ class GstEnginePipeline : public QObject {
|
||||
static bool HandoffCallback(GstPad*, GstBuffer*, gpointer);
|
||||
static bool EventHandoffCallback(GstPad*, GstEvent*, gpointer);
|
||||
static void SourceDrainedCallback(GstURIDecodeBin*, gpointer);
|
||||
|
||||
void TagMessageReceived(GstMessage*);
|
||||
void ErrorMessageReceived(GstMessage*);
|
||||
void ElementMessageReceived(GstMessage*);
|
||||
void StateChangedMessageReceived(GstMessage*);
|
||||
|
||||
QString ParseTag(GstTagList* list, const char* tag) const;
|
||||
|
||||
bool Init();
|
||||
@ -191,6 +201,8 @@ class GstEnginePipeline : public QObject {
|
||||
// Also we have to wait for the decodebin to be connected.
|
||||
bool pipeline_is_initialised_;
|
||||
bool pipeline_is_connected_;
|
||||
// Cached error thrown from GStreamer during pipeline's initialization.
|
||||
PipelineError pipeline_error_;
|
||||
qint64 pending_seek_nanosec_;
|
||||
|
||||
int volume_percent_;
|
||||
|
@ -237,11 +237,7 @@ QVariant Playlist::data(const QModelIndex& index, int role) const {
|
||||
// Don't forget to change Playlist::CompareItems when adding new columns
|
||||
switch (index.column()) {
|
||||
case Column_Title:
|
||||
if (!song.title().isEmpty())
|
||||
return song.title();
|
||||
if (!song.basefilename().isEmpty())
|
||||
return song.basefilename();
|
||||
return song.filename();
|
||||
return song.PrettyTitle();
|
||||
case Column_Artist: return song.artist();
|
||||
case Column_Album: return song.album();
|
||||
case Column_Length: return song.length_nanosec();
|
||||
@ -526,8 +522,9 @@ void Playlist::set_current_row(int i) {
|
||||
}
|
||||
|
||||
if (current_item_index_.isValid()) {
|
||||
emit dataChanged(current_item_index_, current_item_index_.sibling(current_item_index_.row(), ColumnCount-1));
|
||||
emit CurrentSongChanged(current_item_metadata());
|
||||
InformOfCurrentSongChange(current_item_index_,
|
||||
current_item_index_.sibling(current_item_index_.row(), ColumnCount-1),
|
||||
current_item_metadata());
|
||||
}
|
||||
|
||||
// Update the virtual index
|
||||
@ -1251,8 +1248,9 @@ void Playlist::SetStreamMetadata(const QUrl& url, const Song& song) {
|
||||
current_item_->SetTemporaryMetadata(song);
|
||||
UpdateScrobblePoint();
|
||||
|
||||
emit dataChanged(index(current_item_index_.row(), 0), index(current_item_index_.row(), ColumnCount-1));
|
||||
emit CurrentSongChanged(song);
|
||||
InformOfCurrentSongChange(index(current_item_index_.row(), 0),
|
||||
index(current_item_index_.row(), ColumnCount-1),
|
||||
song);
|
||||
}
|
||||
|
||||
void Playlist::ClearStreamMetadata() {
|
||||
@ -1353,7 +1351,8 @@ void Playlist::RemoveItemsNotInQueue() {
|
||||
void Playlist::ReloadItems(const QList<int>& rows) {
|
||||
foreach (int row, rows) {
|
||||
item_at(row)->Reload();
|
||||
emit dataChanged(index(row, 0), index(row, ColumnCount-1));
|
||||
InformOfCurrentSongChange(index(row, 0), index(row, ColumnCount-1),
|
||||
item_at(row)->Metadata());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1518,3 +1517,13 @@ void Playlist::set_column_align_center(int column) {
|
||||
void Playlist::set_column_align_right(int column) {
|
||||
column_alignments_[column] = (Qt::AlignRight | Qt::AlignVCenter);
|
||||
}
|
||||
|
||||
void Playlist::InformOfCurrentSongChange(const QModelIndex& top_left, const QModelIndex& bottom_right,
|
||||
const Song& metadata) {
|
||||
emit dataChanged(top_left, bottom_right);
|
||||
// if the song is invalid, we won't play it - there's no point in
|
||||
// informing anybody about the change
|
||||
if(metadata.is_valid()) {
|
||||
emit CurrentSongChanged(metadata);
|
||||
}
|
||||
}
|
||||
|
@ -271,6 +271,9 @@ class Playlist : public QAbstractListModel {
|
||||
|
||||
void RemoveItemsNotInQueue();
|
||||
|
||||
void InformOfCurrentSongChange(const QModelIndex& top_left, const QModelIndex& bottom_right,
|
||||
const Song& metadata);
|
||||
|
||||
private slots:
|
||||
void TracksAboutToBeDequeued(const QModelIndex&, int begin, int end);
|
||||
void TracksDequeued();
|
||||
|
@ -329,3 +329,23 @@ void PlaylistManager::PlaySmartPlaylist(GeneratorPtr generator, bool as_new, boo
|
||||
|
||||
current()->InsertSmartPlaylist(generator);
|
||||
}
|
||||
|
||||
// When Player has processed the new song chosen by the user...
|
||||
void PlaylistManager::SongChangeRequestProcessed(const QUrl& url, bool valid) {
|
||||
foreach(Playlist* playlist, GetAllPlaylists()) {
|
||||
PlaylistItemPtr current = playlist->current_item();
|
||||
|
||||
if(current) {
|
||||
Song current_song = current->Metadata();
|
||||
|
||||
// if validity has changed, reload the item
|
||||
if(current_song.url() == url && current_song.filetype() != Song::Type_Stream &&
|
||||
current_song.is_valid() != QFile::exists(current_song.filename())) {
|
||||
playlist->ReloadItems(QList<int>() << playlist->current_row());
|
||||
}
|
||||
|
||||
// we have at most one current item
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -71,6 +71,8 @@ public slots:
|
||||
virtual void Remove(int id) = 0;
|
||||
virtual void ChangePlaylistOrder(const QList<int>& ids) = 0;
|
||||
|
||||
virtual void SongChangeRequestProcessed(const QUrl& url, bool valid) = 0;
|
||||
|
||||
virtual void SetCurrentPlaylist(int id) = 0;
|
||||
virtual void SetActivePlaylist(int id) = 0;
|
||||
virtual void SetActiveToCurrent() = 0;
|
||||
@ -173,6 +175,8 @@ public slots:
|
||||
|
||||
void PlaySmartPlaylist(smart_playlists::GeneratorPtr generator, bool as_new, bool clear);
|
||||
|
||||
void SongChangeRequestProcessed(const QUrl& url, bool valid);
|
||||
|
||||
private slots:
|
||||
void OneOfPlaylistsChanged();
|
||||
void UpdateSummaryText();
|
||||
|
@ -85,15 +85,15 @@ QVariant SongPlaylistItem::DatabaseValue(DatabaseColumn column) const {
|
||||
}
|
||||
|
||||
QUrl SongPlaylistItem::Url() const {
|
||||
if (QFile::exists(song_.filename())) {
|
||||
return QUrl::fromLocalFile(song_.filename());
|
||||
} else {
|
||||
return song_.filename();
|
||||
}
|
||||
return song_.url();
|
||||
}
|
||||
|
||||
void SongPlaylistItem::Reload() {
|
||||
song_.InitFromFile(song_.filename(), song_.directory_id());
|
||||
QString old_filename = song_.filename();
|
||||
int old_directory_id = song_.directory_id();
|
||||
|
||||
song_ = Song();
|
||||
song_.InitFromFile(old_filename, old_directory_id);
|
||||
}
|
||||
|
||||
Song SongPlaylistItem::Metadata() const {
|
||||
|
@ -380,6 +380,8 @@ MainWindow::MainWindow(
|
||||
connect(ui_->volume, SIGNAL(valueChanged(int)), player_, SLOT(SetVolume(int)));
|
||||
|
||||
connect(player_, SIGNAL(Error(QString)), SLOT(ShowErrorDialog(QString)));
|
||||
connect(player_, SIGNAL(SongChangeRequestProcessed(QUrl,bool)), playlists_, SLOT(SongChangeRequestProcessed(QUrl,bool)));
|
||||
|
||||
connect(player_, SIGNAL(Paused()), SLOT(MediaPaused()));
|
||||
connect(player_, SIGNAL(Playing()), SLOT(MediaPlaying()));
|
||||
connect(player_, SIGNAL(Stopped()), SLOT(MediaStopped()));
|
||||
|
Loading…
x
Reference in New Issue
Block a user