]> Some of my projects - aniplayer.git/commitdiff
Add playlist support
authorAPTX <marek321@gmail.com>
Sun, 25 Nov 2018 10:00:34 +0000 (19:00 +0900)
committerAPTX <marek321@gmail.com>
Sun, 25 Nov 2018 10:00:34 +0000 (19:00 +0900)
Adds an initial directory playlist. DirectoryPlaylist contains
all files in the same directory as the current file.

core/CMakeLists.txt
core/directoryplaylist.cpp [new file with mode: 0644]
core/directoryplaylist.h [new file with mode: 0644]
core/player.cpp
core/player.h
core/playlist.cpp [new file with mode: 0644]
core/playlist.h [new file with mode: 0644]
uiplugins/ui_desktop_qml_default/qml/PlayerControls.qml

index 7326b90258b662187f4de35efb3ca3ce33f0282e..cbbaafd42d9da75ee0ce78f618a48cd214ef7ddd 100644 (file)
@@ -41,6 +41,8 @@ set(core_SOURCES
     trackmodel.cpp
     chaptermodel.cpp
     annotationmodel.cpp
+    playlist.cpp
+    directoryplaylist.cpp
 )
 
 set(core_HEADERS
@@ -52,6 +54,8 @@ set(core_HEADERS
     trackmodel.h
     chaptermodel.h
     annotationmodel.h
+    playlist.h
+    directoryplaylist.h
 )
 
 set(core_PUBLIC_HEADERS
diff --git a/core/directoryplaylist.cpp b/core/directoryplaylist.cpp
new file mode 100644 (file)
index 0000000..731c4ab
--- /dev/null
@@ -0,0 +1,89 @@
+#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 &current) {
+  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;
+}
diff --git a/core/directoryplaylist.h b/core/directoryplaylist.h
new file mode 100644 (file)
index 0000000..191c642
--- /dev/null
@@ -0,0 +1,22 @@
+#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 &current) override;
+
+private:
+  PlaylistEntry m_current;
+};
+
+#endif // DIRECTORYPLAYLIST_H
index 171eeeb008f05dfbc3938e3d96d0d95ec8524e7f..6b7a5551c43a2e748489c611f5c67f8a8ebf5fb1 100644 (file)
@@ -4,6 +4,8 @@
 #include <QMetaObject>
 #include <QQmlApplicationEngine>
 
+#include "directoryplaylist.h"
+
 #ifdef Q_OS_WIN
 #include <Windows.h>
 #endif
@@ -43,6 +45,11 @@ Player::Player(BackendPluginBase *backendPlugin, QObject *parent)
 
   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() {
@@ -128,6 +135,7 @@ void Player::load(const QUrl &resource) {
     m_backend->open(resource);
   else
     setNextSource(resource);
+  updatePlaylistState(resource);
 }
 
 void Player::loadAndPlay(const QUrl &resource) {
@@ -162,6 +170,14 @@ void Player::togglePlay() { PlayState::Playing == state() ? pause() : play(); }
 
 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);
@@ -348,6 +364,14 @@ void Player::reqisterQmlTypes() {
 
 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;
 }
@@ -357,3 +381,8 @@ void Player::loadNextFile() {
     loadAndPlay(m_nextSource);
   setNextSource(QUrl{});
 }
+
+void Player::updatePlaylistState(const QUrl &resource) {
+  m_playlist->updateCurrentEntry({resource.toLocalFile()});
+}
+
index 4c2b4cb3bdcc80c510082e1b533f63285bd49e38..0414360ebc1ba573d66749e8630f8b87550f97d9 100644 (file)
@@ -12,6 +12,7 @@
 #include "chaptermodel.h"
 #include "trackmodel.h"
 #include "annotationmodel.h"
+#include "playlist.h"
 
 class Player : public QObject,
                public PlayerPluginInterface,
@@ -153,6 +154,10 @@ public slots:
 
   void seek(Player::TimeStamp position);
 
+  // Playlist
+  void next();
+  void previous();
+
   // Volume
   void setVolume(Volume volume);
   void volumeUp(int byPercentagePoints = 5);
@@ -193,10 +198,13 @@ public:
 
 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;
@@ -218,6 +226,7 @@ private:
   TrackModel *m_subtitleTrackModel;
   ChapterModel *m_chapterModel;
   AnnotationModel *m_annotationModel;
+  Playlist *m_playlist;
   QImage m_lastFrame;
   bool m_saveToImage = true;
   bool m_muted = false;
diff --git a/core/playlist.cpp b/core/playlist.cpp
new file mode 100644 (file)
index 0000000..c810040
--- /dev/null
@@ -0,0 +1,67 @@
+#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 &current) {
+  return false;
+}
+
+QDebug &operator<<(QDebug &stream, const PlaylistEntry &entry) {
+  stream.nospace() << "PlaylistEntry{path = " << entry.path << "}";
+  return stream;
+}
diff --git a/core/playlist.h b/core/playlist.h
new file mode 100644 (file)
index 0000000..64acd9a
--- /dev/null
@@ -0,0 +1,49 @@
+#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 &current);
+
+protected:
+  virtual PlaylistEntry doGetCurrent() const;
+  virtual bool doNext();
+  virtual bool doPrevious();
+  virtual bool doUpdateCurrentEntry(const PlaylistEntry &current);
+
+};
+
+#endif // PLAYLIST_H
index 3bca43183096de04d7b71ea16f1928cd25fae492..950aa99a5f895a5f9b7c8cc0e9d1b0461d651abb 100644 (file)
@@ -61,6 +61,18 @@ Flow {
         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 {
@@ -88,6 +100,16 @@ Flow {
             }
         }
     }
+    BasicButton {
+        id: previousPlaylistEntryButton
+        text: "|<<"
+        onClicked: previousPlaylistEntry.trigger();
+    }
+    BasicButton {
+        id: nextPlaylistEntryButton
+        text: ">>|"
+        onClicked: nextPlaylistEntry.trigger();
+    }
     BasicButton {
         id: fullscreenButton
         text: "FS"