[vlc-devel] [RFC 22/82] qt: playlist: implement Qt playlist list model and controler

Pierre Lamot pierre at videolabs.io
Fri Feb 1 14:01:26 CET 2019


From: Romain Vimont <rom1v at videolabs.io>

  Wrap the playlist, playlist items and media (input items) into C++
  classes, for convenience and automatic memory management.

  Use them to implement a list model to be used by a Qt list view.

  the playlist model provides information about the play queue
  the playlist controler allows to manipulate the playback (play, pause, next, ...)
---
 modules/gui/qt/Makefile.am                    |  12 +
 modules/gui/qt/components/playlist/media.hpp  | 101 +++
 .../components/playlist/playlist_common.cpp   |  40 ++
 .../components/playlist/playlist_common.hpp   |  58 ++
 .../playlist/playlist_controler.cpp           | 667 ++++++++++++++++++
 .../playlist/playlist_controler.hpp           | 167 +++++
 .../playlist/playlist_controler_p.hpp         |  69 ++
 .../qt/components/playlist/playlist_item.cpp  |  79 +++
 .../qt/components/playlist/playlist_item.hpp  | 103 +++
 .../qt/components/playlist/playlist_model.cpp | 418 +++++++++++
 .../qt/components/playlist/playlist_model.hpp |  88 +++
 .../components/playlist/playlist_model_p.hpp  |  72 ++
 .../gui/qt/components/playlist_new/media.hpp  |  76 ++
 modules/gui/qt/qt.cpp                         |  12 +
 14 files changed, 1962 insertions(+)
 create mode 100644 modules/gui/qt/components/playlist/media.hpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_common.cpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_common.hpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_controler.cpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_controler.hpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_controler_p.hpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_item.cpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_item.hpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_model.cpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_model.hpp
 create mode 100644 modules/gui/qt/components/playlist/playlist_model_p.hpp
 create mode 100644 modules/gui/qt/components/playlist_new/media.hpp

diff --git a/modules/gui/qt/Makefile.am b/modules/gui/qt/Makefile.am
index 0f6b7d2904..7d6c517459 100644
--- a/modules/gui/qt/Makefile.am
+++ b/modules/gui/qt/Makefile.am
@@ -109,8 +109,17 @@ libqt_plugin_la_SOURCES = \
 	gui/qt/components/epg/EPGView.cpp gui/qt/components/epg/EPGView.hpp \
 	gui/qt/components/epg/EPGWidget.cpp \
 	gui/qt/components/epg/EPGWidget.hpp \
+	gui/qt/components/playlist/media.hpp \
+	gui/qt/components/playlist/playlist_common.cpp \
+	gui/qt/components/playlist/playlist_common.hpp \
+	gui/qt/components/playlist/playlist_controler.cpp \
+	gui/qt/components/playlist/playlist_controler.hpp \
+	gui/qt/components/playlist/playlist_controler_p.hpp \
 	gui/qt/components/playlist/playlist_item.cpp \
 	gui/qt/components/playlist/playlist_item.hpp \
+	gui/qt/components/playlist/playlist_model.cpp \
+	gui/qt/components/playlist/playlist_model.hpp \
+	gui/qt/components/playlist/playlist_model_p.hpp \
 	gui/qt/components/sout/profile_selector.cpp \
 	gui/qt/components/sout/profile_selector.hpp \
 	gui/qt/components/sout/sout_widgets.cpp \
@@ -213,7 +222,10 @@ nodist_libqt_plugin_la_SOURCES = \
 	gui/qt/components/epg/EPGRuler.moc.cpp \
 	gui/qt/components/epg/EPGView.moc.cpp \
 	gui/qt/components/epg/EPGWidget.moc.cpp \
+	gui/qt/components/playlist/playlist_common.moc.cpp \
+	gui/qt/components/playlist/playlist_item.moc.cpp \
 	gui/qt/components/playlist/playlist_model.moc.cpp \
+	gui/qt/components/playlist/playlist_controler.moc.cpp \
 	gui/qt/components/sout/profile_selector.moc.cpp \
 	gui/qt/components/sout/sout_widgets.moc.cpp \
 	gui/qt/util/animators.moc.cpp \
diff --git a/modules/gui/qt/components/playlist/media.hpp b/modules/gui/qt/components/playlist/media.hpp
new file mode 100644
index 0000000000..7fb2867868
--- /dev/null
+++ b/modules/gui/qt/components/playlist/media.hpp
@@ -0,0 +1,101 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_QT_MEDIA_HPP_
+#define VLC_QT_MEDIA_HPP_
+
+#include <vlc_cxx_helpers.hpp>
+#include <vlc_common.h>
+#include <vlc_input_item.h>
+#include <QString>
+#include <QStringList>
+#include "util/qt_dirs.hpp"
+
+namespace vlc {
+  namespace playlist {
+
+using InputItemPtr = vlc_shared_data_ptr_type(input_item_t,
+                                              input_item_Hold,
+                                              input_item_Release);
+
+class Media
+{
+public:
+    Media(input_item_t *media = nullptr)
+    {
+        if (media)
+        {
+            /* the media must be unique in the playlist */
+            ptr.reset(input_item_Copy(media), false);
+            if (!ptr)
+                throw std::bad_alloc();
+        }
+    }
+
+    Media(QString uri, QString name, QStringList* options = nullptr)
+    {
+        auto uUri = uri.toUtf8();
+        auto uName = name.toUtf8();
+        const char *rawUri = uUri.isNull() ? nullptr : uUri.constData();
+        const char *rawName = uName.isNull() ? nullptr : uName.constData();
+        ptr.reset(input_item_New(rawUri, rawName), false);
+        if (!ptr)
+            throw std::bad_alloc();
+
+        if (options && options->count() > 0)
+        {
+            char **ppsz_options = NULL;
+            int i_options = 0;
+
+            ppsz_options = new char *[options->count()];
+            auto optionDeleter = vlc::wrap_carray<char*>(ppsz_options, [&i_options](char *ptr[]) {
+                for(int i = 0; i < i_options; i++)
+                    free(ptr[i]);
+                delete[] ptr;
+            });
+
+            for (int i = 0; i < options->count(); i++)
+            {
+                QString option = colon_unescape( options->at(i) );
+                ppsz_options[i] = strdup(option.toUtf8().constData());
+                if (!ppsz_options[i])
+                    throw std::bad_alloc();
+                i_options++;
+            }
+            input_item_AddOptions( ptr.get(), i_options, ppsz_options, VLC_INPUT_OPTION_TRUSTED );
+        }
+    }
+
+    operator bool() const
+    {
+        return static_cast<bool>(ptr);
+    }
+
+    input_item_t *raw() const
+    {
+        return ptr.get();
+    }
+
+private:
+    InputItemPtr ptr;
+};
+
+  } // namespace playlist
+} // namespace vlc
+
+#endif
diff --git a/modules/gui/qt/components/playlist/playlist_common.cpp b/modules/gui/qt/components/playlist/playlist_common.cpp
new file mode 100644
index 0000000000..c7b6a20ca2
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_common.cpp
@@ -0,0 +1,40 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#include "playlist_common.hpp"
+
+
+PlaylistPtr::PlaylistPtr(QObject* parent)
+    : m_playlist(nullptr)
+{}
+
+PlaylistPtr::PlaylistPtr(vlc_playlist_t* pl, QObject* parent)
+    : m_playlist(pl)
+{}
+
+PlaylistPtr::PlaylistPtr(const PlaylistPtr& ptr)
+    : m_playlist(ptr.m_playlist)
+{
+}
+
+PlaylistPtr&PlaylistPtr::operator=(const PlaylistPtr& ptr)
+{
+    this->m_playlist = ptr.m_playlist;
+    return *this;
+}
+
diff --git a/modules/gui/qt/components/playlist/playlist_common.hpp b/modules/gui/qt/components/playlist/playlist_common.hpp
new file mode 100644
index 0000000000..2a5161cd88
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_common.hpp
@@ -0,0 +1,58 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef PLAYLIST_COMMON_HPP
+#define PLAYLIST_COMMON_HPP
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <QObject>
+#include <vlc_playlist.h>
+
+// QObject wrapper to carry playlist ptr through QML
+class PlaylistPtr
+{
+    Q_GADGET
+public:
+    PlaylistPtr(QObject* parent = nullptr);
+    PlaylistPtr(vlc_playlist_t* pl, QObject* parent = nullptr);
+    PlaylistPtr(const PlaylistPtr& ptr);
+    PlaylistPtr& operator=(const PlaylistPtr& ptr);
+
+    vlc_playlist_t* m_playlist = nullptr;
+};
+
+class PlaylistLocker
+{
+public:
+    inline PlaylistLocker(vlc_playlist_t* playlist)
+        : m_playlist(playlist)
+    {
+        vlc_playlist_Lock(m_playlist);
+    }
+
+    inline  ~PlaylistLocker()
+    {
+        vlc_playlist_Unlock(m_playlist);
+    }
+
+    vlc_playlist_t* m_playlist;
+};
+
+#endif // PLAYLIST_COMMON_HPP
diff --git a/modules/gui/qt/components/playlist/playlist_controler.cpp b/modules/gui/qt/components/playlist/playlist_controler.cpp
new file mode 100644
index 0000000000..6b7906fcd0
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_controler.cpp
@@ -0,0 +1,667 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "playlist_controler.hpp"
+#include "playlist_controler_p.hpp"
+#include "vlc_player.h"
+#include <algorithm>
+#include <QVariant>
+
+namespace vlc {
+  namespace playlist {
+
+static QVector<PlaylistItem> toVec(vlc_playlist_item_t *const items[],
+                                   size_t len)
+{
+    QVector<PlaylistItem> vec;
+    for (size_t i = 0; i < len; ++i)
+        vec.push_back(items[i]);
+    return vec;
+}
+
+template <typename RAW, typename WRAPPER>
+static QVector<RAW> toRaw(const QVector<WRAPPER> &items)
+{
+    QVector<RAW> vec;
+    int count = items.size();
+    vec.reserve(count);
+    for (int i = 0; i < count; ++i)
+        vec.push_back(items[i].raw());
+    return vec;
+}
+
+
+extern "C" { // for C callbacks
+
+static void
+on_playlist_items_reset(vlc_playlist_t *playlist,
+                        vlc_playlist_item_t *const items[],
+                        size_t len, void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+    auto vec = toVec(items, len);
+    size_t totalCount = vlc_playlist_Count(playlist);
+
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        PlaylistControlerModel* q = that->q_func();
+        bool empty = vec.size() == 0;
+        if (that->m_empty != empty)
+        {
+            that->m_empty = empty;
+            emit q->isEmptyChanged(empty);
+        }
+        emit q->itemsReset(vec);
+
+        if (totalCount != that->m_count)
+        {
+            that->m_count = totalCount;
+            emit q->countChanged(totalCount);
+        }
+    });
+}
+
+static void
+on_playlist_items_added(vlc_playlist_t *playlist, size_t index,
+                        vlc_playlist_item_t *const items[], size_t len,
+                        void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+    auto vec = toVec(items, len);
+    size_t totalCount = vlc_playlist_Count(playlist);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        PlaylistControlerModel* q = that->q_func();
+        if (that->m_empty && vec.size() > 0)
+        {
+            that->m_empty = false;
+            emit q->isEmptyChanged(that->m_empty);
+        }
+        emit q->itemsAdded(index, vec);
+
+        if (totalCount != that->m_count)
+        {
+            that->m_count = totalCount;
+            emit q->countChanged(totalCount);
+        }
+    });
+}
+
+static void
+on_playlist_items_moved(vlc_playlist_t *playlist, size_t index, size_t count,
+                        size_t target, void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        emit that->q_func()->itemsMoved(index, count, target);
+    });
+}
+
+static void
+on_playlist_items_removed(vlc_playlist_t *playlist, size_t index, size_t count,
+                          void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+    size_t totalCount = vlc_playlist_Count(playlist);
+    bool empty = (totalCount == 0);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        PlaylistControlerModel* q = that->q_func();
+        if (that->m_empty != empty)
+        {
+            that->m_empty = empty;
+            emit q->isEmptyChanged(empty);
+        }
+        emit q->itemsRemoved(index, count);
+        if (totalCount != that->m_count)
+        {
+            that->m_count = totalCount;
+            emit q->countChanged(totalCount);
+        }
+    });
+}
+
+static void
+on_playlist_items_updated(vlc_playlist_t *playlist, size_t index,
+                          vlc_playlist_item_t *const items[], size_t len,
+                          void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+    auto vec = toVec(items, len);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        emit that->q_func()->itemsUpdated(index, vec);
+    });
+}
+
+static void
+on_playlist_playback_repeat_changed(vlc_playlist_t *playlist,
+                                    enum vlc_playlist_playback_repeat repeat,
+                                    void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        PlaylistControlerModel::PlaybackRepeat repeatMode = static_cast<PlaylistControlerModel::PlaybackRepeat>(repeat);
+        if (that->m_repeat != repeatMode )
+        {
+            that->m_repeat = repeatMode;
+            emit that->q_func()->repeatModeChanged(repeatMode);
+        }
+    });
+}
+
+static void
+on_playlist_playback_order_changed(vlc_playlist_t *playlist,
+                                   enum vlc_playlist_playback_order order,
+                                   void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        bool isRandom = order == VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM;
+        if (that->m_random != isRandom)
+        {
+            that->m_random = isRandom;
+            emit that->q_func()->randomChanged(isRandom);
+        }
+    });
+}
+
+static void
+on_playlist_current_item_changed(vlc_playlist_t *playlist, ssize_t index,
+                                 void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+
+
+    vlc_playlist_item_t* playlistItem = nullptr;
+    if (index >= 0)
+        playlistItem = vlc_playlist_Get(playlist, index);
+    PlaylistItem newItem{ playlistItem };
+
+    that->callAsync([=](){
+        PlaylistControlerModel* q = that->q_func();
+
+        if (that->m_playlist != playlist)
+            return;
+        if (that->m_currentIndex != index)
+        {
+            that->m_currentIndex = index;
+            emit q->currentIndexChanged(that->m_currentIndex);
+        }
+        that->m_currentItem = newItem;
+        emit q->currentItemChanged();
+    });
+}
+
+static void
+on_playlist_has_prev_changed(vlc_playlist_t *playlist, bool has_prev,
+                             void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        if (that->m_hasPrev != has_prev)
+        {
+            that->m_hasPrev = has_prev;
+            emit that->q_func()->hasPrevChanged(has_prev);
+        }
+    });
+}
+
+static void
+on_playlist_has_next_changed(vlc_playlist_t *playlist, bool has_next,
+                             void *userdata)
+{
+    PlaylistControlerModelPrivate *that = static_cast<PlaylistControlerModelPrivate *>(userdata);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        if (that->m_hasNext != has_next)
+        {
+            that->m_hasNext = has_next;
+            emit that->q_func()->hasNextChanged(has_next);
+        }
+    });
+}
+
+} // extern "C"
+
+static const struct vlc_playlist_callbacks playlist_callbacks = {
+    /* C++ (before C++20) does not support designated initializers */
+    on_playlist_items_reset,
+    on_playlist_items_added,
+    on_playlist_items_moved,
+    on_playlist_items_removed,
+    on_playlist_items_updated,
+    on_playlist_playback_repeat_changed,
+    on_playlist_playback_order_changed,
+    on_playlist_current_item_changed,
+    on_playlist_has_prev_changed,
+    on_playlist_has_next_changed,
+};
+
+
+//private API
+
+PlaylistControlerModelPrivate::PlaylistControlerModelPrivate(PlaylistControlerModel* playlistControler)
+    : q_ptr(playlistControler)
+{
+}
+
+PlaylistControlerModelPrivate::~PlaylistControlerModelPrivate()
+{
+    if (m_playlist && m_listener)
+    {
+        PlaylistLocker lock(m_playlist);
+        vlc_playlist_RemoveListener(m_playlist, m_listener);
+    }
+}
+
+//public API
+
+PlaylistControlerModel::PlaylistControlerModel(QObject *parent)
+    : QObject(parent)
+    , d_ptr( new PlaylistControlerModelPrivate(this) )
+{
+}
+
+PlaylistControlerModel::PlaylistControlerModel(vlc_playlist_t *playlist, QObject *parent)
+    : QObject(parent)
+    , d_ptr( new PlaylistControlerModelPrivate(this) )
+{
+    setPlaylistPtr(playlist);
+}
+
+PlaylistControlerModel::~PlaylistControlerModel()
+{
+}
+
+PlaylistItem PlaylistControlerModel::getCurrentItem() const
+{
+    Q_D(const PlaylistControlerModel);
+    return d->m_currentItem;
+}
+
+void PlaylistControlerModel::append(const QVariantList& sourceList, bool startPlaying)
+{
+    QVector<Media> mediaList;
+    std::transform(sourceList.begin(), sourceList.end(),
+                   std::back_inserter(mediaList), [](const QVariant& value) {
+        if (value.canConvert<QUrl>())
+        {
+            auto mrl = value.value<QUrl>();
+            return Media(mrl.toString(QUrl::None), mrl.fileName());
+        }
+        else if (value.canConvert<QString>())
+        {
+            auto mrl = value.value<QString>();
+            return Media(mrl, mrl);
+        }
+        return Media{};
+    });
+    append(mediaList, startPlaying);
+}
+
+void PlaylistControlerModel::insert(unsigned index, const QVariantList& sourceList, bool startPlaying)
+{
+    QVector<Media> mediaList;
+    std::transform(sourceList.begin(), sourceList.end(),
+                   std::back_inserter(mediaList), [](const QVariant& value) {
+        if (value.canConvert<QUrl>())
+        {
+            auto mrl = value.value<QUrl>();
+            return Media(mrl.toString(QUrl::None), mrl.fileName());
+        }
+        else if (value.canConvert<QString>())
+        {
+            auto mrl = value.value<QString>();
+            return Media(mrl, mrl);
+        }
+        return Media{};
+    });
+    insert(index, mediaList, startPlaying);
+}
+
+
+void
+PlaylistControlerModel::append(const QVector<Media> &media, bool startPlaying)
+{
+    Q_D(PlaylistControlerModel);
+
+    PlaylistLocker locker(d->m_playlist);
+
+    auto rawMedia = toRaw<input_item_t *>(media);
+    int ret = vlc_playlist_Append(d->m_playlist,
+                                  rawMedia.constData(), rawMedia.size());
+    if (ret != VLC_SUCCESS)
+        throw std::bad_alloc();
+    if (startPlaying)
+    {
+        ssize_t playIndex = (ssize_t)vlc_playlist_Count( d->m_playlist ) - rawMedia.size();
+        ret = vlc_playlist_GoTo(d->m_playlist, playIndex);
+        if (ret != VLC_SUCCESS)
+            return;
+        vlc_playlist_Start(d->m_playlist);
+    }
+}
+
+void
+PlaylistControlerModel::insert(size_t index, const QVector<Media> &media, bool startPlaying)
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker locker(d->m_playlist);
+
+    auto rawMedia = toRaw<input_item_t *>(media);
+    int ret = vlc_playlist_RequestInsert(d->m_playlist, index,
+                                         rawMedia.constData(), rawMedia.size());
+    if (ret != VLC_SUCCESS)
+        throw std::bad_alloc();
+    if (startPlaying)
+    {
+        ret = vlc_playlist_GoTo(d->m_playlist, index);
+        if (ret != VLC_SUCCESS)
+            return;
+        vlc_playlist_Start(d->m_playlist);
+    }
+}
+
+void
+PlaylistControlerModel::move(const QVector<PlaylistItem> &items, size_t target,
+               ssize_t indexHint)
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker locker(d->m_playlist);
+
+    auto rawItems = toRaw<vlc_playlist_item_t *>(items);
+    int ret = vlc_playlist_RequestMove(d->m_playlist, rawItems.constData(),
+                                       rawItems.size(), target, indexHint);
+    if (ret != VLC_SUCCESS)
+        throw std::bad_alloc();
+}
+
+void
+PlaylistControlerModel::remove(const QVector<PlaylistItem> &items, ssize_t indexHint)
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker locker(d->m_playlist);
+    auto rawItems = toRaw<vlc_playlist_item_t *>(items);
+    int ret = vlc_playlist_RequestRemove(d->m_playlist, rawItems.constData(),
+                                         rawItems.size(), indexHint);
+    if (ret != VLC_SUCCESS)
+        throw std::bad_alloc();
+}
+
+void
+PlaylistControlerModel::shuffle()
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker locker(d->m_playlist);
+    vlc_playlist_Shuffle(d->m_playlist);
+}
+
+void
+PlaylistControlerModel::sort(const QVector<vlc_playlist_sort_criterion> &criteria)
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker locker(d->m_playlist);
+    vlc_playlist_Sort(d->m_playlist, criteria.constData(), criteria.size());
+}
+
+void PlaylistControlerModel::sort(PlaylistControlerModel::SortKey key, PlaylistControlerModel::SortOrder order)
+{
+    vlc_playlist_sort_criterion crit = {
+        static_cast<vlc_playlist_sort_key>(key) ,
+        static_cast<vlc_playlist_sort_order>(order)
+    };
+    QVector<vlc_playlist_sort_criterion> criteria { crit };
+    sort( criteria );
+}
+
+void PlaylistControlerModel::play()
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_playlist_Start( d->m_playlist );
+}
+
+void PlaylistControlerModel::pause()
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_playlist_Pause( d->m_playlist );
+}
+
+void PlaylistControlerModel::stop()
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_playlist_Stop( d->m_playlist );
+}
+
+void PlaylistControlerModel::next()
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_playlist_Next( d->m_playlist );
+}
+
+void PlaylistControlerModel::prev()
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_playlist_Prev( d->m_playlist );
+}
+
+void PlaylistControlerModel::prevOrReset()
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    bool seek = false;
+    vlc_player_t* player = vlc_playlist_GetPlayer( d->m_playlist );
+    Q_ASSERT(player);
+    if( !vlc_player_IsStarted(player) || vlc_player_GetTime(player) < VLC_TICK_FROM_MS(10) )
+    {
+        int ret = vlc_playlist_Prev( d->m_playlist );
+        if (ret == VLC_SUCCESS)
+            vlc_playlist_Start( d->m_playlist );
+    }
+    else
+        seek = true;
+    if (seek)
+        vlc_player_JumpPos( player, 0.f );
+}
+
+void PlaylistControlerModel::togglePlayPause()
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_player_t* player = vlc_playlist_GetPlayer( d->m_playlist );
+    if ( vlc_player_IsStarted(player) )
+        vlc_player_TogglePause( player );
+    else
+        vlc_playlist_Start( d->m_playlist );
+}
+
+void PlaylistControlerModel::toggleRandom()
+{
+    Q_D(PlaylistControlerModel);
+    vlc_playlist_playback_order new_order;
+    PlaylistLocker lock{ d->m_playlist };
+    if ( d->m_random )
+        new_order = VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL;
+    else
+        new_order = VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM;
+    vlc_playlist_SetPlaybackOrder( d->m_playlist, new_order );
+    config_PutInt( "random", new_order );
+}
+
+void PlaylistControlerModel::toggleRepeatMode()
+{
+    Q_D(PlaylistControlerModel);
+    vlc_playlist_playback_repeat new_repeat;
+    /* Toggle Normal -> Loop -> Repeat -> Normal ... */
+    switch ( d->m_repeat ) {
+    case VLC_PLAYLIST_PLAYBACK_REPEAT_NONE:
+        new_repeat = VLC_PLAYLIST_PLAYBACK_REPEAT_ALL;
+        break;
+    case VLC_PLAYLIST_PLAYBACK_REPEAT_ALL:
+        new_repeat = VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT;
+        break;
+    case VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT:
+    default:
+        new_repeat = VLC_PLAYLIST_PLAYBACK_REPEAT_NONE;
+        break;
+    }
+    {
+        PlaylistLocker lock{ d->m_playlist };
+        vlc_playlist_SetPlaybackRepeat( d->m_playlist, new_repeat );
+    }
+    config_PutInt( "repeat", new_repeat );
+}
+
+void PlaylistControlerModel::clear()
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_playlist_Clear( d->m_playlist );
+}
+
+void PlaylistControlerModel::goTo(uint index, bool startPlaying)
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    if (index > vlc_playlist_Count( d->m_playlist ) -1)
+        return;
+    vlc_playlist_GoTo( d->m_playlist, index );
+    if (startPlaying)
+        vlc_playlist_Start( d->m_playlist );
+}
+
+bool PlaylistControlerModel::isRandom() const
+{
+    Q_D(const PlaylistControlerModel);
+    return d->m_random;
+}
+
+void PlaylistControlerModel::setRandom(bool random)
+{
+    Q_D(const PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_playlist_SetPlaybackOrder( d->m_playlist, random ? VLC_PLAYLIST_PLAYBACK_ORDER_RANDOM : VLC_PLAYLIST_PLAYBACK_ORDER_NORMAL );
+}
+
+PlaylistPtr PlaylistControlerModel::getPlaylistPtr() const
+{
+    Q_D(const PlaylistControlerModel);
+    return PlaylistPtr(d->m_playlist);
+}
+
+void PlaylistControlerModel::setPlaylistPtr(vlc_playlist_t* newPlaylist)
+{
+    Q_D(PlaylistControlerModel);
+    if (d->m_playlist && d->m_listener)
+    {
+        PlaylistLocker locker(d->m_playlist);
+        vlc_playlist_RemoveListener(d->m_playlist, d->m_listener);
+        d->m_playlist = nullptr;
+        d->m_listener = nullptr;
+    }
+    if (newPlaylist)
+    {
+        PlaylistLocker locker(newPlaylist);
+        d->m_playlist = newPlaylist;
+        d->m_listener = vlc_playlist_AddListener(d->m_playlist, &playlist_callbacks, d, true);
+    }
+    emit playlistPtrChanged( PlaylistPtr(newPlaylist) );
+}
+
+void PlaylistControlerModel::setPlaylistPtr(PlaylistPtr ptr)
+{
+    setPlaylistPtr(ptr.m_playlist);
+}
+
+PlaylistControlerModel::PlaybackRepeat PlaylistControlerModel::getRepeatMode() const
+{
+    Q_D(const PlaylistControlerModel);
+    return d->m_repeat;
+}
+
+void PlaylistControlerModel::setRepeatMode(PlaylistControlerModel::PlaybackRepeat mode)
+{
+    Q_D(const PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_playlist_SetPlaybackRepeat( d->m_playlist, static_cast<vlc_playlist_playback_repeat>(mode) );
+}
+
+
+bool PlaylistControlerModel::isPlayAndExit() const
+{
+    Q_D(const PlaylistControlerModel);
+    return d->m_isPlayAndExit;
+}
+
+void PlaylistControlerModel::setPlayAndExit(bool enable)
+{
+    Q_D(PlaylistControlerModel);
+    PlaylistLocker lock{ d->m_playlist };
+    vlc_player_t* player = vlc_playlist_GetPlayer( d->m_playlist );
+    vlc_player_SetMediaStoppedAction( player, enable ? VLC_PLAYER_MEDIA_STOPPED_EXIT :  VLC_PLAYER_MEDIA_STOPPED_CONTINUE );
+}
+
+bool PlaylistControlerModel::isEmpty() const
+{
+    Q_D(const PlaylistControlerModel);
+    return d->m_empty;
+}
+
+size_t PlaylistControlerModel::count() const
+{
+    Q_D(const PlaylistControlerModel);
+    return d->m_count;
+}
+
+bool PlaylistControlerModel::hasNext() const
+{
+    Q_D(const PlaylistControlerModel);
+    return d->m_hasNext;
+}
+
+bool PlaylistControlerModel::hasPrev() const
+{
+    Q_D(const PlaylistControlerModel);
+    return d->m_hasPrev;
+}
+
+
+  } // namespace playlist
+} // namespace vlc
diff --git a/modules/gui/qt/components/playlist/playlist_controler.hpp b/modules/gui/qt/components/playlist/playlist_controler.hpp
new file mode 100644
index 0000000000..a14def350b
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_controler.hpp
@@ -0,0 +1,167 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_QT_PLAYLIST_NEW_HPP_
+#define VLC_QT_PLAYLIST_NEW_HPP_
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <QObject>
+#include <QVector>
+#include "media.hpp"
+#include "playlist_common.hpp"
+#include "playlist_item.hpp"
+
+namespace vlc {
+  namespace playlist {
+
+class PlaylistControlerModelPrivate;
+class PlaylistControlerModel : public QObject
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(PlaylistControlerModel)
+
+public:
+    enum PlaybackRepeat
+    {
+        PLAYBACK_REPEAT_NONE = VLC_PLAYLIST_PLAYBACK_REPEAT_NONE,
+        PLAYBACK_REPEAT_CURRENT = VLC_PLAYLIST_PLAYBACK_REPEAT_CURRENT,
+        PLAYBACK_REPEAT_ALL = VLC_PLAYLIST_PLAYBACK_REPEAT_ALL
+    };
+    Q_ENUM(PlaybackRepeat)
+
+    enum SortKey
+    {
+        SORT_KEY_TITLE = VLC_PLAYLIST_SORT_KEY_TITLE,
+        SORT_KEY_DURATION = VLC_PLAYLIST_SORT_KEY_DURATION,
+        SORT_KEY_ARTIST = VLC_PLAYLIST_SORT_KEY_ARTIST,
+        SORT_KEY_ALBUM = VLC_PLAYLIST_SORT_KEY_ALBUM,
+        SORT_KEY_ALBUM_ARTIST = VLC_PLAYLIST_SORT_KEY_ALBUM_ARTIST,
+        SORT_KEY_GENRE = VLC_PLAYLIST_SORT_KEY_GENRE,
+        SORT_KEY_DATE = VLC_PLAYLIST_SORT_KEY_DATE,
+        SORT_KEY_TRACK_NUMBER = VLC_PLAYLIST_SORT_KEY_TRACK_NUMBER,
+        SORT_KEY_DISC_NUMBER = VLC_PLAYLIST_SORT_KEY_DISC_NUMBER,
+        SORT_KEY_URL = VLC_PLAYLIST_SORT_KEY_URL,
+        SORT_KEY_RATIN = VLC_PLAYLIST_SORT_KEY_RATING
+    };
+    Q_ENUM(SortKey)
+
+    enum SortOrder
+    {
+        SORT_ORDER_ASC = VLC_PLAYLIST_SORT_ORDER_ASCENDING,
+        SORT_ORDER_DESC = VLC_PLAYLIST_SORT_ORDER_DESCENDING,
+    };
+    Q_ENUM(SortOrder)
+
+    Q_PROPERTY(PlaylistPtr playlistPtr READ getPlaylistPtr WRITE setPlaylistPtr NOTIFY playlistPtrChanged)
+
+    Q_PROPERTY(PlaylistItem currentItem READ getCurrentItem NOTIFY currentItemChanged)
+
+    Q_PROPERTY(bool hasNext READ hasNext NOTIFY hasNextChanged)
+    Q_PROPERTY(bool hasPrev READ hasPrev NOTIFY hasPrevChanged)
+    Q_PROPERTY(bool random READ isRandom WRITE setRandom NOTIFY randomChanged )
+    Q_PROPERTY(PlaybackRepeat repeatMode READ getRepeatMode WRITE setRepeatMode NOTIFY repeatModeChanged)
+    Q_PROPERTY(bool playAndExit READ isPlayAndExit WRITE setPlayAndExit NOTIFY playAndExitChanged)
+    Q_PROPERTY(bool empty READ isEmpty NOTIFY isEmptyChanged)
+    Q_PROPERTY(size_t count READ count NOTIFY countChanged)
+
+public:
+    Q_INVOKABLE void play();
+    Q_INVOKABLE void pause();
+    Q_INVOKABLE void stop();
+    Q_INVOKABLE void next();
+    Q_INVOKABLE void prev();
+    Q_INVOKABLE void prevOrReset();
+    Q_INVOKABLE void togglePlayPause();
+
+    Q_INVOKABLE void toggleRandom();
+    Q_INVOKABLE void toggleRepeatMode();
+
+    Q_INVOKABLE void clear();
+    Q_INVOKABLE void goTo(uint index, bool startPlaying = false);
+
+    Q_INVOKABLE void append(const QVariantList&, bool startPlaying = false);
+    Q_INVOKABLE void insert(unsigned index, const QVariantList&, bool startPlaying = false);
+
+    void append(const QVector<Media> &, bool startPlaying = false);
+    void insert(size_t index, const QVector<Media> &, bool startPlaying = false);
+    void move(const QVector<PlaylistItem> &, size_t target, ssize_t indexHint);
+    void remove(const QVector<PlaylistItem> &, ssize_t indexHint);
+
+    Q_INVOKABLE void shuffle();
+    void sort(const QVector<vlc_playlist_sort_criterion> &);
+    Q_INVOKABLE void sort(SortKey key, SortOrder order);
+
+public:
+    PlaylistControlerModel(QObject *parent = nullptr);
+    PlaylistControlerModel(vlc_playlist_t *playlist, QObject *parent = nullptr);
+    virtual ~PlaylistControlerModel();
+
+public slots:
+    PlaylistItem getCurrentItem() const;
+
+    bool hasNext() const;
+    bool hasPrev() const;
+
+    bool isRandom() const;
+    void setRandom( bool );
+
+    PlaybackRepeat getRepeatMode() const;
+    void setRepeatMode( PlaybackRepeat mode );
+
+    bool isPlayAndExit() const;
+    void setPlayAndExit(bool );
+
+    bool isEmpty() const;
+    size_t count() const;
+
+    PlaylistPtr getPlaylistPtr() const;
+    void setPlaylistPtr(PlaylistPtr id);
+    void setPlaylistPtr(vlc_playlist_t* newPlaylist);
+
+signals:
+    void playlistPtrChanged( PlaylistPtr );
+
+    void currentItemChanged( );
+
+    void hasNextChanged( bool );
+    void hasPrevChanged( bool );
+    void randomChanged( bool );
+    void playAndExitChanged( bool );
+    void repeatModeChanged( PlaybackRepeat );
+    void isEmptyChanged( bool empty );
+    void countChanged(size_t );
+
+    void currentIndexChanged(ssize_t index);
+    void itemsReset(QVector<PlaylistItem>);
+    void itemsAdded(size_t index, QVector<PlaylistItem>);
+    void itemsMoved(size_t index, size_t count, size_t target);
+    void itemsRemoved(size_t index, size_t count);
+    void itemsUpdated(size_t index, QVector<PlaylistItem>);
+
+private:
+    Q_DECLARE_PRIVATE(PlaylistControlerModel)
+    QScopedPointer<PlaylistControlerModelPrivate> d_ptr;
+};
+
+  } // namespace playlist
+} // namespace vlc
+
+#endif
diff --git a/modules/gui/qt/components/playlist/playlist_controler_p.hpp b/modules/gui/qt/components/playlist/playlist_controler_p.hpp
new file mode 100644
index 0000000000..5e44b0a294
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_controler_p.hpp
@@ -0,0 +1,69 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef PLAYLIST_CONTROLER_P_HPP
+#define PLAYLIST_CONTROLER_P_HPP
+
+#include "playlist_controler.hpp"
+
+namespace vlc {
+namespace playlist {
+
+class PlaylistControlerModelPrivate
+{
+    Q_DISABLE_COPY(PlaylistControlerModelPrivate)
+public:
+    Q_DECLARE_PUBLIC(PlaylistControlerModel)
+    PlaylistControlerModel * const q_ptr;
+
+public:
+    PlaylistControlerModelPrivate(PlaylistControlerModel* playlistControler);
+    PlaylistControlerModelPrivate() = delete;
+    ~PlaylistControlerModelPrivate();
+
+    ///call function @a fun on object thread
+    template <typename Fun>
+    inline void callAsync(Fun&& fun)
+    {
+        Q_Q(PlaylistControlerModel);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
+        QMetaObject::invokeMethod(q, std::forward<Fun>(fun), Qt::QueuedConnection, nullptr);
+#else
+        QObject src;
+        QObject::connect(&src, &QObject::destroyed, q, std::forward<Fun>(fun), Qt::QueuedConnection);
+#endif
+    }
+
+    //playlist
+    vlc_playlist_t* m_playlist = nullptr;
+    vlc_playlist_listener_id* m_listener = nullptr;
+
+    ssize_t m_currentIndex = -1;
+    PlaylistItem m_currentItem;
+    bool m_hasNext= false;
+    bool m_hasPrev = false;
+    PlaylistControlerModel::PlaybackRepeat m_repeat = PlaylistControlerModel::PLAYBACK_REPEAT_NONE;
+    bool m_random = false;
+    bool m_isPlayAndExit;
+    bool m_empty = true;
+    size_t m_count = 0;
+};
+
+} //namespace playlist
+} //namespace vlc
+
+#endif // PLAYLIST_CONTROLER_P_HPP
diff --git a/modules/gui/qt/components/playlist/playlist_item.cpp b/modules/gui/qt/components/playlist/playlist_item.cpp
new file mode 100644
index 0000000000..8281daa059
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_item.cpp
@@ -0,0 +1,79 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#include "playlist_item.hpp"
+
+//namespace vlc {
+//namespace playlist {
+
+PlaylistItem::PlaylistItem(vlc_playlist_item_t* item)
+{
+    d = new Data();
+    if (item)
+    {
+        d->item.reset(item);
+        sync();
+    }
+}
+
+QString PlaylistItem::getTitle() const
+{
+    return d->title;
+}
+
+QString PlaylistItem::getArtist() const
+{
+    return d->artist;
+}
+
+QString PlaylistItem::getAlbum() const
+{
+    return d->album;
+}
+
+QUrl PlaylistItem::getArtwork() const
+{
+    return d->artwork;
+}
+
+vlc_tick_t PlaylistItem::getDuration() const
+{
+    return d->duration;
+}
+
+void PlaylistItem::sync() {
+    input_item_t *media = vlc_playlist_item_GetMedia(d->item.get());
+    vlc_mutex_lock(&media->lock);
+    d->title = media->psz_name;
+    d->duration = media->i_duration;
+
+    if (media->p_meta) {
+        d->artist = vlc_meta_Get(media->p_meta, vlc_meta_Artist);
+        d->album = vlc_meta_Get(media->p_meta, vlc_meta_Album);
+        d->artwork = vlc_meta_Get(media->p_meta, vlc_meta_ArtworkURL);
+    }
+    vlc_mutex_unlock(&media->lock);
+}
+
+PlaylistItem::operator bool() const
+{
+    return d;
+}
+
+
+//}
+//}
diff --git a/modules/gui/qt/components/playlist/playlist_item.hpp b/modules/gui/qt/components/playlist/playlist_item.hpp
new file mode 100644
index 0000000000..e10d2a3b08
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_item.hpp
@@ -0,0 +1,103 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef VLC_QT_PLAYLIST_NEW_ITEM_HPP_
+#define VLC_QT_PLAYLIST_NEW_ITEM_HPP_
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "vlc_player.h"
+#include <vlc_cxx_helpers.hpp>
+#include <vlc_input_item.h>
+#include <vlc_playlist.h>
+#include <QExplicitlySharedDataPointer>
+#include <QUrl>
+#include <QMetaType>
+
+
+//namespace vlc {
+//  namespace playlist {
+
+using PlaylistItemPtr = vlc_shared_data_ptr_type(vlc_playlist_item_t,
+                                                 vlc_playlist_item_Hold,
+                                                 vlc_playlist_item_Release);
+
+/**
+ * Playlist item wrapper.
+ *
+ * It contains both the PlaylistItemPtr and cached data saved while the playlist
+ * is locked, so that the fields may be read without synchronization or race
+ * conditions.
+ */
+class PlaylistItem
+{
+    Q_GADGET
+public:
+    Q_PROPERTY(QString title READ getTitle CONSTANT )
+    Q_PROPERTY(QString artist READ getArtist CONSTANT )
+    Q_PROPERTY(QString album READ getAlbum CONSTANT )
+    Q_PROPERTY(QUrl artwork READ getArtwork CONSTANT )
+    Q_PROPERTY(vlc_tick_t duration READ getDuration CONSTANT )
+
+    PlaylistItem(vlc_playlist_item_t *item = nullptr);
+
+    operator bool() const;
+
+    vlc_playlist_item_t *raw() const {
+        return d ? d->item.get() : nullptr;
+    }
+
+    QString getTitle() const;
+
+    QString getArtist() const;
+
+    QString getAlbum() const;
+
+    QUrl getArtwork() const;
+
+    vlc_tick_t getDuration() const;
+
+
+    void sync();
+
+private:
+    struct Data : public QSharedData {
+        PlaylistItemPtr item;
+
+        /* cached values */
+        QString title;
+        QString artist;
+        QString album;
+        QUrl artwork;
+
+        vlc_tick_t duration;
+    };
+
+    QExplicitlySharedDataPointer<Data> d;
+};
+
+/* PlaylistItem has the same size as a raw pointer */
+static_assert(sizeof(PlaylistItem) == sizeof(void *));
+
+//  } // namespace playlist
+//} // namespace vlc
+
+Q_DECLARE_METATYPE(PlaylistItem)
+
+#endif
diff --git a/modules/gui/qt/components/playlist/playlist_model.cpp b/modules/gui/qt/components/playlist/playlist_model.cpp
new file mode 100644
index 0000000000..add1ff1501
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_model.cpp
@@ -0,0 +1,418 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "playlist_model.hpp"
+#include "playlist_model_p.hpp"
+#include <algorithm>
+#include <assert.h>
+
+namespace vlc {
+namespace playlist {
+
+namespace {
+
+static QVector<PlaylistItem> toVec(vlc_playlist_item_t *const items[],
+                                   size_t len)
+{
+    QVector<PlaylistItem> vec;
+    for (size_t i = 0; i < len; ++i)
+        vec.push_back(items[i]);
+    return vec;
+}
+
+template <typename RAW, typename WRAPPER>
+static QVector<RAW> toRaw(const QVector<WRAPPER> &items)
+{
+    QVector<RAW> vec;
+    int count = items.size();
+    vec.reserve(count);
+    for (int i = 0; i < count; ++i)
+        vec.push_back(items[i].raw());
+    return vec;
+}
+}
+
+extern "C" { // for C callbacks
+
+static void
+on_playlist_items_reset(vlc_playlist_t *playlist,
+                        vlc_playlist_item_t *const items[],
+                        size_t len, void *userdata)
+{
+    PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+    QVector<PlaylistItem> newContent = toVec(items, len);
+    that->callAsync([=]() {
+        if (that->m_playlist != playlist)
+            return;
+        that->onItemsReset(newContent);
+    });
+}
+
+static void
+on_playlist_items_added(vlc_playlist_t *playlist, size_t index,
+                        vlc_playlist_item_t *const items[], size_t len,
+                        void *userdata)
+{
+    PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+    QVector<PlaylistItem> added = toVec(items, len);
+    that->callAsync([=]() {
+        if (that->m_playlist != playlist)
+            return;
+        that->onItemsAdded(added, index);
+    });
+}
+
+static void
+on_playlist_items_moved(vlc_playlist_t *playlist, size_t index, size_t count,
+                        size_t target, void *userdata)
+{
+    PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+    that->callAsync([=]() {
+        if (that->m_playlist != playlist)
+            return;
+        that->onItemsMoved(index, count, target);
+    });
+}
+
+static void
+on_playlist_items_removed(vlc_playlist_t *playlist, size_t index, size_t count,
+                          void *userdata)
+{
+    PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        that->onItemsRemoved(index, count);
+    });
+}
+
+static void
+on_playlist_items_updated(vlc_playlist_t *playlist, size_t index,
+                          vlc_playlist_item_t *const items[], size_t len,
+                          void *userdata)
+{
+    PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+    QVector<PlaylistItem> updated = toVec(items, len);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        int count = updated.size();
+        for (int i = 0; i < count; ++i)
+            that->m_items[index + i] = updated[i]; /* sync metadata */
+        that->notifyItemsChanged(index, count);
+    });
+}
+
+static void
+on_playlist_current_item_changed(vlc_playlist_t *playlist, ssize_t index,
+                                 void *userdata)
+{
+    PlaylistListModelPrivate *that = static_cast<PlaylistListModelPrivate *>(userdata);
+    that->callAsync([=](){
+        if (that->m_playlist != playlist)
+            return;
+        ssize_t oldCurrent = that->m_current;
+        that->m_current = index;
+        if (oldCurrent != -1)
+            that->notifyItemsChanged(oldCurrent, 1, {PlaylistListModel::IsCurrentRole});
+        if (index != -1)
+            that->notifyItemsChanged(index, 1, {PlaylistListModel::IsCurrentRole});
+        emit that->q_func()->currentIndexChanged(index);
+    });
+}
+
+} // extern "C"
+
+static const struct vlc_playlist_callbacks playlist_callbacks = {
+    /* C++ (before C++20) does not support designated initializers */
+    on_playlist_items_reset,
+    on_playlist_items_added,
+    on_playlist_items_moved,
+    on_playlist_items_removed,
+    on_playlist_items_updated,
+    nullptr,
+    nullptr,
+    on_playlist_current_item_changed,
+    nullptr,
+    nullptr,
+};
+
+// private API
+
+PlaylistListModelPrivate::PlaylistListModelPrivate(PlaylistListModel* playlistListModel)
+    : q_ptr(playlistListModel)
+{
+}
+
+PlaylistListModelPrivate::~PlaylistListModelPrivate()
+{
+    if (m_playlist && m_listener)
+    {
+        PlaylistLocker locker(m_playlist);
+        vlc_playlist_RemoveListener(m_playlist, m_listener);
+    }
+}
+
+void PlaylistListModelPrivate::onItemsReset(const QVector<PlaylistItem>& newContent)
+{
+    Q_Q(PlaylistListModel);
+    q->beginResetModel();
+    m_items = newContent;
+    q->endResetModel();
+}
+
+void PlaylistListModelPrivate::onItemsAdded(const QVector<PlaylistItem>& added, size_t index)
+{
+    Q_Q(PlaylistListModel);
+    int count = added.size();
+    q->beginInsertRows({}, index, index + count - 1);
+    m_items.insert(index, count, nullptr);
+    std::move(added.cbegin(), added.cend(), m_items.begin() + index);
+    q->endInsertRows();
+}
+
+void PlaylistListModelPrivate::onItemsMoved(size_t index, size_t count, size_t target)
+{
+    Q_Q(PlaylistListModel);
+    size_t qtTarget = target;
+    if (qtTarget > index)
+        /* Qt interprets the target index as the index of the insertion _before_
+       * the move, while the playlist core interprets it as the new index of
+       * the slice _after_ the move. */
+        qtTarget += count;
+
+    q->beginMoveRows({}, index, index + count - 1, {}, qtTarget);
+    if (index < target)
+        std::rotate(m_items.begin() + index,
+                    m_items.begin() + index + count,
+                    m_items.begin() + target + count);
+    else
+        std::rotate(m_items.begin() + target,
+                    m_items.begin() + index,
+                    m_items.begin() + index + count);
+    q->endMoveRows();
+}
+
+void PlaylistListModelPrivate::onItemsRemoved(size_t index, size_t count)
+{
+    Q_Q(PlaylistListModel);
+    q->beginRemoveRows({}, index, index + count - 1);
+    m_items.remove(index, count);
+    q->endRemoveRows();
+}
+
+
+void
+PlaylistListModelPrivate::notifyItemsChanged(int idx, int count, const QVector<int> &roles)
+{
+    Q_Q(PlaylistListModel);
+    QModelIndex first = q->index(idx, 0);
+    QModelIndex last = q->index(idx + count - 1);
+    emit q->dataChanged(first, last, roles);
+}
+
+// public API
+
+PlaylistListModel::PlaylistListModel(QObject *parent)
+    : QAbstractListModel(parent)
+    , d_ptr(new PlaylistListModelPrivate(this))
+{
+}
+
+PlaylistListModel::PlaylistListModel(vlc_playlist_t *raw_playlist, QObject *parent)
+    : QAbstractListModel(parent)
+    , d_ptr(new PlaylistListModelPrivate(this))
+{
+    setPlaylistId(PlaylistPtr(raw_playlist));
+}
+
+PlaylistListModel::~PlaylistListModel()
+{
+}
+
+QHash<int, QByteArray>
+PlaylistListModel::roleNames() const
+{
+    return {
+        { TitleRole, "title" },
+        { DurationRole, "duration" },
+        { IsCurrentRole, "isCurrent" },
+        { ArtistRole , "artist" },
+        { AlbumRole  , "album" },
+        { ArtworkRole, "artwork" },
+    };
+}
+
+int
+PlaylistListModel::rowCount(const QModelIndex &parent) const
+{
+    Q_UNUSED(parent);
+    Q_D(const PlaylistListModel);
+    if (! d->m_playlist)
+        return 0;
+    return d->m_items.size();
+}
+
+const PlaylistItem &
+PlaylistListModel::itemAt(int index) const
+{
+    Q_D(const PlaylistListModel);
+    return d->m_items[index];
+}
+
+void PlaylistListModel::removeItems(const QList<int>& indexes)
+{
+    Q_D(PlaylistListModel);
+    if (!d->m_playlist)
+        return;
+    if (indexes.size() == 0)
+        return;
+    QVector<vlc_playlist_item_t *> itemsToRemove;
+    std::transform(indexes.begin(), indexes.end(),std::back_inserter(itemsToRemove), [&] (int index) {
+        return d->m_items[index].raw();
+    });
+
+    {
+        PlaylistLocker locker(d->m_playlist);
+        int ret = vlc_playlist_RequestRemove(d->m_playlist, itemsToRemove.constData(),
+                                             itemsToRemove.size(), indexes[0]);
+        if (ret != VLC_SUCCESS)
+            throw std::bad_alloc();
+    }
+}
+
+void PlaylistListModel::moveItems(const QList<int> &indexes, int target)
+{
+    Q_D(PlaylistListModel);
+    if (!d->m_playlist)
+        return;
+    int targetPre = target;
+    int targetPost = target;
+    if (indexes.size() == 0)
+        return;
+    QVector<vlc_playlist_item_t*> itemsToMove;
+    //std::sort(indexes.begin(), indexes.end()); //Indexes are already sorted
+    std::transform(indexes.begin(), indexes.end(),std::back_inserter(itemsToMove), [&] (int index) {
+        return d->m_items[index].raw();
+    });
+    //the target is in the referential of the list before doing the operation,
+    //the playlist expect the target to be defined as the index after the operation
+    for ( const int index : indexes ) {
+        if (index < targetPre)
+            targetPost--;
+        else
+            break;
+    }
+
+    {
+        PlaylistLocker locker(d->m_playlist);
+        int ret = vlc_playlist_RequestMove(d->m_playlist, itemsToMove.constData(),
+                                           itemsToMove.size(), targetPost, indexes[0]);
+        if (ret != VLC_SUCCESS)
+            throw std::bad_alloc();
+    }
+}
+
+int PlaylistListModel::getCurrentIndex() const
+{
+    Q_D(const PlaylistListModel);
+    return d->m_current;
+}
+
+PlaylistPtr PlaylistListModel::getPlaylistId() const
+{
+    Q_D(const PlaylistListModel);
+    if (!d->m_playlist)
+        return {};
+    return PlaylistPtr(d->m_playlist);
+}
+
+void PlaylistListModel::setPlaylistId(vlc_playlist_t* playlist)
+{
+    Q_D(PlaylistListModel);
+    if (d->m_playlist && d->m_listener)
+    {
+        PlaylistLocker locker(d->m_playlist);
+        vlc_playlist_RemoveListener(d->m_playlist, d->m_listener);
+        d->m_playlist = nullptr;
+        d->m_listener = nullptr;
+    }
+    if (playlist)
+    {
+        PlaylistLocker locker(playlist);
+        d->m_playlist = playlist;
+        d->m_listener = vlc_playlist_AddListener(d->m_playlist, &playlist_callbacks, d, true);
+    }
+    emit playlistIdChanged( PlaylistPtr(d->m_playlist) );
+}
+
+void PlaylistListModel::setPlaylistId(PlaylistPtr id)
+{
+    setPlaylistId(id.m_playlist);
+}
+
+QVariant
+PlaylistListModel::data(const QModelIndex &index, int role) const
+{
+    Q_D(const PlaylistListModel);
+    if (!d->m_playlist)
+        return {};
+
+    ssize_t row = index.row();
+    if (row < 0 || row >= d->m_items.size())
+        return {};
+
+    switch (role)
+    {
+    case TitleRole:
+        return d->m_items[row].getTitle();
+    case IsCurrentRole:
+        return row == d->m_current;
+    case DurationRole:
+    {
+        int64_t t_sec = SEC_FROM_VLC_TICK(d->m_items[row].getDuration());
+        int sec = t_sec % 60;
+        int min = (t_sec / 60) % 60;
+        int hour = t_sec / 3600;
+        if (hour == 0)
+            return QString("%1:%2")
+                    .arg(min, 2, 10, QChar('0'))
+                    .arg(sec, 2, 10, QChar('0'));
+        else
+            return QString("%1:%2:%3")
+                    .arg(hour, 2, 10, QChar('0'))
+                    .arg(min, 2, 10, QChar('0'))
+                    .arg(sec, 2, 10, QChar('0'));
+    }
+    case ArtistRole:
+        return d->m_items[row].getArtist();
+    case AlbumRole:
+        return d->m_items[row].getAlbum();
+    case ArtworkRole:
+        return d->m_items[row].getArtwork();
+    default:
+        return {};
+    }
+}
+
+} // namespace playlist
+} // namespace vlc
diff --git a/modules/gui/qt/components/playlist/playlist_model.hpp b/modules/gui/qt/components/playlist/playlist_model.hpp
new file mode 100644
index 0000000000..6d1eafb2f4
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_model.hpp
@@ -0,0 +1,88 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_QT_PLAYLIST_NEW_MODEL_HPP_
+#define VLC_QT_PLAYLIST_NEW_MODEL_HPP_
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <QAbstractListModel>
+#include <QVector>
+#include "playlist_common.hpp"
+#include "playlist_item.hpp"
+
+namespace vlc {
+namespace playlist {
+
+class PlaylistListModelPrivate;
+class PlaylistListModel : public QAbstractListModel
+{
+    Q_OBJECT
+    Q_PROPERTY(PlaylistPtr playlistId READ getPlaylistId WRITE setPlaylistId NOTIFY playlistIdChanged)
+    Q_PROPERTY(int currentIndex READ getCurrentIndex NOTIFY currentIndexChanged)
+
+public:
+    enum Roles
+    {
+        TitleRole = Qt::UserRole,
+        DurationRole,
+        IsCurrentRole,
+        ArtistRole,
+        AlbumRole,
+        ArtworkRole
+    };
+
+    PlaylistListModel(QObject *parent = nullptr);
+    PlaylistListModel(vlc_playlist_t *playlist, QObject *parent = nullptr);
+    ~PlaylistListModel();
+
+    QHash<int, QByteArray> roleNames() const override;
+    int rowCount(const QModelIndex &parent = {}) const override;
+    QVariant data(const QModelIndex &index,
+                  int role = Qt::DisplayRole) const override;
+
+    /* provided for convenience */
+    const PlaylistItem &itemAt(int index) const;
+
+    Q_INVOKABLE virtual void removeItems(const QList<int> &indexes);
+    Q_INVOKABLE virtual void moveItems(const QList<int> &indexes, int target);
+
+    int getCurrentIndex() const;
+
+public slots:
+    PlaylistPtr getPlaylistId() const;
+    void setPlaylistId(PlaylistPtr id);
+    void setPlaylistId(vlc_playlist_t* playlist);
+
+signals:
+    void playlistIdChanged(const PlaylistPtr& );
+    void currentIndexChanged( int );
+
+private:
+    Q_DECLARE_PRIVATE(PlaylistListModel)
+    QScopedPointer<PlaylistListModelPrivate> d_ptr;
+
+};
+
+
+  } // namespace playlist
+} // namespace vlc
+
+#endif
diff --git a/modules/gui/qt/components/playlist/playlist_model_p.hpp b/modules/gui/qt/components/playlist/playlist_model_p.hpp
new file mode 100644
index 0000000000..315dc1bdde
--- /dev/null
+++ b/modules/gui/qt/components/playlist/playlist_model_p.hpp
@@ -0,0 +1,72 @@
+/*****************************************************************************
+ * playlist_model_p.hpp
+ *****************************************************************************
+ * Copyright (C) 2018 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+#ifndef PLAYLIST_MODEL_P_HPP
+#define PLAYLIST_MODEL_P_HPP
+
+#include "playlist_model.hpp"
+
+namespace vlc {
+namespace playlist {
+
+class PlaylistListModelPrivate
+{
+    Q_DISABLE_COPY(PlaylistListModelPrivate)
+public:
+    Q_DECLARE_PUBLIC(PlaylistListModel)
+    PlaylistListModel* const q_ptr;
+
+public:
+    PlaylistListModelPrivate(PlaylistListModel* playlistList);
+    ~PlaylistListModelPrivate();
+
+    ///call function @a fun on object thread
+    template <typename Fun>
+    inline void callAsync(Fun&& fun)
+    {
+        Q_Q(PlaylistListModel);
+#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
+        QMetaObject::invokeMethod(q, std::forward<Fun>(fun), Qt::QueuedConnection, nullptr);
+#else
+        QObject src;
+        QObject::connect(&src, &QObject::destroyed, q, std::forward<Fun>(fun), Qt::QueuedConnection);
+#endif
+    }
+
+    void onItemsReset(const QVector<PlaylistItem>& items);
+    void onItemsAdded(const QVector<PlaylistItem>& added, size_t index);
+    void onItemsMoved(size_t index, size_t count, size_t target);
+    void onItemsRemoved(size_t index, size_t count);
+
+    void notifyItemsChanged(int index, int count,
+                            const QVector<int> &roles = {});
+
+    vlc_playlist_t* m_playlist = nullptr;
+    vlc_playlist_listener_id *m_listener = nullptr;
+
+    /* access only from the UI thread */
+    QVector<PlaylistItem> m_items;
+    ssize_t m_current = -1;
+};
+
+} //namespace playlist
+} //namespace vlc
+
+
+#endif // PLAYLIST_MODEL_P_HPP
diff --git a/modules/gui/qt/components/playlist_new/media.hpp b/modules/gui/qt/components/playlist_new/media.hpp
new file mode 100644
index 0000000000..b69d3bf520
--- /dev/null
+++ b/modules/gui/qt/components/playlist_new/media.hpp
@@ -0,0 +1,76 @@
+/*****************************************************************************
+ * Copyright (C) 2019 VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * ( at your option ) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifndef VLC_QT_MEDIA_HPP_
+#define VLC_QT_MEDIA_HPP_
+
+#include <vlc_cxx_helpers.hpp>
+#include <vlc_common.h>
+#include <vlc_input_item.h>
+#include <QString>
+
+namespace vlc {
+  namespace playlist {
+
+using InputItemPtr = vlc_shared_data_ptr_type(input_item_t,
+                                              input_item_Hold,
+                                              input_item_Release);
+
+class Media
+{
+public:
+    Media(input_item_t *media = nullptr)
+    {
+        if (media)
+        {
+            /* the media must be unique in the playlist */
+            ptr.reset(input_item_Copy(media), false);
+            if (!ptr)
+                throw std::bad_alloc();
+        }
+    }
+
+    Media(QString uri, QString name)
+    {
+        auto uUri = uri.toUtf8();
+        auto uName = name.toUtf8();
+        const char *rawUri = uUri.isNull() ? nullptr : uUri.constData();
+        const char *rawName = uName.isNull() ? nullptr : uName.constData();
+        ptr.reset(input_item_New(rawUri, rawName), false);
+        if (!ptr)
+            throw std::bad_alloc();
+    }
+
+    operator bool() const
+    {
+        return ptr;
+    }
+
+    input_item_t *raw() const
+    {
+        return ptr.get();
+    }
+
+private:
+    InputItemPtr ptr;
+};
+
+  } // namespace playlist
+} // namespace vlc
+
+#endif
diff --git a/modules/gui/qt/qt.cpp b/modules/gui/qt/qt.cpp
index a0fb7789c4..c5d57ffef1 100644
--- a/modules/gui/qt/qt.cpp
+++ b/modules/gui/qt/qt.cpp
@@ -59,6 +59,9 @@ extern "C" char **environ;
 #include "util/qvlcapp.hpp"     /* QVLCApplication definition */
 #include "components/playlist/playlist_model.hpp" /* for ~PLModel() */
 
+#include <QVector>
+#include "components/playlist_new/playlist_item.hpp"
+
 #include <vlc_plugin.h>
 #include <vlc_vout_window.h>
 
@@ -497,6 +500,13 @@ static void Close( vlc_object_t *p_this )
     busy = false;
 }
 
+static inline void qRegisterMetaTypes()
+{
+    // register all types used by signal/slots
+    qRegisterMetaType<size_t>("size_t");
+    qRegisterMetaType<ssize_t>("ssize_t");
+}
+
 static void *Thread( void *obj )
 {
     intf_thread_t *p_intf = (intf_thread_t *)obj;
@@ -655,6 +665,8 @@ static void *Thread( void *obj )
     if( s_style.compare("") != 0 )
         QApplication::setStyle( s_style );
 
+    qRegisterMetaTypes();
+
     /* Launch */
     app.exec();
 
-- 
2.19.1



More information about the vlc-devel mailing list