trackmodel.cpp
chaptermodel.cpp
annotationmodel.cpp
+ playlist.cpp
+ directoryplaylist.cpp
)
set(core_HEADERS
trackmodel.h
chaptermodel.h
annotationmodel.h
+ playlist.h
+ directoryplaylist.h
)
set(core_PUBLIC_HEADERS
--- /dev/null
+#include "directoryplaylist.h"
+
+#include <QFileInfo>
+#include <QDir>
+
+#include <QLoggingCategory>
+#include <QDebug>
+
+Q_LOGGING_CATEGORY(directoryPlaylistCategory, "playlist.directory")
+
+DirectoryPlaylist::DirectoryPlaylist(QObject* parent) : Playlist{parent} {}
+
+PlaylistEntry DirectoryPlaylist::doGetCurrent() const { return m_current; }
+
+bool DirectoryPlaylist::doNext() {
+ const auto fileInfo = QFileInfo{m_current.path};
+
+ if (!fileInfo.exists()) {
+ qCDebug(directoryPlaylistCategory)
+ << "Current entry does not found on filesystem";
+ return false;
+ }
+
+ const auto dir = fileInfo.dir();
+ const auto entries = dir.entryInfoList(QDir::Files,
+ QDir::Name | QDir::IgnoreCase);
+ auto it = std::find(std::begin(entries), std::end(entries), fileInfo);
+
+ if (it == std::end(entries)) {
+ qCDebug(directoryPlaylistCategory) << "Could not find entry in directory";
+ return false;
+ }
+
+ ++it;
+
+ if (it == std::end(entries)) {
+ qCDebug(directoryPlaylistCategory) << "Current entry is the last entry";
+ return false;
+ }
+
+ m_current.path = it->absoluteFilePath();
+ return true;
+}
+
+bool DirectoryPlaylist::doPrevious() {
+ const auto fileInfo = QFileInfo{m_current.path};
+
+ if (!fileInfo.exists()) {
+ qCDebug(directoryPlaylistCategory)
+ << "Current entry does not found on filesystem";
+ return false;
+ }
+
+ const auto dir = fileInfo.dir();
+ const auto entries = dir.entryInfoList(QDir::Files,
+ QDir::Name | QDir::IgnoreCase);
+ auto it = std::find(std::rbegin(entries), std::rend(entries), fileInfo);
+
+ if (it == std::rend(entries)) {
+ qCDebug(directoryPlaylistCategory) << "Could not find entry in directory";
+ return false;
+ }
+
+ ++it;
+
+ if (it == std::rend(entries)) {
+ qCDebug(directoryPlaylistCategory) << "Current entry is the first entry";
+ return false;
+ }
+
+ m_current.path = it->absoluteFilePath();
+ return true;
+}
+
+bool DirectoryPlaylist::doUpdateCurrentEntry(const PlaylistEntry ¤t) {
+ const auto fileInfo = QFileInfo{current.path};
+
+ if (!fileInfo.exists()) {
+ qCDebug(directoryPlaylistCategory)
+ << "Could not find updated entry on filesystem";
+ return false;
+ }
+
+ qCDebug(directoryPlaylistCategory)
+ << "Updating current entry. New directory:" << fileInfo.absolutePath()
+ << "New entry:" << fileInfo.fileName();
+ m_current = current;
+ return true;
+}
--- /dev/null
+#ifndef DIRECTORYPLAYLIST_H
+#define DIRECTORYPLAYLIST_H
+
+#include "playlist.h"
+
+class DirectoryPlaylist : public Playlist
+{
+public:
+ DirectoryPlaylist(QObject* parent = nullptr);
+
+ // Playlist interface
+protected:
+ PlaylistEntry doGetCurrent() const override;
+ bool doNext() override;
+ bool doPrevious() override;
+ bool doUpdateCurrentEntry(const PlaylistEntry ¤t) override;
+
+private:
+ PlaylistEntry m_current;
+};
+
+#endif // DIRECTORYPLAYLIST_H
#include <QMetaObject>
#include <QQmlApplicationEngine>
+#include "directoryplaylist.h"
+
#ifdef Q_OS_WIN
#include <Windows.h>
#endif
m_annotationModel = new AnnotationModel{this};
Q_CHECK_PTR(m_annotationModel);
+
+ m_playlist = new DirectoryPlaylist{this};
+ Q_CHECK_PTR(m_playlist);
+ connect(m_playlist, SIGNAL(currentChanged(PlaylistEntry)), this,
+ SLOT(playPlaylistEntry(PlaylistEntry)));
}
Player::~Player() {
m_backend->open(resource);
else
setNextSource(resource);
+ updatePlaylistState(resource);
}
void Player::loadAndPlay(const QUrl &resource) {
void Player::seek(Player::TimeStamp position) { m_backend->seek(position); }
+void Player::next() {
+ m_playlist->next();
+}
+
+void Player::previous() {
+ m_playlist->previous();
+}
+
void Player::setVolume(Volume volume) {
volume = qBound(Volume{}, volume, m_maxVolume);
m_backend->setVolume(volume);
void Player::onNewFrame() { emit frameChanged(m_lastFrame); }
+void Player::playPlaylistEntry(const PlaylistEntry &entry) {
+ loadAndPlay(entry.url());
+}
+
+void Player::playNextPlaylistEntryOnEndOfFile() {
+ m_playlist->next();
+}
+
bool Player::canLoadVideoNow() const {
return m_backendInstanceReady && m_renderer && m_rendererReady;
}
loadAndPlay(m_nextSource);
setNextSource(QUrl{});
}
+
+void Player::updatePlaylistState(const QUrl &resource) {
+ m_playlist->updateCurrentEntry({resource.toLocalFile()});
+}
+
#include "chaptermodel.h"
#include "trackmodel.h"
#include "annotationmodel.h"
+#include "playlist.h"
class Player : public QObject,
public PlayerPluginInterface,
void seek(Player::TimeStamp position);
+ // Playlist
+ void next();
+ void previous();
+
// Volume
void setVolume(Volume volume);
void volumeUp(int byPercentagePoints = 5);
private slots:
void onNewFrame();
+ void playPlaylistEntry(const PlaylistEntry &entry);
+ void playNextPlaylistEntryOnEndOfFile();
private:
bool canLoadVideoNow() const;
void loadNextFile();
+ void updatePlaylistState(const QUrl &resource);
BackendInstance *m_backend = nullptr;
QUrl m_currentSource;
TrackModel *m_subtitleTrackModel;
ChapterModel *m_chapterModel;
AnnotationModel *m_annotationModel;
+ Playlist *m_playlist;
QImage m_lastFrame;
bool m_saveToImage = true;
bool m_muted = false;
--- /dev/null
+#include "playlist.h"
+
+#include <QLoggingCategory>
+#include <QDebug>
+
+Q_LOGGING_CATEGORY(playlistCategory, "playlist")
+
+Playlist::Playlist(QObject *parent) : QObject{parent} {}
+
+PlaylistEntry Playlist::current() const {
+ return doGetCurrent();
+}
+
+void Playlist::updateCurrentEntry(const PlaylistEntry &entry) {
+ doUpdateCurrentEntry(entry);
+}
+
+void Playlist::next() {
+ const auto current = doGetCurrent();
+
+ if (!doNext()) {
+ qCDebug(playlistCategory()) << "No next entry available";
+ return;
+ }
+
+ const auto nextCurrent = doGetCurrent();
+
+ qCDebug(playlistCategory()) << "Got next entry:" << nextCurrent;
+
+ if (current != nextCurrent) {
+ emit currentChanged(nextCurrent);
+ }
+}
+
+void Playlist::previous() {
+ const auto current = doGetCurrent();
+
+ if (!doPrevious()) {
+ qCDebug(playlistCategory()) << "No previous entry available";
+ return;
+ }
+
+ const auto nextCurrent = doGetCurrent();
+
+ qCDebug(playlistCategory()) << "Got previous entry:" << nextCurrent;
+
+ if (current != nextCurrent) {
+ emit currentChanged(nextCurrent);
+ }
+}
+
+PlaylistEntry Playlist::doGetCurrent() const {
+ return {};
+}
+
+bool Playlist::doNext() { return false; }
+
+bool Playlist::doPrevious() { return false; }
+
+bool Playlist::doUpdateCurrentEntry(const PlaylistEntry ¤t) {
+ return false;
+}
+
+QDebug &operator<<(QDebug &stream, const PlaylistEntry &entry) {
+ stream.nospace() << "PlaylistEntry{path = " << entry.path << "}";
+ return stream;
+}
--- /dev/null
+#ifndef PLAYLIST_H
+#define PLAYLIST_H
+
+#include <QObject>
+#include <QUrl>
+
+class PlaylistEntry {
+public:
+ QString path;
+
+ QUrl url() const { return QUrl::fromLocalFile(path); }
+
+ friend bool operator==(const PlaylistEntry &a, const PlaylistEntry &b) {
+ return a.path == b.path;
+ }
+
+ friend bool operator!=(const PlaylistEntry &a, const PlaylistEntry &b) {
+ return a.path != b.path;
+ }
+};
+
+QDebug &operator<<(QDebug &stream, const PlaylistEntry &entry);
+
+class Playlist : public QObject {
+ Q_OBJECT
+ Q_PROPERTY(PlaylistEntry current READ current NOTIFY currentChanged)
+
+public:
+ Playlist(QObject *parent = nullptr);
+
+ PlaylistEntry current() const;
+ void updateCurrentEntry(const PlaylistEntry &entry);
+
+public slots:
+ void next();
+ void previous();
+
+signals:
+ void currentChanged(const PlaylistEntry ¤t);
+
+protected:
+ virtual PlaylistEntry doGetCurrent() const;
+ virtual bool doNext();
+ virtual bool doPrevious();
+ virtual bool doUpdateCurrentEntry(const PlaylistEntry ¤t);
+
+};
+
+#endif // PLAYLIST_H
onTriggered: controlledPlayer.position -= 5
shortcut: "left"
}
+ Action {
+ id: nextPlaylistEntry
+ enabled: controlledPlayer
+ onTriggered: controlledPlayer.next()
+ shortcut: "ctrl+right"
+ }
+ Action {
+ id: previousPlaylistEntry
+ enabled: controlledPlayer
+ onTriggered: controlledPlayer.previous()
+ shortcut: "ctrl+left"
+ }
OpenButton {
}
}
}
+ BasicButton {
+ id: previousPlaylistEntryButton
+ text: "|<<"
+ onClicked: previousPlaylistEntry.trigger();
+ }
+ BasicButton {
+ id: nextPlaylistEntryButton
+ text: ">>|"
+ onClicked: nextPlaylistEntry.trigger();
+ }
BasicButton {
id: fullscreenButton
text: "FS"