File indexing completed on 2024-05-12 16:21:17

0001 // SPDX-FileCopyrightText: 2022 Jonah BrĂ¼chert <jbb@kaidan.im>
0002 //
0003 // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0004 
0005 #include "localplaylistsmodel.h"
0006 #include "playlistutils.h"
0007 
0008 #include "library.h"
0009 #include <qglobal.h>
0010 #include <qsqldatabase.h>
0011 #include <threadeddatabase.h>
0012 
0013 #include <KLocalizedString>
0014 
0015 Q_DECLARE_METATYPE(std::vector<QString>);
0016 
0017 LocalPlaylistsModel::LocalPlaylistsModel(QObject *parent)
0018     : QAbstractListModel(parent)
0019 {
0020     importer = new PlaylistImporter(this);
0021     connect(importer, &PlaylistImporter::playlistEntriesChanged, this, &LocalPlaylistsModel::playlistEntriesChanged);
0022     connect(importer,&PlaylistImporter::importFinished, this, &LocalPlaylistsModel::importFinished);
0023     connect(importer, &PlaylistImporter::refreshModel, this, &LocalPlaylistsModel::refreshModel);
0024     connect(&Library::instance(), &Library::playlistsChanged,
0025             this, &LocalPlaylistsModel::refreshModel);
0026     refreshModel();
0027 }
0028 
0029 int LocalPlaylistsModel::rowCount(const QModelIndex &index) const
0030 {
0031     return index.isValid() ? 0 : m_playlists.size();
0032 }
0033 
0034 QHash<int, QByteArray> LocalPlaylistsModel::roleNames() const
0035 {
0036     return {
0037         {Roles::PlaylistId, "playlistId"},
0038         {Roles::Title, "title"},
0039         {Roles::Description, "description"},
0040         {Roles::CreatedOn, "createdOn"},
0041         {Roles::ThumbnailIds, "thumbnailIds"},
0042     };
0043 }
0044 
0045 QVariant LocalPlaylistsModel::data(const QModelIndex &index, int role) const
0046 {
0047     switch (role) {
0048     case Roles::PlaylistId:
0049         return m_playlists[index.row()].playlistId;
0050     case Roles::Title:
0051         return m_playlists[index.row()].title;
0052     case Roles::Description:
0053         return m_playlists[index.row()].description;
0054     case Roles::CreatedOn:
0055         return m_playlists[index.row()].createdOn;
0056     case Roles::ThumbnailIds:
0057         return QVariant::fromValue(m_thumbnailIds.at(index.row()));
0058     }
0059 
0060     Q_UNREACHABLE();
0061 }
0062 
0063 void LocalPlaylistsModel::refreshModel()
0064 {
0065     QCoro::connect(Library::instance().database().getResults<Playlist>("select * from playlists"), this, [this](const auto &&playlists) {
0066         m_thumbnailIds.resize(playlists.size());
0067         for (size_t i = 0; i < playlists.size(); i++) {
0068             auto future = Library::instance().database().getResults<SingleValue<QString>>("select video_id from playlist_entries where playlist_id = ? order by random() limit 4", playlists.at(i).playlistId);
0069             QCoro::connect(std::move(future), this, [this, playlists, i](auto &&ids) {
0070                 std::ranges::transform(ids, std::back_inserter(m_thumbnailIds[i]), [](auto &&id) { return id.value; });
0071                 dataChanged(index(i), index(i), {Roles::ThumbnailIds});
0072             });
0073         }
0074 
0075         beginResetModel();
0076         m_playlists = playlists;
0077         endResetModel();
0078     });
0079 }
0080 void LocalPlaylistsModel::addPlaylist(const QString &title, const QString &description)
0081 {
0082     QCoro::connect(Library::instance().database().execute("insert into playlists (title, description) values (?, ?)", title, description), &Library::instance(), &Library::playlistsChanged);
0083 }
0084 
0085 void LocalPlaylistsModel::addPlaylistEntry(qint64 playlistId, const QString &videoId, const QString &title, const QString &artist, const QString &album)
0086 {
0087     importer->addPlaylistEntry(playlistId, videoId, title, artist, album);
0088 }
0089 
0090 void LocalPlaylistsModel::addPlaylistEntry(qint64 playlistId, const playlist::Track &track)
0091 {
0092     importer->addPlaylistEntry(playlistId, track);
0093 }
0094 
0095 void LocalPlaylistsModel::importPlaylist(const QString &url)
0096 {
0097     importer->importPlaylist(url);
0098 }
0099 
0100 void LocalPlaylistsModel::renamePlaylist(qint64 playlistId, const QString &name, const QString &description)
0101 {
0102     importer->renamePlaylist(playlistId, name, description);
0103 }
0104 
0105 void LocalPlaylistsModel::deletePlaylist(qint64 playlistId)
0106 {
0107     QCoro::connect(Library::instance().database().execute("delete from playlists where playlist_id = ?", playlistId), this, &LocalPlaylistsModel::refreshModel);
0108 }
0109 
0110 QStringView LocalPlaylistsModel::cropURL(QStringView srcUrl)
0111 {
0112     // Find entry point
0113     constexpr auto urlFragment = QStringView(u"?list=");
0114     qsizetype urlPos = srcUrl.indexOf(urlFragment);
0115     if (urlPos != -1) {
0116         urlPos += urlFragment.size();
0117     } else {
0118         urlPos = 0;
0119     }
0120     auto mid = srcUrl.mid(urlPos);
0121 
0122     // Find exit point
0123     urlPos = std::min(mid.indexOf(u"?"), mid.indexOf(u"&"));
0124     return mid.mid(0, urlPos);
0125 }