refactor(playlist): playlist itself as model

This could make state management easier, and also make it reusable
just in case we need to attach the playlist to a view.
This commit is contained in:
2024-07-20 23:18:42 +08:00
parent 4a095a0cfd
commit eb2e2e93f9
3 changed files with 252 additions and 197 deletions

View File

@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: 2022 Gary Wang <wzc782970009@gmail.com>
// SPDX-FileCopyrightText: 2024 Gary Wang <git@blumia.net>
//
// SPDX-License-Identifier: MIT
@ -9,166 +9,222 @@
#include <QFileInfo>
#include <QUrl>
PlaylistManager::PlaylistManager(PlaylistType type, QObject *parent)
: QObject(parent)
, m_type(type)
PlaylistModel::PlaylistModel(QObject *parent)
: QAbstractListModel(parent)
{
}
PlaylistModel::~PlaylistModel()
{
}
void PlaylistModel::setPlaylist(const QList<QUrl> &urls)
{
beginResetModel();
m_playlist = urls;
endResetModel();
}
QModelIndex PlaylistModel::loadPlaylist(const QList<QUrl> & urls)
{
if (urls.isEmpty()) return QModelIndex();
if (urls.count() == 1) {
return loadPlaylist(urls.constFirst());
} else {
setPlaylist(urls);
return createIndex(0);
}
}
QModelIndex PlaylistModel::loadPlaylist(const QUrl &url)
{
QFileInfo info(url.toLocalFile());
QDir dir(info.path());
QString && currentFileName = info.fileName();
if (dir.path() == m_currentDir) {
int index = indexOf(url);
return index == -1 ? appendToPlaylist(url) : createIndex(index);
}
QStringList entryList = dir.entryList(
m_autoLoadSuffixes,
QDir::Files | QDir::NoSymLinks, QDir::NoSort);
QCollator collator;
collator.setNumericMode(true);
std::sort(entryList.begin(), entryList.end(), collator);
QList<QUrl> playlist;
int index = -1;
for (int i = 0; i < entryList.count(); i++) {
const QString & fileName = entryList.at(i);
const QString & oneEntry = dir.absoluteFilePath(fileName);
const QUrl & url = QUrl::fromLocalFile(oneEntry);
playlist.append(url);
if (fileName == currentFileName) {
index = i;
}
}
if (index == -1) {
index = playlist.count();
playlist.append(url);
}
m_currentDir = dir.path();
setPlaylist(playlist);
return createIndex(index);
}
QModelIndex PlaylistModel::appendToPlaylist(const QUrl &url)
{
const int lastIndex = rowCount();
beginInsertRows(QModelIndex(), lastIndex, lastIndex);
m_playlist.append(url);
endInsertRows();
return createIndex(lastIndex);
}
bool PlaylistModel::removeAt(int index)
{
if (index < 0 || index >= rowCount()) return false;
beginRemoveRows(QModelIndex(), index, index);
m_playlist.removeAt(index);
endRemoveRows();
return true;
}
int PlaylistModel::indexOf(const QUrl &url) const
{
return m_playlist.indexOf(url);
}
QStringList PlaylistModel::autoLoadFilterSuffixes() const
{
return m_autoLoadSuffixes;
}
QModelIndex PlaylistModel::createIndex(int row) const
{
return QAbstractItemModel::createIndex(row, 0, nullptr);
}
int PlaylistModel::rowCount(const QModelIndex &parent) const
{
return m_playlist.count();
}
QVariant PlaylistModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) return QVariant();
switch (role) {
case Qt::DisplayRole:
return m_playlist.at(index.row()).fileName();
case UrlRole:
return m_playlist.at(index.row());
}
return QVariant();
}
PlaylistManager::PlaylistManager(QObject *parent)
: QObject(parent)
{
connect(&m_model, &PlaylistModel::rowsRemoved, this,
[this](const QModelIndex &, int, int) {
if (m_model.rowCount() <= m_currentIndex) {
setProperty("currentIndex", m_currentIndex - 1);
}
});
auto onRowCountChanged = [this](){
emit totalCountChanged(m_model.rowCount());
};
connect(&m_model, &PlaylistModel::rowsInserted, this, onRowCountChanged);
connect(&m_model, &PlaylistModel::rowsRemoved, this, onRowCountChanged);
connect(&m_model, &PlaylistModel::modelReset, this, onRowCountChanged);
}
PlaylistManager::~PlaylistManager()
{
}
void PlaylistManager::setPlaylistType(PlaylistManager::PlaylistType type)
const PlaylistModel *PlaylistManager::model() const
{
m_type = type;
}
PlaylistManager::PlaylistType PlaylistManager::playlistType() const
{
return m_type;
}
QStringList PlaylistManager::autoLoadFilterSuffix() const
{
return m_autoLoadSuffix;
}
void PlaylistManager::setAutoLoadFilterSuffix(const QStringList & nameFilters)
{
m_autoLoadSuffix = nameFilters;
}
void PlaylistManager::clear()
{
m_currentIndex = -1;
m_playlist.clear();
return &m_model;
}
void PlaylistManager::setPlaylist(const QList<QUrl> &urls)
{
m_playlist = urls;
m_model.setPlaylist(urls);
}
void PlaylistManager::setCurrentFile(const QString & filePath)
QModelIndex PlaylistManager::loadPlaylist(const QList<QUrl> &urls)
{
QFileInfo info(filePath);
QDir dir(info.path());
QString && currentFileName = info.fileName();
switch (playlistType()) {
case PL_SAMEFOLDER: {
if (dir.path() == m_currentDir) {
int index = indexOf(filePath);
m_currentIndex = index == -1 ? appendFile(filePath) : index;
} else {
QStringList entryList = dir.entryList(
m_autoLoadSuffix,
QDir::Files | QDir::NoSymLinks, QDir::NoSort);
QCollator collator;
collator.setNumericMode(true);
std::sort(entryList.begin(), entryList.end(), collator);
clear();
int index = -1;
for (int i = 0; i < entryList.count(); i++) {
const QString & fileName = entryList.at(i);
const QString & oneEntry = dir.absoluteFilePath(fileName);
const QUrl & url = QUrl::fromLocalFile(oneEntry);
m_playlist.append(url);
if (fileName == currentFileName) {
index = i;
}
}
m_currentIndex = index == -1 ? appendFile(filePath) : index;
m_currentDir = dir.path();
}
break;
}
case PL_USERPLAYLIST:{
int index = indexOf(filePath);
m_currentIndex = index == -1 ? appendFile(filePath) : index;
break;
}
default:
break;
}
emit currentIndexChanged(m_currentIndex);
emit loaded(m_playlist.count());
QModelIndex idx = m_model.loadPlaylist(urls);
setProperty("currentIndex", idx.row());
return idx;
}
void PlaylistManager::setCurrentIndex(int index)
int PlaylistManager::totalCount() const
{
if (index < 0 || index >= m_playlist.count()) return;
m_currentIndex = index;
emit currentIndexChanged(m_currentIndex);
return m_model.rowCount();
}
int PlaylistManager::appendFile(const QString &filePath)
QModelIndex PlaylistManager::previousIndex() const
{
int index = m_playlist.length();
m_playlist.append(QUrl::fromLocalFile(filePath));
int count = totalCount();
if (count == 0) return QModelIndex();
return index;
return m_model.createIndex(m_currentIndex - 1 < 0 ? count - 1 : m_currentIndex - 1);
}
// Note: this will only remove file out of the list, this will NOT delete the file
void PlaylistManager::removeFileAt(int index)
QModelIndex PlaylistManager::nextIndex() const
{
m_playlist.removeAt(index);
int count = totalCount();
if (count == 0) return QModelIndex();
if (m_playlist.count() <= m_currentIndex) {
m_currentIndex--;
return m_model.createIndex(m_currentIndex + 1 == count ? 0 : m_currentIndex + 1);
}
QModelIndex PlaylistManager::curIndex() const
{
return m_model.createIndex(m_currentIndex);
}
void PlaylistManager::setCurrentIndex(const QModelIndex &index)
{
if (index.isValid() && index.row() >= 0 && index.row() < totalCount()) {
setProperty("currentIndex", index.row());
}
}
int PlaylistManager::indexOf(const QString &filePath)
QUrl PlaylistManager::urlByIndex(const QModelIndex &index)
{
const QUrl & url = QUrl::fromLocalFile(filePath);
return m_playlist.indexOf(url);
return m_model.data(index, PlaylistModel::UrlRole).toUrl();
}
int PlaylistManager::count() const
QString PlaylistManager::localFileByIndex(const QModelIndex &index)
{
return m_playlist.count();
return urlByIndex(index).toLocalFile();
}
std::tuple<int, QString> PlaylistManager::previousFile() const
bool PlaylistManager::removeAt(const QModelIndex &index)
{
int count = m_playlist.count();
if (count == 0) return std::make_tuple(-1, QString());
int index = m_currentIndex - 1 < 0 ? count - 1 : m_currentIndex - 1;
return std::make_tuple(index, m_playlist.at(index).toLocalFile());
return m_model.removeAt(index.row());
}
std::tuple<int, QString> PlaylistManager::nextFile() const
void PlaylistManager::setAutoLoadFilterSuffixes(const QStringList &nameFilters)
{
int count = m_playlist.count();
if (count == 0) return std::make_tuple(-1, QString());
int index = m_currentIndex + 1 == count ? 0 : m_currentIndex + 1;
return std::make_tuple(index, m_playlist.at(index).toLocalFile());
}
std::tuple<int, QString> PlaylistManager::currentFile() const
{
if (m_playlist.count() == 0) return std::make_tuple(-1, QString());
return std::make_tuple(m_currentIndex, m_playlist.at(m_currentIndex).toLocalFile());
}
std::tuple<int, QUrl> PlaylistManager::currentFileUrl() const
{
if (m_playlist.count() == 0) return std::make_tuple(-1, QUrl());
return std::make_tuple(m_currentIndex, m_playlist.at(m_currentIndex));
m_model.setProperty("autoLoadFilterSuffixes", nameFilters);
}
QList<QUrl> PlaylistManager::convertToUrlList(const QStringList &files)