1 /*
2    SPDX-FileCopyrightText: 2018 (c) Matthieu Gallien <matthieu_gallien@yahoo.fr>
3 
4    SPDX-License-Identifier: LGPL-3.0-or-later
5  */
6 
7 #include "trackmetadatamodel.h"
8 
9 #include "musiclistenersmanager.h"
10 
11 #include <KI18n/KLocalizedString>
12 
13 #include <QtConcurrent>
14 
15 #include <algorithm>
16 
TrackMetadataModel(QObject * parent)17 TrackMetadataModel::TrackMetadataModel(QObject *parent)
18     : QAbstractListModel(parent)
19 {
20     connect(&mLyricsValueWatcher, &QFutureWatcher<QString>::finished,
21             this, &TrackMetadataModel::lyricsValueIsReady);
22 }
23 
~TrackMetadataModel()24 TrackMetadataModel::~TrackMetadataModel()
25 {
26     if (mLyricsValueWatcher.isRunning() && !mLyricsValueWatcher.isFinished()) {
27         mLyricsValueWatcher.waitForFinished();
28     }
29 }
30 
rowCount(const QModelIndex & parent) const31 int TrackMetadataModel::rowCount(const QModelIndex &parent) const
32 {
33     if (parent.isValid()) {
34         return 0;
35     }
36 
37     return mTrackKeys.count();
38 }
39 
data(const QModelIndex & index,int role) const40 QVariant TrackMetadataModel::data(const QModelIndex &index, int role) const
41 {
42     auto result = QVariant{};
43 
44     const auto currentKey = mTrackKeys[index.row()];
45 
46     switch (role)
47     {
48     case Qt::DisplayRole:
49         switch (currentKey)
50         {
51         case DataTypes::TrackNumberRole:
52         {
53             auto trackNumber = mTrackData.trackNumber();
54             if (trackNumber > 0) {
55                 result = trackNumber;
56             }
57             break;
58         }
59         case DataTypes::DiscNumberRole:
60         {
61             auto discNumber = mTrackData.discNumber();
62             if (discNumber > 0) {
63                 result = discNumber;
64             }
65             break;
66         }
67         case DataTypes::ChannelsRole:
68         {
69             auto channels = mTrackData.channels();
70             if (channels > 0) {
71                 result = channels;
72             }
73             break;
74         }
75         case DataTypes::BitRateRole:
76         {
77             auto bitRate = mTrackData.bitRate();
78             if (bitRate > 0) {
79                 result = bitRate;
80             }
81             break;
82         }
83         case DataTypes::SampleRateRole:
84         {
85             auto sampleRate = mTrackData.sampleRate();
86             if (sampleRate > 0) {
87                 result = sampleRate;
88             }
89             break;
90         }
91         case DataTypes::DurationRole:
92         {
93             auto trackDuration = mTrackData.duration();
94             if (trackDuration.hour() == 0) {
95                 result = trackDuration.toString(QStringLiteral("mm:ss"));
96             } else {
97                 result = trackDuration.toString();
98             }
99             break;
100         }
101         default:
102             result = mTrackData[currentKey];
103             break;
104         }
105         break;
106     case ItemNameRole:
107         result = nameFromRole(currentKey);
108         break;
109     case ItemTypeRole:
110         switch (currentKey)
111         {
112         case DataTypes::TitleRole:
113             result = TextEntry;
114             break;
115         case DataTypes::ResourceRole:
116         case DataTypes::ImageUrlRole:
117             result = UrlEntry;
118             break;
119         case DataTypes::ArtistRole:
120             result = TextEntry;
121             break;
122         case DataTypes::AlbumRole:
123             result = TextEntry;
124             break;
125         case DataTypes::AlbumArtistRole:
126             result = TextEntry;
127             break;
128         case DataTypes::TrackNumberRole:
129             result = IntegerEntry;
130             break;
131         case DataTypes::DiscNumberRole:
132             result = IntegerEntry;
133             break;
134         case DataTypes::RatingRole:
135             result = RatingEntry;
136             break;
137         case DataTypes::GenreRole:
138             result = TextEntry;
139             break;
140         case DataTypes::LyricistRole:
141             result = TextEntry;
142             break;
143         case DataTypes::ComposerRole:
144             result = TextEntry;
145             break;
146         case DataTypes::CommentRole:
147             result = TextEntry;
148             break;
149         case DataTypes::YearRole:
150             result = IntegerEntry;
151             break;
152         case DataTypes::LastPlayDate:
153             result = DateEntry;
154             break;
155         case DataTypes::PlayCounter:
156             result = IntegerEntry;
157             break;
158         case DataTypes::LyricsRole:
159             result = LongTextEntry;
160             break;
161         case DataTypes::SampleRateRole:
162             result = IntegerEntry;
163             break;
164         case DataTypes::BitRateRole:
165             result = IntegerEntry;
166             break;
167         case DataTypes::ChannelsRole:
168             result = IntegerEntry;
169             break;
170         case DataTypes::FirstPlayDate:
171             result = DateEntry;
172             break;
173         case DataTypes::DurationRole:
174             result = DurationEntry;
175             break;
176         case DataTypes::SecondaryTextRole:
177         case DataTypes::ShadowForImageRole:
178         case DataTypes::ChildModelRole:
179         case DataTypes::StringDurationRole:
180         case DataTypes::IsValidAlbumArtistRole:
181         case DataTypes::AllArtistsRole:
182         case DataTypes::HighestTrackRating:
183         case DataTypes::IdRole:
184         case DataTypes::ParentIdRole:
185         case DataTypes::DatabaseIdRole:
186         case DataTypes::IsSingleDiscAlbumRole:
187         case DataTypes::ContainerDataRole:
188         case DataTypes::IsPartialDataRole:
189         case DataTypes::AlbumIdRole:
190         case DataTypes::HasEmbeddedCover:
191         case DataTypes::FileModificationTime:
192         case DataTypes::PlayFrequency:
193         case DataTypes::ElementTypeRole:
194         case DataTypes::FullDataRole:
195         case DataTypes::IsDirectoryRole:
196         case DataTypes::IsPlayListRole:
197         case DataTypes::FilePathRole:
198             break;
199         }
200         break;
201     }
202 
203     return result;
204 }
205 
setData(const QModelIndex & index,const QVariant & value,int role)206 bool TrackMetadataModel::setData(const QModelIndex &index, const QVariant &value, int role)
207 {
208     if (data(index, role) != value) {
209         auto dataType = mTrackKeys[index.row()];
210 
211         mTrackData[dataType] = value;
212         mFullData[dataType] = value;
213 
214         Q_EMIT dataChanged(index, index, QVector<int>() << role);
215         return true;
216     }
217     return false;
218 }
219 
roleNames() const220 QHash<int, QByteArray> TrackMetadataModel::roleNames() const
221 {
222     auto names = QAbstractListModel::roleNames();
223 
224     names[ItemNameRole] = "name";
225     names[ItemTypeRole] = "type";
226 
227     return names;
228 }
229 
fileUrl() const230 QString TrackMetadataModel::fileUrl() const
231 {
232     if (mFileUrl.isLocalFile()) {
233         return mFileUrl.toLocalFile();
234     } else {
235         return mFileUrl.toString();
236     }
237 }
238 
coverUrl() const239 QUrl TrackMetadataModel::coverUrl() const
240 {
241     if (mCoverImage.isEmpty()) {
242         return QUrl(QStringLiteral("image://icon/media-optical-audio"));
243     } else {
244         return mCoverImage;
245     }
246 }
247 
manager() const248 MusicListenersManager *TrackMetadataModel::manager() const
249 {
250     return mManager;
251 }
252 
lyrics() const253 QString TrackMetadataModel::lyrics() const
254 {
255     return mFullData[TrackDataType::key_type::LyricsRole].toString();
256 }
257 
databaseId() const258 qulonglong TrackMetadataModel::databaseId() const
259 {
260     return mDatabaseId;
261 }
262 
trackData(const TrackMetadataModel::TrackDataType & trackData)263 void TrackMetadataModel::trackData(const TrackMetadataModel::TrackDataType &trackData)
264 {
265     if ((mDatabaseId != 0 && trackData.databaseId() != mDatabaseId) ||
266             (!mFileUrl.isEmpty() && trackData.resourceURI() != mFileUrl) ||
267             (!mFullData.isEmpty() && trackData.databaseId() != mFullData.databaseId())) {
268         return;
269     }
270 
271     const QList<DataTypes::ColumnsRoles> fieldsForTrack({DataTypes::TitleRole, DataTypes::ArtistRole,
272                                                          DataTypes::AlbumRole, DataTypes::AlbumArtistRole,
273                                                          DataTypes::TrackNumberRole, DataTypes::DiscNumberRole,
274                                                          DataTypes::RatingRole, DataTypes::GenreRole,
275                                                          DataTypes::LyricistRole, DataTypes::ComposerRole,
276                                                          DataTypes::CommentRole, DataTypes::YearRole,
277                                                          DataTypes::ChannelsRole, DataTypes::BitRateRole,
278                                                          DataTypes::SampleRateRole, DataTypes::LyricsRole,
279                                                          DataTypes::LastPlayDate, DataTypes::PlayCounter,
280                                                          DataTypes::DurationRole});
281 
282     fillDataFromTrackData(trackData, fieldsForTrack);
283 }
284 
fillDataFromTrackData(const TrackMetadataModel::TrackDataType & trackData,const QList<DataTypes::ColumnsRoles> & fieldsForTrack)285 void TrackMetadataModel::fillDataFromTrackData(const TrackMetadataModel::TrackDataType &trackData,
286                                                const QList<DataTypes::ColumnsRoles> &fieldsForTrack)
287 {
288     beginResetModel();
289     mFullData = trackData;
290     mTrackData.clear();
291     mTrackKeys.clear();
292 
293     for (DataTypes::ColumnsRoles role : fieldsForTrack) {
294         if (trackData.constFind(role) != trackData.constEnd()) {
295             mTrackKeys.push_back(role);
296             mTrackData[role] = trackData[role];
297         }
298     }
299     filterDataFromTrackData();
300     endResetModel();
301 
302     if (trackData.hasDatabaseId()) {
303         fetchLyrics();
304     }
305 
306     mDatabaseId = trackData[DataTypes::DatabaseIdRole].toULongLong();
307     Q_EMIT databaseIdChanged();
308 
309     mCoverImage = trackData[DataTypes::ImageUrlRole].toUrl();
310     Q_EMIT coverUrlChanged();
311 
312     auto rawFileUrl = trackData[DataTypes::ResourceRole].toUrl();
313 
314     mFileUrl = rawFileUrl;
315     Q_EMIT fileUrlChanged();
316 }
317 
filterDataFromTrackData()318 void TrackMetadataModel::filterDataFromTrackData()
319 {
320 }
321 
removeMetaData(DataTypes::ColumnsRoles metaData)322 void TrackMetadataModel::removeMetaData(DataTypes::ColumnsRoles metaData)
323 {
324     auto itMetaData = std::find(mTrackKeys.begin(), mTrackKeys.end(), metaData);
325     if (itMetaData == mTrackKeys.end()) {
326         return;
327     }
328 
329     mTrackKeys.erase(itMetaData);
330     mTrackData.remove(metaData);
331 }
332 
dataFromType(TrackDataType::key_type metaData) const333 TrackMetadataModel::TrackDataType::mapped_type TrackMetadataModel::dataFromType(TrackDataType::key_type metaData) const
334 {
335     return mFullData[metaData];
336 }
337 
fillLyricsDataFromTrack()338 void TrackMetadataModel::fillLyricsDataFromTrack()
339 {
340     beginInsertRows({}, mTrackData.size(), mTrackData.size());
341     mTrackKeys.push_back(DataTypes::LyricsRole);
342     mTrackData[DataTypes::LyricsRole] = mLyricsValueWatcher.result();
343     endInsertRows();
344 }
345 
allTrackData() const346 const TrackMetadataModel::TrackDataType &TrackMetadataModel::allTrackData() const
347 {
348     return mFullData;
349 }
350 
lyricsValueIsReady()351 void TrackMetadataModel::lyricsValueIsReady()
352 {
353     if (!mLyricsValueWatcher.result().isEmpty()) {
354         fillLyricsDataFromTrack();
355 
356         mFullData[DataTypes::LyricsRole] = mLyricsValueWatcher.result();
357 
358         Q_EMIT lyricsChanged();
359     }
360 }
361 
initializeByIdAndUrl(ElisaUtils::PlayListEntryType type,qulonglong databaseId,const QUrl & url)362 void TrackMetadataModel::initializeByIdAndUrl(ElisaUtils::PlayListEntryType type, qulonglong databaseId, const QUrl &url)
363 {
364     if (!mFullData.isEmpty()) {
365         beginResetModel();
366         mFullData.clear();
367         mTrackData.clear();
368         mCoverImage.clear();
369         mFileUrl.clear();
370         endResetModel();
371 
372         Q_EMIT lyricsChanged();
373     }
374 
375     mFileUrl = url;
376     mDatabaseId = databaseId;
377 
378     Q_EMIT needDataByDatabaseIdAndUrl(type, databaseId, url);
379 }
380 
initialize(MusicListenersManager * newManager,DatabaseInterface * trackDatabase)381 void TrackMetadataModel::initialize(MusicListenersManager *newManager, DatabaseInterface *trackDatabase)
382 {
383     mManager = newManager;
384     Q_EMIT managerChanged();
385 
386     if (mManager) {
387         mDataLoader.setDatabase(mManager->viewDatabase());
388     } else if (trackDatabase) {
389         mDataLoader.setDatabase(trackDatabase);
390     }
391 
392     if (mManager) {
393         mManager->connectModel(&mDataLoader);
394     }
395 
396     connect(this, &TrackMetadataModel::needDataByDatabaseIdAndUrl,
397             &mDataLoader, &ModelDataLoader::loadDataByDatabaseIdAndUrl);
398     connect(this, &TrackMetadataModel::needDataByUrl,
399             &mDataLoader, &ModelDataLoader::loadDataByUrl);
400     connect(&mDataLoader, &ModelDataLoader::trackModified,
401             this, &TrackMetadataModel::trackData);
402     connect(&mDataLoader, &ModelDataLoader::allTrackData,
403             this, &TrackMetadataModel::trackData);
404     connect(&mDataLoader, &ModelDataLoader::allRadioData,
405             this, &TrackMetadataModel::radioData);
406     connect(&mDataLoader, &ModelDataLoader::radioAdded,
407             this, &TrackMetadataModel::radioData);
408     connect(&mDataLoader, &ModelDataLoader::radioModified,
409             this, &TrackMetadataModel::radioData);
410 }
411 
modelDataLoader()412 ModelDataLoader &TrackMetadataModel::modelDataLoader()
413 {
414     return mDataLoader;
415 }
416 
displayedTrackData() const417 const TrackMetadataModel::TrackDataType &TrackMetadataModel::displayedTrackData() const
418 {
419     return mTrackData;
420 }
421 
trackKey(int index) const422 DataTypes::ColumnsRoles TrackMetadataModel::trackKey(int index) const
423 {
424     return mTrackKeys[index];
425 }
426 
removeDataByIndex(int index)427 void TrackMetadataModel::removeDataByIndex(int index)
428 {
429     auto dataKey = mTrackKeys[index];
430 
431     mTrackData[dataKey] = {};
432     mFullData[dataKey] = {};
433     mTrackKeys.removeAt(index);
434 }
435 
addDataByName(const QString & name)436 void TrackMetadataModel::addDataByName(const QString &name)
437 {
438     DataTypes::ColumnsRoles newRole = DataTypes::TitleRole;
439 
440     if (name == i18nc("Track title for track metadata view", "Title")) {
441         newRole = DataTypes::TitleRole;
442     } else if (name == i18nc("Track artist for track metadata view", "Artist")) {
443         newRole = DataTypes::ArtistRole;
444     } else if (name == i18nc("Album name for track metadata view", "Album")) {
445         newRole = DataTypes::AlbumRole;
446     } else if (name == i18nc("Album artist for track metadata view", "Album Artist")) {
447         newRole = DataTypes::AlbumArtistRole;
448     } else if (name == i18nc("Track number for track metadata view", "Track Number")) {
449         newRole = DataTypes::TrackNumberRole;
450     } else if (name == i18nc("Disc number for track metadata view", "Disc Number")) {
451         newRole = DataTypes::DiscNumberRole;
452     } else if (name == i18nc("Rating label for information panel", "Rating")) {
453         newRole = DataTypes::RatingRole;
454     } else if (name == i18nc("Genre label for track metadata view", "Genre")) {
455         newRole = DataTypes::GenreRole;
456     } else if (name == i18nc("Lyricist label for track metadata view", "Lyricist")) {
457         newRole = DataTypes::LyricistRole;
458     } else if (name == i18nc("Composer name for track metadata view", "Composer")) {
459         newRole = DataTypes::ComposerRole;
460     } else if (name == i18nc("Comment label for track metadata view", "Comment")) {
461         newRole = DataTypes::CommentRole;
462     } else if (name == i18nc("Year label for track metadata view", "Year")) {
463         newRole = DataTypes::YearRole;
464     } else if (name == i18nc("Channels label for track metadata view", "Channels")) {
465         newRole = DataTypes::ChannelsRole;
466     } else if (name == i18nc("Bit rate label for track metadata view", "Bit Rate")) {
467         newRole = DataTypes::BitRateRole;
468     } else if (name == i18nc("Sample Rate label for track metadata view", "Sample Rate")) {
469         newRole = DataTypes::SampleRateRole;
470     } else if (name == i18nc("Lyrics label for track metadata view", "Lyrics")) {
471         newRole = DataTypes::LyricsRole;
472     } else if (name == i18nc("Duration label for track metadata view", "Duration")) {
473         newRole = DataTypes::DurationRole;
474     }
475 
476     mTrackData[newRole] = {};
477     mFullData[newRole] = {};
478     mTrackKeys.push_back(newRole);
479 }
480 
nameFromRole(DataTypes::ColumnsRoles role)481 QString TrackMetadataModel::nameFromRole(DataTypes::ColumnsRoles role)
482 {
483     auto result = QString{};
484     switch (role)
485     {
486     case DataTypes::TitleRole:
487         result = i18nc("Track title for track metadata view", "Title");
488         break;
489     case DataTypes::DurationRole:
490         result = i18nc("Duration label for track metadata view", "Duration");
491         break;
492     case DataTypes::ArtistRole:
493         result = i18nc("Track artist for track metadata view", "Artist");
494         break;
495     case DataTypes::AlbumRole:
496         result = i18nc("Album name for track metadata view", "Album");
497         break;
498     case DataTypes::AlbumArtistRole:
499         result = i18nc("Album artist for track metadata view", "Album Artist");
500         break;
501     case DataTypes::TrackNumberRole:
502         result = i18nc("Track number for track metadata view", "Track Number");
503         break;
504     case DataTypes::DiscNumberRole:
505         result = i18nc("Disc number for track metadata view", "Disc Number");
506         break;
507     case DataTypes::RatingRole:
508         result = i18nc("Rating label for information panel", "Rating");
509         break;
510     case DataTypes::GenreRole:
511         result = i18nc("Genre label for track metadata view", "Genre");
512         break;
513     case DataTypes::LyricistRole:
514         result = i18nc("Lyricist label for track metadata view", "Lyricist");
515         break;
516     case DataTypes::ComposerRole:
517         result = i18nc("Composer name for track metadata view", "Composer");
518         break;
519     case DataTypes::CommentRole:
520         result = i18nc("Comment label for track metadata view", "Comment");
521         break;
522     case DataTypes::YearRole:
523         result = i18nc("Year label for track metadata view", "Year");
524         break;
525     case DataTypes::ChannelsRole:
526         result = i18nc("Channels label for track metadata view", "Channels");
527         break;
528     case DataTypes::BitRateRole:
529         result = i18nc("Bit rate label for track metadata view", "Bit Rate");
530         break;
531     case DataTypes::SampleRateRole:
532         result = i18nc("Sample Rate label for track metadata view", "Sample Rate");
533         break;
534     case DataTypes::LastPlayDate:
535         result = i18nc("Last play date label for track metadata view", "Last played");
536         break;
537     case DataTypes::FirstPlayDate:
538         result = i18nc("First play date label for track metadata view", "First played");
539         break;
540     case DataTypes::PlayCounter:
541         result = i18nc("Play counter label for track metadata view", "Play count");
542         break;
543     case DataTypes::LyricsRole:
544         result = i18nc("Lyrics label for track metadata view", "Lyrics");
545         break;
546     case DataTypes::ResourceRole:
547         result = i18nc("Radio HTTP address for radio metadata view", "Stream Http Address");
548         break;
549     case DataTypes::ImageUrlRole:
550         result = i18nc("Image address for radio metadata view", "Image Address");
551         break;
552     case DataTypes::SecondaryTextRole:
553     case DataTypes::ShadowForImageRole:
554     case DataTypes::ChildModelRole:
555     case DataTypes::StringDurationRole:
556     case DataTypes::IsValidAlbumArtistRole:
557     case DataTypes::AllArtistsRole:
558     case DataTypes::HighestTrackRating:
559     case DataTypes::IdRole:
560     case DataTypes::ParentIdRole:
561     case DataTypes::DatabaseIdRole:
562     case DataTypes::IsSingleDiscAlbumRole:
563     case DataTypes::ContainerDataRole:
564     case DataTypes::IsPartialDataRole:
565     case DataTypes::AlbumIdRole:
566     case DataTypes::HasEmbeddedCover:
567     case DataTypes::FileModificationTime:
568     case DataTypes::PlayFrequency:
569     case DataTypes::ElementTypeRole:
570     case DataTypes::FullDataRole:
571     case DataTypes::IsDirectoryRole:
572     case DataTypes::IsPlayListRole:
573     case DataTypes::FilePathRole:
574         break;
575     }
576     return result;
577 }
578 
metadataExists(DataTypes::ColumnsRoles metadataRole) const579 bool TrackMetadataModel::metadataExists(DataTypes::ColumnsRoles metadataRole) const
580 {
581     return std::find(mTrackKeys.begin(), mTrackKeys.end(), metadataRole) != mTrackKeys.end();
582 }
583 
fetchLyrics()584 void TrackMetadataModel::fetchLyrics()
585 {
586     auto fileUrl = mFullData[DataTypes::ResourceRole].toUrl();
587     auto lyricicsValue = QtConcurrent::run(QThreadPool::globalInstance(), [fileUrl, this]() {
588         auto locker = QMutexLocker(&mFileScannerMutex);
589         auto trackData = mFileScanner.scanOneFile(fileUrl);
590         if (!trackData.lyrics().isEmpty()) {
591             return trackData.lyrics();
592         }
593         return QString{};
594     });
595 
596     mLyricsValueWatcher.setFuture(lyricicsValue);
597 }
598 
initializeForNewRadio()599 void TrackMetadataModel::initializeForNewRadio()
600 {
601     mFullData.clear();
602     mTrackData.clear();
603 
604     fillDataForNewRadio();
605 }
606 
fillDataForNewRadio()607 void TrackMetadataModel::fillDataForNewRadio()
608 {
609     beginResetModel();
610     mFullData.clear();
611     mTrackData.clear();
612     mTrackKeys.clear();
613 
614     auto allRoles = {DataTypes::TitleRole, DataTypes::ResourceRole,
615                      DataTypes::CommentRole, DataTypes::ImageUrlRole};
616 
617     for (auto role : allRoles) {
618         mTrackKeys.push_back(role);
619         if (role == DataTypes::DatabaseIdRole) {
620             mFullData[role] = -1;
621         } else {
622             mFullData[role] = QString();
623         }
624     }
625     mTrackData = mFullData;
626     mFullData[DataTypes::ElementTypeRole] = ElisaUtils::Radio;
627     filterDataFromTrackData();
628     endResetModel();
629 }
630 
initializeByUrl(ElisaUtils::PlayListEntryType type,const QUrl & url)631 void TrackMetadataModel::initializeByUrl(ElisaUtils::PlayListEntryType type, const QUrl &url)
632 {
633     mFullData.clear();
634     mTrackData.clear();
635     mCoverImage.clear();
636     mFileUrl.clear();
637 
638     Q_EMIT lyricsChanged();
639 
640     mFileUrl = url;
641 
642     Q_EMIT needDataByUrl(type, url);
643 }
644 
setManager(MusicListenersManager * newManager)645 void TrackMetadataModel::setManager(MusicListenersManager *newManager)
646 {
647     initialize(newManager, nullptr);
648 }
649 
setDatabase(DatabaseInterface * trackDatabase)650 void TrackMetadataModel::setDatabase(DatabaseInterface *trackDatabase)
651 {
652     initialize(nullptr, trackDatabase);
653 }
654 
radioData(const TrackDataType & radiosData)655 void TrackMetadataModel::radioData(const TrackDataType &radiosData)
656 {
657     if (!mFullData.isEmpty() && mFullData[DataTypes::DatabaseIdRole].toInt() != -1 &&
658             mFullData.databaseId() != radiosData.databaseId()) {
659         return;
660     }
661 
662     const QList<DataTypes::ColumnsRoles> fieldsForTrack({DataTypes::TitleRole, DataTypes::ResourceRole,
663                                                                  DataTypes::CommentRole, DataTypes::ImageUrlRole});
664 
665     fillDataFromTrackData(radiosData, fieldsForTrack);
666 }
667 
668 #include "moc_trackmetadatamodel.cpp"
669