diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..1c17650 --- /dev/null +++ b/.clang-format @@ -0,0 +1,5 @@ +--- +Language: Cpp +BasedOnStyle: WebKit +... + diff --git a/format-code.sh b/format-code.sh index bd0d747..46e37ad 100755 --- a/format-code.sh +++ b/format-code.sh @@ -1,6 +1,8 @@ #!/usr/bin/env bash +set -x SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null && pwd )" pushd $SOURCE_DIR -find . -name "*.qml" -exec qmlfmt -w {} \; -find . -name "*.cpp" -o -name "*.hpp" -o -name "*.c" -o -name "*.h" -exec clang-format90 -style mozilla -i {} \; +find . -name "*.qml" -exec qmlfmt -i 2 -w {} \; +clang-format -style=file -i $(find src -name "*.cpp" -o -name "*.hpp" -o -name "*.c" -o -name "*.h") +#find . -name "*.cpp" -o -name "*.hpp" -o -name "*.c" -o -name "*.h" -exec clang-format -style=file -i {} \; popd diff --git a/src/Backends/MPV/MPVBackend.cpp b/src/Backends/MPV/MPVBackend.cpp index dcb2c3d..e44596d 100644 --- a/src/Backends/MPV/MPVBackend.cpp +++ b/src/Backends/MPV/MPVBackend.cpp @@ -1,24 +1,24 @@ #include "src/Backends/MPV/MPVBackend.hpp" -#include -#include +#include "src/Backends/MPVCommon/MPVCommon.hpp" +#include "src/qthelper.hpp" +#include "src/utils.hpp" #include #include -#include -#include #include +#include +#include #include #include #include #include #include #include -#include +#include #include #include -#include "src/qthelper.hpp" +#include #include -#include "src/Backends/MPVCommon/MPVCommon.hpp" -#include "src/utils.hpp" +#include class QQuickItem; class QSize; @@ -26,328 +26,308 @@ class QSize; #ifdef ENABLE_X11 #include // IWYU pragma: keep #include // IWYU pragma: keep -#include // IWYU pragma: keep #include // IWYU pragma: keep #include // IWYU pragma: keep +#include // IWYU pragma: keep #endif #include // IWYU pragma: keep #endif - - bool usedirect = false; namespace { -void -wakeup(void* ctx) +void wakeup(void* ctx) { - QCoreApplication::postEvent((MPVBackend*)ctx, new QEvent(QEvent::User)); + QCoreApplication::postEvent((MPVBackend*)ctx, new QEvent(QEvent::User)); } -void -on_mpv_redraw(void* ctx) +void on_mpv_redraw(void* ctx) { - QMetaObject::invokeMethod( - reinterpret_cast(ctx), "update", Qt::QueuedConnection); + QMetaObject::invokeMethod( + reinterpret_cast(ctx), "update", Qt::QueuedConnection); } static void* get_proc_address_mpv(void* ctx, const char* name) { - return reinterpret_cast( - reinterpret_cast(ctx)->getProcAddress(QByteArray(name))); + return reinterpret_cast( + reinterpret_cast(ctx)->getProcAddress(QByteArray(name))); } } // namespace -class MpvRenderer : public QQuickFramebufferObject::Renderer -{ - MPVBackend* obj; +class MpvRenderer : public QQuickFramebufferObject::Renderer { + MPVBackend* obj; public: - MpvRenderer(MPVBackend* new_obj) - : obj{ new_obj } - { - if (usedirect) { - int r = - mpv_opengl_cb_init_gl(obj->mpv_gl_cb, NULL, get_proc_address_mpv, QOpenGLContext::currentContext()); - if (r < 0) { - std::cout << "No." << std::endl; - throw std::runtime_error("failed to initialize mpv GL context"); - } + MpvRenderer(MPVBackend* new_obj) + : obj{ new_obj } + { + if (usedirect) { + int r = mpv_opengl_cb_init_gl(obj->mpv_gl_cb, NULL, get_proc_address_mpv, QOpenGLContext::currentContext()); + if (r < 0) { + std::cout << "No." << std::endl; + throw std::runtime_error("failed to initialize mpv GL context"); + } + } } - } + virtual ~MpvRenderer() {} - virtual ~MpvRenderer() {} - - // This function is called when a new FBO is needed. - // This happens on the initial frame. - QOpenGLFramebufferObject* createFramebufferObject(const QSize& size) - { - // init mpv_gl: - if (!obj->mpv_gl && !usedirect) { - mpv_opengl_init_params gl_init_params{ get_proc_address_mpv, - QOpenGLContext::currentContext(), - nullptr }; - mpv_render_param params[]{ - { MPV_RENDER_PARAM_API_TYPE, - const_cast(MPV_RENDER_API_TYPE_OPENGL) }, - { MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params }, - { MPV_RENDER_PARAM_INVALID, nullptr }, - { MPV_RENDER_PARAM_INVALID, nullptr } - }; + // This function is called when a new FBO is needed. + // This happens on the initial frame. + QOpenGLFramebufferObject* createFramebufferObject(const QSize& size) + { + // init mpv_gl: + if (!obj->mpv_gl && !usedirect) { + mpv_opengl_init_params gl_init_params{ get_proc_address_mpv, + QOpenGLContext::currentContext(), + nullptr }; + mpv_render_param params[]{ + { MPV_RENDER_PARAM_API_TYPE, + const_cast(MPV_RENDER_API_TYPE_OPENGL) }, + { MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params }, + { MPV_RENDER_PARAM_INVALID, nullptr }, + { MPV_RENDER_PARAM_INVALID, nullptr } + }; #if defined(__linux__) || defined(__FreeBSD__) #ifdef ENABLE_X11 - if (QGuiApplication::platformName().contains("xcb")) { - params[2].type = MPV_RENDER_PARAM_X11_DISPLAY; - params[2].data = QX11Info::display(); - } + if (QGuiApplication::platformName().contains("xcb")) { + params[2].type = MPV_RENDER_PARAM_X11_DISPLAY; + params[2].data = QX11Info::display(); + } #endif - if (QGuiApplication::platformName().contains("wayland")) { - params[2].type = MPV_RENDER_PARAM_WL_DISPLAY; - auto *native = QGuiApplication::platformNativeInterface(); - params[2].data = native->nativeResourceForWindow("display", NULL); - } + if (QGuiApplication::platformName().contains("wayland")) { + params[2].type = MPV_RENDER_PARAM_WL_DISPLAY; + auto* native = QGuiApplication::platformNativeInterface(); + params[2].data = native->nativeResourceForWindow("display", NULL); + } #endif - if (mpv_render_context_create(&obj->mpv_gl, obj->mpv, params) < 0) { - std::cout << "Failed to use render API, try setting Backend/direct to true in settings." << std::endl; - throw std::runtime_error("failed to initialize mpv GL context"); - } - mpv_render_context_set_update_callback(obj->mpv_gl, on_mpv_redraw, obj); - } - QMetaObject::invokeMethod(obj, "startPlayer"); + if (mpv_render_context_create(&obj->mpv_gl, obj->mpv, params) < 0) { + std::cout << "Failed to use render API, try setting Backend/direct to true in settings." << std::endl; + throw std::runtime_error("failed to initialize mpv GL context"); + } + mpv_render_context_set_update_callback(obj->mpv_gl, on_mpv_redraw, obj); + } + QMetaObject::invokeMethod(obj, "startPlayer"); - - return QQuickFramebufferObject::Renderer::createFramebufferObject(size); - } - - void render() - { - obj->window()->resetOpenGLState(); - QOpenGLFramebufferObject* fbo = framebufferObject(); - if (usedirect) { - mpv_opengl_cb_draw(obj->mpv_gl_cb, fbo->handle(), fbo->width(), fbo->height()); - } else { - mpv_opengl_fbo mpfbo{ .fbo = static_cast(fbo->handle()), - .w = fbo->width(), - .h = fbo->height(), - .internal_format = 0 }; - int flip_y{ 0 }; - mpv_render_param params[] = { { MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo }, - { MPV_RENDER_PARAM_FLIP_Y, &flip_y }, - { MPV_RENDER_PARAM_INVALID, nullptr } }; - mpv_render_context_render(obj->mpv_gl, params); + return QQuickFramebufferObject::Renderer::createFramebufferObject(size); } - obj->window()->resetOpenGLState(); - } + void render() + { + obj->window()->resetOpenGLState(); + QOpenGLFramebufferObject* fbo = framebufferObject(); + if (usedirect) { + mpv_opengl_cb_draw(obj->mpv_gl_cb, fbo->handle(), fbo->width(), fbo->height()); + } else { + mpv_opengl_fbo mpfbo{ .fbo = static_cast(fbo->handle()), + .w = fbo->width(), + .h = fbo->height(), + .internal_format = 0 }; + int flip_y{ 0 }; + mpv_render_param params[] = { { MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo }, + { MPV_RENDER_PARAM_FLIP_Y, &flip_y }, + { MPV_RENDER_PARAM_INVALID, nullptr } }; + mpv_render_context_render(obj->mpv_gl, params); + } + + obj->window()->resetOpenGLState(); + } }; MPVBackend::MPVBackend(QQuickItem* parent) - : QQuickFramebufferObject(parent) - , mpv{ mpv_create() } - , mpv_gl(nullptr) - , mpv_gl_cb(nullptr) + : QQuickFramebufferObject(parent) + , mpv{ mpv_create() } + , mpv_gl(nullptr) + , mpv_gl_cb(nullptr) { - if (!mpv) - throw std::runtime_error("could not create mpv context"); + if (!mpv) + throw std::runtime_error("could not create mpv context"); - QSettings settings; - usedirect = settings.value("Backend/direct", false).toBool(); + QSettings settings; + usedirect = settings.value("Backend/direct", false).toBool(); - mpv_set_option_string(mpv, "terminal", "false"); - mpv_set_option_string(mpv, "msg-level", "all=v"); + mpv_set_option_string(mpv, "terminal", "false"); + mpv_set_option_string(mpv, "msg-level", "all=v"); - // Fix? - mpv_set_option_string(mpv, "ytdl", "yes"); + // Fix? + mpv_set_option_string(mpv, "ytdl", "yes"); - mpv_set_option_string(mpv, "slang", "en"); + mpv_set_option_string(mpv, "slang", "en"); - mpv_set_option_string(mpv, "config", "yes"); - mpv_observe_property(mpv, 0, "tracks-menu", MPV_FORMAT_NONE); - mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "playback-abort", MPV_FORMAT_NONE); - mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "audio-device-list", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "playlist-pos", MPV_FORMAT_DOUBLE); - mpv_observe_property(mpv, 0, "volume", MPV_FORMAT_NONE); - mpv_observe_property(mpv, 0, "mute", MPV_FORMAT_NONE); - mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE); - mpv_observe_property(mpv, 0, "media-title", MPV_FORMAT_STRING); - mpv_observe_property(mpv, 0, "sub-text", MPV_FORMAT_STRING); - mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE); - mpv_observe_property(mpv, 0, "demuxer-cache-duration", MPV_FORMAT_DOUBLE); - mpv_observe_property(mpv, 0, "pause", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "playlist", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "speed", MPV_FORMAT_DOUBLE); + mpv_set_option_string(mpv, "config", "yes"); + mpv_observe_property(mpv, 0, "tracks-menu", MPV_FORMAT_NONE); + mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "playback-abort", MPV_FORMAT_NONE); + mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "audio-device-list", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "playlist-pos", MPV_FORMAT_DOUBLE); + mpv_observe_property(mpv, 0, "volume", MPV_FORMAT_NONE); + mpv_observe_property(mpv, 0, "mute", MPV_FORMAT_NONE); + mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE); + mpv_observe_property(mpv, 0, "media-title", MPV_FORMAT_STRING); + mpv_observe_property(mpv, 0, "sub-text", MPV_FORMAT_STRING); + mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE); + mpv_observe_property(mpv, 0, "demuxer-cache-duration", MPV_FORMAT_DOUBLE); + mpv_observe_property(mpv, 0, "pause", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "playlist", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "speed", MPV_FORMAT_DOUBLE); - mpv_request_log_messages(mpv, "v"); + mpv_request_log_messages(mpv, "v"); - mpv_set_wakeup_callback(mpv, wakeup, this); + mpv_set_wakeup_callback(mpv, wakeup, this); - if (mpv_initialize(mpv) < 0) - throw std::runtime_error("could not initialize mpv context"); + if (mpv_initialize(mpv) < 0) + throw std::runtime_error("could not initialize mpv context"); - if (usedirect) { - mpv_set_option_string(mpv, "vo", "libmpv"); - mpv_gl_cb = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); - if (!mpv_gl_cb) - throw std::runtime_error("OpenGL not compiled in"); - mpv_opengl_cb_set_update_callback(mpv_gl_cb, on_mpv_redraw, (void*)this); - } else { - mpv_set_option_string(mpv, "vo", "libmpv"); - } + if (usedirect) { + mpv_set_option_string(mpv, "vo", "libmpv"); + mpv_gl_cb = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); + if (!mpv_gl_cb) + throw std::runtime_error("OpenGL not compiled in"); + mpv_opengl_cb_set_update_callback(mpv_gl_cb, on_mpv_redraw, (void*)this); + } else { + mpv_set_option_string(mpv, "vo", "libmpv"); + } - connect(this, - &MPVBackend::onUpdate, - this, - &MPVBackend::doUpdate, - Qt::QueuedConnection); - connect(this, - &MPVBackend::positionChanged, - this, - &MPVBackend::updateDurationString, - Qt::QueuedConnection); - connect(this, - &MPVBackend::durationChanged, - this, - &MPVBackend::updateDurationString, - Qt::QueuedConnection); + connect(this, + &MPVBackend::onUpdate, + this, + &MPVBackend::doUpdate, + Qt::QueuedConnection); + connect(this, + &MPVBackend::positionChanged, + this, + &MPVBackend::updateDurationString, + Qt::QueuedConnection); + connect(this, + &MPVBackend::durationChanged, + this, + &MPVBackend::updateDurationString, + Qt::QueuedConnection); } MPVBackend::~MPVBackend() { - printf("Shutting down...\n"); - Utils::SetDPMS(true); - command("write-watch-later-config"); + printf("Shutting down...\n"); + Utils::SetDPMS(true); + command("write-watch-later-config"); - if (usedirect && mpv_gl_cb) { - mpv_opengl_cb_uninit_gl(mpv_gl_cb); - } else if (mpv_gl){ - mpv_render_context_free(mpv_gl); - } + if (usedirect && mpv_gl_cb) { + mpv_opengl_cb_uninit_gl(mpv_gl_cb); + } else if (mpv_gl) { + mpv_render_context_free(mpv_gl); + } - mpv_terminate_destroy(mpv); - printf("MPV terminated.\n"); + mpv_terminate_destroy(mpv); + printf("MPV terminated.\n"); } -void -MPVBackend::on_update(void* ctx) +void MPVBackend::on_update(void* ctx) { - MPVBackend* self = (MPVBackend*)ctx; - emit self->onUpdate(); + MPVBackend* self = (MPVBackend*)ctx; + emit self->onUpdate(); } -void -MPVBackend::doUpdate() +void MPVBackend::doUpdate() { - update(); + update(); } QVariant MPVBackend::getProperty(const QString& name) const { - return mpv::qt::get_property_variant(mpv, name); + return mpv::qt::get_property_variant(mpv, name); } -void -MPVBackend::command(const QVariant& params) +void MPVBackend::command(const QVariant& params) { - mpv::qt::node_builder node(params); - mpv_command_node(mpv, node.node(), nullptr); + mpv::qt::node_builder node(params); + mpv_command_node(mpv, node.node(), nullptr); } -void -MPVBackend::setProperty(const QString& name, const QVariant& value) +void MPVBackend::setProperty(const QString& name, const QVariant& value) { - mpv::qt::node_builder node(value); - qDebug() << "Setting property" << name << "to" << value; - mpv_set_property(mpv, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); + mpv::qt::node_builder node(value); + qDebug() << "Setting property" << name << "to" << value; + mpv_set_property(mpv, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); } -void -MPVBackend::setOption(const QString& name, const QVariant& value) +void MPVBackend::setOption(const QString& name, const QVariant& value) { - mpv::qt::set_option_variant(mpv, name, value); + mpv::qt::set_option_variant(mpv, name, value); } QVariant MPVBackend::playerCommand(const Enums::Commands& cmd) { - return playerCommand(cmd, QVariant("NoArgProvided")); + return playerCommand(cmd, QVariant("NoArgProvided")); } QVariant MPVBackend::playerCommand(const Enums::Commands& cmd, const QVariant& args) { - return MPVCommon::playerCommand(this, cmd, args); + return MPVCommon::playerCommand(this, cmd, args); } QString MPVBackend::getStats() { - return MPVCommon::getStats(this); + return MPVCommon::getStats(this); } - -void -MPVBackend::updateDurationString(int numTime) +void MPVBackend::updateDurationString(int numTime) { - QMetaMethod metaMethod = sender()->metaObject()->method(senderSignalIndex()); - MPVCommon::updateDurationString(this, numTime, metaMethod); + QMetaMethod metaMethod = sender()->metaObject()->method(senderSignalIndex()); + MPVCommon::updateDurationString(this, numTime, metaMethod); } - -void -MPVBackend::toggleOnTop() +void MPVBackend::toggleOnTop() { - onTop = !onTop; - Utils::AlwaysOnTop(window()->winId(), onTop); + onTop = !onTop; + Utils::AlwaysOnTop(window()->winId(), onTop); } -bool -MPVBackend::event(QEvent* event) +bool MPVBackend::event(QEvent* event) { - if (event->type() == QEvent::User) { - on_mpv_events(); - } - return QObject::event(event); -} - -void -MPVBackend::on_mpv_events() -{ - while (mpv) { - mpv_event* event = mpv_wait_event(mpv, 0); - if (event->event_id == MPV_EVENT_NONE) { - break; + if (event->type() == QEvent::User) { + on_mpv_events(); + } + return QObject::event(event); +} + +void MPVBackend::on_mpv_events() +{ + while (mpv) { + mpv_event* event = mpv_wait_event(mpv, 0); + if (event->event_id == MPV_EVENT_NONE) { + break; + } + handle_mpv_event(event); } - handle_mpv_event(event); - } } QVariantMap MPVBackend::getAudioDevices(const QVariant& drivers) const { - return MPVCommon::getAudioDevices(drivers); + return MPVCommon::getAudioDevices(drivers); } -void -MPVBackend::handle_mpv_event(mpv_event* event) +void MPVBackend::handle_mpv_event(mpv_event* event) { - MPVCommon::handle_mpv_event(this, event); + MPVCommon::handle_mpv_event(this, event); } QQuickFramebufferObject::Renderer* MPVBackend::createRenderer() const { - window()->setIcon(QIcon(":/icon.png")); - window()->setPersistentOpenGLContext(true); - window()->setPersistentSceneGraph(true); - return new MpvRenderer(const_cast(this)); + window()->setIcon(QIcon(":/icon.png")); + window()->setPersistentOpenGLContext(true); + window()->setPersistentSceneGraph(true); + return new MpvRenderer(const_cast(this)); } diff --git a/src/Backends/MPV/MPVBackend.hpp b/src/Backends/MPV/MPVBackend.hpp index 219a84a..6b1c5b6 100644 --- a/src/Backends/MPV/MPVBackend.hpp +++ b/src/Backends/MPV/MPVBackend.hpp @@ -1,101 +1,100 @@ #ifndef MPVBackend_H #define MPVBackend_H -#include -#include -#include +#include "src/backendinterface.hpp" +#include "src/enums.hpp" +#include #include #include -#include -#include #include +#include #include #include #include -#include "src/backendinterface.hpp" -#include "src/enums.hpp" +#include +#include +#include class MPVBackend - : public QQuickFramebufferObject - , public BackendInterface -{ - Q_INTERFACES(BackendInterface) + : public QQuickFramebufferObject, + public BackendInterface { + Q_INTERFACES(BackendInterface) - Q_OBJECT - Q_PROPERTY(bool logging READ logging WRITE setLogging) + Q_OBJECT + Q_PROPERTY(bool logging READ logging WRITE setLogging) - mpv_handle* mpv; - mpv_render_context* mpv_gl; - mpv_opengl_cb_context* mpv_gl_cb; + mpv_handle* mpv; + mpv_render_context* mpv_gl; + mpv_opengl_cb_context* mpv_gl_cb; - QSettings settings; - bool onTop = false; - bool m_logging = true; + QSettings settings; + bool onTop = false; + bool m_logging = true; - friend class MpvRenderer; + friend class MpvRenderer; public: - static void on_update(void* ctx); + static void on_update(void* ctx); - MPVBackend(QQuickItem* parent = 0); - virtual ~MPVBackend(); - virtual Renderer* createRenderer() const; + MPVBackend(QQuickItem* parent = 0); + virtual ~MPVBackend(); + virtual Renderer* createRenderer() const; - void setLogging(bool a) - { - if (a != m_logging) { - m_logging = a; + void setLogging(bool a) + { + if (a != m_logging) { + m_logging = a; + } } - } - bool logging() const { return m_logging; } + bool logging() const { return m_logging; } - int lastTime = 0; - double lastSpeed = 0; - QString totalDurationString; - QString lastPositionString; + int lastTime = 0; + double lastSpeed = 0; + QString totalDurationString; + QString lastPositionString; public slots: - QVariant playerCommand(const Enums::Commands& command, const QVariant& args); - QVariant playerCommand(const Enums::Commands& command); - void toggleOnTop(); - QString getStats(); - // Optional but handy for MPV or custom backend settings. - void command(const QVariant& params); - void setProperty(const QString& name, const QVariant& value); - void setOption(const QString& name, const QVariant& value); - QVariant getProperty(const QString& name) const; - // Just used for adding missing audio devices to list. - QVariantMap getAudioDevices(const QVariant& drivers) const; - bool event(QEvent* event); + QVariant playerCommand(const Enums::Commands& command, const QVariant& args); + QVariant playerCommand(const Enums::Commands& command); + void toggleOnTop(); + QString getStats(); + // Optional but handy for MPV or custom backend settings. + void command(const QVariant& params); + void setProperty(const QString& name, const QVariant& value); + void setOption(const QString& name, const QVariant& value); + QVariant getProperty(const QString& name) const; + // Just used for adding missing audio devices to list. + QVariantMap getAudioDevices(const QVariant& drivers) const; + bool event(QEvent* event); signals: - void onUpdate(); - void mpv_events(); - void onMpvEvent(mpv_event* event); - // All below required for Player API - void playStatusChanged(const Enums::PlayStatus& status); - void volumeStatusChanged(const Enums::VolumeStatus& status); - void volumeChanged(const int& volume); - void durationChanged(const double& duration); - void positionChanged(const double& position); - void cachedDurationChanged(const double& duration); - void playlistPositionChanged(const double& position); - void titleChanged(const QString& title); - void subtitlesChanged(const QString& subtitles); - void durationStringChanged(const QString& string); - void tracksChanged(const QVariantList& tracks); - void audioDevicesChanged(const QVariantMap& devices); - void playlistChanged(const QVariantList& devices); - void chaptersChanged(const QVariantList& devices); - void speedChanged(const double& speed); + void onUpdate(); + void mpv_events(); + void onMpvEvent(mpv_event* event); + // All below required for Player API + void playStatusChanged(const Enums::PlayStatus& status); + void volumeStatusChanged(const Enums::VolumeStatus& status); + void volumeChanged(const int& volume); + void durationChanged(const double& duration); + void positionChanged(const double& position); + void cachedDurationChanged(const double& duration); + void playlistPositionChanged(const double& position); + void titleChanged(const QString& title); + void subtitlesChanged(const QString& subtitles); + void durationStringChanged(const QString& string); + void tracksChanged(const QVariantList& tracks); + void audioDevicesChanged(const QVariantMap& devices); + void playlistChanged(const QVariantList& devices); + void chaptersChanged(const QVariantList& devices); + void speedChanged(const double& speed); private slots: - void doUpdate(); - void on_mpv_events(); - void updateDurationString(int numTime); + void doUpdate(); + void on_mpv_events(); + void updateDurationString(int numTime); private: - void handle_mpv_event(mpv_event* event); + void handle_mpv_event(mpv_event* event); }; #endif diff --git a/src/Backends/MPVCommon/MPVCommon.cpp b/src/Backends/MPVCommon/MPVCommon.cpp index b1a6e7c..da9814e 100644 --- a/src/Backends/MPVCommon/MPVCommon.cpp +++ b/src/Backends/MPVCommon/MPVCommon.cpp @@ -1,47 +1,47 @@ #include "src/Backends/MPVCommon/MPVCommon.hpp" +#include "spdlog/logger.h" +#include "src/backendinterface.hpp" +#include "src/logger.h" +#include "src/utils.hpp" #include #include #include #include #include #include -#include #include +#include #include #include -#include -#include #include #include -#include "spdlog/logger.h" -#include "src/backendinterface.hpp" -#include "src/logger.h" -#include "src/utils.hpp" +#include +#include auto mpvLogger = initLogger("mpv"); QString humanSize(uint64_t bytes) { - const char *suffix[5] = {"B", "KB", "MB", "GB", "TB"}; - char length = sizeof(suffix) / sizeof(suffix[0]); + const char* suffix[5] = { "B", "KB", "MB", "GB", "TB" }; + char length = sizeof(suffix) / sizeof(suffix[0]); - int i = 0; - double dblBytes = bytes; + int i = 0; + double dblBytes = bytes; - if (bytes > 1024) { - for (i = 0; (bytes / 1024) > 0 && i 1024) { + for (i = 0; (bytes / 1024) > 0 && i < length - 1; i++, bytes /= 1024) + dblBytes = bytes / 1024.0; + } - static char output[200]; - sprintf(output, "%.02lf %s", dblBytes, suffix[i]); - return QString(output); + static char output[200]; + sprintf(output, "%.02lf %s", dblBytes, suffix[i]); + return QString(output); } -static inline QVariant mpvnode_to_variant(const mpv_node *node) +static inline QVariant mpvnode_to_variant(const mpv_node* node) { if (!node) { - return QVariant(); + return QVariant(); } switch (node->format) { @@ -54,18 +54,18 @@ static inline QVariant mpvnode_to_variant(const mpv_node *node) case MPV_FORMAT_DOUBLE: return QVariant(node->u.double_); case MPV_FORMAT_NODE_ARRAY: { - mpv_node_list *list = node->u.list; + mpv_node_list* list = node->u.list; QVariantList qlist; for (int n = 0; n < list->num; n++) qlist.append(mpvnode_to_variant(&list->values[n])); return QVariant(qlist); } case MPV_FORMAT_NODE_MAP: { - mpv_node_list *list = node->u.list; + mpv_node_list* list = node->u.list; QVariantMap qmap; for (int n = 0; n < list->num; n++) { qmap.insert(QString::fromUtf8(list->keys[n]), - mpvnode_to_variant(&list->values[n])); + mpvnode_to_variant(&list->values[n])); } return QVariant(qmap); } @@ -74,498 +74,487 @@ static inline QVariant mpvnode_to_variant(const mpv_node *node) } } - namespace MPVCommon { -QString getStats(BackendInterface *b) { - QString stats; - stats = - ""; - QString filename = b->getProperty("filename").toString(); - // File Info - stats += "File: " + filename; - stats += "
"; - QString title = b->getProperty("media-title").toString(); - if (title != filename) { - stats += "Title: " + title + "
"; - } - QString fileFormat = b->getProperty("file-format").toString(); - stats += "Format/Protocol: " + fileFormat + "
"; - double cacheUsed = b->getProperty("cache-used").toDouble(); - int demuxerSecs = b->getProperty("demuxer-cache-duration").toInt(); - QVariantMap demuxerState = b->getProperty("demuxer-cache-state").toMap(); - int demuxerCache = demuxerState.value("fw-bytes", QVariant(0)).toInt(); - - if (demuxerSecs + demuxerCache + cacheUsed > 0) { - QString cacheStats; - cacheStats += "Total Cache: "; - cacheStats += humanSize(demuxerCache + cacheUsed); - cacheStats += " (Demuxer: "; - - cacheStats += humanSize(demuxerCache); - cacheStats += ", "; - cacheStats += QString::number(demuxerSecs) + "s) "; - double cacheSpeed = b->getProperty("cache-speed").toDouble(); - if (cacheSpeed > 0) { - cacheStats += "Speed: "; - cacheStats += humanSize(demuxerSecs); - cacheStats += "/s"; - } - cacheStats += "
"; - stats += cacheStats; - } - QString fileSize = - humanSize(b->getProperty("file-size").toInt()).remove("-"); - stats += "Size: " + fileSize + "
"; - - stats += "
"; - // Video Info - QVariant videoParams = b->getProperty("video-params"); - if (videoParams.isNull()) { - videoParams = b->getProperty("video-out-params"); - } - if (!videoParams.isNull()) { - stats += "Video: " + b->getProperty("video-codec").toString(); +QString getStats(BackendInterface* b) +{ + QString stats; + stats = ""; + QString filename = b->getProperty("filename").toString(); + // File Info + stats += "File: " + filename; stats += "
"; - QString avsync = QString::number(b->getProperty("avsync").toDouble(), 'f', 3); - stats += "A-V: " + QString(avsync) + "
"; - - stats += "Dropped Frames: "; - int dFDC = b->getProperty("decoder-frame-drop-count").toInt(); - if (dFDC > 0) { - stats += QString::number(dFDC) + " (decoder) "; + QString title = b->getProperty("media-title").toString(); + if (title != filename) { + stats += "Title: " + title + "
"; } - int fDC = b->getProperty("frame-drop-count").toInt(); - if (fDC > 0) { - stats += QString::number(fDC) + " (output)"; - } - stats += "
"; - - int dFPS = b->getProperty("display-fps").toInt(); - int eDFPS = b->getProperty("estimated-display-fps").toInt(); - if ((dFPS + eDFPS) > 0) { - stats += "Display FPS: "; - - if (dFPS > 0) { - stats += QString::number(dFPS); - stats += " (specified) "; - } - if (eDFPS > 0) { - stats += QString::number(eDFPS); - stats += " (estimated)"; - } - stats += "
"; - } - - int cFPS = b->getProperty("container-fps").toInt(); - int eVFPS = b->getProperty("estimated-vf-fps").toInt(); - if ((cFPS + eVFPS) > 0) { - stats += "FPS: "; - - if (cFPS > 0) { - stats += QString::number(cFPS); - stats += " (specified) "; - } - if (eVFPS > 0) { - stats += QString::number(eVFPS); - stats += " (estimated)"; - } - stats += "
"; - } - QVariantMap vPM = videoParams.toMap(); - stats += "Native Resolution: "; - stats += vPM["w"].toString() + " x " + vPM["h"].toString(); - stats += "
"; - - stats += "Window Scale: "; - stats += vPM["window-scale"].toString(); - stats += "
"; - - stats += "Aspect Ratio: "; - stats += vPM["aspect"].toString(); - stats += "
"; - - stats += "Pixel Format: "; - stats += vPM["pixelformat"].toString(); - stats += "
"; - - stats += "Primaries: "; - stats += vPM["primaries"].toString(); - stats += " Colormatrix: "; - stats += vPM["colormatrix"].toString(); - stats += "
"; - - stats += "Levels: "; - stats += vPM["colorlevels"].toString(); - double sigPeak = vPM.value("sig-peak", QVariant(0.0)).toInt(); - if (sigPeak > 0) { - stats += " (HDR Peak: " + QString::number(sigPeak) + ")"; - } - stats += "
"; - - stats += "Gamma: "; - stats += vPM["gamma"].toString(); - stats += "
"; - - int pVB = b->getProperty("packet-video-bitrate").toInt(); - if (pVB > 0) { - stats += "Bitrate: "; - stats += humanSize(pVB) + "/s"; - stats += "
"; + QString fileFormat = b->getProperty("file-format").toString(); + stats += "Format/Protocol: " + fileFormat + "
"; + double cacheUsed = b->getProperty("cache-used").toDouble(); + int demuxerSecs = b->getProperty("demuxer-cache-duration").toInt(); + QVariantMap demuxerState = b->getProperty("demuxer-cache-state").toMap(); + int demuxerCache = demuxerState.value("fw-bytes", QVariant(0)).toInt(); + + if (demuxerSecs + demuxerCache + cacheUsed > 0) { + QString cacheStats; + cacheStats += "Total Cache: "; + cacheStats += humanSize(demuxerCache + cacheUsed); + cacheStats += " (Demuxer: "; + + cacheStats += humanSize(demuxerCache); + cacheStats += ", "; + cacheStats += QString::number(demuxerSecs) + "s) "; + double cacheSpeed = b->getProperty("cache-speed").toDouble(); + if (cacheSpeed > 0) { + cacheStats += "Speed: "; + cacheStats += humanSize(demuxerSecs); + cacheStats += "/s"; + } + cacheStats += "
"; + stats += cacheStats; } + QString fileSize = humanSize(b->getProperty("file-size").toInt()).remove("-"); + stats += "Size: " + fileSize + "
"; stats += "
"; - } - QVariant audioParams = b->getProperty("audio-params"); - if (audioParams.isNull()) { - audioParams = b->getProperty("audio-out-params"); - } - if (!audioParams.isNull()) { - stats += "Audio: " + b->getProperty("audio-codec").toString(); - stats += "
"; - QVariantMap aPM = audioParams.toMap(); + // Video Info + QVariant videoParams = b->getProperty("video-params"); + if (videoParams.isNull()) { + videoParams = b->getProperty("video-out-params"); + } + if (!videoParams.isNull()) { + stats += "Video: " + b->getProperty("video-codec").toString(); + stats += "
"; + QString avsync = QString::number(b->getProperty("avsync").toDouble(), 'f', 3); + stats += "A-V: " + QString(avsync) + "
"; - stats += "Format: "; - stats += aPM["format"].toString(); - stats += "
"; + stats += "Dropped Frames: "; + int dFDC = b->getProperty("decoder-frame-drop-count").toInt(); + if (dFDC > 0) { + stats += QString::number(dFDC) + " (decoder) "; + } + int fDC = b->getProperty("frame-drop-count").toInt(); + if (fDC > 0) { + stats += QString::number(fDC) + " (output)"; + } + stats += "
"; - stats += "Sample Rate: "; - stats += aPM["samplerate"].toString() + " Hz"; - stats += "
"; + int dFPS = b->getProperty("display-fps").toInt(); + int eDFPS = b->getProperty("estimated-display-fps").toInt(); + if ((dFPS + eDFPS) > 0) { + stats += "Display FPS: "; - stats += "Channels: "; - stats += aPM["chanel-count"].toString(); - stats += "
"; + if (dFPS > 0) { + stats += QString::number(dFPS); + stats += " (specified) "; + } + if (eDFPS > 0) { + stats += QString::number(eDFPS); + stats += " (estimated)"; + } + stats += "
"; + } - int pAB = b->getProperty("packet-audio-bitrate").toInt(); - if (pAB > 0) { - stats += "Bitrate: "; - stats += humanSize(pAB) + "/s"; - stats += "
"; + int cFPS = b->getProperty("container-fps").toInt(); + int eVFPS = b->getProperty("estimated-vf-fps").toInt(); + if ((cFPS + eVFPS) > 0) { + stats += "FPS: "; + + if (cFPS > 0) { + stats += QString::number(cFPS); + stats += " (specified) "; + } + if (eVFPS > 0) { + stats += QString::number(eVFPS); + stats += " (estimated)"; + } + stats += "
"; + } + QVariantMap vPM = videoParams.toMap(); + stats += "Native Resolution: "; + stats += vPM["w"].toString() + " x " + vPM["h"].toString(); + stats += "
"; + + stats += "Window Scale: "; + stats += vPM["window-scale"].toString(); + stats += "
"; + + stats += "Aspect Ratio: "; + stats += vPM["aspect"].toString(); + stats += "
"; + + stats += "Pixel Format: "; + stats += vPM["pixelformat"].toString(); + stats += "
"; + + stats += "Primaries: "; + stats += vPM["primaries"].toString(); + stats += " Colormatrix: "; + stats += vPM["colormatrix"].toString(); + stats += "
"; + + stats += "Levels: "; + stats += vPM["colorlevels"].toString(); + double sigPeak = vPM.value("sig-peak", QVariant(0.0)).toInt(); + if (sigPeak > 0) { + stats += " (HDR Peak: " + QString::number(sigPeak) + ")"; + } + stats += "
"; + + stats += "Gamma: "; + stats += vPM["gamma"].toString(); + stats += "
"; + + int pVB = b->getProperty("packet-video-bitrate").toInt(); + if (pVB > 0) { + stats += "Bitrate: "; + stats += humanSize(pVB) + "/s"; + stats += "
"; + } + + stats += "
"; + } + QVariant audioParams = b->getProperty("audio-params"); + if (audioParams.isNull()) { + audioParams = b->getProperty("audio-out-params"); + } + if (!audioParams.isNull()) { + stats += "Audio: " + b->getProperty("audio-codec").toString(); + stats += "
"; + QVariantMap aPM = audioParams.toMap(); + + stats += "Format: "; + stats += aPM["format"].toString(); + stats += "
"; + + stats += "Sample Rate: "; + stats += aPM["samplerate"].toString() + " Hz"; + stats += "
"; + + stats += "Channels: "; + stats += aPM["chanel-count"].toString(); + stats += "
"; + + int pAB = b->getProperty("packet-audio-bitrate").toInt(); + if (pAB > 0) { + stats += "Bitrate: "; + stats += humanSize(pAB) + "/s"; + stats += "
"; + } + + stats += "
"; } - stats += "
"; - } - - return stats; + return stats; } -QVariant playerCommand(BackendInterface *b, const Enums::Commands& cmd, const QVariant& args) +QVariant playerCommand(BackendInterface* b, const Enums::Commands& cmd, const QVariant& args) { - switch (cmd) { + switch (cmd) { case Enums::Commands::TogglePlayPause: { - b->command(QVariantList() << "cycle" - << "pause"); - break; + b->command(QVariantList() << "cycle" + << "pause"); + break; } case Enums::Commands::ToggleMute: { - b->command(QVariantList() << "cycle" - << "mute"); - break; + b->command(QVariantList() << "cycle" + << "mute"); + break; } case Enums::Commands::SetAudioDevice: { - b->setProperty("audio-device", args.toString()); - break; + b->setProperty("audio-device", args.toString()); + break; } case Enums::Commands::SetVolume: { - b->command(QVariantList() << "set" - << "volume" << args); - break; + b->command(QVariantList() << "set" + << "volume" << args); + break; } case Enums::Commands::AddVolume: { - b->command(QVariantList() << "add" - << "volume" << args); - break; + b->command(QVariantList() << "add" + << "volume" << args); + break; } case Enums::Commands::AddSpeed: { - QString speedString = - QString::number(b->getProperty("speed").toDouble() + args.toDouble()); - QVariant newSpeed = - QVariant(speedString.left(speedString.lastIndexOf('.') + 2)); + QString speedString = QString::number(b->getProperty("speed").toDouble() + args.toDouble()); + QVariant newSpeed = QVariant(speedString.left(speedString.lastIndexOf('.') + 2)); - b->playerCommand(Enums::Commands::SetSpeed, newSpeed); - break; + b->playerCommand(Enums::Commands::SetSpeed, newSpeed); + break; } case Enums::Commands::SubtractSpeed: { - QString speedString = - QString::number(b->getProperty("speed").toDouble() - args.toDouble()); - QVariant newSpeed = - QVariant(speedString.left(speedString.lastIndexOf('.') + 2)); - b->playerCommand(Enums::Commands::SetSpeed, newSpeed); - break; + QString speedString = QString::number(b->getProperty("speed").toDouble() - args.toDouble()); + QVariant newSpeed = QVariant(speedString.left(speedString.lastIndexOf('.') + 2)); + b->playerCommand(Enums::Commands::SetSpeed, newSpeed); + break; } case Enums::Commands::ChangeSpeed: { - b->playerCommand( - Enums::Commands::SetSpeed, - QVariant(b->getProperty("speed").toDouble() * args.toDouble())); - break; + b->playerCommand( + Enums::Commands::SetSpeed, + QVariant(b->getProperty("speed").toDouble() * args.toDouble())); + break; } case Enums::Commands::SetSpeed: { - b->command(QVariantList() << "set" - << "speed" << args.toString()); - break; + b->command(QVariantList() << "set" + << "speed" << args.toString()); + break; } case Enums::Commands::ToggleStats: { - b->command(QVariantList() << "script-binding" - << "stats/display-stats-toggle"); - break; + b->command(QVariantList() << "script-binding" + << "stats/display-stats-toggle"); + break; } case Enums::Commands::NextAudioTrack: { - b->command(QVariantList() << "cycle" - << "audio"); - break; + b->command(QVariantList() << "cycle" + << "audio"); + break; } case Enums::Commands::NextSubtitleTrack: { - b->command(QVariantList() << "cycle" - << "sub"); + b->command(QVariantList() << "cycle" + << "sub"); - break; + break; } case Enums::Commands::NextVideoTrack: { - b->command(QVariantList() << "cycle" - << "video"); - break; + b->command(QVariantList() << "cycle" + << "video"); + break; } case Enums::Commands::PreviousPlaylistItem: { - b->command(QVariantList() << "playlist-prev"); + b->command(QVariantList() << "playlist-prev"); - break; + break; } case Enums::Commands::NextPlaylistItem: { - b->command(QVariantList() << "playlist-next" - << "force"); - break; + b->command(QVariantList() << "playlist-next" + << "force"); + break; } case Enums::Commands::LoadFile: { - b->command(QVariantList() << "loadfile" << args); + b->command(QVariantList() << "loadfile" << args); - break; + break; } case Enums::Commands::AppendFile: { - b->command(QVariantList() << "loadfile" << args << "append-play"); - break; + b->command(QVariantList() << "loadfile" << args << "append-play"); + break; } case Enums::Commands::Seek: { - b->command(QVariantList() << "seek" << args); + b->command(QVariantList() << "seek" << args); - break; + break; } case Enums::Commands::SeekAbsolute: { - b->command(QVariantList() << "seek" << args << "absolute"); + b->command(QVariantList() << "seek" << args << "absolute"); - break; + break; } case Enums::Commands::ForwardFrame: { - b->command(QVariantList() << "frame-step"); + b->command(QVariantList() << "frame-step"); - break; + break; } case Enums::Commands::BackwardFrame: { - b->command(QVariantList() << "frame-back-step"); + b->command(QVariantList() << "frame-back-step"); - break; + break; } case Enums::Commands::SetTrack: { - b->command(QVariantList() << "set" << args.toList()[0] << args.toList()[1]); + b->command(QVariantList() << "set" << args.toList()[0] << args.toList()[1]); - break; + break; } case Enums::Commands::SetPlaylistPos: { - b->command(QVariantList() << "set" - << "playlist-pos" << args); + b->command(QVariantList() << "set" + << "playlist-pos" << args); - break; + break; } case Enums::Commands::ForcePause: { - b->command(QVariantList() << "set" - << "pause" - << "yes"); + b->command(QVariantList() << "set" + << "pause" + << "yes"); - break; + break; } default: { - //qDebug() << "Command not found: " << cmd; - break; + //qDebug() << "Command not found: " << cmd; + break; } - } - return QVariant("NoOutput"); + } + return QVariant("NoOutput"); } -void updateDurationString(BackendInterface *b, int numTime, QMetaMethod metaMethod) +void updateDurationString(BackendInterface* b, int numTime, QMetaMethod metaMethod) { - QVariant speed = b->getProperty("speed"); - QSettings settings; - if (metaMethod.name() == "positionChanged") { - if (speed != b->lastSpeed) { - b->lastSpeed = speed.toDouble(); - } else { - if (numTime == b->lastTime) { - return; - } + QVariant speed = b->getProperty("speed"); + QSettings settings; + if (metaMethod.name() == "positionChanged") { + if (speed != b->lastSpeed) { + b->lastSpeed = speed.toDouble(); + } else { + if (numTime == b->lastTime) { + return; + } + } + b->lastTime = numTime; + b->lastPositionString = Utils::createTimestamp(b->lastTime); + } else if (metaMethod.name() == "durationChanged") { + b->totalDurationString = Utils::createTimestamp(numTime); } - b->lastTime = numTime; - b->lastPositionString = Utils::createTimestamp(b->lastTime); - } else if (metaMethod.name() == "durationChanged") { - b->totalDurationString = Utils::createTimestamp(numTime); - } - QString durationString; - durationString += b->lastPositionString; - durationString += " / "; - durationString += b->totalDurationString; - if (b->lastSpeed != 1) { - if (settings.value("Appearance/themeName", "").toString() != - "RoosterTeeth") { - durationString += " (" + speed.toString() + "x)"; + QString durationString; + durationString += b->lastPositionString; + durationString += " / "; + durationString += b->totalDurationString; + if (b->lastSpeed != 1) { + if (settings.value("Appearance/themeName", "").toString() != "RoosterTeeth") { + durationString += " (" + speed.toString() + "x)"; + } } - } - emit b->durationStringChanged(durationString); + emit b->durationStringChanged(durationString); } -void -handle_mpv_event(BackendInterface *b, mpv_event* event) +void handle_mpv_event(BackendInterface* b, mpv_event* event) { - switch (event->event_id) { + switch (event->event_id) { case MPV_EVENT_PROPERTY_CHANGE: { - mpv_event_property* prop = (mpv_event_property*)event->data; - if (strcmp(prop->name, "time-pos") == 0) { - if (prop->format == MPV_FORMAT_DOUBLE) { - double time = *(double*)prop->data; - emit b->positionChanged(time); + mpv_event_property* prop = (mpv_event_property*)event->data; + if (strcmp(prop->name, "time-pos") == 0) { + if (prop->format == MPV_FORMAT_DOUBLE) { + double time = *(double*)prop->data; + emit b->positionChanged(time); + } + } else if (strcmp(prop->name, "duration") == 0) { + if (prop->format == MPV_FORMAT_DOUBLE) { + double time = *(double*)prop->data; + emit b->durationChanged(time); + } + } else if (strcmp(prop->name, "mute") == 0 || strcmp(prop->name, "volume") == 0) { + double volume = b->getProperty("volume").toDouble(); + bool mute = b->getProperty("mute").toBool(); + if (mute || volume == 0) { + emit b->volumeStatusChanged(Enums::VolumeStatus::Muted); + } else { + if (volume < 25) { + emit b->volumeStatusChanged(Enums::VolumeStatus::Low); + } else { + emit b->volumeStatusChanged(Enums::VolumeStatus::Normal); + } + } + // emit volumeChanged(volume); + } else if (strcmp(prop->name, "media-title") == 0) { + if (prop->format == MPV_FORMAT_STRING) { + char* title = *(char**)prop->data; + emit b->titleChanged(QString(title)); + } + } else if (strcmp(prop->name, "sub-text") == 0) { + if (prop->format == MPV_FORMAT_STRING) { + char* subs = *(char**)prop->data; + emit b->subtitlesChanged(QString(subs)); + } + } else if (strcmp(prop->name, "demuxer-cache-duration") == 0) { + if (prop->format == MPV_FORMAT_DOUBLE) { + double duration = *(double*)prop->data; + emit b->cachedDurationChanged(duration); + } + } else if (strcmp(prop->name, "playlist-pos") == 0) { + if (prop->format == MPV_FORMAT_DOUBLE) { + double pos = *(double*)prop->data; + emit b->playlistPositionChanged(pos); + } + } else if (strcmp(prop->name, "pause") == 0) { + mpv_node* nod = (mpv_node*)prop->data; + if (mpvnode_to_variant(nod).toBool()) { + emit b->playStatusChanged(Enums::PlayStatus::Paused); + // Utils::SetScreensaver(window()->winId(), true); + } else { + emit b->playStatusChanged(Enums::PlayStatus::Playing); + // Utils::SetScreensaver(window()->winId(), false); + } + } else if (strcmp(prop->name, "track-list") == 0) { + mpv_node* nod = (mpv_node*)prop->data; + emit b->tracksChanged(mpvnode_to_variant(nod).toList()); + } else if (strcmp(prop->name, "audio-device-list") == 0) { + mpv_node* nod = (mpv_node*)prop->data; + emit b->audioDevicesChanged(b->getAudioDevices(mpvnode_to_variant(nod))); + } else if (strcmp(prop->name, "playlist") == 0) { + mpv_node* nod = (mpv_node*)prop->data; + emit b->playlistChanged(mpvnode_to_variant(nod).toList()); + } else if (strcmp(prop->name, "chapter-list") == 0) { + mpv_node* nod = (mpv_node*)prop->data; + emit b->chaptersChanged(mpvnode_to_variant(nod).toList()); + } else if (strcmp(prop->name, "speed") == 0) { + double speed = *(double*)prop->data; + emit b->speedChanged(speed); } - } else if (strcmp(prop->name, "duration") == 0) { - if (prop->format == MPV_FORMAT_DOUBLE) { - double time = *(double*)prop->data; - emit b->durationChanged(time); - } - } else if (strcmp(prop->name, "mute") == 0 || - strcmp(prop->name, "volume") == 0) { - double volume = b->getProperty("volume").toDouble(); - bool mute = b->getProperty("mute").toBool(); - if (mute || volume == 0) { - emit b->volumeStatusChanged(Enums::VolumeStatus::Muted); - } else { - if (volume < 25) { - emit b->volumeStatusChanged(Enums::VolumeStatus::Low); - } else { - emit b->volumeStatusChanged(Enums::VolumeStatus::Normal); - } - } - // emit volumeChanged(volume); - } else if (strcmp(prop->name, "media-title") == 0) { - if (prop->format == MPV_FORMAT_STRING) { - char* title = *(char**)prop->data; - emit b->titleChanged(QString(title)); - } - } else if (strcmp(prop->name, "sub-text") == 0) { - if (prop->format == MPV_FORMAT_STRING) { - char* subs = *(char**)prop->data; - emit b->subtitlesChanged(QString(subs)); - } - } else if (strcmp(prop->name, "demuxer-cache-duration") == 0) { - if (prop->format == MPV_FORMAT_DOUBLE) { - double duration = *(double*)prop->data; - emit b->cachedDurationChanged(duration); - } - } else if (strcmp(prop->name, "playlist-pos") == 0) { - if (prop->format == MPV_FORMAT_DOUBLE) { - double pos = *(double*)prop->data; - emit b->playlistPositionChanged(pos); - } - } else if (strcmp(prop->name, "pause") == 0) { - mpv_node* nod = (mpv_node*)prop->data; - if (mpvnode_to_variant(nod).toBool()) { - emit b->playStatusChanged(Enums::PlayStatus::Paused); - // Utils::SetScreensaver(window()->winId(), true); - } else { - emit b->playStatusChanged(Enums::PlayStatus::Playing); - // Utils::SetScreensaver(window()->winId(), false); - } - } else if (strcmp(prop->name, "track-list") == 0) { - mpv_node* nod = (mpv_node*)prop->data; - emit b->tracksChanged(mpvnode_to_variant(nod).toList()); - } else if (strcmp(prop->name, "audio-device-list") == 0) { - mpv_node* nod = (mpv_node*)prop->data; - emit b->audioDevicesChanged(b->getAudioDevices(mpvnode_to_variant(nod))); - } else if (strcmp(prop->name, "playlist") == 0) { - mpv_node* nod = (mpv_node*)prop->data; - emit b->playlistChanged(mpvnode_to_variant(nod).toList()); - } else if (strcmp(prop->name, "chapter-list") == 0) { - mpv_node* nod = (mpv_node*)prop->data; - emit b->chaptersChanged(mpvnode_to_variant(nod).toList()); - } else if (strcmp(prop->name, "speed") == 0) { - double speed = *(double*)prop->data; - emit b->speedChanged(speed); - } - break; + break; } case MPV_EVENT_LOG_MESSAGE: { - struct mpv_event_log_message* msg = - (struct mpv_event_log_message*)event->data; + struct mpv_event_log_message* msg = (struct mpv_event_log_message*)event->data; QString logMsg = "[" + QString(msg->prefix) + "] " + QString(msg->text); QString msgLevel = QString(msg->level); if (msgLevel.startsWith("d") || msgLevel.startsWith("t")) { - mpvLogger->info("{}", logMsg.toStdString()); + mpvLogger->info("{}", logMsg.toStdString()); } else if (msgLevel.startsWith("v") || msgLevel.startsWith("i")) { - mpvLogger->info("{}", logMsg.toStdString()); + mpvLogger->info("{}", logMsg.toStdString()); } else { - mpvLogger->debug("{}", logMsg.toStdString()); + mpvLogger->debug("{}", logMsg.toStdString()); } - break; + break; } case MPV_EVENT_SHUTDOWN: { - qApp->exit(); - break; + qApp->exit(); + break; } default: { - break; + break; + } } - } } QVariantMap getAudioDevices(const QVariant& drivers) { - QVariantMap newDrivers; - if (drivers.isNull()) { + QVariantMap newDrivers; + if (drivers.isNull()) { + return newDrivers; + } + + QSequentialIterable iterable = drivers.value(); + foreach (const QVariant& v, iterable) { + QVariantMap item = v.toMap(); + newDrivers[item["description"].toString()] = item; + } return newDrivers; - } - - QSequentialIterable iterable = drivers.value(); - foreach (const QVariant& v, iterable) { - QVariantMap item = v.toMap(); - newDrivers[item["description"].toString()] = item; - } - return newDrivers; } - } \ No newline at end of file diff --git a/src/Backends/MPVCommon/MPVCommon.hpp b/src/Backends/MPVCommon/MPVCommon.hpp index 4116d2a..e3710fd 100644 --- a/src/Backends/MPVCommon/MPVCommon.hpp +++ b/src/Backends/MPVCommon/MPVCommon.hpp @@ -1,24 +1,21 @@ #ifndef MPVCommon_H #define MPVCommon_H -#include +#include "src/enums.hpp" #include #include #include -#include "src/enums.hpp" +#include class BackendInterface; class QMetaMethod; - namespace MPVCommon { -QString getStats(BackendInterface *b); -QVariant playerCommand(BackendInterface *b, const Enums::Commands& cmd, const QVariant& args); -void updateDurationString(BackendInterface *b, int numTime, QMetaMethod metaMethod); -void handle_mpv_event(BackendInterface *b, mpv_event* event); +QString getStats(BackendInterface* b); +QVariant playerCommand(BackendInterface* b, const Enums::Commands& cmd, const QVariant& args); +void updateDurationString(BackendInterface* b, int numTime, QMetaMethod metaMethod); +void handle_mpv_event(BackendInterface* b, mpv_event* event); QVariantMap getAudioDevices(const QVariant& drivers); - - } #endif \ No newline at end of file diff --git a/src/Backends/MPVNoFBO/MPVNoFBOBackend.cpp b/src/Backends/MPVNoFBO/MPVNoFBOBackend.cpp index 15d06fe..cda0adb 100644 --- a/src/Backends/MPVNoFBO/MPVNoFBOBackend.cpp +++ b/src/Backends/MPVNoFBO/MPVNoFBOBackend.cpp @@ -1,316 +1,295 @@ #include "src/Backends/MPVNoFBO/MPVNoFBOBackend.hpp" +#include "src/Backends/MPVCommon/MPVCommon.hpp" +#include "src/qthelper.hpp" +#include "src/utils.hpp" #include #include #include -#include #include +#include #include #include #include #include +#include +#include #include #include -#include -#include "src/qthelper.hpp" -#include -#include "src/Backends/MPVCommon/MPVCommon.hpp" -#include "src/utils.hpp" - -void -nofbowakeup(void* ctx) +void nofbowakeup(void* ctx) { - QCoreApplication::postEvent((MPVNoFBOBackend*)ctx, new QEvent(QEvent::User)); + QCoreApplication::postEvent((MPVNoFBOBackend*)ctx, new QEvent(QEvent::User)); } static void* get_proc_address(void* ctx, const char* name) { - (void)ctx; - QOpenGLContext* glctx = QOpenGLContext::currentContext(); - if (!glctx) - return NULL; - return (void*)glctx->getProcAddress(QByteArray(name)); + (void)ctx; + QOpenGLContext* glctx = QOpenGLContext::currentContext(); + if (!glctx) + return NULL; + return (void*)glctx->getProcAddress(QByteArray(name)); } MPVNoFBORenderer::MPVNoFBORenderer(mpv_handle* a_mpv, mpv_opengl_cb_context* a_mpv_gl) - : mpv(a_mpv) - , mpv_gl(a_mpv_gl) - , window(0) - , size() + : mpv(a_mpv) + , mpv_gl(a_mpv_gl) + , window(0) + , size() { - int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL); - if (r < 0) - qDebug() << "could not initialize OpenGL"; + int r = mpv_opengl_cb_init_gl(mpv_gl, NULL, get_proc_address, NULL); + if (r < 0) + qDebug() << "could not initialize OpenGL"; } MPVNoFBORenderer::~MPVNoFBORenderer() { - // Until this call is done, we need to make sure the player remains - // alive. This is done implicitly with the mpv::qt::Handle instance - // in this class. - exit(0); + // Until this call is done, we need to make sure the player remains + // alive. This is done implicitly with the mpv::qt::Handle instance + // in this class. + exit(0); } -void -MPVNoFBORenderer::paint() +void MPVNoFBORenderer::paint() { - window->resetOpenGLState(); + window->resetOpenGLState(); - // This uses 0 as framebuffer, which indicates that mpv will render directly - // to the frontbuffer. Note that mpv will always switch framebuffers - // explicitly. Some QWindow setups (such as using QQuickWidget) actually - // want you to render into a FBO in the beforeRendering() signal, and this - // code won't work there. - // The negation is used for rendering with OpenGL's flipped coordinates. - mpv_opengl_cb_draw(mpv_gl, 0, size.width(), -size.height()); + // This uses 0 as framebuffer, which indicates that mpv will render directly + // to the frontbuffer. Note that mpv will always switch framebuffers + // explicitly. Some QWindow setups (such as using QQuickWidget) actually + // want you to render into a FBO in the beforeRendering() signal, and this + // code won't work there. + // The negation is used for rendering with OpenGL's flipped coordinates. + mpv_opengl_cb_draw(mpv_gl, 0, size.width(), -size.height()); - window->resetOpenGLState(); + window->resetOpenGLState(); } MPVNoFBOBackend::MPVNoFBOBackend(QQuickItem* parent) - : QQuickItem(parent) - , mpv_gl(0) - , renderer(0) + : QQuickItem(parent) + , mpv_gl(0) + , renderer(0) { - mpv = mpv_create(); - if (!mpv) - throw std::runtime_error("could not create mpv context"); + mpv = mpv_create(); + if (!mpv) + throw std::runtime_error("could not create mpv context"); - mpv_set_option_string(mpv, "terminal", "no"); - mpv_set_option_string(mpv, "msg-level", "all=v"); + mpv_set_option_string(mpv, "terminal", "no"); + mpv_set_option_string(mpv, "msg-level", "all=v"); - // Fix? - mpv_set_option_string(mpv, "ytdl", "yes"); + // Fix? + mpv_set_option_string(mpv, "ytdl", "yes"); - mpv_set_option_string(mpv, "slang", "en"); + mpv_set_option_string(mpv, "slang", "en"); - mpv_set_option_string(mpv, "config", "yes"); - mpv_observe_property(mpv, 0, "tracks-menu", MPV_FORMAT_NONE); - mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "playback-abort", MPV_FORMAT_NONE); - mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "audio-device-list", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "playlist-pos", MPV_FORMAT_DOUBLE); - mpv_observe_property(mpv, 0, "volume", MPV_FORMAT_NONE); - mpv_observe_property(mpv, 0, "mute", MPV_FORMAT_NONE); - mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE); - mpv_observe_property(mpv, 0, "media-title", MPV_FORMAT_STRING); - mpv_observe_property(mpv, 0, "sub-text", MPV_FORMAT_STRING); - mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE); - mpv_observe_property(mpv, 0, "demuxer-cache-duration", MPV_FORMAT_DOUBLE); - mpv_observe_property(mpv, 0, "pause", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "playlist", MPV_FORMAT_NODE); - mpv_observe_property(mpv, 0, "speed", MPV_FORMAT_DOUBLE); - mpv_set_wakeup_callback(mpv, nofbowakeup, this); + mpv_set_option_string(mpv, "config", "yes"); + mpv_observe_property(mpv, 0, "tracks-menu", MPV_FORMAT_NONE); + mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "playback-abort", MPV_FORMAT_NONE); + mpv_observe_property(mpv, 0, "chapter-list", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "track-list", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "audio-device-list", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "playlist-pos", MPV_FORMAT_DOUBLE); + mpv_observe_property(mpv, 0, "volume", MPV_FORMAT_NONE); + mpv_observe_property(mpv, 0, "mute", MPV_FORMAT_NONE); + mpv_observe_property(mpv, 0, "duration", MPV_FORMAT_DOUBLE); + mpv_observe_property(mpv, 0, "media-title", MPV_FORMAT_STRING); + mpv_observe_property(mpv, 0, "sub-text", MPV_FORMAT_STRING); + mpv_observe_property(mpv, 0, "time-pos", MPV_FORMAT_DOUBLE); + mpv_observe_property(mpv, 0, "demuxer-cache-duration", MPV_FORMAT_DOUBLE); + mpv_observe_property(mpv, 0, "pause", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "playlist", MPV_FORMAT_NODE); + mpv_observe_property(mpv, 0, "speed", MPV_FORMAT_DOUBLE); + mpv_set_wakeup_callback(mpv, nofbowakeup, this); - if (mpv_initialize(mpv) < 0) - throw std::runtime_error("could not initialize mpv context"); + if (mpv_initialize(mpv) < 0) + throw std::runtime_error("could not initialize mpv context"); - // Make use of the MPV_SUB_API_OPENGL_CB API. - mpv::qt::set_option_variant(mpv, "vo", "opengl-cb"); + // Make use of the MPV_SUB_API_OPENGL_CB API. + mpv::qt::set_option_variant(mpv, "vo", "opengl-cb"); - // Setup the callback that will make QtQuick update and redraw if there - // is a new video frame. Use a queued connection: this makes sure the - // doUpdate() function is run on the GUI thread. - mpv_gl = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); - if (!mpv_gl) - throw std::runtime_error("OpenGL not compiled in"); - mpv_opengl_cb_set_update_callback( - mpv_gl, MPVNoFBOBackend::on_update, (void*)this); + // Setup the callback that will make QtQuick update and redraw if there + // is a new video frame. Use a queued connection: this makes sure the + // doUpdate() function is run on the GUI thread. + mpv_gl = (mpv_opengl_cb_context*)mpv_get_sub_api(mpv, MPV_SUB_API_OPENGL_CB); + if (!mpv_gl) + throw std::runtime_error("OpenGL not compiled in"); + mpv_opengl_cb_set_update_callback( + mpv_gl, MPVNoFBOBackend::on_update, (void*)this); - connect(this, - &MPVNoFBOBackend::onUpdate, - this, - &MPVNoFBOBackend::doUpdate, - Qt::QueuedConnection); - connect(this, - &MPVNoFBOBackend::positionChanged, - &MPVNoFBOBackend::updateDurationString); - connect(this, - &MPVNoFBOBackend::durationChanged, - &MPVNoFBOBackend::updateDurationString); + connect(this, + &MPVNoFBOBackend::onUpdate, + this, + &MPVNoFBOBackend::doUpdate, + Qt::QueuedConnection); + connect(this, + &MPVNoFBOBackend::positionChanged, + &MPVNoFBOBackend::updateDurationString); + connect(this, + &MPVNoFBOBackend::durationChanged, + &MPVNoFBOBackend::updateDurationString); - connect(this, - &QQuickItem::windowChanged, - this, - &MPVNoFBOBackend::handleWindowChanged); + connect(this, + &QQuickItem::windowChanged, + this, + &MPVNoFBOBackend::handleWindowChanged); } MPVNoFBOBackend::~MPVNoFBOBackend() { - printf("Shutting down...\n"); - qApp->quit(); - printf("MPV terminated.\n"); + printf("Shutting down...\n"); + qApp->quit(); + printf("MPV terminated.\n"); } -void -MPVNoFBOBackend::sync() +void MPVNoFBOBackend::sync() { - if (!renderer) { - window()->setIcon(QIcon(":/icon.png")); - renderer = new MPVNoFBORenderer(mpv, mpv_gl); - connect(window(), + if (!renderer) { + window()->setIcon(QIcon(":/icon.png")); + renderer = new MPVNoFBORenderer(mpv, mpv_gl); + connect(window(), &QQuickWindow::beforeRendering, renderer, &MPVNoFBORenderer::paint, Qt::DirectConnection); - QMetaObject::invokeMethod(this, "startPlayer"); - } - renderer->window = window(); - renderer->size = window()->size() * window()->devicePixelRatio(); + QMetaObject::invokeMethod(this, "startPlayer"); + } + renderer->window = window(); + renderer->size = window()->size() * window()->devicePixelRatio(); } -void -MPVNoFBOBackend::swapped() +void MPVNoFBOBackend::swapped() { - mpv_opengl_cb_report_flip(mpv_gl, 0); + mpv_opengl_cb_report_flip(mpv_gl, 0); } -void -MPVNoFBOBackend::cleanup() +void MPVNoFBOBackend::cleanup() { - if (renderer) { - delete renderer; - renderer = 0; - } + if (renderer) { + delete renderer; + renderer = 0; + } } -void -MPVNoFBOBackend::on_update(void* ctx) +void MPVNoFBOBackend::on_update(void* ctx) { - MPVNoFBOBackend* self = (MPVNoFBOBackend*)ctx; - emit self->onUpdate(); + MPVNoFBOBackend* self = (MPVNoFBOBackend*)ctx; + emit self->onUpdate(); } -void -MPVNoFBOBackend::doUpdate() +void MPVNoFBOBackend::doUpdate() { - window()->update(); - update(); + window()->update(); + update(); } QVariant MPVNoFBOBackend::getProperty(const QString& name) const { - return mpv::qt::get_property_variant(mpv, name); + return mpv::qt::get_property_variant(mpv, name); } -void -MPVNoFBOBackend::command(const QVariant& params) +void MPVNoFBOBackend::command(const QVariant& params) { - mpv::qt::command_variant(mpv, params); + mpv::qt::command_variant(mpv, params); } -void -MPVNoFBOBackend::setProperty(const QString& name, const QVariant& value) +void MPVNoFBOBackend::setProperty(const QString& name, const QVariant& value) { - mpv::qt::set_property_variant(mpv, name, value); + mpv::qt::set_property_variant(mpv, name, value); } -void -MPVNoFBOBackend::setOption(const QString& name, const QVariant& value) +void MPVNoFBOBackend::setOption(const QString& name, const QVariant& value) { - mpv::qt::set_option_variant(mpv, name, value); + mpv::qt::set_option_variant(mpv, name, value); } -void -MPVNoFBOBackend::launchAboutQt() +void MPVNoFBOBackend::launchAboutQt() { - QApplication* qapp = - qobject_cast(QCoreApplication::instance()); - qapp->aboutQt(); + QApplication* qapp = qobject_cast(QCoreApplication::instance()); + qapp->aboutQt(); } QVariant MPVNoFBOBackend::playerCommand(const Enums::Commands& cmd) { - return playerCommand(cmd, QVariant("NoArgProvided")); + return playerCommand(cmd, QVariant("NoArgProvided")); } QVariant MPVNoFBOBackend::playerCommand(const Enums::Commands& cmd, - const QVariant& args) + const QVariant& args) { - return MPVCommon::playerCommand(this, cmd, args); + return MPVCommon::playerCommand(this, cmd, args); } -void -MPVNoFBOBackend::handleWindowChanged(QQuickWindow* win) +void MPVNoFBOBackend::handleWindowChanged(QQuickWindow* win) { - if (!win) - return; - connect(win, - &QQuickWindow::beforeSynchronizing, - this, - &MPVNoFBOBackend::sync, - Qt::DirectConnection); - connect(win, - &QQuickWindow::sceneGraphInvalidated, - this, - &MPVNoFBOBackend::cleanup, - Qt::DirectConnection); - connect(win, - &QQuickWindow::frameSwapped, - this, - &MPVNoFBOBackend::swapped, - Qt::DirectConnection); - win->setClearBeforeRendering(false); + if (!win) + return; + connect(win, + &QQuickWindow::beforeSynchronizing, + this, + &MPVNoFBOBackend::sync, + Qt::DirectConnection); + connect(win, + &QQuickWindow::sceneGraphInvalidated, + this, + &MPVNoFBOBackend::cleanup, + Qt::DirectConnection); + connect(win, + &QQuickWindow::frameSwapped, + this, + &MPVNoFBOBackend::swapped, + Qt::DirectConnection); + win->setClearBeforeRendering(false); } -void -MPVNoFBOBackend::toggleOnTop() +void MPVNoFBOBackend::toggleOnTop() { - onTop = !onTop; - Utils::AlwaysOnTop(window()->winId(), onTop); + onTop = !onTop; + Utils::AlwaysOnTop(window()->winId(), onTop); } -bool -MPVNoFBOBackend::event(QEvent* event) +bool MPVNoFBOBackend::event(QEvent* event) { - if (event->type() == QEvent::User) { - on_mpv_events(); - } - return QObject::event(event); -} - -void -MPVNoFBOBackend::on_mpv_events() -{ - while (mpv) { - mpv_event* event = mpv_wait_event(mpv, 0); - if (event->event_id == MPV_EVENT_NONE) { - break; + if (event->type() == QEvent::User) { + on_mpv_events(); } - handle_mpv_event(event); - } + return QObject::event(event); } -void -MPVNoFBOBackend::updateDurationString(int numTime) +void MPVNoFBOBackend::on_mpv_events() { - QMetaMethod metaMethod = sender()->metaObject()->method(senderSignalIndex()); - MPVCommon::updateDurationString(this, numTime, metaMethod); + while (mpv) { + mpv_event* event = mpv_wait_event(mpv, 0); + if (event->event_id == MPV_EVENT_NONE) { + break; + } + handle_mpv_event(event); + } +} + +void MPVNoFBOBackend::updateDurationString(int numTime) +{ + QMetaMethod metaMethod = sender()->metaObject()->method(senderSignalIndex()); + MPVCommon::updateDurationString(this, numTime, metaMethod); } QVariantMap MPVNoFBOBackend::getAudioDevices(const QVariant& drivers) const { - return MPVCommon::getAudioDevices(drivers); + return MPVCommon::getAudioDevices(drivers); } -void -MPVNoFBOBackend::handle_mpv_event(mpv_event* event) +void MPVNoFBOBackend::handle_mpv_event(mpv_event* event) { - MPVCommon::handle_mpv_event(this, event); + MPVCommon::handle_mpv_event(this, event); } QString MPVNoFBOBackend::getStats() { - return MPVCommon::getStats(this); + return MPVCommon::getStats(this); } - - diff --git a/src/Backends/MPVNoFBO/MPVNoFBOBackend.hpp b/src/Backends/MPVNoFBO/MPVNoFBOBackend.hpp index 9674c0e..7d015c7 100644 --- a/src/Backends/MPVNoFBO/MPVNoFBOBackend.hpp +++ b/src/Backends/MPVNoFBO/MPVNoFBOBackend.hpp @@ -1,8 +1,8 @@ #ifndef MPVNoFBOBackend_H #define MPVNoFBOBackend_H -#include -#include +#include "src/backendinterface.hpp" +#include "src/enums.hpp" #include #include #include @@ -12,110 +12,108 @@ #include #include #include -#include "src/backendinterface.hpp" -#include "src/enums.hpp" +#include +#include -class MPVNoFBORenderer : public QObject -{ - Q_OBJECT - mpv_handle* mpv; - mpv_opengl_cb_context* mpv_gl; +class MPVNoFBORenderer : public QObject { + Q_OBJECT + mpv_handle* mpv; + mpv_opengl_cb_context* mpv_gl; public: - QQuickWindow* window; - QSize size; + QQuickWindow* window; + QSize size; - friend class MpvObject; - MPVNoFBORenderer(mpv_handle* a_mpv, mpv_opengl_cb_context* a_mpv_gl); - virtual ~MPVNoFBORenderer(); + friend class MpvObject; + MPVNoFBORenderer(mpv_handle* a_mpv, mpv_opengl_cb_context* a_mpv_gl); + virtual ~MPVNoFBORenderer(); public slots: - void paint(); + void paint(); }; class MPVNoFBOBackend - : public QQuickItem - , public BackendInterface -{ - Q_INTERFACES(BackendInterface) + : public QQuickItem, + public BackendInterface { + Q_INTERFACES(BackendInterface) - Q_OBJECT - Q_PROPERTY(bool logging READ logging WRITE setLogging) + Q_OBJECT + Q_PROPERTY(bool logging READ logging WRITE setLogging) - mpv_handle* mpv; - mpv_opengl_cb_context* mpv_gl; - MPVNoFBORenderer* renderer; - bool onTop = false; - bool m_logging = true; - QSettings settings; + mpv_handle* mpv; + mpv_opengl_cb_context* mpv_gl; + MPVNoFBORenderer* renderer; + bool onTop = false; + bool m_logging = true; + QSettings settings; public: - static void on_update(void* ctx); + static void on_update(void* ctx); - void setLogging(bool a) - { - if (a != m_logging) { - m_logging = a; + void setLogging(bool a) + { + if (a != m_logging) { + m_logging = a; + } } - } - bool logging() const { return m_logging; } + bool logging() const { return m_logging; } - MPVNoFBOBackend(QQuickItem* parent = 0); - virtual ~MPVNoFBOBackend(); + MPVNoFBOBackend(QQuickItem* parent = 0); + virtual ~MPVNoFBOBackend(); - int lastTime = 0; - double lastSpeed = 0; - QString totalDurationString; - QString lastPositionString; + int lastTime = 0; + double lastSpeed = 0; + QString totalDurationString; + QString lastPositionString; public slots: - QVariant playerCommand(const Enums::Commands& command, const QVariant& args); - QVariant playerCommand(const Enums::Commands& command); - void launchAboutQt(); - void toggleOnTop(); - QString getStats(); - // Optional but handy for MPV or custom backend settings. - void command(const QVariant& params); - void setProperty(const QString& name, const QVariant& value); - void setOption(const QString& name, const QVariant& value); - QVariant getProperty(const QString& name) const; + QVariant playerCommand(const Enums::Commands& command, const QVariant& args); + QVariant playerCommand(const Enums::Commands& command); + void launchAboutQt(); + void toggleOnTop(); + QString getStats(); + // Optional but handy for MPV or custom backend settings. + void command(const QVariant& params); + void setProperty(const QString& name, const QVariant& value); + void setOption(const QString& name, const QVariant& value); + QVariant getProperty(const QString& name) const; - void sync(); - void swapped(); - void cleanup(); + void sync(); + void swapped(); + void cleanup(); - // Just used for adding missing audio devices to list. - QVariantMap getAudioDevices(const QVariant& drivers) const; + // Just used for adding missing audio devices to list. + QVariantMap getAudioDevices(const QVariant& drivers) const; - bool event(QEvent* event); + bool event(QEvent* event); signals: - void onUpdate(); - void mpv_events(); - // All below required for Player API - void playStatusChanged(const Enums::PlayStatus& status); - void volumeStatusChanged(const Enums::VolumeStatus& status); - void volumeChanged(const int& volume); - void durationChanged(const double& duration); - void positionChanged(const double& position); - void cachedDurationChanged(const double& duration); - void playlistPositionChanged(const double& position); - void titleChanged(const QString& title); - void subtitlesChanged(const QString& subtitles); - void durationStringChanged(const QString& string); - void tracksChanged(const QVariantList& tracks); - void audioDevicesChanged(const QVariantMap& devices); - void playlistChanged(const QVariantList& devices); - void chaptersChanged(const QVariantList& devices); - void speedChanged(const double& speed); + void onUpdate(); + void mpv_events(); + // All below required for Player API + void playStatusChanged(const Enums::PlayStatus& status); + void volumeStatusChanged(const Enums::VolumeStatus& status); + void volumeChanged(const int& volume); + void durationChanged(const double& duration); + void positionChanged(const double& position); + void cachedDurationChanged(const double& duration); + void playlistPositionChanged(const double& position); + void titleChanged(const QString& title); + void subtitlesChanged(const QString& subtitles); + void durationStringChanged(const QString& string); + void tracksChanged(const QVariantList& tracks); + void audioDevicesChanged(const QVariantMap& devices); + void playlistChanged(const QVariantList& devices); + void chaptersChanged(const QVariantList& devices); + void speedChanged(const double& speed); private slots: - void doUpdate(); - void on_mpv_events(); - void updateDurationString(int numTime); - void handleWindowChanged(QQuickWindow* win); + void doUpdate(); + void on_mpv_events(); + void updateDurationString(int numTime); + void handleWindowChanged(QQuickWindow* win); private: - void handle_mpv_event(mpv_event* event); + void handle_mpv_event(mpv_event* event); }; #endif \ No newline at end of file diff --git a/src/Process.cpp b/src/Process.cpp index c0a67bc..5a934fc 100644 --- a/src/Process.cpp +++ b/src/Process.cpp @@ -4,22 +4,22 @@ class QObject; Process::Process(QObject* parent) - : QProcess(parent) -{} - -void -Process::start(const QString& program, const QVariantList& arguments) + : QProcess(parent) { - QStringList args; +} - for (int i = 0; i < arguments.length(); i++) - args << arguments[i].toString(); +void Process::start(const QString& program, const QVariantList& arguments) +{ + QStringList args; - QProcess::start(program, args); + for (int i = 0; i < arguments.length(); i++) + args << arguments[i].toString(); + + QProcess::start(program, args); } QString Process::getOutput() { - return QProcess::readAllStandardOutput(); + return QProcess::readAllStandardOutput(); } diff --git a/src/Process.h b/src/Process.h index b09f0c5..33d8854 100644 --- a/src/Process.h +++ b/src/Process.h @@ -6,15 +6,14 @@ #include #include -class Process : public QProcess -{ - Q_OBJECT +class Process : public QProcess { + Q_OBJECT public: - explicit Process(QObject* parent = 0); + explicit Process(QObject* parent = 0); - Q_INVOKABLE void start(const QString& program, const QVariantList& arguments); + Q_INVOKABLE void start(const QString& program, const QVariantList& arguments); - Q_INVOKABLE QString getOutput(); + Q_INVOKABLE QString getOutput(); }; #endif \ No newline at end of file diff --git a/src/ThumbnailCache.cpp b/src/ThumbnailCache.cpp index ff80c07..b500b35 100644 --- a/src/ThumbnailCache.cpp +++ b/src/ThumbnailCache.cpp @@ -3,9 +3,9 @@ #include #include #include +#include #include #include -#include #include #include #include @@ -13,57 +13,54 @@ #include ThumbnailCache::ThumbnailCache(QObject* parent) - : QObject(parent) - , manager(new QNetworkAccessManager(this)) + : QObject(parent) + , manager(new QNetworkAccessManager(this)) { - cacheFolder = - QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + - "/thumbs"); - if (!cacheFolder.exists()) { - cacheFolder.mkpath("."); - } + cacheFolder = QDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/thumbs"); + if (!cacheFolder.exists()) { + cacheFolder.mkpath("."); + } } -void -ThumbnailCache::addURL(const QString& name, const QString& mediaURL) +void ThumbnailCache::addURL(const QString& name, const QString& mediaURL) { - QString hashedURL = QString( - QCryptographicHash::hash(name.toUtf8(), QCryptographicHash::Md5).toHex()); - QString cacheFilename = hashedURL + ".jpg"; - QString cachedFilePath = cacheFolder.absoluteFilePath(cacheFilename); - if (cacheFolder.exists(cacheFilename)) { - emit thumbnailReady(name, mediaURL, "file://" + cachedFilePath); - return; - } + QString hashedURL = QString( + QCryptographicHash::hash(name.toUtf8(), QCryptographicHash::Md5).toHex()); + QString cacheFilename = hashedURL + ".jpg"; + QString cachedFilePath = cacheFolder.absoluteFilePath(cacheFilename); + if (cacheFolder.exists(cacheFilename)) { + emit thumbnailReady(name, mediaURL, "file://" + cachedFilePath); + return; + } - QString url(mediaURL); - QFileInfo isFile = QFileInfo(url); - if (isFile.exists()) { - QImageReader reader(url); - QImage image = reader.read(); + QString url(mediaURL); + QFileInfo isFile = QFileInfo(url); + if (isFile.exists()) { + QImageReader reader(url); + QImage image = reader.read(); - image.save(cachedFilePath, "JPG"); + image.save(cachedFilePath, "JPG"); - emit thumbnailReady(name, mediaURL, "file://" + cachedFilePath); - return; - } + emit thumbnailReady(name, mediaURL, "file://" + cachedFilePath); + return; + } - QNetworkRequest request(url); + QNetworkRequest request(url); - QNetworkReply* reply = manager->get(request); + QNetworkReply* reply = manager->get(request); - connect(reply, &QNetworkReply::finished, [=] { - QByteArray response_data = reply->readAll(); + connect(reply, &QNetworkReply::finished, [=] { + QByteArray response_data = reply->readAll(); - QBuffer buffer(&response_data); - buffer.open(QIODevice::ReadOnly); + QBuffer buffer(&response_data); + buffer.open(QIODevice::ReadOnly); - QImageReader reader(&buffer); - QImage image = reader.read(); + QImageReader reader(&buffer); + QImage image = reader.read(); - image.save(cachedFilePath, "JPG"); + image.save(cachedFilePath, "JPG"); - emit thumbnailReady(name, mediaURL, "file://" + cachedFilePath); - }); + emit thumbnailReady(name, mediaURL, "file://" + cachedFilePath); + }); } diff --git a/src/ThumbnailCache.h b/src/ThumbnailCache.h index f828082..6ea404a 100644 --- a/src/ThumbnailCache.h +++ b/src/ThumbnailCache.h @@ -1,27 +1,25 @@ #ifndef ThumbnailCache_H #define ThumbnailCache_H #include +#include #include #include -#include -class ThumbnailCache : public QObject -{ - Q_OBJECT +class ThumbnailCache : public QObject { + Q_OBJECT public: - explicit ThumbnailCache(QObject* parent = nullptr); + explicit ThumbnailCache(QObject* parent = nullptr); public slots: - Q_INVOKABLE void addURL(const QString& name, const QString& url); + Q_INVOKABLE void addURL(const QString& name, const QString& url); signals: - void thumbnailReady(const QString& name, - const QString& url, - const QString& filePath); + void thumbnailReady(const QString& name, const QString& url, + const QString& filePath); private: - QNetworkAccessManager* manager; - QDir cacheFolder; + QNetworkAccessManager* manager; + QDir cacheFolder; }; #endif \ No newline at end of file diff --git a/src/backendinterface.hpp b/src/backendinterface.hpp index 268954e..aace662 100644 --- a/src/backendinterface.hpp +++ b/src/backendinterface.hpp @@ -2,45 +2,45 @@ #define BackendInterface_H #include "enums.hpp" #include -class BackendInterface -{ +class BackendInterface { public: - virtual ~BackendInterface(){}; - int lastTime = 0; - double lastSpeed = 0; - QString totalDurationString; - QString lastPositionString; + virtual ~BackendInterface(){}; + int lastTime = 0; + double lastSpeed = 0; + QString totalDurationString; + QString lastPositionString; public slots: - // All 5 required for Player API - virtual QVariant playerCommand(const Enums::Commands& command, - const QVariant& args) = 0; - virtual QVariant playerCommand(const Enums::Commands& command) = 0; - virtual void toggleOnTop() = 0; - // Optional but handy for MPV or custom backend settings. - virtual void command(const QVariant& params) = 0; - virtual void setProperty(const QString& name, const QVariant& value) = 0; - virtual void setOption(const QString& name, const QVariant& value) = 0; - virtual QVariant getProperty(const QString& name) const = 0; - virtual QVariantMap getAudioDevices(const QVariant& drivers) const = 0; + // All 5 required for Player API + virtual QVariant playerCommand(const Enums::Commands& command, + const QVariant& args) + = 0; + virtual QVariant playerCommand(const Enums::Commands& command) = 0; + virtual void toggleOnTop() = 0; + // Optional but handy for MPV or custom backend settings. + virtual void command(const QVariant& params) = 0; + virtual void setProperty(const QString& name, const QVariant& value) = 0; + virtual void setOption(const QString& name, const QVariant& value) = 0; + virtual QVariant getProperty(const QString& name) const = 0; + virtual QVariantMap getAudioDevices(const QVariant& drivers) const = 0; signals: - // All below required for Player API - virtual void playStatusChanged(const Enums::PlayStatus& status) = 0; - virtual void volumeStatusChanged(const Enums::VolumeStatus& status) = 0; - virtual void volumeChanged(const int& volume) = 0; - virtual void durationChanged(const double& duration) = 0; - virtual void positionChanged(const double& position) = 0; - virtual void cachedDurationChanged(const double& duration) = 0; - virtual void playlistPositionChanged(const double& position) = 0; - virtual void titleChanged(const QString& title) = 0; - virtual void subtitlesChanged(const QString& subtitles) = 0; - virtual void durationStringChanged(const QString& string) = 0; - virtual void tracksChanged(const QVariantList& tracks) = 0; - virtual void audioDevicesChanged(const QVariantMap& devices) = 0; - virtual void playlistChanged(const QVariantList& devices) = 0; - virtual void chaptersChanged(const QVariantList& devices) = 0; - virtual void speedChanged(const double& speed) = 0; + // All below required for Player API + virtual void playStatusChanged(const Enums::PlayStatus& status) = 0; + virtual void volumeStatusChanged(const Enums::VolumeStatus& status) = 0; + virtual void volumeChanged(const int& volume) = 0; + virtual void durationChanged(const double& duration) = 0; + virtual void positionChanged(const double& position) = 0; + virtual void cachedDurationChanged(const double& duration) = 0; + virtual void playlistPositionChanged(const double& position) = 0; + virtual void titleChanged(const QString& title) = 0; + virtual void subtitlesChanged(const QString& subtitles) = 0; + virtual void durationStringChanged(const QString& string) = 0; + virtual void tracksChanged(const QVariantList& tracks) = 0; + virtual void audioDevicesChanged(const QVariantMap& devices) = 0; + virtual void playlistChanged(const QVariantList& devices) = 0; + virtual void chaptersChanged(const QVariantList& devices) = 0; + virtual void speedChanged(const double& speed) = 0; }; Q_DECLARE_INTERFACE(BackendInterface, "NamedKitten.BackendInterface"); diff --git a/src/enums.hpp b/src/enums.hpp index bac6a34..6dc99c2 100644 --- a/src/enums.hpp +++ b/src/enums.hpp @@ -6,61 +6,55 @@ namespace Enums { Q_NAMESPACE -enum class PlayStatus : int -{ - Playing = 0, - Paused = 1 +enum class PlayStatus : int { + Playing = 0, + Paused = 1 }; Q_ENUM_NS(PlayStatus) -enum class VolumeStatus : int -{ - Muted = 0, - Low = 1, - Normal = 2 +enum class VolumeStatus : int { + Muted = 0, + Low = 1, + Normal = 2 }; Q_ENUM_NS(VolumeStatus) -enum class Commands : int -{ - TogglePlayPause = 0, - ToggleMute = 1, - SetAudioDevice = 2, - AddVolume = 3, - SetVolume = 4, - AddSpeed = 5, - SubtractSpeed = 6, - ChangeSpeed = 7, - SetSpeed = 8, - ToggleStats = 9, - NextAudioTrack = 10, - NextVideoTrack = 11, - NextSubtitleTrack = 12, - PreviousPlaylistItem = 13, - NextPlaylistItem = 14, - LoadFile = 15, - AppendFile = 16, - Seek = 17, - SeekAbsolute = 18, - ForwardFrame = 19, - BackwardFrame = 20, - SetTrack = 21, - SetPlaylistPos = 22, - ForcePause = 23, +enum class Commands : int { + TogglePlayPause = 0, + ToggleMute = 1, + SetAudioDevice = 2, + AddVolume = 3, + SetVolume = 4, + AddSpeed = 5, + SubtractSpeed = 6, + ChangeSpeed = 7, + SetSpeed = 8, + ToggleStats = 9, + NextAudioTrack = 10, + NextVideoTrack = 11, + NextSubtitleTrack = 12, + PreviousPlaylistItem = 13, + NextPlaylistItem = 14, + LoadFile = 15, + AppendFile = 16, + Seek = 17, + SeekAbsolute = 18, + ForwardFrame = 19, + BackwardFrame = 20, + SetTrack = 21, + SetPlaylistPos = 22, + ForcePause = 23, }; Q_ENUM_NS(Commands) -enum class Backends : int -{ - MPVBackend = 0, - DirectMPVBackend = 1 +enum class Backends : int { + MPVBackend = 0, + DirectMPVBackend = 1 }; Q_ENUM_NS(Backends) - } // Forces meta generation. -class Dummy : public QObject -{ - Q_OBJECT +class Dummy : public QObject { + Q_OBJECT }; #endif diff --git a/src/logger.cpp b/src/logger.cpp index 6846923..7d0a25d 100644 --- a/src/logger.cpp +++ b/src/logger.cpp @@ -1,39 +1,37 @@ -#include -#include -#include -#include -#include -#include -#include -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export -#include // IWYU pragma: export #include "spdlog/common.h" #include "spdlog/details/file_helper-inl.h" #include "spdlog/sinks/ansicolor_sink-inl.h" #include "spdlog/sinks/base_sink-inl.h" #include "spdlog/sinks/basic_file_sink-inl.h" #include "spdlog/spdlog-inl.h" +#include +#include +#include +#include +#include // IWYU pragma: keep +#include +#include // IWYU pragma: keep +#include +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include // IWYU pragma: export +#include std::shared_ptr initLogger(std::string name) { - QSettings settings("KittehPlayer", "KittehPlayer"); + QSettings settings("KittehPlayer", "KittehPlayer"); - QString logFile = - settings.value("Logging/logFile", "/tmp/KittehPlayer.log").toString(); + QString logFile = settings.value("Logging/logFile", "/tmp/KittehPlayer.log").toString(); - std::vector sinks; - sinks.push_back(std::make_shared()); - sinks.push_back(std::make_shared( - logFile.toUtf8().constData())); - auto console = - std::make_shared(name, begin(sinks), end(sinks)); - console->set_pattern("[%l][%n] %v%$"); - spdlog::register_logger(console); + std::vector sinks; + sinks.push_back(std::make_shared()); + sinks.push_back(std::make_shared( + logFile.toUtf8().constData())); + auto console = std::make_shared(name, begin(sinks), end(sinks)); + console->set_pattern("[%l][%n] %v%$"); + spdlog::register_logger(console); - return spdlog::get(name); + return spdlog::get(name); } diff --git a/src/logger.h b/src/logger.h index 4df0fd6..f849056 100644 --- a/src/logger.h +++ b/src/logger.h @@ -1,13 +1,12 @@ #ifndef LOGGER_HPP #define LOGGER_HPP -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep +#include // IWYU pragma: keep #include // IWYU pragma: keep -#include // IWYU pragma: keep +#include // IWYU pragma: keep -std::shared_ptr -initLogger(std::string name); +std::shared_ptr initLogger(std::string name); #endif diff --git a/src/main.cpp b/src/main.cpp index 7c9b6f8..0e744f2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,24 +1,24 @@ -#include -#include #include #include -#include #include +#include +#include +#include #ifdef QT_QML_DEBUG #warning "QML Debugging Enabled!!!" #include #endif +#include "logger.h" +#include "spdlog/logger.h" #include #include #include #include -#include #include #include #include #include -#include "logger.h" -#include "spdlog/logger.h" +#include extern void registerTypes(); @@ -29,94 +29,89 @@ extern void registerTypes(); auto qmlLogger = initLogger("qml"); auto miscLogger = initLogger("misc"); -void -spdLogger(QtMsgType type, const QMessageLogContext& context, const QString& msg) +void spdLogger(QtMsgType type, const QMessageLogContext& context, const QString& msg) { - std::string localMsg = msg.toUtf8().constData(); - std::shared_ptr logger; - if (QString(context.category).startsWith(QString("qml"))) { - logger = qmlLogger; - } else { - logger = miscLogger; - } + std::string localMsg = msg.toUtf8().constData(); + std::shared_ptr logger; + if (QString(context.category).startsWith(QString("qml"))) { + logger = qmlLogger; + } else { + logger = miscLogger; + } - switch (type) { + switch (type) { case QtDebugMsg: - logger->debug("{}", localMsg); - break; + logger->debug("{}", localMsg); + break; case QtInfoMsg: - logger->info("{}", localMsg); - break; + logger->info("{}", localMsg); + break; case QtWarningMsg: - logger->warn("{}", localMsg); - break; + logger->warn("{}", localMsg); + break; case QtCriticalMsg: - logger->critical("{}", localMsg); - break; + logger->critical("{}", localMsg); + break; case QtFatalMsg: - logger->critical("{}", localMsg); - abort(); - } + logger->critical("{}", localMsg); + abort(); + } } -int -main(int argc, char* argv[]) +int main(int argc, char* argv[]) { - qInstallMessageHandler(spdLogger); + qInstallMessageHandler(spdLogger); - auto launcherLogger = initLogger("launcher"); - launcherLogger->info("Starting up!"); + auto launcherLogger = initLogger("launcher"); + launcherLogger->info("Starting up!"); - setenv("QT_QUICK_CONTROLS_STYLE", "Desktop", 1); - QApplication app(argc, argv); + setenv("QT_QUICK_CONTROLS_STYLE", "Desktop", 1); + QApplication app(argc, argv); - app.setOrganizationName("KittehPlayer"); - app.setOrganizationDomain("kitteh.pw"); - app.setApplicationName("KittehPlayer"); + app.setOrganizationName("KittehPlayer"); + app.setOrganizationDomain("kitteh.pw"); + app.setApplicationName("KittehPlayer"); #ifdef QT_QML_DEBUG - // Allows debug. - QQmlDebuggingEnabler enabler; + // Allows debug. + QQmlDebuggingEnabler enabler; #endif - QSettings settings; + QSettings settings; - bool ranFirstTimeSetup = settings.value("Setup/ranSetup", false).toBool(); + bool ranFirstTimeSetup = settings.value("Setup/ranSetup", false).toBool(); #ifdef __linux__ - // WARNING, THIS IS A BIG HACK - // this is only to make it so KittehPlayer works first try on pinephone. - // TODO: launch a opengl window or use offscreen to see if GL_ARB_framebuffer_object - // can be found - if (! (settings.value("Backend/disableSunxiCheck", false).toBool() || ranFirstTimeSetup)) { - FILE *fd = popen("grep sun[x8]i /proc/modules", "r"); - char buf[16]; - if (fread(buf, 1, sizeof (buf), fd) > 0) { - launcherLogger->info("Running on sunxi, switching to NoFBO."); - settings.setValue("Appearance/clickToPause", false); - settings.setValue("Appearance/doubleTapToSeek", true); - settings.setValue("Appearance/scaleFactor", 2.2); - settings.setValue("Appearance/subtitlesFontSize", 38); - settings.setValue("Appearance/uiFadeTimer", 0); + // WARNING, THIS IS A BIG HACK + // this is only to make it so KittehPlayer works first try on pinephone. + // TODO: launch a opengl window or use offscreen to see if GL_ARB_framebuffer_object + // can be found + if (!(settings.value("Backend/disableSunxiCheck", false).toBool() || ranFirstTimeSetup)) { + FILE* fd = popen("grep sun[x8]i /proc/modules", "r"); + char buf[16]; + if (fread(buf, 1, sizeof(buf), fd) > 0) { + launcherLogger->info("Running on sunxi, switching to NoFBO."); + settings.setValue("Appearance/clickToPause", false); + settings.setValue("Appearance/doubleTapToSeek", true); + settings.setValue("Appearance/scaleFactor", 2.2); + settings.setValue("Appearance/subtitlesFontSize", 38); + settings.setValue("Appearance/uiFadeTimer", 0); + } } - } #endif + settings.setValue("Setup/ranSetup", true); - settings.setValue("Setup/ranSetup", true); + QString newpath = QProcessEnvironment::systemEnvironment().value("APPDIR", "") + "/usr/bin:" + QProcessEnvironment::systemEnvironment().value("PATH", ""); + setenv("PATH", newpath.toUtf8().constData(), 1); - QString newpath = - QProcessEnvironment::systemEnvironment().value("APPDIR", "") + - "/usr/bin:" + QProcessEnvironment::systemEnvironment().value("PATH", ""); - setenv("PATH", newpath.toUtf8().constData(), 1); + registerTypes(); - registerTypes(); + setlocale(LC_NUMERIC, "C"); + launcherLogger->info("Loading player..."); - setlocale(LC_NUMERIC, "C"); - launcherLogger->info("Loading player..."); + QQmlApplicationEngine engine; + engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); - QQmlApplicationEngine engine; - engine.load(QUrl(QStringLiteral("qrc:///main.qml"))); - - return app.exec(); + return app.exec(); } diff --git a/src/qml/ControlsBar.qml b/src/qml/ControlsBar.qml index 678cdb7..db6fddc 100644 --- a/src/qml/ControlsBar.qml +++ b/src/qml/ControlsBar.qml @@ -4,99 +4,99 @@ import QtQuick.Layouts 1.2 import player 1.0 Item { - id: controlsBarItem - property var combinedHeight: progressBar.height + controlsBackground.height - property bool controlsShowing: true + id: controlsBarItem + property var combinedHeight: progressBar.height + controlsBackground.height + property bool controlsShowing: true + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + + Connections { + target: globalConnections + onHideUI: function (force) { + controlsBarItem.controlsShowing = false + } + onShowUI: { + controlsBarItem.controlsShowing = true + } + } + + Connections { + target: appearance + onThemeNameChanged: setControlsTheme(appearance.themeName) + } + + function setControlsTheme(themeName) { + for (var i = 0; i < controlsBar.children.length; ++i) { + if (controlsBar.children[i].objectName == "buttonLayout") { + controlsBar.children[i].destroy() + } + } + + var component = Qt.createComponent(themeName + "ButtonLayout.qml") + if (component.status == Component.Error) { + console.error("Error loading component: " + component.errorString()) + } + component.createObject(controlsBar, {}) + } + + SubtitlesBar { + anchors.bottom: controlsBackground.top + } + + VideoProgress { + id: progressBar + visible: controlsBarItem.controlsShowing + && appearance.themeName != "RoosterTeeth" + bottomPadding: 0 + rightPadding: 0 + leftPadding: 0 + z: 20 anchors { - bottom: parent.bottom - left: parent.left - right: parent.right + bottom: controlsBackground.top + left: controlsBackground.left + right: controlsBackground.right + leftMargin: parent.width / 128 + rightMargin: parent.width / 128 + bottomMargin: 0 } + } - Connections { - target: globalConnections - onHideUI: function (force) { - controlsBarItem.controlsShowing = false - } - onShowUI: { - controlsBarItem.controlsShowing = true - } + Rectangle { + id: controlsBackground + height: controlsBar.visible ? controlsBar.height + + (appearance.themeName + == "RoosterTeeth" ? 0 : progressBar.topPadding) : 0 + Layout.fillWidth: true + Layout.fillHeight: true + color: getAppearanceValueForTheme(appearance.themeName, "mainBackground") + visible: controlsBarItem.controlsShowing + z: 10 + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right } + } - Connections { - target: appearance - onThemeNameChanged: setControlsTheme(appearance.themeName) + Item { + id: controlsBar + height: controlsBar.visible ? mainWindow.virtualHeight / 20 : 0 + visible: controlsBarItem.controlsShowing + z: 30 + anchors { + right: parent.right + rightMargin: parent.width / 128 + left: parent.left + leftMargin: parent.width / 128 + bottom: parent.bottom + bottomMargin: 0 } + } - function setControlsTheme(themeName) { - for (var i = 0; i < controlsBar.children.length; ++i) { - if (controlsBar.children[i].objectName == "buttonLayout") { - controlsBar.children[i].destroy() - } - } - - var component = Qt.createComponent(themeName + "ButtonLayout.qml") - if (component.status == Component.Error) { - console.error("Error loading component: " + component.errorString()) - } - component.createObject(controlsBar, {}) - } - - SubtitlesBar { - anchors.bottom: controlsBackground.top - } - - VideoProgress { - id: progressBar - visible: controlsBarItem.controlsShowing && appearance.themeName != "RoosterTeeth" - bottomPadding: 0 - rightPadding: 0 - leftPadding: 0 - z: 20 - anchors { - bottom: controlsBackground.top - left: controlsBackground.left - right: controlsBackground.right - leftMargin: parent.width / 128 - rightMargin: parent.width / 128 - bottomMargin: 0 - } - } - - Rectangle { - id: controlsBackground - height: controlsBar.visible ? controlsBar.height - + (appearance.themeName - == "RoosterTeeth" ? 0 : progressBar.topPadding) : 0 - Layout.fillWidth: true - Layout.fillHeight: true - color: getAppearanceValueForTheme(appearance.themeName, - "mainBackground") - visible: controlsBarItem.controlsShowing - z: 10 - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - } - } - - Item { - id: controlsBar - height: controlsBar.visible ? mainWindow.virtualHeight / 20 : 0 - visible: controlsBarItem.controlsShowing - z: 30 - anchors { - right: parent.right - rightMargin: parent.width / 128 - left: parent.left - leftMargin: parent.width / 128 - bottom: parent.bottom - bottomMargin: 0 - } - } - - Component.onCompleted: { - setControlsTheme(appearance.themeName) - } + Component.onCompleted: { + setControlsTheme(appearance.themeName) + } } diff --git a/src/qml/CustomMenu.qml b/src/qml/CustomMenu.qml index 24bb7f3..c7e7884 100644 --- a/src/qml/CustomMenu.qml +++ b/src/qml/CustomMenu.qml @@ -2,12 +2,11 @@ import QtQuick 2.0 import QtQuick.Controls 2.3 Menu { - width: 300 - background: Rectangle { - implicitWidth: parent.width - implicitHeight: 10 - color: getAppearanceValueForTheme(appearance.themeName, - "mainBackground") - } - delegate: CustomMenuItem {} + width: 300 + background: Rectangle { + implicitWidth: parent.width + implicitHeight: 10 + color: getAppearanceValueForTheme(appearance.themeName, "mainBackground") + } + delegate: CustomMenuItem {} } diff --git a/src/qml/Dialogs/PlaylistDialog.qml b/src/qml/Dialogs/PlaylistDialog.qml index dd678c2..5922462 100644 --- a/src/qml/Dialogs/PlaylistDialog.qml +++ b/src/qml/Dialogs/PlaylistDialog.qml @@ -5,218 +5,214 @@ import QtQuick.Window 2.2 import player 1.0 Dialog { - id: playlistDialog - title: "Playlist" - height: Math.max(480, childrenRect.height * playlistListView.count) - width: 720 - modality: Qt.NonModal - property int thumbnailJobsRunning: 0 - property variant thumbnailJobs: [] - property int titleJobsRunning: 0 - property variant titleJobs: [] + id: playlistDialog + title: "Playlist" + height: Math.max(480, childrenRect.height * playlistListView.count) + width: 720 + modality: Qt.NonModal + property int thumbnailJobsRunning: 0 + property variant thumbnailJobs: [] + property int titleJobsRunning: 0 + property variant titleJobs: [] - function addThumbnailToCache(name, output) { - output = output.replace("maxresdefault", "sddefault").split('\n')[0] - thumbnailCache.addURL(name, output) - thumbnailJobs.shift() - thumbnailJobsRunning -= 1 - } + function addThumbnailToCache(name, output) { + output = output.replace("maxresdefault", "sddefault").split('\n')[0] + thumbnailCache.addURL(name, output) + thumbnailJobs.shift() + thumbnailJobsRunning -= 1 + } - ThumbnailCache { - id: thumbnailCache - } + ThumbnailCache { + id: thumbnailCache + } - Rectangle { - visible: false - id: titleGetter - signal titleFound(string name, string title) - } + Rectangle { + visible: false + id: titleGetter + signal titleFound(string name, string title) + } - Timer { - interval: 100 - repeat: true - triggeredOnStart: true - running: true - onTriggered: { - if (thumbnailJobsRunning < 2) { - if (thumbnailJobs.length > 0) { - if (thumbnailJobs[0].startsWith( - "https://www.youtube.com/playlist?list=")) { - thumbnailJobs.shift() - return - } - var component = Qt.createComponent("ThumbnailProcess.qml") - var thumbnailerProcess = component.createObject( - playlistDialog, { - "name": thumbnailJobs[0] - }) - if (String(titleJobs[0]).indexOf("://") !== -1) { + Timer { + interval: 100 + repeat: true + triggeredOnStart: true + running: true + onTriggered: { + if (thumbnailJobsRunning < 2) { + if (thumbnailJobs.length > 0) { + if (thumbnailJobs[0].startsWith( + "https://www.youtube.com/playlist?list=")) { + thumbnailJobs.shift() + return + } + var component = Qt.createComponent("ThumbnailProcess.qml") + var thumbnailerProcess = component.createObject(playlistDialog, { + "name": thumbnailJobs[0] + }) + if (String(titleJobs[0]).indexOf("://") !== -1) { - thumbnailerProcess.start( - "youtube-dl", - ["--get-thumbnail", thumbnailJobs[0]]) - } else { - thumbnailerProcess.start( - "ffmpegthumbnailer", - ["-i", thumbnailJobs[0], "-o", "/tmp/" + Qt.md5( - thumbnailJobs[0]) + ".png"]) - } + thumbnailerProcess.start("youtube-dl", + ["--get-thumbnail", thumbnailJobs[0]]) + } else { + thumbnailerProcess.start( + "ffmpegthumbnailer", + ["-i", thumbnailJobs[0], "-o", "/tmp/" + Qt.md5( + thumbnailJobs[0]) + ".png"]) + } - thumbnailJobsRunning += 1 - } - } + thumbnailJobsRunning += 1 } + } } + } - Timer { - interval: 100 - repeat: true - triggeredOnStart: true - running: true - onTriggered: { - if (titleJobsRunning < 5) { - if (titleJobs.length > 0) { - if (titleJobs[0].startsWith( - "https://www.youtube.com/playlist?list=")) { - titleJobs.shift() - return - } - var component = Qt.createComponent("TitleProcess.qml") - var titleProcess = component.createObject(playlistDialog, { - "name": titleJobs[0] - }) - titleProcess.start("youtube-dl", - ["--get-title", titleJobs[0]]) - titleJobs.shift() - titleJobsRunning += 1 - } - } + Timer { + interval: 100 + repeat: true + triggeredOnStart: true + running: true + onTriggered: { + if (titleJobsRunning < 5) { + if (titleJobs.length > 0) { + if (titleJobs[0].startsWith( + "https://www.youtube.com/playlist?list=")) { + titleJobs.shift() + return + } + var component = Qt.createComponent("TitleProcess.qml") + var titleProcess = component.createObject(playlistDialog, { + "name": titleJobs[0] + }) + titleProcess.start("youtube-dl", ["--get-title", titleJobs[0]]) + titleJobs.shift() + titleJobsRunning += 1 } + } } + } - Connections { - target: player - onPlaylistChanged: function (playlist) { - playlistModel.clear() - thumbnailJobs = [] - titleJobs = [] - titleJobsRunning = 0 - thumbnailJobsRunning = 0 - for (var thing in playlist) { - var item = playlist[thing] - playlistModel.append({ - "playlistItemTitle": item["title"], - "playlistItemFilename": item["filename"], - "current": item["current"], - "playlistPos": thing - }) - } - } + Connections { + target: player + onPlaylistChanged: function (playlist) { + playlistModel.clear() + thumbnailJobs = [] + titleJobs = [] + titleJobsRunning = 0 + thumbnailJobsRunning = 0 + for (var thing in playlist) { + var item = playlist[thing] + playlistModel.append({ + "playlistItemTitle": item["title"], + "playlistItemFilename": item["filename"], + "current": item["current"], + "playlistPos": thing + }) + } } + } - Component { - id: playlistDelegate - Item { - id: playlistItem - property string itemURL: "" - property string itemTitle: "" - width: playlistDialog.width - height: childrenRect.height - function getText(title, filename) { - var itemText = "" - if (title.length > 0) { - itemText += 'Title: ' + title + "
" - } - if (filename.length > 0) { - itemText += 'Filename: ' + filename - } - return itemText - } - Connections { - target: thumbnailCache - onThumbnailReady: function (name, url, path) { - if (name == playlistItem.itemURL) { - thumbnail.source = path - } - } - } - - Connections { - target: titleGetter - onTitleFound: function (name, title) { - if (name == playlistItem.itemURL) { - titleJobsRunning -= 1 - playlistItem.itemTitle = title - } - } - } - - Image { - id: thumbnail - source: "" - height: source.toString().length > 1 ? 144 : 0 - width: source.toString().length > 1 ? 256 : 0 - } - - Button { - width: parent.width - 20 - id: playlistItemButton - font.pixelSize: 12 - padding: 0 - anchors.left: thumbnail.right - bottomPadding: 0 - contentItem: Text { - id: playlistItemText - font: parent.font - color: "white" - text: playlistItem.getText(itemTitle, itemURL) - height: parent.height - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - wrapMode: Text.Wrap - } - - onClicked: { - player.playerCommand(Enums.Commands.SetPlaylistPos, - playlistPos) - } - background: Rectangle { - color: current ? "orange" : "transparent" - } - } - - Component.onCompleted: { - - if (typeof playlistItemTitle !== "undefined") { - playlistItem.itemTitle = playlistItemTitle - } else { - playlistDialog.titleJobs.push(playlistItemFilename) - } - if (typeof playlistItemFilename !== "undefined") { - playlistItem.itemURL = playlistItemFilename - } else { - playlistItem.itemURL = "" - } - playlistDialog.thumbnailJobs.push(playlistItemFilename) - } + Component { + id: playlistDelegate + Item { + id: playlistItem + property string itemURL: "" + property string itemTitle: "" + width: playlistDialog.width + height: childrenRect.height + function getText(title, filename) { + var itemText = "" + if (title.length > 0) { + itemText += 'Title: ' + title + "
" } + if (filename.length > 0) { + itemText += 'Filename: ' + filename + } + return itemText + } + Connections { + target: thumbnailCache + onThumbnailReady: function (name, url, path) { + if (name == playlistItem.itemURL) { + thumbnail.source = path + } + } + } + + Connections { + target: titleGetter + onTitleFound: function (name, title) { + if (name == playlistItem.itemURL) { + titleJobsRunning -= 1 + playlistItem.itemTitle = title + } + } + } + + Image { + id: thumbnail + source: "" + height: source.toString().length > 1 ? 144 : 0 + width: source.toString().length > 1 ? 256 : 0 + } + + Button { + width: parent.width - 20 + id: playlistItemButton + font.pixelSize: 12 + padding: 0 + anchors.left: thumbnail.right + bottomPadding: 0 + contentItem: Text { + id: playlistItemText + font: parent.font + color: "white" + text: playlistItem.getText(itemTitle, itemURL) + height: parent.height + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + wrapMode: Text.Wrap + } + + onClicked: { + player.playerCommand(Enums.Commands.SetPlaylistPos, playlistPos) + } + background: Rectangle { + color: current ? "orange" : "transparent" + } + } + + Component.onCompleted: { + + if (typeof playlistItemTitle !== "undefined") { + playlistItem.itemTitle = playlistItemTitle + } else { + playlistDialog.titleJobs.push(playlistItemFilename) + } + if (typeof playlistItemFilename !== "undefined") { + playlistItem.itemURL = playlistItemFilename + } else { + playlistItem.itemURL = "" + } + playlistDialog.thumbnailJobs.push(playlistItemFilename) + } } + } - ListView { - id: playlistListView - anchors.fill: parent - model: ListModel { - id: playlistModel - } - delegate: playlistDelegate - highlight: Item {} - snapMode: ListView.SnapToItem - flickableDirection: Flickable.VerticalFlick - boundsBehavior: Flickable.StopAtBounds - ScrollBar.vertical: ScrollBar { - active: playlistListView.count > 1 ? true : true - } - focus: true + ListView { + id: playlistListView + anchors.fill: parent + model: ListModel { + id: playlistModel } + delegate: playlistDelegate + highlight: Item {} + snapMode: ListView.SnapToItem + flickableDirection: Flickable.VerticalFlick + boundsBehavior: Flickable.StopAtBounds + ScrollBar.vertical: ScrollBar { + active: playlistListView.count > 1 ? true : true + } + focus: true + } } diff --git a/src/qml/Dialogs/SettingsDialog.qml b/src/qml/Dialogs/SettingsDialog.qml index e64b2d1..930de7a 100644 --- a/src/qml/Dialogs/SettingsDialog.qml +++ b/src/qml/Dialogs/SettingsDialog.qml @@ -6,154 +6,160 @@ import QtQuick.Window 2.2 import player 1.0 Dialog { - id: settingsDialog - title: translate.getTranslation("SETTINGS", i18n.language) - height: 100 - width: 720 - modality: Qt.NonModal + id: settingsDialog + title: translate.getTranslation("SETTINGS", i18n.language) + height: 100 + width: 720 + modality: Qt.NonModal - signal done; + signal done - ScrollView { - id: content - height: parent.height - width: parent.width - clip: true - ScrollBar.vertical.policy: ScrollBar.AlwaysOn + ScrollView { + id: content + height: parent.height + width: parent.width + clip: true + ScrollBar.vertical.policy: ScrollBar.AlwaysOn + Item { + id: settingsContent + implicitHeight: childrenRect.height + implicitWidth: childrenRect.width + ColumnLayout { + Text { + height: 30 + text: translate.getTranslation("LANGUAGE", i18n.language) + verticalAlignment: Text.AlignVCenter + } + LanguageSettings { + Layout.leftMargin: 30 + } + Text { + height: 30 + text: translate.getTranslation("APPEARANCE", i18n.language) + verticalAlignment: Text.AlignVCenter + } + CheckBox { + checked: appearance.titleOnlyOnFullscreen + onClicked: appearance.titleOnlyOnFullscreen = !appearance.titleOnlyOnFullscreen + text: translate.getTranslation("TITLE_ONLY_ON_FULLSCREEN", + i18n.language) + Layout.leftMargin: 30 + } + CheckBox { + checked: appearance.doubleTapToSeek + onClicked: appearance.doubleTapToSeek = !appearance.doubleTapToSeek + text: translate.getTranslation("DOUBLE_TAP_TO_SEEK", i18n.language) + Layout.leftMargin: 30 + } Item { - id: settingsContent - implicitHeight: childrenRect.height - implicitWidth: childrenRect.width - ColumnLayout { - Text { - height: 30 - text: translate.getTranslation("LANGUAGE", i18n.language) - verticalAlignment: Text.AlignVCenter - } - LanguageSettings { Layout.leftMargin: 30 } - Text { - height: 30 - text: translate.getTranslation("APPEARANCE", i18n.language) - verticalAlignment: Text.AlignVCenter - } - CheckBox { - checked: appearance.titleOnlyOnFullscreen - onClicked: appearance.titleOnlyOnFullscreen = !appearance.titleOnlyOnFullscreen - text: translate.getTranslation("TITLE_ONLY_ON_FULLSCREEN", i18n.language) - Layout.leftMargin: 30 - } - CheckBox { - checked: appearance.doubleTapToSeek - onClicked: appearance.doubleTapToSeek = !appearance.doubleTapToSeek - text: translate.getTranslation("DOUBLE_TAP_TO_SEEK", i18n.language) - Layout.leftMargin: 30 - } - Item { - Layout.leftMargin: 30 - Layout.bottomMargin: 10 - height: 30 - Text { - id: seekByLabel - height: 30 - text: translate.getTranslation("DOUBLE_TAP_TO_SEEK_BY", i18n.language) - verticalAlignment: Text.AlignVCenter - } - TextField { - id: seekBy - anchors.left: seekByLabel.right - anchors.leftMargin: 10 - validator: IntValidator {} - inputMethodHints: Qt.ImhFormattedNumbersOnly - text: appearance.doubleTapToSeekBy - function setSeekBy() { - appearance.doubleTapToSeekBy = parseInt(seekBy.text) - } - onEditingFinished: setSeekBy() - } - } - Item { - height: 30 - Layout.bottomMargin: 10 - Layout.leftMargin: 30 - Text { - id: fontLabel - height: 30 - text: translate.getTranslation("FONT", i18n.language) - verticalAlignment: Text.AlignVCenter - } - TextField { - id: fontInput - anchors.left: fontLabel.right - anchors.leftMargin: 10 - text: appearance.fontName - function setFont() { - appearance.fontName = fontInput.text - } - onEditingFinished: setFont() - } - } - Item { - Layout.leftMargin: 30 - Layout.bottomMargin: 10 - height: 30 - Text { - id: subtitlesFontSizeLabel - height: 30 - text: translate.getTranslation("SUBTITLES_FONT_SIZE", i18n.language) - verticalAlignment: Text.AlignVCenter - } - TextField { - id: subtitlesFontSizeInput - anchors.left: subtitlesFontSizeLabel.right - anchors.leftMargin: 10 - validator: IntValidator {} - inputMethodHints: Qt.ImhFormattedNumbersOnly - text: appearance.subtitlesFontSize - function setSubtitlesFontSize() { - appearance.subtitlesFontSize = parseInt(subtitlesFontSizeInput.text) - } - onEditingFinished: setSubtitlesFontSize() - } - } - Item { - Layout.leftMargin: 30 - Layout.bottomMargin: 10 - height: 30 - Text { - id: uiFadeTimeLabel - height: 30 - text: translate.getTranslation("UI_FADE_TIME", i18n.language) - verticalAlignment: Text.AlignVCenter - } - TextField { - id: uiFadeTimeInput - anchors.left: uiFadeTimeLabel.right - anchors.leftMargin: 10 - validator: IntValidator { bottom: 0 } - inputMethodHints: Qt.ImhFormattedNumbersOnly - text: appearance.uiFadeTimer - function setUIFadeTime() { - appearance.uiFadeTimer = parseInt(uiFadeTimeInput.text) - } - onEditingFinished: setUIFadeTime() - } - } + Layout.leftMargin: 30 + Layout.bottomMargin: 10 + height: 30 + Text { + id: seekByLabel + height: 30 + text: translate.getTranslation("DOUBLE_TAP_TO_SEEK_BY", + i18n.language) + verticalAlignment: Text.AlignVCenter + } + TextField { + id: seekBy + anchors.left: seekByLabel.right + anchors.leftMargin: 10 + validator: IntValidator {} + inputMethodHints: Qt.ImhFormattedNumbersOnly + text: appearance.doubleTapToSeekBy + function setSeekBy() { + appearance.doubleTapToSeekBy = parseInt(seekBy.text) } + onEditingFinished: setSeekBy() + } } - - } - - Connections { - target: settingsDialog - onAccepted: { - seekBy.setSeekBy() - fontInput.setFont() - subtitlesFontSizeInput.setSubtitlesFontSize() - uiFadeTimeInput.setUIFadeTime() - settingsDialog.done() + Item { + height: 30 + Layout.bottomMargin: 10 + Layout.leftMargin: 30 + Text { + id: fontLabel + height: 30 + text: translate.getTranslation("FONT", i18n.language) + verticalAlignment: Text.AlignVCenter + } + TextField { + id: fontInput + anchors.left: fontLabel.right + anchors.leftMargin: 10 + text: appearance.fontName + function setFont() { + appearance.fontName = fontInput.text + } + onEditingFinished: setFont() + } } + Item { + Layout.leftMargin: 30 + Layout.bottomMargin: 10 + height: 30 + Text { + id: subtitlesFontSizeLabel + height: 30 + text: translate.getTranslation("SUBTITLES_FONT_SIZE", i18n.language) + verticalAlignment: Text.AlignVCenter + } + TextField { + id: subtitlesFontSizeInput + anchors.left: subtitlesFontSizeLabel.right + anchors.leftMargin: 10 + validator: IntValidator {} + inputMethodHints: Qt.ImhFormattedNumbersOnly + text: appearance.subtitlesFontSize + function setSubtitlesFontSize() { + appearance.subtitlesFontSize = parseInt( + subtitlesFontSizeInput.text) + } + onEditingFinished: setSubtitlesFontSize() + } + } + Item { + Layout.leftMargin: 30 + Layout.bottomMargin: 10 + height: 30 + Text { + id: uiFadeTimeLabel + height: 30 + text: translate.getTranslation("UI_FADE_TIME", i18n.language) + verticalAlignment: Text.AlignVCenter + } + TextField { + id: uiFadeTimeInput + anchors.left: uiFadeTimeLabel.right + anchors.leftMargin: 10 + validator: IntValidator { + bottom: 0 + } + inputMethodHints: Qt.ImhFormattedNumbersOnly + text: appearance.uiFadeTimer + function setUIFadeTime() { + appearance.uiFadeTimer = parseInt(uiFadeTimeInput.text) + } + onEditingFinished: setUIFadeTime() + } + } + } } - Component.onCompleted: { - settingsDialog.open() + } + + Connections { + target: settingsDialog + onAccepted: { + seekBy.setSeekBy() + fontInput.setFont() + subtitlesFontSizeInput.setSubtitlesFontSize() + uiFadeTimeInput.setUIFadeTime() + settingsDialog.done() } + } + Component.onCompleted: { + settingsDialog.open() + } } diff --git a/src/qml/Dialogs/SettingsItems/LanguageSettings.qml b/src/qml/Dialogs/SettingsItems/LanguageSettings.qml index 5f8eb21..cf08957 100644 --- a/src/qml/Dialogs/SettingsItems/LanguageSettings.qml +++ b/src/qml/Dialogs/SettingsItems/LanguageSettings.qml @@ -3,28 +3,32 @@ import QtQuick.Controls 2.3 import "translations.js" as Translations ComboBox { - id: languageSelector - height: 30 - editable: false - pressed: true - model: Object.keys(Translations.languages).map(function(key) {return Translations.languages[key];}) - delegate: ItemDelegate { - height: 25 - width: languageSelector.width - contentItem: Text { - text: modelData - color: "#21be2b" - font: languageSelector.font - elide: Text.ElideRight - verticalAlignment: Text.AlignVCenter - } - highlighted: languageSelector.highlightedIndex === index - } - onActivated: { - console.warn(currentText) - i18n.language = Object.keys(Translations.languages).filter(function(key) {return Translations.languages[key] === currentText})[0]; - } - Component.onCompleted: { - currentIndex = languageSelector.find(Translations.languages[i18n.language]) + id: languageSelector + height: 30 + editable: false + pressed: true + model: Object.keys(Translations.languages).map(function (key) { + return Translations.languages[key] + }) + delegate: ItemDelegate { + height: 25 + width: languageSelector.width + contentItem: Text { + text: modelData + color: "#21be2b" + font: languageSelector.font + elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter } + highlighted: languageSelector.highlightedIndex === index + } + onActivated: { + console.warn(currentText) + i18n.language = Object.keys(Translations.languages).filter(function (key) { + return Translations.languages[key] === currentText + })[0] + } + Component.onCompleted: { + currentIndex = languageSelector.find(Translations.languages[i18n.language]) + } } diff --git a/src/qml/Items/AudioDeviceItem.qml b/src/qml/Items/AudioDeviceItem.qml index 450ccf1..5efdbe1 100644 --- a/src/qml/Items/AudioDeviceItem.qml +++ b/src/qml/Items/AudioDeviceItem.qml @@ -2,12 +2,12 @@ import QtQuick.Controls 2.3 import player 1.0 Action { - id: audioDeviceItem - property string deviceID: "none" - checkable: false - checked: false + id: audioDeviceItem + property string deviceID: "none" + checkable: false + checked: false - onTriggered: { - player.playerCommand(Enums.Commands.SetAudioDevice, deviceID) - } + onTriggered: { + player.playerCommand(Enums.Commands.SetAudioDevice, deviceID) + } } diff --git a/src/qml/Items/ChapterMarkerItem.qml b/src/qml/Items/ChapterMarkerItem.qml index 71670f1..74a0e60 100644 --- a/src/qml/Items/ChapterMarkerItem.qml +++ b/src/qml/Items/ChapterMarkerItem.qml @@ -1,24 +1,23 @@ import QtQuick 2.0 Rectangle { - id: chapterMarker - property int time: 0 - color: getAppearanceValueForTheme(appearance.themeName, - "chapterMarkerColor") - width: 4 - height: parent.height - x: progressBar.background.width / progressBar.to * time - z: 9000 - anchors { - top: parent.top - bottom: parent.bottom - } + id: chapterMarker + property int time: 0 + color: getAppearanceValueForTheme(appearance.themeName, "chapterMarkerColor") + width: 4 + height: parent.height + x: progressBar.background.width / progressBar.to * time + z: 9000 + anchors { + top: parent.top + bottom: parent.bottom + } - Connections { - target: player - enabled: true - onChaptersChanged: { - chapterMarker.destroy() - } + Connections { + target: player + enabled: true + onChaptersChanged: { + chapterMarker.destroy() } + } } diff --git a/src/qml/Items/CustomMenuItem.qml b/src/qml/Items/CustomMenuItem.qml index 588049e..e0d638d 100644 --- a/src/qml/Items/CustomMenuItem.qml +++ b/src/qml/Items/CustomMenuItem.qml @@ -2,25 +2,25 @@ import QtQuick 2.0 import QtQuick.Controls 2.3 MenuItem { - id: menuItem - implicitHeight: 20 + id: menuItem + implicitHeight: 20 - contentItem: Text { - text: menuItem.text - opacity: 1 - color: menuItem.highlighted ? "#5a50da" : "white" - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - font { - family: appearance.fontName - bold: menuItem.highlighted - } + contentItem: Text { + text: menuItem.text + opacity: 1 + color: menuItem.highlighted ? "#5a50da" : "white" + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + font { + family: appearance.fontName + bold: menuItem.highlighted } + } - background: Rectangle { - anchors.fill: parent - opacity: 1 - color: menuItem.highlighted ? "#c0c0f0" : "transparent" - } + background: Rectangle { + anchors.fill: parent + opacity: 1 + color: menuItem.highlighted ? "#c0c0f0" : "transparent" + } } diff --git a/src/qml/Items/ThumbnailProcess.qml b/src/qml/Items/ThumbnailProcess.qml index 8d1ad2a..e57cd13 100644 --- a/src/qml/Items/ThumbnailProcess.qml +++ b/src/qml/Items/ThumbnailProcess.qml @@ -1,14 +1,13 @@ import player 1.0 Process { - id: thumbnailerProcess - property string name: "" - onFinished: function () { - if (String(name).indexOf("://") !== -1) { - playlistDialog.addThumbnailToCache(name, getOutput()) - } else { - playlistDialog.addThumbnailToCache(name, - "/tmp/" + Qt.md5(name) + ".png") - } + id: thumbnailerProcess + property string name: "" + onFinished: function () { + if (String(name).indexOf("://") !== -1) { + playlistDialog.addThumbnailToCache(name, getOutput()) + } else { + playlistDialog.addThumbnailToCache(name, "/tmp/" + Qt.md5(name) + ".png") } + } } diff --git a/src/qml/Items/TitleProcess.qml b/src/qml/Items/TitleProcess.qml index 47ef97d..5a014c4 100644 --- a/src/qml/Items/TitleProcess.qml +++ b/src/qml/Items/TitleProcess.qml @@ -1,9 +1,9 @@ import player 1.0 Process { - id: titleProcess - property string name: "" - onReadyRead: function () { - titleGetter.titleFound(name, getOutput()) - } + id: titleProcess + property string name: "" + onReadyRead: function () { + titleGetter.titleFound(name, getOutput()) + } } diff --git a/src/qml/Items/TrackItem.qml b/src/qml/Items/TrackItem.qml index 76df324..0314ce1 100644 --- a/src/qml/Items/TrackItem.qml +++ b/src/qml/Items/TrackItem.qml @@ -2,13 +2,13 @@ import QtQuick.Controls 2.3 import player 1.0 Action { - id: trackItem - property string trackType: "none" - property string trackID: "none" - checkable: true - checked: false + id: trackItem + property string trackType: "none" + property string trackID: "none" + checkable: true + checked: false - onTriggered: { - player.playerCommand(Enums.Commands.SetTrack, [trackType, trackID]) - } + onTriggered: { + player.playerCommand(Enums.Commands.SetTrack, [trackType, trackID]) + } } diff --git a/src/qml/MainMenu.qml b/src/qml/MainMenu.qml index cc0a372..fb11d2f 100644 --- a/src/qml/MainMenu.qml +++ b/src/qml/MainMenu.qml @@ -7,596 +7,586 @@ import player 1.0 import "codes.js" as LanguageCodes MenuBar { - id: menuBar - //width: parent.width - height: mainWindow.virtualHeight / 32 - function anythingOpen() { - for (var i = 0, len = menuBar.count; i < len; i++) { - if (menuBar.menuAt(i).opened) { - return true - } + id: menuBar + //width: parent.width + height: mainWindow.virtualHeight / 32 + function anythingOpen() { + for (var i = 0, len = menuBar.count; i < len; i++) { + if (menuBar.menuAt(i).opened) { + return true + } + } + } + + Connections { + target: player + onTracksChanged: function (tracks) { + menuBar.updateTracks(tracks) + } + } + + function updateTracks(tracks) { + var newTracks = tracks + var trackMenus = [audioMenu, videoMenu, subMenu] + for (var a = 0; a < trackMenus.length; a++) { + var menu = trackMenus[a] + for (var i = 0, len = menu.count; i < len; i++) { + var action = menu.actionAt(i) + if (action) { + if (action.trackID != "no") { + menu.removeAction(action) + } } + } } - Connections { - target: player - onTracksChanged: function (tracks) { - menuBar.updateTracks(tracks) - } + for (var i = 0, len = newTracks.length; i < len; i++) { + var track = newTracks[i] + var trackID = track["id"] + var trackType = track["type"] + var trackLang = LanguageCodes.localeCodeToEnglish(String(track["lang"])) + trackLang = trackLang == undefined ? "" : trackLang + var trackTitle = track["title"] == undefined ? "" : track["title"] + " " + var component = Qt.createComponent("TrackItem.qml") + if (trackType == "sub") { + var action = component.createObject(subMenu, { + "text": trackLang, + "trackID": String(trackID), + "trackType": "sid", + "checked": track["selected"] + }) + action.ActionGroup.group = subMenuGroup + subMenu.addAction(action) + } else if (trackType == "audio") { + var action = component.createObject(audioMenu, { + "text": trackTitle + trackLang, + "trackID": String(trackID), + "trackType": "aid", + "checked": track["selected"] + }) + action.ActionGroup.group = audioMenuGroup + audioMenu.addAction(action) + } else if (trackType == "video") { + var action = component.createObject(videoMenu, { + "text": "Video " + trackID + trackTitle, + "trackID": String(trackID), + "trackType": "vid", + "checked": track["selected"] + }) + action.ActionGroup.group = videoMenuGroup + videoMenu.addAction(action) + } } + } - function updateTracks(tracks) { - var newTracks = tracks - var trackMenus = [audioMenu, videoMenu, subMenu] - for (var a = 0; a < trackMenus.length; a++) { - var menu = trackMenus[a] - for (var i = 0, len = menu.count; i < len; i++) { - var action = menu.actionAt(i) - if (action) { - if (action.trackID != "no") { - menu.removeAction(action) - } - } - } - } - - for (var i = 0, len = newTracks.length; i < len; i++) { - var track = newTracks[i] - var trackID = track["id"] - var trackType = track["type"] - var trackLang = LanguageCodes.localeCodeToEnglish( - String(track["lang"])) - trackLang = trackLang == undefined ? "" : trackLang - var trackTitle = track["title"] == undefined ? "" : track["title"] + " " - var component = Qt.createComponent("TrackItem.qml") - if (trackType == "sub") { - var action = component.createObject(subMenu, { - "text": trackLang, - "trackID": String( - trackID), - "trackType": "sid", - "checked": track["selected"] - }) - action.ActionGroup.group = subMenuGroup - subMenu.addAction(action) - } else if (trackType == "audio") { - var action = component.createObject(audioMenu, { - "text": trackTitle + trackLang, - "trackID": String( - trackID), - "trackType": "aid", - "checked": track["selected"] - }) - action.ActionGroup.group = audioMenuGroup - audioMenu.addAction(action) - } else if (trackType == "video") { - var action = component.createObject(videoMenu, { - "text": "Video " + trackID + trackTitle, - "trackID": String( - trackID), - "trackType": "vid", - "checked": track["selected"] - }) - action.ActionGroup.group = videoMenuGroup - videoMenu.addAction(action) - } - } + FileDialog { + id: fileDialog + title: translate.getTranslation("OPEN_FILE", i18n.language) + nameFilters: ["All files (*)"] + selectMultiple: false + onAccepted: { + player.playerCommand(Enums.Commands.LoadFile, String(fileDialog.fileUrl)) + fileDialog.close() } - - FileDialog { - id: fileDialog - title: translate.getTranslation("OPEN_FILE", i18n.language) - nameFilters: ["All files (*)"] - selectMultiple: false - onAccepted: { - player.playerCommand(Enums.Commands.LoadFile, - String(fileDialog.fileUrl)) - fileDialog.close() - } - onRejected: { - fileDialog.close() - } + onRejected: { + fileDialog.close() } + } - Dialog { - id: loadDialog - title: translate.getTranslation("URL_FILE_PATH", i18n.language) - standardButtons: StandardButton.Cancel | StandardButton.Open - onAccepted: { - player.playerCommand(Enums.Commands.LoadFile, pathText.text) - pathText.text = "" - } - TextField { - id: pathText - placeholderText: translate.getTranslation("URL_FILE_PATH", - i18n.language) - } + Dialog { + id: loadDialog + title: translate.getTranslation("URL_FILE_PATH", i18n.language) + standardButtons: StandardButton.Cancel | StandardButton.Open + onAccepted: { + player.playerCommand(Enums.Commands.LoadFile, pathText.text) + pathText.text = "" } - - PlaylistDialog { - id: playlistDialog + TextField { + id: pathText + placeholderText: translate.getTranslation("URL_FILE_PATH", i18n.language) } + } - Loader { - id: settingsDialogLoader - active: false - source: "SettingsDialog.qml" - } - Connections { - target: settingsDialogLoader.item - onDone: { - settingsDialogLoader.active = false - } + PlaylistDialog { + id: playlistDialog + } + + Loader { + id: settingsDialogLoader + active: false + source: "SettingsDialog.qml" + } + Connections { + target: settingsDialogLoader.item + onDone: { + settingsDialogLoader.active = false } + } - delegate: MenuBarItem { - id: menuBarItem - padding: 4 - topPadding: padding - leftPadding: padding - rightPadding: padding - bottomPadding: padding + delegate: MenuBarItem { + id: menuBarItem + padding: 4 + topPadding: padding + leftPadding: padding + rightPadding: padding + bottomPadding: padding - contentItem: Text { - id: menuBarItemText - text: menuBarItem.text - font { - family: appearance.fontName - pixelSize: menuBar.height / 2 - bold: menuBarItem.highlighted - } - opacity: 1 - color: menuBarItem.highlighted ? "#5a50da" : "white" - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - renderType: Text.NativeRendering - } - - background: Rectangle { - implicitWidth: 10 - implicitHeight: 10 - opacity: 1 - color: menuBarItem.highlighted ? "#c0c0f0" : "transparent" - } + contentItem: Text { + id: menuBarItemText + text: menuBarItem.text + font { + family: appearance.fontName + pixelSize: menuBar.height / 2 + bold: menuBarItem.highlighted + } + opacity: 1 + color: menuBarItem.highlighted ? "#5a50da" : "white" + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + renderType: Text.NativeRendering } background: Rectangle { - width: parent.width - implicitHeight: 10 - color: getAppearanceValueForTheme(appearance.themeName, - "mainBackground") + implicitWidth: 10 + implicitHeight: 10 + opacity: 1 + color: menuBarItem.highlighted ? "#c0c0f0" : "transparent" } + } + + background: Rectangle { + width: parent.width + implicitHeight: 10 + color: getAppearanceValueForTheme(appearance.themeName, "mainBackground") + } + + CustomMenu { + id: fileMenuBarItem + title: translate.getTranslation("FILE_MENU", i18n.language) + font.family: appearance.fontName + + Action { + text: translate.getTranslation("OPEN_FILE", i18n.language) + onTriggered: fileDialog.open() + shortcut: keybinds.openFile + } + Action { + text: translate.getTranslation("OPEN_URL", i18n.language) + onTriggered: loadDialog.open() + shortcut: keybinds.openURI + } + Action { + text: translate.getTranslation("UPDATE_APPIMAGE", i18n.language) + onTriggered: utils.updateAppImage() + } + Action { + text: translate.getTranslation("EXIT", i18n.language) + onTriggered: Qt.quit() + shortcut: keybinds.quit + } + } + + CustomMenu { + id: playbackMenuBarItem + title: translate.getTranslation("PLAYBACK", i18n.language) + Action { + text: translate.getTranslation("PLAY_PAUSE", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.TogglePlayPause) + } + shortcut: String(keybinds.playPause) + } + Action { + text: translate.getTranslation("REWIND_10S", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.Seek, "-10") + } + shortcut: keybinds.rewind10 + } + Action { + text: translate.getTranslation("FORWARD_10S", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.Seek, "10") + } + shortcut: keybinds.forward10 + } + Action { + text: translate.getTranslation("FORWARD_5S", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.Seek, "-5") + } + shortcut: keybinds.rewind5 + } + Action { + text: translate.getTranslation("FORWARD_5S", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.Seek, "5") + } + shortcut: keybinds.forward5 + } + Action { + text: translate.getTranslation("SPEED_DECREASE_POINT_ONE", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.SubtractSpeed, 0.1) + } + shortcut: keybinds.decreaseSpeedByPointOne + } + Action { + text: translate.getTranslation("SPEED_INCREASE_POINT_ONE", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.AddSpeed, 0.1) + } + shortcut: keybinds.increaseSpeedByPointOne + } + Action { + text: translate.getTranslation("HALVE_SPEED", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.ChangeSpeed, 0.5) + } + shortcut: keybinds.halveSpeed + } + Action { + text: translate.getTranslation("DOUBLE_SPEED", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.ChangeSpeed, 2) + } + shortcut: keybinds.doubleSpeed + } + Action { + text: translate.getTranslation("FORWARD_FRAME", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.ForwardFrame) + } + shortcut: keybinds.forwardFrame + } + Action { + text: translate.getTranslation("BACKWARD_FRAME", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.BackwardFrame) + } + shortcut: keybinds.backwardFrame + } + } + + CustomMenu { + id: audioMenuBarItem + title: translate.getTranslation("AUDIO", i18n.language) + Action { + text: translate.getTranslation("CYCLE_AUDIO_TRACK", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.NextAudioTrack) + } + shortcut: keybinds.cycleAudio + } + Action { + text: translate.getTranslation("INCREASE_VOLUME", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.AddVolume, "2") + } + shortcut: keybinds.increaseVolume + } + Action { + text: translate.getTranslation("DECREASE_VOLUME", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.AddVolume, "-2") + } + shortcut: keybinds.decreaseVolume + } + Action { + text: translate.getTranslation("MUTE_VOLUME", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.ToggleMute) + } + shortcut: keybinds.mute + } + + MenuSeparator {} CustomMenu { - id: fileMenuBarItem - title: translate.getTranslation("FILE_MENU", i18n.language) - font.family: appearance.fontName + title: translate.getTranslation("AUDIO_DEVICES", i18n.language) + id: audioDeviceMenu + objectName: "audioDeviceMenu" - Action { - text: translate.getTranslation("OPEN_FILE", i18n.language) - onTriggered: fileDialog.open() - shortcut: keybinds.openFile + Connections { + target: player + onAudioDevicesChanged: function (ad) { + audioDeviceMenu.updateAudioDevices(ad) } - Action { - text: translate.getTranslation("OPEN_URL", i18n.language) - onTriggered: loadDialog.open() - shortcut: keybinds.openURI + } + function updateAudioDevices(audioDevices) { + for (var i = 0, len = audioDeviceMenu.count; i < len; i++) { + audioDeviceMenu.takeAction(0) } - Action { - text: translate.getTranslation("UPDATE_APPIMAGE", i18n.language) - onTriggered: utils.updateAppImage() - } - Action { - text: translate.getTranslation("EXIT", i18n.language) - onTriggered: Qt.quit() - shortcut: keybinds.quit + for (var thing in audioDevices) { + var audioDevice = audioDevices[thing] + var component = Qt.createComponent("AudioDeviceItem.qml") + var action = component.createObject(audioDeviceMenu, { + "text": audioDevices[thing]["description"], + "deviceID": String( + audioDevices[thing]["name"]) + }) + action.ActionGroup.group = audioDeviceMenuGroup + audioDeviceMenu.addAction(action) } + } + + ActionGroup { + id: audioDeviceMenuGroup + } } + MenuSeparator {} + CustomMenu { - id: playbackMenuBarItem - title: translate.getTranslation("PLAYBACK", i18n.language) - Action { - text: translate.getTranslation("PLAY_PAUSE", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.TogglePlayPause) - } - shortcut: String(keybinds.playPause) - } - Action { - text: translate.getTranslation("REWIND_10S", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.Seek, "-10") - } - shortcut: keybinds.rewind10 - } - Action { - text: translate.getTranslation("FORWARD_10S", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.Seek, "10") - } - shortcut: keybinds.forward10 - } - Action { - text: translate.getTranslation("FORWARD_5S", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.Seek, "-5") - } - shortcut: keybinds.rewind5 - } - Action { - text: translate.getTranslation("FORWARD_5S", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.Seek, "5") - } - shortcut: keybinds.forward5 - } - Action { - text: translate.getTranslation("SPEED_DECREASE_POINT_ONE", - i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.SubtractSpeed, 0.1) - } - shortcut: keybinds.decreaseSpeedByPointOne - } - Action { - text: translate.getTranslation("SPEED_INCREASE_POINT_ONE", - i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.AddSpeed, 0.1) - } - shortcut: keybinds.increaseSpeedByPointOne - } - Action { - text: translate.getTranslation("HALVE_SPEED", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.ChangeSpeed, 0.5) - } - shortcut: keybinds.halveSpeed - } - Action { - text: translate.getTranslation("DOUBLE_SPEED", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.ChangeSpeed, 2) - } - shortcut: keybinds.doubleSpeed - } - Action { - text: translate.getTranslation("FORWARD_FRAME", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.ForwardFrame) - } - shortcut: keybinds.forwardFrame - } - Action { - text: translate.getTranslation("BACKWARD_FRAME", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.BackwardFrame) - } - shortcut: keybinds.backwardFrame - } + title: translate.getTranslation("AUDIO", i18n.language) + id: audioMenu + ActionGroup { + id: audioMenuGroup + } + TrackItem { + text: translate.getTranslation("DISABLE_TRACK", i18n.language) + trackType: "aid" + trackID: "no" + ActionGroup.group: audioMenuGroup + } } + } + + CustomMenu { + id: videoMenuBarItem + title: translate.getTranslation("VIDEO", i18n.language) + Action { + text: translate.getTranslation("CYCLE_VIDEO", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.NextVideoTrack) + } + shortcut: keybinds.cycleVideo + } + MenuSeparator {} CustomMenu { - id: audioMenuBarItem - title: translate.getTranslation("AUDIO", i18n.language) - Action { - text: translate.getTranslation("CYCLE_AUDIO_TRACK", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.NextAudioTrack) - } - shortcut: keybinds.cycleAudio - } - Action { - text: translate.getTranslation("INCREASE_VOLUME", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.AddVolume, "2") - } - shortcut: keybinds.increaseVolume - } - Action { - text: translate.getTranslation("DECREASE_VOLUME", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.AddVolume, "-2") - } - shortcut: keybinds.decreaseVolume - } - Action { - text: translate.getTranslation("MUTE_VOLUME", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.ToggleMute) - } - shortcut: keybinds.mute - } - - MenuSeparator {} - - CustomMenu { - title: translate.getTranslation("AUDIO_DEVICES", i18n.language) - id: audioDeviceMenu - objectName: "audioDeviceMenu" - - Connections { - target: player - onAudioDevicesChanged: function (ad) { - audioDeviceMenu.updateAudioDevices(ad) - } - } - function updateAudioDevices(audioDevices) { - for (var i = 0, len = audioDeviceMenu.count; i < len; i++) { - audioDeviceMenu.takeAction(0) - } - for (var thing in audioDevices) { - var audioDevice = audioDevices[thing] - var component = Qt.createComponent("AudioDeviceItem.qml") - var action = component.createObject(audioDeviceMenu, { - "text": audioDevices[thing]["description"], - "deviceID": String( - audioDevices[thing]["name"]) - }) - action.ActionGroup.group = audioDeviceMenuGroup - audioDeviceMenu.addAction(action) - } - } - - ActionGroup { - id: audioDeviceMenuGroup - } - } - - MenuSeparator {} - - CustomMenu { - title: translate.getTranslation("AUDIO", i18n.language) - id: audioMenu - ActionGroup { - id: audioMenuGroup - } - TrackItem { - text: translate.getTranslation("DISABLE_TRACK", i18n.language) - trackType: "aid" - trackID: "no" - ActionGroup.group: audioMenuGroup - } - } + title: translate.getTranslation("VIDEO", i18n.language) + id: videoMenu + ActionGroup { + id: videoMenuGroup + } + TrackItem { + text: translate.getTranslation("DISABLE_TRACK", i18n.language) + trackType: "vid" + trackID: "no" + ActionGroup.group: videoMenuGroup + } } + } + CustomMenu { + id: subsMenuBarItem + title: translate.getTranslation("SUBTITLES", i18n.language) + Action { + text: translate.getTranslation("CYCLE_SUB_TRACK", i18n.language) + onTriggered: { + player.playerCommand(Enums.Commands.NextSubtitleTrack) + } + shortcut: keybinds.cycleSub + } + Action { + text: translate.getTranslation("TOGGLE_MPV_SUBS", i18n.language) + onTriggered: { + appearance.useMpvSubs = !appearance.useMpvSubs + } + shortcut: keybinds.cycleSubBackwards + } + MenuSeparator {} CustomMenu { - id: videoMenuBarItem - title: translate.getTranslation("VIDEO", i18n.language) - Action { - text: translate.getTranslation("CYCLE_VIDEO", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.NextVideoTrack) - } - shortcut: keybinds.cycleVideo - } - MenuSeparator {} - - CustomMenu { - title: translate.getTranslation("VIDEO", i18n.language) - id: videoMenu - ActionGroup { - id: videoMenuGroup - } - TrackItem { - text: translate.getTranslation("DISABLE_TRACK", i18n.language) - trackType: "vid" - trackID: "no" - ActionGroup.group: videoMenuGroup - } - } + title: translate.getTranslation("SUBTITLES", i18n.language) + id: subMenu + ActionGroup { + id: subMenuGroup + } + TrackItem { + text: translate.getTranslation("DISABLE_TRACK", i18n.language) + trackType: "sid" + trackID: "no" + ActionGroup.group: subMenuGroup + } } - CustomMenu { - id: subsMenuBarItem - title: translate.getTranslation("SUBTITLES", i18n.language) - Action { - text: translate.getTranslation("CYCLE_SUB_TRACK", i18n.language) - onTriggered: { - player.playerCommand(Enums.Commands.NextSubtitleTrack) - } - shortcut: keybinds.cycleSub - } - Action { - text: translate.getTranslation("TOGGLE_MPV_SUBS", i18n.language) - onTriggered: { - appearance.useMpvSubs = !appearance.useMpvSubs - } - shortcut: keybinds.cycleSubBackwards - } - MenuSeparator {} + } - CustomMenu { - title: translate.getTranslation("SUBTITLES", i18n.language) - id: subMenu - ActionGroup { - id: subMenuGroup - } - TrackItem { - text: translate.getTranslation("DISABLE_TRACK", i18n.language) - trackType: "sid" - trackID: "no" - ActionGroup.group: subMenuGroup - } - } - } + CustomMenu { + id: viewMenuBarItem + title: translate.getTranslation("VIEW", i18n.language) CustomMenu { - id: viewMenuBarItem - title: translate.getTranslation("VIEW", i18n.language) - - CustomMenu { - title: translate.getTranslation("THEME", i18n.language) - id: themeMenu - Action { - text: "YouTube" - onTriggered: appearance.themeName = text - checkable: true - checked: appearance.themeName == text - } - Action { - text: "Niconico" - onTriggered: appearance.themeName = text - checkable: true - checked: appearance.themeName == text - } - Action { - text: "RoosterTeeth" - onTriggered: appearance.themeName = text - checkable: true - checked: appearance.themeName == text - } - } - - Action { - text: translate.getTranslation("FULLSCREEN", i18n.language) - onTriggered: { - toggleFullscreen() - } - shortcut: keybinds.fullscreen - } - Action { - text: translate.getTranslation("STATS", i18n.language) - onTriggered: { - statsForNerdsText.visible = !statsForNerdsText.visible - } - shortcut: keybinds.statsForNerds - } - - Action { - text: translate.getTranslation("TOGGLE_NYAN_CAT", i18n.language) - onTriggered: { - fun.nyanCat = !fun.nyanCat - } - shortcut: keybinds.nyanCat - } - Action { - text: translate.getTranslation("TOGGLE_ALWAYS_ON_TOP", - i18n.language) - onTriggered: { - player.toggleOnTop() - } - } - Action { - text: translate.getTranslation("PLAYLIST_MENU", i18n.language) - onTriggered: { - playlistDialog.open() - } - } - Action { - text: translate.getTranslation("SETTINGS", i18n.language) - onTriggered: { - settingsDialogLoader.active = true - } - } - Action { - // Pretty sure for legal reasons this is needed unless I buy a Qt License - text: translate.getTranslation("ABOUT_QT", i18n.language) - onTriggered: { - utils.launchAboutQt() - } - } - } - - Item { - id: skipToNinthDuration - property var duration: 0 - Connections { - target: player - onDurationChanged: function (duration) { - skipToNinthDuration.duration = duration - } - } - } - - function skipToNinth(val) { - var skipto = 0 - if (val != 0) { - skipto = Math.floor(skipToNinthDuration.duration / 9 * val) - } - player.playerCommand(Enums.Commands.SeekAbsolute, skipto) + title: translate.getTranslation("THEME", i18n.language) + id: themeMenu + Action { + text: "YouTube" + onTriggered: appearance.themeName = text + checkable: true + checked: appearance.themeName == text + } + Action { + text: "Niconico" + onTriggered: appearance.themeName = text + checkable: true + checked: appearance.themeName == text + } + Action { + text: "RoosterTeeth" + onTriggered: appearance.themeName = text + checkable: true + checked: appearance.themeName == text + } } Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "1" + text: translate.getTranslation("FULLSCREEN", i18n.language) + onTriggered: { + toggleFullscreen() + } + shortcut: keybinds.fullscreen } Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "2" - } - Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "3" - } - Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "4" - } - Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "5" - } - Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "6" - } - Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "7" - } - Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "8" - } - Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "9" - } - Action { - onTriggered: skipToNinth(parseInt(shortcut)) - shortcut: "0" + text: translate.getTranslation("STATS", i18n.language) + onTriggered: { + statsForNerdsText.visible = !statsForNerdsText.visible + } + shortcut: keybinds.statsForNerds } Action { - onTriggered: player.command(keybinds.customKeybind0Command) - shortcut: keybinds.customKeybind0 + text: translate.getTranslation("TOGGLE_NYAN_CAT", i18n.language) + onTriggered: { + fun.nyanCat = !fun.nyanCat + } + shortcut: keybinds.nyanCat } Action { - onTriggered: player.command(keybinds.customKeybind1Command) - shortcut: keybinds.customKeybind1 + text: translate.getTranslation("TOGGLE_ALWAYS_ON_TOP", i18n.language) + onTriggered: { + player.toggleOnTop() + } } Action { - onTriggered: player.command(keybinds.customKeybind2Command) - shortcut: keybinds.customKeybind2 + text: translate.getTranslation("PLAYLIST_MENU", i18n.language) + onTriggered: { + playlistDialog.open() + } } Action { - onTriggered: player.command(keybinds.customKeybind3Command) - shortcut: keybinds.customKeybind3 + text: translate.getTranslation("SETTINGS", i18n.language) + onTriggered: { + settingsDialogLoader.active = true + } } Action { - onTriggered: player.command(keybinds.customKeybind4Command) - shortcut: keybinds.customKeybind4 + // Pretty sure for legal reasons this is needed unless I buy a Qt License + text: translate.getTranslation("ABOUT_QT", i18n.language) + onTriggered: { + utils.launchAboutQt() + } } - Action { - onTriggered: player.command(keybinds.customKeybind5Command) - shortcut: keybinds.customKeybind5 + } + + Item { + id: skipToNinthDuration + property var duration: 0 + Connections { + target: player + onDurationChanged: function (duration) { + skipToNinthDuration.duration = duration + } } - Action { - onTriggered: player.command(keybinds.customKeybind6Command) - shortcut: keybinds.customKeybind6 - } - Action { - onTriggered: player.command(keybinds.customKeybind7Command) - shortcut: keybinds.customKeybind7 - } - Action { - onTriggered: player.command(keybinds.customKeybind8Command) - shortcut: keybinds.customKeybind8 - } - Action { - onTriggered: player.command(keybinds.customKeybind9Command) - shortcut: keybinds.customKeybind9 + } + + function skipToNinth(val) { + var skipto = 0 + if (val != 0) { + skipto = Math.floor(skipToNinthDuration.duration / 9 * val) } + player.playerCommand(Enums.Commands.SeekAbsolute, skipto) + } + + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "1" + } + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "2" + } + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "3" + } + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "4" + } + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "5" + } + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "6" + } + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "7" + } + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "8" + } + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "9" + } + Action { + onTriggered: skipToNinth(parseInt(shortcut)) + shortcut: "0" + } + + Action { + onTriggered: player.command(keybinds.customKeybind0Command) + shortcut: keybinds.customKeybind0 + } + Action { + onTriggered: player.command(keybinds.customKeybind1Command) + shortcut: keybinds.customKeybind1 + } + Action { + onTriggered: player.command(keybinds.customKeybind2Command) + shortcut: keybinds.customKeybind2 + } + Action { + onTriggered: player.command(keybinds.customKeybind3Command) + shortcut: keybinds.customKeybind3 + } + Action { + onTriggered: player.command(keybinds.customKeybind4Command) + shortcut: keybinds.customKeybind4 + } + Action { + onTriggered: player.command(keybinds.customKeybind5Command) + shortcut: keybinds.customKeybind5 + } + Action { + onTriggered: player.command(keybinds.customKeybind6Command) + shortcut: keybinds.customKeybind6 + } + Action { + onTriggered: player.command(keybinds.customKeybind7Command) + shortcut: keybinds.customKeybind7 + } + Action { + onTriggered: player.command(keybinds.customKeybind8Command) + shortcut: keybinds.customKeybind8 + } + Action { + onTriggered: player.command(keybinds.customKeybind9Command) + shortcut: keybinds.customKeybind9 + } } diff --git a/src/qml/NiconicoButtonLayout.qml b/src/qml/NiconicoButtonLayout.qml index 0f8765d..338db53 100644 --- a/src/qml/NiconicoButtonLayout.qml +++ b/src/qml/NiconicoButtonLayout.qml @@ -2,89 +2,89 @@ import QtQuick 2.0 import player 1.0 Item { - objectName: "buttonLayout" - id: layout - anchors.fill: controlsBar + objectName: "buttonLayout" + id: layout + anchors.fill: controlsBar - PlayPauseButton { - id: playPauseButton - anchors { - left: parent.left - top: parent.top - bottom: parent.bottom - } + PlayPauseButton { + id: playPauseButton + anchors { + left: parent.left + top: parent.top + bottom: parent.bottom } - VolumeButton { - id: volumeButton - anchors { - left: playPauseButton.right - top: parent.top - bottom: parent.bottom - } + } + VolumeButton { + id: volumeButton + anchors { + left: playPauseButton.right + top: parent.top + bottom: parent.bottom } - VolumeSlider { - anchors { - left: volumeButton.right - top: parent.top - bottom: parent.bottom - } + } + VolumeSlider { + anchors { + left: volumeButton.right + top: parent.top + bottom: parent.bottom } + } - PlaylistPrevButton { - id: playlistPrevButton - anchors { - right: backwardButton.left - top: parent.top - bottom: parent.bottom - } + PlaylistPrevButton { + id: playlistPrevButton + anchors { + right: backwardButton.left + top: parent.top + bottom: parent.bottom } - BackwardButton { - id: backwardButton - anchors { - right: timeLabel.left - top: parent.top - bottom: parent.bottom - } + } + BackwardButton { + id: backwardButton + anchors { + right: timeLabel.left + top: parent.top + bottom: parent.bottom } - TimeLabel { - id: timeLabel - anchors { - centerIn: parent - top: parent.top - bottom: parent.bottom - } + } + TimeLabel { + id: timeLabel + anchors { + centerIn: parent + top: parent.top + bottom: parent.bottom } - ForwardButton { - id: forwardButton - anchors { - left: timeLabel.right - top: parent.top - bottom: parent.bottom - } + } + ForwardButton { + id: forwardButton + anchors { + left: timeLabel.right + top: parent.top + bottom: parent.bottom } - PlaylistNextButton { - id: playlistNextButton - anchors { - left: forwardButton.right - top: parent.top - bottom: parent.bottom - } + } + PlaylistNextButton { + id: playlistNextButton + anchors { + left: forwardButton.right + top: parent.top + bottom: parent.bottom } + } - FullscreenButton { - id: fullscreenButton - anchors { - right: settingsButton.left - top: parent.top - bottom: parent.bottom - } + FullscreenButton { + id: fullscreenButton + anchors { + right: settingsButton.left + top: parent.top + bottom: parent.bottom } - SettingsButton { - id: settingsButton - anchors { - right: parent.right - top: parent.top - bottom: parent.bottom - } + } + SettingsButton { + id: settingsButton + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom } + } } diff --git a/src/qml/RoosterTeethButtonLayout.qml b/src/qml/RoosterTeethButtonLayout.qml index 475268a..2e7b8ae 100644 --- a/src/qml/RoosterTeethButtonLayout.qml +++ b/src/qml/RoosterTeethButtonLayout.qml @@ -2,110 +2,110 @@ import QtQuick 2.0 import player 1.0 Item { - objectName: "buttonLayout" - id: layout - anchors.fill: controlsBar + objectName: "buttonLayout" + id: layout + anchors.fill: controlsBar - PlayPauseButton { - id: playPauseButton - anchors { - top: parent.top - bottom: parent.bottom - left: parent.left - } + PlayPauseButton { + id: playPauseButton + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left } + } - MouseArea { - id: mouseAreaVolumeArea - anchors { - right: volumeSliderArea.right - bottom: volumeButton.bottom - left: volumeButton.left - } - height: parent.height + (volumeSliderArea.visible ? volumeSliderArea.height : 0) - hoverEnabled: true - z: 500 - propagateComposedEvents: true - acceptedButtons: Qt.NoButton - onEntered: { - mouseAreaPlayerTimer.stop() - } - onExited: { - mouseAreaPlayerTimer.restart() - } + MouseArea { + id: mouseAreaVolumeArea + anchors { + right: volumeSliderArea.right + bottom: volumeButton.bottom + left: volumeButton.left } + height: parent.height + (volumeSliderArea.visible ? volumeSliderArea.height : 0) + hoverEnabled: true + z: 500 + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + onEntered: { + mouseAreaPlayerTimer.stop() + } + onExited: { + mouseAreaPlayerTimer.restart() + } + } - VolumeButton { - id: volumeButton - anchors { - left: playPauseButton.right - top: parent.top - bottom: parent.bottom - } - hoverEnabled: true + VolumeButton { + id: volumeButton + anchors { + left: playPauseButton.right + top: parent.top + bottom: parent.bottom } + hoverEnabled: true + } - VerticalVolume { - id: volumeSliderArea - anchors { - bottom: volumeButton.top - left: volumeButton.left - right: volumeButton.right - } - width: volumeButton.width - visible: mouseAreaVolumeArea.containsMouse || volumeButton.hovered + VerticalVolume { + id: volumeSliderArea + anchors { + bottom: volumeButton.top + left: volumeButton.left + right: volumeButton.right } + width: volumeButton.width + visible: mouseAreaVolumeArea.containsMouse || volumeButton.hovered + } - TimeLabel { - id: timeLabel - anchors { - left: volumeButton.right - top: parent.top - bottom: parent.bottom - } + TimeLabel { + id: timeLabel + anchors { + left: volumeButton.right + top: parent.top + bottom: parent.bottom } + } - VideoProgress { - id: videoProgressRoosterTeeth - anchors { - top: parent.top - bottom: parent.bottom - left: timeLabel.right - leftMargin: parent.width / 128 - right: speedText.left - rightMargin: parent.width / 128 - } - rightPadding: 0 - leftPadding: 0 - height: parent.height - to: progressBar.to - value: progressBar.value - center: true + VideoProgress { + id: videoProgressRoosterTeeth + anchors { + top: parent.top + bottom: parent.bottom + left: timeLabel.right + leftMargin: parent.width / 128 + right: speedText.left + rightMargin: parent.width / 128 } + rightPadding: 0 + leftPadding: 0 + height: parent.height + to: progressBar.to + value: progressBar.value + center: true + } - SpeedText { - id: speedText - anchors { - top: parent.top - bottom: parent.bottom - right: fullscreenButton.left - } + SpeedText { + id: speedText + anchors { + top: parent.top + bottom: parent.bottom + right: fullscreenButton.left } + } - FullscreenButton { - id: fullscreenButton - anchors { - right: settingsButton.left - top: parent.top - bottom: parent.bottom - } + FullscreenButton { + id: fullscreenButton + anchors { + right: settingsButton.left + top: parent.top + bottom: parent.bottom } - SettingsButton { - id: settingsButton - anchors { - right: parent.right - top: parent.top - bottom: parent.bottom - } + } + SettingsButton { + id: settingsButton + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom } + } } diff --git a/src/qml/SubtitlesBar.qml b/src/qml/SubtitlesBar.qml index 17c9593..2a5e737 100644 --- a/src/qml/SubtitlesBar.qml +++ b/src/qml/SubtitlesBar.qml @@ -4,64 +4,64 @@ import QtQuick.Layouts 1.2 import player 1.0 Item { - id: subtitlesBar - visible: !appearance.useMpvSubs - height: player.height / 8 + id: subtitlesBar + visible: !appearance.useMpvSubs + height: player.height / 8 + anchors { + bottomMargin: 5 + right: parent.right + left: parent.left + } + RowLayout { + id: nativeSubtitles + height: childrenRect.height + visible: true anchors { - bottomMargin: 5 - right: parent.right - left: parent.left + left: subtitlesBar.left + right: subtitlesBar.right + bottom: parent.bottom + bottomMargin: 10 } - RowLayout { - id: nativeSubtitles - height: childrenRect.height - visible: true - anchors { - left: subtitlesBar.left - right: subtitlesBar.right - bottom: parent.bottom - bottomMargin: 10 + Item { + id: subsContainer + height: childrenRect.height + Layout.fillWidth: true + Layout.fillHeight: true + Layout.rightMargin: 0 + Layout.leftMargin: 0 + Layout.maximumWidth: nativeSubtitles.width + Label { + id: nativeSubs + objectName: "nativeSubs" + anchors.horizontalCenter: parent.horizontalCenter + color: "white" + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + horizontalAlignment: Text.AlignHCenter + opacity: 1 + font { + pixelSize: appearance.subtitlesFontSize + family: appearance.fontName } - Item { - id: subsContainer - height: childrenRect.height - Layout.fillWidth: true - Layout.fillHeight: true - Layout.rightMargin: 0 - Layout.leftMargin: 0 - Layout.maximumWidth: nativeSubtitles.width - Label { - id: nativeSubs - objectName: "nativeSubs" - anchors.horizontalCenter: parent.horizontalCenter - color: "white" - wrapMode: Text.WrapAtWordBoundaryOrAnywhere - horizontalAlignment: Text.AlignHCenter - opacity: 1 - font { - pixelSize: appearance.subtitlesFontSize - family: appearance.fontName - } - background: Rectangle { - id: subsBackground - color: getAppearanceValueForTheme(appearance.themeName, - "mainBackground") - width: subsContainer.childrenRect.width - height: subsContainer.childrenRect.height - } - onWidthChanged: { - if (width > parent.width - 10) - width = parent.width - 10 - } - onTextChanged: if (width <= parent.width - 10) - width = undefined - Connections { - target: player - onSubtitlesChanged: function (subtitles) { - nativeSubs.text = subtitles - } - } - } + background: Rectangle { + id: subsBackground + color: getAppearanceValueForTheme(appearance.themeName, + "mainBackground") + width: subsContainer.childrenRect.width + height: subsContainer.childrenRect.height } + onWidthChanged: { + if (width > parent.width - 10) + width = parent.width - 10 + } + onTextChanged: if (width <= parent.width - 10) + width = undefined + Connections { + target: player + onSubtitlesChanged: function (subtitles) { + nativeSubs.text = subtitles + } + } + } } -} \ No newline at end of file + } +} diff --git a/src/qml/UIComponents/BackwardButton.qml b/src/qml/UIComponents/BackwardButton.qml index 09fc396..eded69f 100644 --- a/src/qml/UIComponents/BackwardButton.qml +++ b/src/qml/UIComponents/BackwardButton.qml @@ -1,8 +1,8 @@ import player 1.0 SmoothButton { - iconSource: "icons/" + appearance.themeName + "/backward.svg" - onClicked: { - player.playerCommand(Enums.Commands.Seek, "-10") - } + iconSource: "icons/" + appearance.themeName + "/backward.svg" + onClicked: { + player.playerCommand(Enums.Commands.Seek, "-10") + } } diff --git a/src/qml/UIComponents/ForwardButton.qml b/src/qml/UIComponents/ForwardButton.qml index 9077287..0db9a8a 100644 --- a/src/qml/UIComponents/ForwardButton.qml +++ b/src/qml/UIComponents/ForwardButton.qml @@ -1,8 +1,8 @@ import player 1.0 SmoothButton { - iconSource: "icons/" + appearance.themeName + "/forward.svg" - onClicked: { - player.playerCommand(Enums.Commands.Seek, "10") - } + iconSource: "icons/" + appearance.themeName + "/forward.svg" + onClicked: { + player.playerCommand(Enums.Commands.Seek, "10") + } } diff --git a/src/qml/UIComponents/FullscreenButton.qml b/src/qml/UIComponents/FullscreenButton.qml index f555341..e358405 100644 --- a/src/qml/UIComponents/FullscreenButton.qml +++ b/src/qml/UIComponents/FullscreenButton.qml @@ -1,6 +1,6 @@ SmoothButton { - iconSource: "icons/" + appearance.themeName + "/fullscreen.svg" - onClicked: { - toggleFullscreen() - } + iconSource: "icons/" + appearance.themeName + "/fullscreen.svg" + onClicked: { + toggleFullscreen() + } } diff --git a/src/qml/UIComponents/MenuTitleBar.qml b/src/qml/UIComponents/MenuTitleBar.qml index 9fd2e95..4a7ef2e 100644 --- a/src/qml/UIComponents/MenuTitleBar.qml +++ b/src/qml/UIComponents/MenuTitleBar.qml @@ -3,75 +3,73 @@ import QtQuick.Controls 2.3 import QtQuick.Window 2.2 Item { - id: menuTitleBar + id: menuTitleBar + height: menuBar.height + visible: true + anchors { + left: parent.left + right: parent.right + top: parent.top + } + + Connections { + target: globalConnections + onHideUI: function () { + if (!menuBar.anythingOpen()) { + menuTitleBar.visible = false + } + } + onShowUI: { + menuTitleBar.visible = true + } + } + + MainMenu { + id: menuBar + } + + Rectangle { height: menuBar.height - visible: true + color: getAppearanceValueForTheme(appearance.themeName, "mainBackground") anchors { + right: parent.right + left: menuBar.right + top: parent.top + } + + Text { + id: titleLabel + objectName: "titleLabel" + text: translate.getTranslation("TITLE", i18n.language) + color: "white" + width: parent.width + height: parent.height + fontSizeMode: Text.VerticalFit + opacity: 1 + visible: menuTitleBar.visible + && ((!appearance.titleOnlyOnFullscreen) + || (mainWindow.visibility == Window.FullScreen + || mainWindow.visibility == Window.Maximized)) + font { + family: appearance.fontName + bold: true + pixelSize: appearance.scaleFactor * (height - anchors.topMargin - anchors.bottomMargin - 2) + } + anchors { left: parent.left - right: parent.right + leftMargin: 4 + bottom: parent.bottom + bottomMargin: 4 top: parent.top - } + } - Connections { - target: globalConnections - onHideUI: function () { - if (!menuBar.anythingOpen()) { - menuTitleBar.visible = false - } - } - onShowUI: { - menuTitleBar.visible = true - } - } - - MainMenu { - id: menuBar - } - - Rectangle { - height: menuBar.height - color: getAppearanceValueForTheme(appearance.themeName, - "mainBackground") - anchors { - right: parent.right - left: menuBar.right - top: parent.top - } - - Text { - id: titleLabel - objectName: "titleLabel" - text: translate.getTranslation("TITLE", i18n.language) - color: "white" - width: parent.width - height: parent.height - fontSizeMode: Text.VerticalFit - opacity: 1 - visible: menuTitleBar.visible - && ((!appearance.titleOnlyOnFullscreen) - || (mainWindow.visibility == Window.FullScreen - || mainWindow.visibility == Window.Maximized)) - font { - family: appearance.fontName - bold: true - pixelSize: appearance.scaleFactor * (height - anchors.topMargin - - anchors.bottomMargin - 2) - } - anchors { - left: parent.left - leftMargin: 4 - bottom: parent.bottom - bottomMargin: 4 - top: parent.top - } - - Connections { - target: player - onTitleChanged: function (title) { - titleLabel.text = title - mainWindow.title = "KittehPlayer - " + title - } - } + Connections { + target: player + onTitleChanged: function (title) { + titleLabel.text = title + mainWindow.title = "KittehPlayer - " + title } + } } + } } diff --git a/src/qml/UIComponents/PlayPauseButton.qml b/src/qml/UIComponents/PlayPauseButton.qml index 31ca3e0..357b9bb 100644 --- a/src/qml/UIComponents/PlayPauseButton.qml +++ b/src/qml/UIComponents/PlayPauseButton.qml @@ -2,15 +2,16 @@ import QtQuick 2.0 import player 1.0 SmoothButton { - property var playing: Enums.PlayStatus.Playing - iconSource: "icons/" + appearance.themeName + (playing == Enums.PlayStatus.Playing ? "/pause.svg" : "/play.svg") - onClicked: { - player.playerCommand(Enums.Commands.TogglePlayPause) - } - Connections { - target: player - onPlayStatusChanged: function (status) { - playing = status - } + property var playing: Enums.PlayStatus.Playing + iconSource: "icons/" + appearance.themeName + + (playing == Enums.PlayStatus.Playing ? "/pause.svg" : "/play.svg") + onClicked: { + player.playerCommand(Enums.Commands.TogglePlayPause) + } + Connections { + target: player + onPlayStatusChanged: function (status) { + playing = status } + } } diff --git a/src/qml/UIComponents/PlaylistNextButton.qml b/src/qml/UIComponents/PlaylistNextButton.qml index 3ac3a29..903461f 100644 --- a/src/qml/UIComponents/PlaylistNextButton.qml +++ b/src/qml/UIComponents/PlaylistNextButton.qml @@ -1,8 +1,8 @@ import player 1.0 SmoothButton { - iconSource: "icons/" + appearance.themeName + "/next.svg" - onClicked: { - player.playerCommand(Enums.Commands.NextPlaylistItem) - } + iconSource: "icons/" + appearance.themeName + "/next.svg" + onClicked: { + player.playerCommand(Enums.Commands.NextPlaylistItem) + } } diff --git a/src/qml/UIComponents/PlaylistPrevButton.qml b/src/qml/UIComponents/PlaylistPrevButton.qml index 4b97339..6e71c42 100644 --- a/src/qml/UIComponents/PlaylistPrevButton.qml +++ b/src/qml/UIComponents/PlaylistPrevButton.qml @@ -2,22 +2,22 @@ import QtQuick 2.0 import player 1.0 SmoothButton { - id: playlistPrevButton - iconSource: "icons/" + appearance.themeName + "/prev.svg" - visible: appearance.themeName == "Youtube" ? false : true - onClicked: { - player.playerCommand(Enums.Commands.PreviousPlaylistItem) - } - Connections { - target: player - onPlaylistPositionChanged: function (position) { - if (appearance.themeName == "YouTube") { - if (position != 0) { - visible = true - } else { - visible = false - } - } + id: playlistPrevButton + iconSource: "icons/" + appearance.themeName + "/prev.svg" + visible: appearance.themeName == "Youtube" ? false : true + onClicked: { + player.playerCommand(Enums.Commands.PreviousPlaylistItem) + } + Connections { + target: player + onPlaylistPositionChanged: function (position) { + if (appearance.themeName == "YouTube") { + if (position != 0) { + visible = true + } else { + visible = false } + } } + } } diff --git a/src/qml/UIComponents/SettingsButton.qml b/src/qml/UIComponents/SettingsButton.qml index 7d64f6d..223ef09 100644 --- a/src/qml/UIComponents/SettingsButton.qml +++ b/src/qml/UIComponents/SettingsButton.qml @@ -1,21 +1,21 @@ import player 1.0 SmoothButton { - id: settingsButton - iconSource: "icons/" + appearance.themeName + "/settings.svg" - onClicked: { - switch(appearance.themeName) { - case "YouTube": - appearance.themeName = "RoosterTeeth" - break - case "RoosterTeeth": - appearance.themeName = "Niconico" - break - case "Niconico": - appearance.themeName = "YouTube" - break - default: - appearance.themeName = "YouTube" - } + id: settingsButton + iconSource: "icons/" + appearance.themeName + "/settings.svg" + onClicked: { + switch (appearance.themeName) { + case "YouTube": + appearance.themeName = "RoosterTeeth" + break + case "RoosterTeeth": + appearance.themeName = "Niconico" + break + case "Niconico": + appearance.themeName = "YouTube" + break + default: + appearance.themeName = "YouTube" } + } } diff --git a/src/qml/UIComponents/SmoothButton.qml b/src/qml/UIComponents/SmoothButton.qml index e777c98..de5f798 100644 --- a/src/qml/UIComponents/SmoothButton.qml +++ b/src/qml/UIComponents/SmoothButton.qml @@ -3,47 +3,48 @@ import QtQuick.Controls 2.3 import QtGraphicalEffects 1.0 Control { - id: root + id: root + hoverEnabled: true + property alias iconSource: buttonImage.source + property alias containsMouse: mouseArea.containsMouse + + background: null + + focusPolicy: Qt.NoFocus + + signal clicked + leftPadding: root.height / (appearance.themeName == "Niconico" ? 2.5 : 12) + rightPadding: root.leftPadding + + contentItem: Image { + id: buttonImage + smooth: false + fillMode: Image.PreserveAspectFit + sourceSize.height: Math.floor( + root.parent.height / (appearance.themeName == "Niconico" ? 1.8 : 1.25)) + sourceSize.width: Math.floor( + root.parent.height / (appearance.themeName == "Niconico" ? 1.8 : 1.25)) + } + ColorOverlay { + id: colorOverlay + anchors.fill: buttonImage + source: buttonImage + color: getAppearanceValueForTheme(appearance.themeName, "buttonColor") + cached: true + Binding on color { + when: root.hovered + value: root.hovered ? getAppearanceValueForTheme( + appearance.themeName, + "buttonHoverColor") : getAppearanceValueForTheme( + appearance.themeName, "buttonColor") + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent hoverEnabled: true - property alias iconSource: buttonImage.source - property alias containsMouse: mouseArea.containsMouse - - background: null - - focusPolicy: Qt.NoFocus - - signal clicked - leftPadding: root.height / (appearance.themeName == "Niconico" ? 2.5 : 12) - rightPadding: root.leftPadding - - contentItem: Image { - id: buttonImage - smooth: false - fillMode: Image.PreserveAspectFit - sourceSize.height: Math.floor(root.parent.height / (appearance.themeName == "Niconico" ? 1.8 : 1.25)) - sourceSize.width: Math.floor(root.parent.height / (appearance.themeName == "Niconico" ? 1.8 : 1.25)) - } - ColorOverlay { - id: colorOverlay - anchors.fill: buttonImage - source: buttonImage - color: getAppearanceValueForTheme(appearance.themeName, "buttonColor") - cached: true - Binding on color { - when: root.hovered - value: root.hovered ? getAppearanceValueForTheme( - appearance.themeName, - "buttonHoverColor") : getAppearanceValueForTheme( - appearance.themeName, "buttonColor") - } - } - - - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - propagateComposedEvents: true - onClicked: root.clicked() - } + propagateComposedEvents: true + onClicked: root.clicked() + } } diff --git a/src/qml/UIComponents/SpeedText.qml b/src/qml/UIComponents/SpeedText.qml index 4832642..7100a3e 100644 --- a/src/qml/UIComponents/SpeedText.qml +++ b/src/qml/UIComponents/SpeedText.qml @@ -3,30 +3,30 @@ import QtQuick.Controls 2.3 import player 1.0 Text { - id: speedText - text: "1x" - verticalAlignment: Text.AlignVCenter - color: speedStatusMouseArea.containsMouse ? getAppearanceValueForTheme( - appearance.themeName, - "buttonHoverColor") : getAppearanceValueForTheme( - appearance.themeName, - "buttonColor") - font { - family: appearance.fontName - pixelSize: layout.height / 2.5 - } - Connections { - target: player - onSpeedChanged: function (speed) { - text = String(speed) + "x" - } - } - MouseArea { - id: speedStatusMouseArea - anchors.fill: parent - height: parent.height - hoverEnabled: true - propagateComposedEvents: true - acceptedButtons: Qt.NoButton + id: speedText + text: "1x" + verticalAlignment: Text.AlignVCenter + color: speedStatusMouseArea.containsMouse ? getAppearanceValueForTheme( + appearance.themeName, + "buttonHoverColor") : getAppearanceValueForTheme( + appearance.themeName, + "buttonColor") + font { + family: appearance.fontName + pixelSize: layout.height / 2.5 + } + Connections { + target: player + onSpeedChanged: function (speed) { + text = String(speed) + "x" } + } + MouseArea { + id: speedStatusMouseArea + anchors.fill: parent + height: parent.height + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + } } diff --git a/src/qml/UIComponents/TimeLabel.qml b/src/qml/UIComponents/TimeLabel.qml index 24d1944..c92c368 100644 --- a/src/qml/UIComponents/TimeLabel.qml +++ b/src/qml/UIComponents/TimeLabel.qml @@ -2,17 +2,17 @@ import QtQuick 2.0 import QtQuick.Controls 2.3 Text { - id: timeLabel - text: "0:00 / 0:00" - color: "white" - font.family: appearance.fontName - font.pixelSize: layout.height / 2.5 - verticalAlignment: Text.AlignVCenter - renderType: Text.NativeRendering - Connections { - target: player - onDurationStringChanged: function (durationString) { - timeLabel.text = durationString - } + id: timeLabel + text: "0:00 / 0:00" + color: "white" + font.family: appearance.fontName + font.pixelSize: layout.height / 2.5 + verticalAlignment: Text.AlignVCenter + renderType: Text.NativeRendering + Connections { + target: player + onDurationStringChanged: function (durationString) { + timeLabel.text = durationString } + } } diff --git a/src/qml/UIComponents/VerticalVolume.qml b/src/qml/UIComponents/VerticalVolume.qml index 3c254e9..5099bce 100644 --- a/src/qml/UIComponents/VerticalVolume.qml +++ b/src/qml/UIComponents/VerticalVolume.qml @@ -3,70 +3,69 @@ import QtQuick.Controls 2.3 import player 1.0 Rectangle { - id: volumeSliderArea - height: visible ? 70 : 0 - color: getAppearanceValueForTheme(appearance.themeName, "mainBackground") - visible: false - Slider { - id: volumeSlider - anchors.fill: parent - to: 100 - value: 100 + id: volumeSliderArea + height: visible ? 70 : 0 + color: getAppearanceValueForTheme(appearance.themeName, "mainBackground") + visible: false + Slider { + id: volumeSlider + anchors.fill: parent + to: 100 + value: 100 - orientation: Qt.Vertical + orientation: Qt.Vertical - implicitWidth: Math.max( - background ? background.implicitWidth : 0, - (handle ? handle.implicitWidth : 0) + leftPadding + rightPadding) - implicitHeight: Math.max( - background.implicitHeight, - handle.implicitHeight + topPadding + bottomPadding) + implicitWidth: Math.max( + background ? background.implicitWidth : 0, + (handle ? handle.implicitWidth : 0) + leftPadding + rightPadding) + implicitHeight: Math.max(background.implicitHeight, + handle.implicitHeight + topPadding + bottomPadding) - padding: 6 + padding: 6 - Connections { - target: player - onVolumeChanged: function (volume) { - volumeSlider.value = volume - } - } - - onMoved: { - player.playerCommand(Enums.Commands.SetVolume, - Math.round(volumeSlider.value).toString()) - } - - handle: Rectangle { - x: volumeSlider.leftPadding + ((volumeSlider.availableWidth - width) / 2) - y: volumeSlider.topPadding + (volumeSlider.visualPosition - * (volumeSlider.availableHeight - height)) - implicitWidth: 10 - implicitHeight: 10 - radius: width / 2 - color: "white" - border.width: 0 - } - - background: Rectangle { - x: volumeSlider.leftPadding + ((volumeSlider.availableWidth - width) / 2) - y: volumeSlider.topPadding - implicitWidth: 4 - implicitHeight: 70 - width: implicitWidth - height: volumeSlider.availableHeight - radius: 3 - color: getAppearanceValueForTheme(appearance.themeName, - "progressBackgroundColor") - - Rectangle { - y: volumeSlider.visualPosition * parent.height - width: 4 - height: volumeSlider.position * parent.height - - radius: 3 - color: getAppearanceValueForTheme(appearance.themeName, - "volumeSliderBackground") - } - } + Connections { + target: player + onVolumeChanged: function (volume) { + volumeSlider.value = volume + } } + + onMoved: { + player.playerCommand(Enums.Commands.SetVolume, + Math.round(volumeSlider.value).toString()) + } + + handle: Rectangle { + x: volumeSlider.leftPadding + ((volumeSlider.availableWidth - width) / 2) + y: volumeSlider.topPadding + (volumeSlider.visualPosition + * (volumeSlider.availableHeight - height)) + implicitWidth: 10 + implicitHeight: 10 + radius: width / 2 + color: "white" + border.width: 0 + } + + background: Rectangle { + x: volumeSlider.leftPadding + ((volumeSlider.availableWidth - width) / 2) + y: volumeSlider.topPadding + implicitWidth: 4 + implicitHeight: 70 + width: implicitWidth + height: volumeSlider.availableHeight + radius: 3 + color: getAppearanceValueForTheme(appearance.themeName, + "progressBackgroundColor") + + Rectangle { + y: volumeSlider.visualPosition * parent.height + width: 4 + height: volumeSlider.position * parent.height + + radius: 3 + color: getAppearanceValueForTheme(appearance.themeName, + "volumeSliderBackground") + } + } + } } diff --git a/src/qml/UIComponents/VideoProgress.qml b/src/qml/UIComponents/VideoProgress.qml index b9d2472..ef6e4af 100644 --- a/src/qml/UIComponents/VideoProgress.qml +++ b/src/qml/UIComponents/VideoProgress.qml @@ -3,200 +3,200 @@ import QtQuick.Controls 2.3 import player 1.0 Slider { - id: progressBar - objectName: "progressBar" - property string currentMediaURL: "" - property bool playing: false - property bool center: false - to: 1 - value: 0.0 + id: progressBar + objectName: "progressBar" + property string currentMediaURL: "" + property bool playing: false + property bool center: false + to: 1 + value: 0.0 + + Rectangle { + id: timestampBox + visible: false + width: hoverProgressLabel.width + height: hoverProgressLabel.height + z: 100 + color: getAppearanceValueForTheme(appearance.themeName, "mainBackground") + Text { + id: hoverProgressLabel + text: "0:00" + color: "white" + font.family: appearance.fontName + font.pixelSize: mainWindow.virtualHeight / 50 + horizontalAlignment: Text.AlignHCenter + renderType: Text.NativeRendering + } + } + + Connections { + target: player + onPlayStatusChanged: function (status) { + if (status == Enums.PlayStatus.Playing) { + progressBar.playing = true + } else if (status == Enums.PlayStatus.Paused) { + progressBar.playing = false + } + } + onPositionChanged: function (position) { + if (!pressed) { + progressBar.value = position + } + } + onDurationChanged: function (duration) { + progressBar.to = duration + } + onCachedDurationChanged: function (duration) { + cachedLength.duration = duration + } + } + onMoved: { + player.playerCommand(Enums.Commands.SeekAbsolute, value) + } + + function getProgressBarHeight(nyan, isMouse) { + var x = fun.nyanCat ? mainWindow.virtualHeight / 64 : mainWindow.virtualHeight / 380 + if (appearance.themeName == "Niconico" && !fun.nyanCat) { + return x * 2 + } else if (isMouse & !fun.nyanCat) { + return x * 2 + } else { + return x + } + } + function getHandleVisibility(themeName, isMouse) { + if (fun.nyanCat) { + return true + } + if (appearance.themeName == "Niconico") { + return isMouse + } else { + return true + } + } + MouseArea { + id: mouseAreaProgressBar + width: progressBar.width + height: parent.height + anchors.fill: parent + + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + z: 100 + property string currentTime: "" + + onEntered: timestampBox.visible = true + onExited: timestampBox.visible = false + + onPositionChanged: { + // code taken from https://github.com/qt/qtquickcontrols2/blob/39892547145ba4e73bebee86352bd384732b5d19/src/quicktemplates2/qquickslider.cpp#L138 + var a = ((mouseAreaProgressBar.mouseX - (handleRect.width / 2)) + / (progressBar.availableWidth - handleRect.width)) * progressBar.to + hoverProgressLabel.text = utils.createTimestamp(a) + timestampBox.x = mouseAreaProgressBar.mouseX - (timestampBox.width / 2) + timestampBox.y = progressBackground.y - timestampBox.height * 2 + } + } + + background: Rectangle { + anchors.bottom: parent.bottom + anchors.bottomMargin: progressBar.center ? (progressBar.height / 2) - (height / 2) : 0 + id: progressBackground + z: 30 + width: progressBar.availableWidth + height: progressBar.getProgressBarHeight(fun.nyanCat, + mouseAreaProgressBar.containsMouse) + color: getAppearanceValueForTheme(appearance.themeName, + "progressBackgroundColor") + + ProgressBar { + id: cachedLength + background: null + contentItem: Item { + Rectangle { + width: cachedLength.visualPosition * parent.width + height: parent.height + color: getAppearanceValueForTheme(appearance.themeName, + "progressCachedColor") + AnimatedImage { + visible: fun.nyanCat + height: parent.height + id: nyancacheimation + smooth: false + anchors.fill: parent + source: "qrc:/icons/nyancache.gif" + fillMode: Image.TileHorizontally + } + } + } + z: 40 + to: progressBar.to + property int duration + value: progressBar.value + duration + anchors.fill: parent + } + + Item { + anchors.fill: parent + id: chapterMarkers + Connections { + target: player + onChaptersChanged: function (chapters) { + for (var i = 0, len = chapters.length; i < len; i++) { + var component = Qt.createComponent("ChapterMarker.qml") + var marker = component.createObject(chapterMarkers, { + "time": chapters[i]["time"] + }) + } + } + } + } Rectangle { - id: timestampBox - visible: false - width: hoverProgressLabel.width - height: hoverProgressLabel.height - z: 100 - color: getAppearanceValueForTheme(appearance.themeName, - "mainBackground") - Text { - id: hoverProgressLabel - text: "0:00" - color: "white" - font.family: appearance.fontName - font.pixelSize: mainWindow.virtualHeight / 50 - horizontalAlignment: Text.AlignHCenter - renderType: Text.NativeRendering - } - } - - Connections { - target: player - onPlayStatusChanged: function (status) { - if (status == Enums.PlayStatus.Playing) { - progressBar.playing = true - } else if (status == Enums.PlayStatus.Paused) { - progressBar.playing = false - } - } - onPositionChanged: function (position) { - if (!pressed) { - progressBar.value = position - } - } - onDurationChanged: function (duration) { - progressBar.to = duration - } - onCachedDurationChanged: function (duration) { - cachedLength.duration = duration - } - } - onMoved: { - player.playerCommand(Enums.Commands.SeekAbsolute, value) - } - - function getProgressBarHeight(nyan, isMouse) { - var x = fun.nyanCat ? mainWindow.virtualHeight / 64 : mainWindow.virtualHeight / 380 - if (appearance.themeName == "Niconico" && !fun.nyanCat) { - return x * 2 - } else if (isMouse & !fun.nyanCat) { - return x * 2 - } else { - return x - } - } - function getHandleVisibility(themeName, isMouse) { - if (fun.nyanCat) { - return true - } - if (appearance.themeName == "Niconico") { - return isMouse - } else { - return true - } - } - MouseArea { - id: mouseAreaProgressBar - width: progressBar.width - height: parent.height + id: progressLength + z: 50 + anchors.left: progressBackground.left + width: progressBar.visualPosition * parent.width + height: parent.height + color: getAppearanceValueForTheme(appearance.themeName, + "progressSliderColor") + Image { + visible: fun.nyanCat + id: rainbow anchors.fill: parent - - hoverEnabled: true - propagateComposedEvents: true - acceptedButtons: Qt.NoButton - z: 100 - property string currentTime: "" - - onEntered: timestampBox.visible = true - onExited: timestampBox.visible = false - - onPositionChanged: { - // code taken from https://github.com/qt/qtquickcontrols2/blob/39892547145ba4e73bebee86352bd384732b5d19/src/quicktemplates2/qquickslider.cpp#L138 - var a = ((mouseAreaProgressBar.mouseX - (handleRect.width / 2)) / (progressBar.availableWidth - handleRect.width)) * progressBar.to - hoverProgressLabel.text = utils.createTimestamp(a) - timestampBox.x = mouseAreaProgressBar.mouseX - (timestampBox.width / 2) - timestampBox.y = progressBackground.y - timestampBox.height * 2 - } + height: parent.height + width: parent.width + source: "qrc:/icons/rainbow.png" + fillMode: Image.TileHorizontally + } } + } - background: Rectangle { - anchors.bottom: parent.bottom - anchors.bottomMargin: progressBar.center ? (progressBar.height / 2) - (height / 2) : 0 - id: progressBackground - z: 30 - width: progressBar.availableWidth - height: progressBar.getProgressBarHeight( - fun.nyanCat, mouseAreaProgressBar.containsMouse) - color: getAppearanceValueForTheme(appearance.themeName, - "progressBackgroundColor") - - ProgressBar { - id: cachedLength - background: null - contentItem: Item { - Rectangle { - width: cachedLength.visualPosition * parent.width - height: parent.height - color: getAppearanceValueForTheme(appearance.themeName, - "progressCachedColor") - AnimatedImage { - visible: fun.nyanCat - height: parent.height - id: nyancacheimation - smooth: false - anchors.fill: parent - source: "qrc:/icons/nyancache.gif" - fillMode: Image.TileHorizontally - } - } - } - z: 40 - to: progressBar.to - property int duration - value: progressBar.value + duration - anchors.fill: parent - } - - Item { - anchors.fill: parent - id: chapterMarkers - Connections { - target: player - onChaptersChanged: function (chapters) { - for (var i = 0, len = chapters.length; i < len; i++) { - var component = Qt.createComponent("ChapterMarker.qml") - var marker = component.createObject(chapterMarkers, { - "time": chapters[i]["time"] - }) - } - } - } - } - - Rectangle { - id: progressLength - z: 50 - anchors.left: progressBackground.left - width: progressBar.visualPosition * parent.width - height: parent.height - color: getAppearanceValueForTheme(appearance.themeName, - "progressSliderColor") - Image { - visible: fun.nyanCat - id: rainbow - anchors.fill: parent - height: parent.height - width: parent.width - source: "qrc:/icons/rainbow.png" - fillMode: Image.TileHorizontally - } - } - } - - handle: Rectangle { - z: 70 - id: handleRect - x: progressBar.visualPosition * (progressBar.availableWidth - width) - anchors.verticalCenter: parent.background.verticalCenter - implicitHeight: radius - implicitWidth: radius - radius: mainWindow.virtualHeight / 59 - color: appearance.themeName - == "RoosterTeeth" ? "white" : fun.nyanCat ? "transparent" : getAppearanceValueForTheme( - appearance.themeName, - "progressSliderColor") - visible: getHandleVisibility(appearance.themeName, - mouseAreaProgressBar.containsMouse) - AnimatedImage { - z: 80 - visible: fun.nyanCat - paused: progressBar.pressed - height: mainWindow.virtualHeight / 28 - id: nyanimation - smooth: false - anchors.centerIn: parent - source: "qrc:/icons/nyancat.gif" - fillMode: Image.PreserveAspectFit - } + handle: Rectangle { + z: 70 + id: handleRect + x: progressBar.visualPosition * (progressBar.availableWidth - width) + anchors.verticalCenter: parent.background.verticalCenter + implicitHeight: radius + implicitWidth: radius + radius: mainWindow.virtualHeight / 59 + color: appearance.themeName + == "RoosterTeeth" ? "white" : fun.nyanCat ? "transparent" : getAppearanceValueForTheme( + appearance.themeName, + "progressSliderColor") + visible: getHandleVisibility(appearance.themeName, + mouseAreaProgressBar.containsMouse) + AnimatedImage { + z: 80 + visible: fun.nyanCat + paused: progressBar.pressed + height: mainWindow.virtualHeight / 28 + id: nyanimation + smooth: false + anchors.centerIn: parent + source: "qrc:/icons/nyancat.gif" + fillMode: Image.PreserveAspectFit } + } } diff --git a/src/qml/UIComponents/VolumeButton.qml b/src/qml/UIComponents/VolumeButton.qml index 237e09f..36fa59f 100644 --- a/src/qml/UIComponents/VolumeButton.qml +++ b/src/qml/UIComponents/VolumeButton.qml @@ -2,21 +2,21 @@ import QtQuick 2.0 import player 1.0 SmoothButton { - id: volumeButton - iconSource: "icons/" + appearance.themeName + "/volume-up.svg" - onClicked: { - player.playerCommand(Enums.Commands.ToggleMute) - } - Connections { - target: player - onVolumeStatusChanged: function(status) { - if (status == Enums.VolumeStatus.Muted) { - volumeButton.iconSource = "qrc:/icons/" + appearance.themeName + "/volume-mute.svg" - } else if (status == Enums.VolumeStatus.Low) { - volumeButton.iconSource = "qrc:/icons/" + appearance.themeName + "/volume-down.svg" - } else if (status == Enums.VolumeStatus.Normal) { - volumeButton.iconSource = "qrc:/icons/" + appearance.themeName + "/volume-up.svg" - } - } + id: volumeButton + iconSource: "icons/" + appearance.themeName + "/volume-up.svg" + onClicked: { + player.playerCommand(Enums.Commands.ToggleMute) + } + Connections { + target: player + onVolumeStatusChanged: function (status) { + if (status == Enums.VolumeStatus.Muted) { + volumeButton.iconSource = "qrc:/icons/" + appearance.themeName + "/volume-mute.svg" + } else if (status == Enums.VolumeStatus.Low) { + volumeButton.iconSource = "qrc:/icons/" + appearance.themeName + "/volume-down.svg" + } else if (status == Enums.VolumeStatus.Normal) { + volumeButton.iconSource = "qrc:/icons/" + appearance.themeName + "/volume-up.svg" + } } + } } diff --git a/src/qml/UIComponents/VolumeSlider.qml b/src/qml/UIComponents/VolumeSlider.qml index b37651d..28f1295 100644 --- a/src/qml/UIComponents/VolumeSlider.qml +++ b/src/qml/UIComponents/VolumeSlider.qml @@ -3,69 +3,67 @@ import QtQuick.Controls 2.3 import player 1.0 Slider { - id: volumeBar - to: 100 - value: 100 - palette.dark: "#f00" - hoverEnabled: true + id: volumeBar + to: 100 + value: 100 + palette.dark: "#f00" + hoverEnabled: true - implicitWidth: Math.max( - background ? background.implicitWidth : 0, - (handle ? handle.implicitWidth : 0) + leftPadding + rightPadding) - implicitHeight: Math.max( - background ? background.implicitHeight : 0, - (handle ? handle.implicitHeight : 0) + topPadding + bottomPadding) - onMoved: { - player.playerCommand(Enums.Commands.SetVolume, - Math.round(volumeBar.value).toString()) - } - Connections { - target: player - onVolumeChanged: function (volume) { - volumeBar.value = volume - } - } - handle: Rectangle { - x: volumeBar.leftPadding + volumeBar.visualPosition * (volumeBar.availableWidth - width) - y: volumeBar.topPadding + volumeBar.availableHeight / 2 - height / 2 - implicitWidth: height - implicitHeight: layout.height / 2.6 - radius: height - visible: appearance.themeName == "Niconico" ? false : true - color: "#f6f6f6" - border.color: "#f6f6f6" + implicitWidth: Math.max( + background ? background.implicitWidth : 0, + (handle ? handle.implicitWidth : 0) + leftPadding + rightPadding) + implicitHeight: Math.max( + background ? background.implicitHeight : 0, + (handle ? handle.implicitHeight : 0) + topPadding + bottomPadding) + onMoved: { + player.playerCommand(Enums.Commands.SetVolume, + Math.round(volumeBar.value).toString()) + } + Connections { + target: player + onVolumeChanged: function (volume) { + volumeBar.value = volume } + } + handle: Rectangle { + x: volumeBar.leftPadding + volumeBar.visualPosition * (volumeBar.availableWidth - width) + y: volumeBar.topPadding + volumeBar.availableHeight / 2 - height / 2 + implicitWidth: height + implicitHeight: layout.height / 2.6 + radius: height + visible: appearance.themeName == "Niconico" ? false : true + color: "#f6f6f6" + border.color: "#f6f6f6" + } - background: Rectangle { - x: volumeBar.leftPadding - y: volumeBar.topPadding + volumeBar.availableHeight / 2 - height / 2 - implicitWidth: layout.width / 11 - implicitHeight: appearance.themeName == "Niconico" ? layout.height / 6 : layout.height / 10 - width: volumeBar.availableWidth - height: implicitHeight - color: getAppearanceValueForTheme(appearance.themeName, - "progressBackgroundColor") - Rectangle { - width: volumeBar.visualPosition * parent.width - height: parent.height - color: getAppearanceValueForTheme(appearance.themeName, - "volumeSliderBackground") - } - + background: Rectangle { + x: volumeBar.leftPadding + y: volumeBar.topPadding + volumeBar.availableHeight / 2 - height / 2 + implicitWidth: layout.width / 11 + implicitHeight: appearance.themeName == "Niconico" ? layout.height / 6 : layout.height / 10 + width: volumeBar.availableWidth + height: implicitHeight + color: getAppearanceValueForTheme(appearance.themeName, + "progressBackgroundColor") + Rectangle { + width: volumeBar.visualPosition * parent.width + height: parent.height + color: getAppearanceValueForTheme(appearance.themeName, + "volumeSliderBackground") + } MouseArea { - acceptedButtons: Qt.NoButton - z: 10 - anchors.fill: parent - propagateComposedEvents: true - onWheel: { - if (wheel.angleDelta.y<0){ - volumeBar.value -= 5 - } - else { - volumeBar.value += 5 - } + acceptedButtons: Qt.NoButton + z: 10 + anchors.fill: parent + propagateComposedEvents: true + onWheel: { + if (wheel.angleDelta.y < 0) { + volumeBar.value -= 5 + } else { + volumeBar.value += 5 } + } } - } + } } diff --git a/src/qml/Utils/Translator.qml b/src/qml/Utils/Translator.qml index bb15918..c9682c7 100644 --- a/src/qml/Utils/Translator.qml +++ b/src/qml/Utils/Translator.qml @@ -2,20 +2,19 @@ import QtQuick 2.0 import "translations.js" as Translations Item { - - function getTranslation(code, language) { - var lang = Translations.translations[language] - if (lang == undefined || lang == "undefined") { - return "TranslationNotFound" - } - var text = String(Translations.translations[i18n.language][code]) - if (text == "undefined"){ - console.warn(code, "missing for language", language) - } - var args = Array.prototype.slice.call(arguments, 1) - var i = 0 - return text.replace(/%s/g, function () { - return args[i++] - }) + function getTranslation(code, language) { + var lang = Translations.translations[language] + if (lang == undefined || lang == "undefined") { + return "TranslationNotFound" } + var text = String(Translations.translations[i18n.language][code]) + if (text == "undefined") { + console.warn(code, "missing for language", language) + } + var args = Array.prototype.slice.call(arguments, 1) + var i = 0 + return text.replace(/%s/g, function () { + return args[i++] + }) + } } diff --git a/src/qml/YouTubeButtonLayout.qml b/src/qml/YouTubeButtonLayout.qml index 965c306..99d5ab2 100644 --- a/src/qml/YouTubeButtonLayout.qml +++ b/src/qml/YouTubeButtonLayout.qml @@ -2,91 +2,91 @@ import QtQuick 2.0 import player 1.0 Item { - objectName: "buttonLayout" - id: layout - anchors.fill: controlsBar + objectName: "buttonLayout" + id: layout + anchors.fill: controlsBar + height: parent.height + + PlaylistPrevButton { + id: playlistPrevButton + anchors { + left: parent.left + top: parent.top + bottom: parent.bottom + } + width: visible ? playlistNextButton.width : 0 + } + PlayPauseButton { + id: playPauseButton + anchors { + left: playlistPrevButton.right + top: parent.top + bottom: parent.bottom + } + leftPadding: 14 + } + PlaylistNextButton { + id: playlistNextButton + anchors { + left: playPauseButton.right + top: parent.top + bottom: parent.bottom + } + } + + MouseArea { + id: mouseAreaVolumeArea + anchors.fill: volumeSlider + width: volumeSlider.width + hoverEnabled: true + propagateComposedEvents: true + acceptedButtons: Qt.NoButton + z: 100 + } + + VolumeButton { + id: volumeButton + anchors { + left: playlistNextButton.right + top: parent.top + bottom: parent.bottom + } + z: 50 + } + VolumeSlider { + id: volumeSlider + anchors { + left: volumeButton.right + top: parent.top + bottom: parent.bottom + } height: parent.height + visible: mouseAreaVolumeArea.containsMouse || volumeButton.hovered + width: visible ? implicitWidth : 0 + } + TimeLabel { + anchors { + left: volumeSlider.right + top: parent.top + bottom: parent.bottom + leftMargin: parent.width / 128 + } + } - PlaylistPrevButton { - id: playlistPrevButton - anchors { - left: parent.left - top: parent.top - bottom: parent.bottom - } - width: visible ? playlistNextButton.width : 0 + SettingsButton { + id: settingsButton + anchors { + right: fullscreenButton.left + top: parent.top + bottom: parent.bottom } - PlayPauseButton { - id: playPauseButton - anchors { - left: playlistPrevButton.right - top: parent.top - bottom: parent.bottom - } - leftPadding: 14 - } - PlaylistNextButton { - id: playlistNextButton - anchors { - left: playPauseButton.right - top: parent.top - bottom: parent.bottom - } - } - - MouseArea { - id: mouseAreaVolumeArea - anchors.fill: volumeSlider - width: volumeSlider.width - hoverEnabled: true - propagateComposedEvents: true - acceptedButtons: Qt.NoButton - z: 100 - } - - VolumeButton { - id: volumeButton - anchors { - left: playlistNextButton.right - top: parent.top - bottom: parent.bottom - } - z: 50 - } - VolumeSlider { - id: volumeSlider - anchors { - left: volumeButton.right - top: parent.top - bottom: parent.bottom - } - height: parent.height - visible: mouseAreaVolumeArea.containsMouse || volumeButton.hovered - width: visible ? implicitWidth : 0 - } - TimeLabel { - anchors { - left: volumeSlider.right - top: parent.top - bottom: parent.bottom - leftMargin: parent.width / 128 - } - } - - SettingsButton { - id: settingsButton - anchors { - right: fullscreenButton.left - top: parent.top - bottom: parent.bottom - } - } - FullscreenButton { - id: fullscreenButton - anchors { - right: parent.right - top: parent.top - bottom: parent.bottom - } + } + FullscreenButton { + id: fullscreenButton + anchors { + right: parent.right + top: parent.top + bottom: parent.bottom } + } } diff --git a/src/qml/main.qml b/src/qml/main.qml index f57ccc9..fa732ae 100644 --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -5,475 +5,471 @@ import Qt.labs.settings 1.0 import player 1.0 Window { - id: mainWindow - title: "KittehPlayer" - visible: true - width: Math.min(720, Screen.width) - height: Math.min(480, Screen.height) - property int virtualHeight: Screen.height * appearance.scaleFactor - property int virtualWidth: Screen.width * appearance.scaleFactor - property bool onTop: false + id: mainWindow + title: "KittehPlayer" + visible: true + width: Math.min(720, Screen.width) + height: Math.min(480, Screen.height) + property int virtualHeight: Screen.height * appearance.scaleFactor + property int virtualWidth: Screen.width * appearance.scaleFactor + property bool onTop: false - QMLDebugger { - id: qmlDebugger + QMLDebugger { + id: qmlDebugger + } + + Item { + id: globalConnections + signal showUI + signal hideUI + } + + function getAppearanceValueForTheme(themeName, name) { + switch (themeName) { + case "YouTube": + return youTubeAppearance[name] + case "Niconico": + return nicoNicoAppearance[name] + case "RoosterTeeth": + return roosterTeethAppearance[name] + default: + appearance.themeName = "YouTube" + return youTubeAppearance[name] + } + } + + Translator { + id: translate + } + + Settings { + id: loggingSettings + category: "Logging" + property string logFile: "/tmp/KittehPlayer.log" + property bool logBackend: true + } + + Settings { + id: backendSettings + category: "Backend" + property string backend: "mpv" + property bool fbo: true + property bool direct: false + } + + Settings { + id: appearance + category: "Appearance" + property bool titleOnlyOnFullscreen: true + property bool useMpvSubs: false + property string themeName: "YouTube" + property string fontName: "Roboto" + property double scaleFactor: 1.0 + property int subtitlesFontSize: 24 + property int uiFadeTimer: 1000 + property bool doubleTapToSeek: true + property double doubleTapToSeekBy: 5 + property bool swipeToResize: true + // Can fix some screen tearing on some devices. + property bool maximizeInsteadOfFullscreen: false + } + + Settings { + id: youTubeAppearance + category: "YouTubeAppearance" + property string mainBackground: "#9C000000" + property string progressBackgroundColor: "#33FFFFFF" + property string progressCachedColor: "#66FFFFFF" + property string buttonColor: "white" + property string buttonHoverColor: "white" + property string progressSliderColor: "red" + property string chapterMarkerColor: "#fc0" + property string volumeSliderBackground: "white" + } + + Settings { + id: nicoNicoAppearance + category: "NicoNicoAppearance" + property string mainBackground: "#9C000000" + property string progressBackgroundColor: "#444" + property string progressCachedColor: "#66FFFFFF" + property string buttonColor: "white" + property string buttonHoverColor: "white" + property string progressSliderColor: "#007cff" + property string chapterMarkerColor: "#fc0" + property string volumeSliderBackground: "#007cff" + } + + Settings { + id: roosterTeethAppearance + category: "RoosterTeethAppearance" + property string mainBackground: "#CC2B333F" + property string progressBackgroundColor: "#444" + property string progressCachedColor: "white" + property string buttonColor: "white" + property string buttonHoverColor: "#c9373f" + property string progressSliderColor: "#c9373f" + property string chapterMarkerColor: "#fc0" + property string volumeSliderBackground: "#c9373f" + } + + Settings { + id: i18n + category: "I18N" + property string language: "english" + } + + Settings { + id: fun + category: "Fun" + property bool nyanCat: false + } + + Settings { + id: keybinds + category: "Keybinds" + property string playPause: "K" + property string forward10: "L" + property string rewind10: "J" + property string forward5: "Right" + property string rewind5: "Left" + property string openFile: "Ctrl+O" + property string openURI: "Ctrl+Shift+O" + property string quit: "Ctrl+Q" + property string fullscreen: "F" + property string tracks: "Ctrl+T" + property string statsForNerds: "I" + property string forwardFrame: "." + property string backwardFrame: "," + property string cycleSub: "Alt+S" + property string cycleSubBackwards: "Alt+Shift+S" + property string cycleAudio: "A" + property string cycleVideo: "V" + property string cycleVideoAspect: "Shift+A" + property string screenshot: "S" + property string screenshotWithoutSubtitles: "Shift+S" + property string fullScreenshot: "Ctrl+S" + property string nyanCat: "Ctrl+N" + property string decreaseSpeedByPointOne: "[" + property string increaseSpeedByPointOne: "]" + property string halveSpeed: "{" + property string doubleSpeed: "}" + property string increaseVolume: "*" + property string decreaseVolume: "/" + property string mute: "m" + property string increaseScale: "Ctrl+Shift+=" + property string resetScale: "Ctrl+Shift+0" + property string decreaseScale: "Ctrl+Shift+-" + property string customKeybind0: "" + property string customKeybind0Command: "" + property string customKeybind1: "" + property string customKeybind1Command: "" + property string customKeybind2: "" + property string customKeybind2Command: "" + property string customKeybind3: "" + property string customKeybind3Command: "" + property string customKeybind4: "" + property string customKeybind4Command: "" + property string customKeybind5: "" + property string customKeybind5Command: "" + property string customKeybind6: "" + property string customKeybind6Command: "" + property string customKeybind7: "" + property string customKeybind7Command: "" + property string customKeybind8: "" + property string customKeybind8Command: "" + property string customKeybind9: "" + property string customKeybind9Command: "" + } + + property int lastScreenVisibility + + function toggleFullscreen() { + var fs = Window.FullScreen + if (appearance.maximizeInsteadOfFullscreen) { + fs = Window.Maximized } - Item { - id: globalConnections - signal showUI - signal hideUI + if (mainWindow.visibility != fs) { + lastScreenVisibility = mainWindow.visibility + mainWindow.visibility = fs + } else { + mainWindow.visibility = lastScreenVisibility + } + } + + Utils { + id: utils + } + + PlayerBackend { + id: player + anchors.fill: parent + width: parent.width + height: parent.height + logging: loggingSettings.logBackend + z: 1 + + Action { + onTriggered: { + appearance.scaleFactor += 0.1 + } + shortcut: keybinds.increaseScale + } + Action { + onTriggered: { + appearance.scaleFactor = 1 + } + shortcut: keybinds.resetScale + } + Action { + onTriggered: { + appearance.scaleFactor -= 0.1 + } + shortcut: keybinds.decreaseScale } - function getAppearanceValueForTheme(themeName, name) { - switch(themeName) { - case"YouTube": - return youTubeAppearance[name] - case "Niconico": - return nicoNicoAppearance[name] - case "RoosterTeeth": - return roosterTeethAppearance[name] - default: - appearance.themeName = "YouTube" - return youTubeAppearance[name] + function startPlayer() { + //console.info(qmlDebugger.properties(player)) + console.info("OwO!") + + var args = Qt.application.arguments + var len = Qt.application.arguments.length + var argNo = 0 + + if (!appearance.useMpvSubs) { + player.setOption("sub-ass-override", "force") + player.setOption("sub-ass", "off") + player.setOption("sub-border-size", "0") + player.setOption("sub-color", "0.0/0.0/0.0/0.0") + player.setOption("sub-border-color", "0.0/0.0/0.0/0.0") + player.setOption("sub-back-color", "0.0/0.0/0.0/0.0") + } + + if (len > 1) { + for (argNo = 1; argNo < len; argNo++) { + var argument = args[argNo] + if (argument.indexOf("KittehPlayer") !== -1) { + continue + } + if (argument.startsWith("--")) { + argument = argument.substr(2) + if (argument.length > 0) { + var splitArg = argument.split(/=(.+)/) + if (splitArg[0] == "screen" || splitArg[0] == "fs-screen") { + for (var i = 0, len = Qt.application.screens.length; i < len; i++) { + var screen = Qt.application.screens[i] + console.log( + "Screen Name: " + screen["name"] + " Screen Number: " + String( + i)) + if (screen["name"] == splitArg[1] || String( + i) == splitArg[1]) { + console.log("Switching to screen: " + screen["name"]) + mainWindow.screen = screen + mainWindow.width = mainWindow.screen.width / 2 + mainWindow.height = mainWindow.screen.height / 2 + mainWindow.x = mainWindow.screen.virtualX + mainWindow.width / 2 + mainWindow.y = mainWindow.screen.virtualY + mainWindow.height / 2 + if (splitArg[0] == "fs-screen") { + toggleFullscreen() + } + continue + } + } + continue + } + if (splitArg[0] == "fullscreen") { + toggleFullscreen() + continue + } + if (splitArg[1] == undefined || splitArg[1].length == 0) { + splitArg[1] = "yes" + } + player.setOption(splitArg[0], splitArg[1]) + } + } else { + player.playerCommand(Enums.Commands.AppendFile, argument) + } } + } + } + } + + Item { + id: controlsOverlay + anchors.centerIn: player + height: player.height + width: player.width + property bool controlsShowing: true + z: 2 + + Connections { + target: globalConnections + onHideUI: function () { + mouseAreaPlayer.cursorShape = Qt.BlankCursor + } + onShowUI: { + mouseAreaPlayer.cursorShape = Qt.ArrowCursor + } } - Translator { - id: translate + MouseArea { + id: mouseAreaBar + width: parent.width + height: controlsBar.combinedHeight * 1.5 + hoverEnabled: true + anchors { + bottom: parent.bottom + bottomMargin: 0 + } + onEntered: { + mouseAreaPlayerTimer.stop() + } } - Settings { - id: loggingSettings - category: "Logging" - property string logFile: "/tmp/KittehPlayer.log" - property bool logBackend: true - } + MouseArea { + id: mouseAreaPlayer + z: 10 + focus: true + width: parent.width + hoverEnabled: true + propagateComposedEvents: true + property real velocity: 0.0 + property int xStart: 0 + property int xPrev: 0 + anchors { + bottom: mouseAreaBar.top + bottomMargin: 10 + right: parent.right + rightMargin: 0 + left: parent.left + leftMargin: 0 + top: topBar.bottom + topMargin: 0 + } - Settings { - id: backendSettings - category: "Backend" - property string backend: "mpv" - property bool fbo: true - property bool direct: false - } - - Settings { - id: appearance - category: "Appearance" - property bool titleOnlyOnFullscreen: true - property bool useMpvSubs: false - property string themeName: "YouTube" - property string fontName: "Roboto" - property double scaleFactor: 1.0 - property int subtitlesFontSize: 24 - property int uiFadeTimer: 1000 - property bool doubleTapToSeek: true - property double doubleTapToSeekBy: 5 - property bool swipeToResize: true - // Can fix some screen tearing on some devices. - property bool maximizeInsteadOfFullscreen: false - } - - Settings { - id: youTubeAppearance - category: "YouTubeAppearance" - property string mainBackground: "#9C000000" - property string progressBackgroundColor: "#33FFFFFF" - property string progressCachedColor: "#66FFFFFF" - property string buttonColor: "white" - property string buttonHoverColor: "white" - property string progressSliderColor: "red" - property string chapterMarkerColor: "#fc0" - property string volumeSliderBackground: "white" - } - - Settings { - id: nicoNicoAppearance - category: "NicoNicoAppearance" - property string mainBackground: "#9C000000" - property string progressBackgroundColor: "#444" - property string progressCachedColor: "#66FFFFFF" - property string buttonColor: "white" - property string buttonHoverColor: "white" - property string progressSliderColor: "#007cff" - property string chapterMarkerColor: "#fc0" - property string volumeSliderBackground: "#007cff" - } - - Settings { - id: roosterTeethAppearance - category: "RoosterTeethAppearance" - property string mainBackground: "#CC2B333F" - property string progressBackgroundColor: "#444" - property string progressCachedColor: "white" - property string buttonColor: "white" - property string buttonHoverColor: "#c9373f" - property string progressSliderColor: "#c9373f" - property string chapterMarkerColor: "#fc0" - property string volumeSliderBackground: "#c9373f" - } - - Settings { - id: i18n - category: "I18N" - property string language: "english" - } - - Settings { - id: fun - category: "Fun" - property bool nyanCat: false - } - - Settings { - id: keybinds - category: "Keybinds" - property string playPause: "K" - property string forward10: "L" - property string rewind10: "J" - property string forward5: "Right" - property string rewind5: "Left" - property string openFile: "Ctrl+O" - property string openURI: "Ctrl+Shift+O" - property string quit: "Ctrl+Q" - property string fullscreen: "F" - property string tracks: "Ctrl+T" - property string statsForNerds: "I" - property string forwardFrame: "." - property string backwardFrame: "," - property string cycleSub: "Alt+S" - property string cycleSubBackwards: "Alt+Shift+S" - property string cycleAudio: "A" - property string cycleVideo: "V" - property string cycleVideoAspect: "Shift+A" - property string screenshot: "S" - property string screenshotWithoutSubtitles: "Shift+S" - property string fullScreenshot: "Ctrl+S" - property string nyanCat: "Ctrl+N" - property string decreaseSpeedByPointOne: "[" - property string increaseSpeedByPointOne: "]" - property string halveSpeed: "{" - property string doubleSpeed: "}" - property string increaseVolume: "*" - property string decreaseVolume: "/" - property string mute: "m" - property string increaseScale: "Ctrl+Shift+=" - property string resetScale: "Ctrl+Shift+0" - property string decreaseScale: "Ctrl+Shift+-" - property string customKeybind0: "" - property string customKeybind0Command: "" - property string customKeybind1: "" - property string customKeybind1Command: "" - property string customKeybind2: "" - property string customKeybind2Command: "" - property string customKeybind3: "" - property string customKeybind3Command: "" - property string customKeybind4: "" - property string customKeybind4Command: "" - property string customKeybind5: "" - property string customKeybind5Command: "" - property string customKeybind6: "" - property string customKeybind6Command: "" - property string customKeybind7: "" - property string customKeybind7Command: "" - property string customKeybind8: "" - property string customKeybind8Command: "" - property string customKeybind9: "" - property string customKeybind9Command: "" - } - - property int lastScreenVisibility - - function toggleFullscreen() { - var fs = Window.FullScreen - if (appearance.maximizeInsteadOfFullscreen) { - fs = Window.Maximized + Timer { + id: mouseTapTimer + interval: 200 + onTriggered: { + if (topBar.visible) { + globalConnections.hideUI() + } else { + globalConnections.showUI() + } + mouseAreaPlayerTimer.restart() } + } - if (mainWindow.visibility != fs) { - lastScreenVisibility = mainWindow.visibility - mainWindow.visibility = fs + function doubleMouseClick(mouse) { + if (appearance.doubleTapToSeek) { + if (mouse.x > (mouseAreaPlayer.width / 2)) { + player.playerCommand(Enums.Commands.Seek, + String(appearance.doubleTapToSeekBy)) + } else { + player.playerCommand(Enums.Commands.Seek, + "-" + String(appearance.doubleTapToSeekBy)) + } } else { - mainWindow.visibility = lastScreenVisibility + toggleFullscreen() } + } + onClicked: function (mouse) { + xStart = mouse.x + xPrev = mouse.x + velocity = 0 + if (mouseTapTimer.running) { + doubleMouseClick(mouse) + mouseTapTimer.stop() + } else { + mouseTapTimer.restart() + } + } + onReleased: { + var isLongEnough = Math.sqrt(xStart * xStart, + mouse.x * mouse.x) > mainWindow.width * 0.3 + if (velocity > 2 && isLongEnough) { + appearance.scaleFactor += 0.2 + } else if (velocity < -2 && isLongEnough) { + if (appearance.scaleFactor > 0.8) { + appearance.scaleFactor -= 0.2 + } + } + } + onPositionChanged: { + if (mouseAreaPlayer.containsPress) { + var currVel = (mouse.x - xPrev) + velocity = (velocity + currVel) / 2.0 + xPrev = mouse.x + } + if (!topBar.visible) { + globalConnections.showUI() + mouseAreaPlayerTimer.restart() + } + } + Action { + onTriggered: { + toggleFullscreen() + } + shortcut: "Esc" + } + + Timer { + id: mouseAreaPlayerTimer + interval: appearance.uiFadeTimer + running: (!appearance.uiFadeTimer == 0) + repeat: false + onTriggered: { + if (!(appearance.uiFadeTimer == 0)) { + globalConnections.hideUI() + } + } + } } - Utils { - id: utils + Timer { + id: statsUpdater + interval: 1000 + running: statsForNerdsText.visible + repeat: true + onTriggered: { + statsForNerdsText.text = player.getStats() + } } - PlayerBackend { - id: player - anchors.fill: parent - width: parent.width - height: parent.height - logging: loggingSettings.logBackend - z: 1 - - Action { - onTriggered: { - appearance.scaleFactor += 0.1 - } - shortcut: keybinds.increaseScale - } - Action { - onTriggered: { - appearance.scaleFactor = 1 - } - shortcut: keybinds.resetScale - } - Action { - onTriggered: { - appearance.scaleFactor -= 0.1 - } - shortcut: keybinds.decreaseScale - } - - function startPlayer() { - //console.info(qmlDebugger.properties(player)) - console.info("OwO!") - - var args = Qt.application.arguments - var len = Qt.application.arguments.length - var argNo = 0 - - if (!appearance.useMpvSubs) { - player.setOption("sub-ass-override", "force") - player.setOption("sub-ass", "off") - player.setOption("sub-border-size", "0") - player.setOption("sub-color", "0.0/0.0/0.0/0.0") - player.setOption("sub-border-color", "0.0/0.0/0.0/0.0") - player.setOption("sub-back-color", "0.0/0.0/0.0/0.0") - } - - if (len > 1) { - for (argNo = 1; argNo < len; argNo++) { - var argument = args[argNo] - if (argument.indexOf("KittehPlayer") !== -1) { - continue - } - if (argument.startsWith("--")) { - argument = argument.substr(2) - if (argument.length > 0) { - var splitArg = argument.split(/=(.+)/) - if (splitArg[0] == "screen" - || splitArg[0] == "fs-screen") { - for (var i = 0, len = Qt.application.screens.length; i < len; i++) { - var screen = Qt.application.screens[i] - console.log("Screen Name: " + screen["name"] - + " Screen Number: " + String( - i)) - if (screen["name"] == splitArg[1] || String( - i) == splitArg[1]) { - console.log("Switching to screen: " + screen["name"]) - mainWindow.screen = screen - mainWindow.width = mainWindow.screen.width / 2 - mainWindow.height = mainWindow.screen.height / 2 - mainWindow.x = mainWindow.screen.virtualX - + mainWindow.width / 2 - mainWindow.y = mainWindow.screen.virtualY - + mainWindow.height / 2 - if (splitArg[0] == "fs-screen") { - toggleFullscreen() - } - continue - } - } - continue - } - if (splitArg[0] == "fullscreen") { - toggleFullscreen() - continue - } - if (splitArg[1] == undefined - || splitArg[1].length == 0) { - splitArg[1] = "yes" - } - player.setOption(splitArg[0], splitArg[1]) - } - } else { - player.playerCommand(Enums.Commands.AppendFile, - argument) - } - } - } - } + Text { + id: statsForNerdsText + text: "" + color: "white" + visible: false + height: parent.height + width: parent.width + textFormat: Text.RichText + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignTop + renderType: Text.NativeRendering + lineHeight: 1 + font { + family: appearance.fontName + pixelSize: mainWindow.virtualHeight / 50 + } + anchors { + fill: parent + topMargin: mainWindow.virtualHeight / 20 + leftMargin: mainWindow.virtualHeight / 20 + } + Component.onCompleted: { + console.error(statsForNerdsText.lineHeight, font.pixelSize) + } } - Item { - id: controlsOverlay - anchors.centerIn: player - height: player.height - width: player.width - property bool controlsShowing: true - z: 2 - - Connections { - target: globalConnections - onHideUI: function () { - mouseAreaPlayer.cursorShape = Qt.BlankCursor - } - onShowUI: { - mouseAreaPlayer.cursorShape = Qt.ArrowCursor - } - } - - MouseArea { - id: mouseAreaBar - width: parent.width - height: controlsBar.combinedHeight * 1.5 - hoverEnabled: true - anchors { - bottom: parent.bottom - bottomMargin: 0 - } - onEntered: { - mouseAreaPlayerTimer.stop() - } - } - - MouseArea { - id: mouseAreaPlayer - z: 10 - focus: true - width: parent.width - hoverEnabled: true - propagateComposedEvents: true - property real velocity: 0.0 - property int xStart: 0 - property int xPrev: 0 - anchors { - bottom: mouseAreaBar.top - bottomMargin: 10 - right: parent.right - rightMargin: 0 - left: parent.left - leftMargin: 0 - top: topBar.bottom - topMargin: 0 - } - - Timer { - id: mouseTapTimer - interval: 200 - onTriggered: { - if (topBar.visible) { - globalConnections.hideUI() - } else { - globalConnections.showUI() - } - mouseAreaPlayerTimer.restart() - } - } - - function doubleMouseClick(mouse) { - if (appearance.doubleTapToSeek) { - if (mouse.x > (mouseAreaPlayer.width / 2)) { - player.playerCommand(Enums.Commands.Seek, String( - appearance.doubleTapToSeekBy)) - } else { - player.playerCommand(Enums.Commands.Seek, "-" + String( - appearance.doubleTapToSeekBy)) - } - } else { - toggleFullscreen() - } - } - onClicked: function (mouse) { - xStart = mouse.x - xPrev = mouse.x - velocity = 0 - if (mouseTapTimer.running) { - doubleMouseClick(mouse) - mouseTapTimer.stop() - } else { - mouseTapTimer.restart() - } - } - onReleased: { - var isLongEnough = Math.sqrt(xStart*xStart, mouse.x*mouse.x) > mainWindow.width * 0.3 - if (velocity > 2 && isLongEnough) { - appearance.scaleFactor += 0.2 - } else if (velocity < -2 && isLongEnough) { - if (appearance.scaleFactor > 0.8) { - appearance.scaleFactor -= 0.2 - } - } - } - onPositionChanged: { - if (mouseAreaPlayer.containsPress) { - var currVel = (mouse.x - xPrev) - velocity = (velocity + currVel) / 2.0 - xPrev = mouse.x - } - if (!topBar.visible) { - globalConnections.showUI() - mouseAreaPlayerTimer.restart() - } - } - Action { - onTriggered: { - toggleFullscreen() - } - shortcut: "Esc" - } - - Timer { - id: mouseAreaPlayerTimer - interval: appearance.uiFadeTimer - running: (! appearance.uiFadeTimer == 0) - repeat: false - onTriggered: { - if (! (appearance.uiFadeTimer == 0) ) { - globalConnections.hideUI() - } - } - } - } - - Timer { - id: statsUpdater - interval: 1000 - running: statsForNerdsText.visible - repeat: true - onTriggered: { - statsForNerdsText.text = player.getStats() - } - } - - Text { - id: statsForNerdsText - text: "" - color: "white" - visible: false - height: parent.height - width: parent.width - textFormat: Text.RichText - horizontalAlignment: Text.AlignLeft - verticalAlignment: Text.AlignTop - renderType: Text.NativeRendering - lineHeight: 1 - font { - family: appearance.fontName - pixelSize: mainWindow.virtualHeight / 50 - } - anchors { - fill: parent - topMargin: mainWindow.virtualHeight / 20 - leftMargin: mainWindow.virtualHeight / 20 - } - Component.onCompleted: { - console.error(statsForNerdsText.lineHeight, font.pixelSize) - } - } - - MenuTitleBar { - id: topBar - z: 200 - } - - ControlsBar { - id: controlsBar - } + MenuTitleBar { + id: topBar + z: 200 } + + ControlsBar { + id: controlsBar + } + } } diff --git a/src/qmldebugger.cpp b/src/qmldebugger.cpp index f5e93aa..f24e56d 100644 --- a/src/qmldebugger.cpp +++ b/src/qmldebugger.cpp @@ -7,28 +7,28 @@ QString QMLDebugger::properties(QQuickItem* item, bool linebreak) { - const QMetaObject* meta = item->metaObject(); + const QMetaObject* meta = item->metaObject(); - QHash list; - for (int i = 0; i < meta->propertyCount(); i++) { - QMetaProperty property = meta->property(i); - const char* name = property.name(); - QVariant value = item->property(name); - list[name] = value; - } - - QString out; - QHashIterator i(list); - while (i.hasNext()) { - i.next(); - if (!out.isEmpty()) { - out += ", "; - if (linebreak) - out += "\n"; + QHash list; + for (int i = 0; i < meta->propertyCount(); i++) { + QMetaProperty property = meta->property(i); + const char* name = property.name(); + QVariant value = item->property(name); + list[name] = value; } - out.append(i.key()); - out.append(": "); - out.append(i.value().toString()); - } - return out; + + QString out; + QHashIterator i(list); + while (i.hasNext()) { + i.next(); + if (!out.isEmpty()) { + out += ", "; + if (linebreak) + out += "\n"; + } + out.append(i.key()); + out.append(": "); + out.append(i.value().toString()); + } + return out; } diff --git a/src/qmldebugger.h b/src/qmldebugger.h index 14a5071..65e5ec8 100644 --- a/src/qmldebugger.h +++ b/src/qmldebugger.h @@ -2,15 +2,14 @@ #define QMLDEBUGGER_H #include -#include #include +#include -class QMLDebugger : public QObject -{ - Q_OBJECT +class QMLDebugger : public QObject { + Q_OBJECT public: - Q_INVOKABLE static QString properties(QQuickItem* item, - bool linebreak = true); + Q_INVOKABLE static QString properties(QQuickItem* item, + bool linebreak = true); }; #endif // QMLDEBUGGER_H diff --git a/src/qthelper.hpp b/src/qthelper.hpp index fb30e2f..7bd4193 100644 --- a/src/qthelper.hpp +++ b/src/qthelper.hpp @@ -5,205 +5,216 @@ #include -#include -#include -#include #include -#include +#include #include +#include +#include +#include namespace mpv { namespace qt { -// Wrapper around mpv_handle. Does refcounting under the hood. -class Handle -{ - struct container { - container(mpv_handle *h) : mpv(h) {} - ~container() { mpv_terminate_destroy(mpv); } - mpv_handle *mpv; - }; - QSharedPointer sptr; -public: - // Construct a new Handle from a raw mpv_handle with refcount 1. If the - // last Handle goes out of scope, the mpv_handle will be destroyed with - // mpv_terminate_destroy(). - // Never destroy the mpv_handle manually when using this wrapper. You - // will create dangling pointers. Just let the wrapper take care of - // destroying the mpv_handle. - // Never create multiple wrappers from the same raw mpv_handle; copy the - // wrapper instead (that's what it's for). - static Handle FromRawHandle(mpv_handle *handle) { - Handle h; - h.sptr = QSharedPointer(new container(handle)); - return h; - } + // Wrapper around mpv_handle. Does refcounting under the hood. + class Handle { + struct container { + container(mpv_handle* h) + : mpv(h) + { + } + ~container() { mpv_terminate_destroy(mpv); } + mpv_handle* mpv; + }; + QSharedPointer sptr; - // Return the raw handle; for use with the libmpv C API. - operator mpv_handle*() const { return sptr ? (*sptr).mpv : 0; } -}; - -static inline QVariant node_to_variant(const mpv_node *node) -{ - switch (node->format) { - case MPV_FORMAT_STRING: - return QVariant(QString::fromUtf8(node->u.string)); - case MPV_FORMAT_FLAG: - return QVariant(static_cast(node->u.flag)); - case MPV_FORMAT_INT64: - return QVariant(static_cast(node->u.int64)); - case MPV_FORMAT_DOUBLE: - return QVariant(node->u.double_); - case MPV_FORMAT_NODE_ARRAY: { - mpv_node_list *list = node->u.list; - QVariantList qlist; - for (int n = 0; n < list->num; n++) - qlist.append(node_to_variant(&list->values[n])); - return QVariant(qlist); - } - case MPV_FORMAT_NODE_MAP: { - mpv_node_list *list = node->u.list; - QVariantMap qmap; - for (int n = 0; n < list->num; n++) { - qmap.insert(QString::fromUtf8(list->keys[n]), - node_to_variant(&list->values[n])); - } - return QVariant(qmap); - } - default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions) - return QVariant(); - } -} - -struct node_builder { - node_builder(const QVariant& v) { - set(&node_, v); - } - ~node_builder() { - free_node(&node_); - } - mpv_node *node() { return &node_; } -private: - Q_DISABLE_COPY(node_builder) - mpv_node node_; - mpv_node_list *create_list(mpv_node *dst, bool is_map, int num) { - dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY; - mpv_node_list *list = new mpv_node_list(); - dst->u.list = list; - if (!list) - goto err; - list->values = new mpv_node[num](); - if (!list->values) - goto err; - if (is_map) { - list->keys = new char*[num](); - if (!list->keys) - goto err; - } - return list; - err: - free_node(dst); - return NULL; - } - char *dup_qstring(const QString &s) { - QByteArray b = s.toUtf8(); - char *r = new char[b.size() + 1]; - if (r) - std::memcpy(r, b.data(), b.size() + 1); - return r; - } - bool test_type(const QVariant &v, QMetaType::Type t) { - // The Qt docs say: "Although this function is declared as returning - // "QVariant::Type(obsolete), the return value should be interpreted - // as QMetaType::Type." - // So a cast really seems to be needed to avoid warnings (urgh). - return static_cast(v.type()) == static_cast(t); - } - void set(mpv_node *dst, const QVariant &src) { - if (test_type(src, QMetaType::QString)) { - dst->format = MPV_FORMAT_STRING; - dst->u.string = dup_qstring(src.toString()); - if (!dst->u.string) - goto fail; - } else if (test_type(src, QMetaType::Bool)) { - dst->format = MPV_FORMAT_FLAG; - dst->u.flag = src.toBool() ? 1 : 0; - } else if (test_type(src, QMetaType::Int) || - test_type(src, QMetaType::LongLong) || - test_type(src, QMetaType::UInt) || - test_type(src, QMetaType::ULongLong)) + public: + // Construct a new Handle from a raw mpv_handle with refcount 1. If the + // last Handle goes out of scope, the mpv_handle will be destroyed with + // mpv_terminate_destroy(). + // Never destroy the mpv_handle manually when using this wrapper. You + // will create dangling pointers. Just let the wrapper take care of + // destroying the mpv_handle. + // Never create multiple wrappers from the same raw mpv_handle; copy the + // wrapper instead (that's what it's for). + static Handle FromRawHandle(mpv_handle* handle) { - dst->format = MPV_FORMAT_INT64; - dst->u.int64 = src.toLongLong(); - } else if (test_type(src, QMetaType::Double)) { - dst->format = MPV_FORMAT_DOUBLE; - dst->u.double_ = src.toDouble(); - } else if (src.canConvert()) { - QVariantList qlist = src.toList(); - mpv_node_list *list = create_list(dst, false, qlist.size()); - if (!list) - goto fail; - list->num = qlist.size(); - for (int n = 0; n < qlist.size(); n++) - set(&list->values[n], qlist[n]); - } else if (src.canConvert()) { - QVariantMap qmap = src.toMap(); - mpv_node_list *list = create_list(dst, true, qmap.size()); - if (!list) - goto fail; - list->num = qmap.size(); - for (int n = 0; n < qmap.size(); n++) { - list->keys[n] = dup_qstring(qmap.keys()[n]); - if (!list->keys[n]) { - free_node(dst); - goto fail; - } - set(&list->values[n], qmap.values()[n]); - } - } else { - goto fail; + Handle h; + h.sptr = QSharedPointer(new container(handle)); + return h; } - return; - fail: - dst->format = MPV_FORMAT_NONE; - } - void free_node(mpv_node *dst) { - switch (dst->format) { - case MPV_FORMAT_STRING: - delete[] dst->u.string; - break; - case MPV_FORMAT_NODE_ARRAY: - case MPV_FORMAT_NODE_MAP: { - mpv_node_list *list = dst->u.list; - if (list) { - for (int n = 0; n < list->num; n++) { - if (list->keys) - delete[] list->keys[n]; - if (list->values) - free_node(&list->values[n]); - } - delete[] list->keys; - delete[] list->values; - } - delete list; - break; - } - default: ; - } - dst->format = MPV_FORMAT_NONE; - } -}; -/** + // Return the raw handle; for use with the libmpv C API. + operator mpv_handle*() const { return sptr ? (*sptr).mpv : 0; } + }; + + static inline QVariant node_to_variant(const mpv_node* node) + { + switch (node->format) { + case MPV_FORMAT_STRING: + return QVariant(QString::fromUtf8(node->u.string)); + case MPV_FORMAT_FLAG: + return QVariant(static_cast(node->u.flag)); + case MPV_FORMAT_INT64: + return QVariant(static_cast(node->u.int64)); + case MPV_FORMAT_DOUBLE: + return QVariant(node->u.double_); + case MPV_FORMAT_NODE_ARRAY: { + mpv_node_list* list = node->u.list; + QVariantList qlist; + for (int n = 0; n < list->num; n++) + qlist.append(node_to_variant(&list->values[n])); + return QVariant(qlist); + } + case MPV_FORMAT_NODE_MAP: { + mpv_node_list* list = node->u.list; + QVariantMap qmap; + for (int n = 0; n < list->num; n++) { + qmap.insert(QString::fromUtf8(list->keys[n]), + node_to_variant(&list->values[n])); + } + return QVariant(qmap); + } + default: // MPV_FORMAT_NONE, unknown values (e.g. future extensions) + return QVariant(); + } + } + + struct node_builder { + node_builder(const QVariant& v) + { + set(&node_, v); + } + ~node_builder() + { + free_node(&node_); + } + mpv_node* node() { return &node_; } + + private: + Q_DISABLE_COPY(node_builder) + mpv_node node_; + mpv_node_list* create_list(mpv_node* dst, bool is_map, int num) + { + dst->format = is_map ? MPV_FORMAT_NODE_MAP : MPV_FORMAT_NODE_ARRAY; + mpv_node_list* list = new mpv_node_list(); + dst->u.list = list; + if (!list) + goto err; + list->values = new mpv_node[num](); + if (!list->values) + goto err; + if (is_map) { + list->keys = new char*[num](); + if (!list->keys) + goto err; + } + return list; + err: + free_node(dst); + return NULL; + } + char* dup_qstring(const QString& s) + { + QByteArray b = s.toUtf8(); + char* r = new char[b.size() + 1]; + if (r) + std::memcpy(r, b.data(), b.size() + 1); + return r; + } + bool test_type(const QVariant& v, QMetaType::Type t) + { + // The Qt docs say: "Although this function is declared as returning + // "QVariant::Type(obsolete), the return value should be interpreted + // as QMetaType::Type." + // So a cast really seems to be needed to avoid warnings (urgh). + return static_cast(v.type()) == static_cast(t); + } + void set(mpv_node* dst, const QVariant& src) + { + if (test_type(src, QMetaType::QString)) { + dst->format = MPV_FORMAT_STRING; + dst->u.string = dup_qstring(src.toString()); + if (!dst->u.string) + goto fail; + } else if (test_type(src, QMetaType::Bool)) { + dst->format = MPV_FORMAT_FLAG; + dst->u.flag = src.toBool() ? 1 : 0; + } else if (test_type(src, QMetaType::Int) || test_type(src, QMetaType::LongLong) || test_type(src, QMetaType::UInt) || test_type(src, QMetaType::ULongLong)) { + dst->format = MPV_FORMAT_INT64; + dst->u.int64 = src.toLongLong(); + } else if (test_type(src, QMetaType::Double)) { + dst->format = MPV_FORMAT_DOUBLE; + dst->u.double_ = src.toDouble(); + } else if (src.canConvert()) { + QVariantList qlist = src.toList(); + mpv_node_list* list = create_list(dst, false, qlist.size()); + if (!list) + goto fail; + list->num = qlist.size(); + for (int n = 0; n < qlist.size(); n++) + set(&list->values[n], qlist[n]); + } else if (src.canConvert()) { + QVariantMap qmap = src.toMap(); + mpv_node_list* list = create_list(dst, true, qmap.size()); + if (!list) + goto fail; + list->num = qmap.size(); + for (int n = 0; n < qmap.size(); n++) { + list->keys[n] = dup_qstring(qmap.keys()[n]); + if (!list->keys[n]) { + free_node(dst); + goto fail; + } + set(&list->values[n], qmap.values()[n]); + } + } else { + goto fail; + } + return; + fail: + dst->format = MPV_FORMAT_NONE; + } + void free_node(mpv_node* dst) + { + switch (dst->format) { + case MPV_FORMAT_STRING: + delete[] dst->u.string; + break; + case MPV_FORMAT_NODE_ARRAY: + case MPV_FORMAT_NODE_MAP: { + mpv_node_list* list = dst->u.list; + if (list) { + for (int n = 0; n < list->num; n++) { + if (list->keys) + delete[] list->keys[n]; + if (list->values) + free_node(&list->values[n]); + } + delete[] list->keys; + delete[] list->values; + } + delete list; + break; + } + default:; + } + dst->format = MPV_FORMAT_NONE; + } + }; + + /** * RAII wrapper that calls mpv_free_node_contents() on the pointer. */ -struct node_autofree { - mpv_node *ptr; - node_autofree(mpv_node *a_ptr) : ptr(a_ptr) {} - ~node_autofree() { mpv_free_node_contents(ptr); } -}; + struct node_autofree { + mpv_node* ptr; + node_autofree(mpv_node* a_ptr) + : ptr(a_ptr) + { + } + ~node_autofree() { mpv_free_node_contents(ptr); } + }; -/** + /** * Return the given property as mpv_node converted to QVariant, or QVariant() * on error. * @@ -211,140 +222,144 @@ struct node_autofree { * * @param name the property name */ -static inline QVariant get_property_variant(mpv_handle *ctx, const QString &name) -{ - mpv_node node; - if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0) - return QVariant(); - node_autofree f(&node); - return node_to_variant(&node); -} + static inline QVariant get_property_variant(mpv_handle* ctx, const QString& name) + { + mpv_node node; + if (mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node) < 0) + return QVariant(); + node_autofree f(&node); + return node_to_variant(&node); + } -/** + /** * Set the given property as mpv_node converted from the QVariant argument. * @deprecated use set_property() instead */ -static inline int set_property_variant(mpv_handle *ctx, const QString &name, - const QVariant &v) -{ - node_builder node(v); - return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); -} + static inline int set_property_variant(mpv_handle* ctx, const QString& name, + const QVariant& v) + { + node_builder node(v); + return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); + } -/** + /** * Set the given option as mpv_node converted from the QVariant argument. * * @deprecated use set_property() instead */ -static inline int set_option_variant(mpv_handle *ctx, const QString &name, - const QVariant &v) -{ - node_builder node(v); - return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); -} + static inline int set_option_variant(mpv_handle* ctx, const QString& name, + const QVariant& v) + { + node_builder node(v); + return mpv_set_option(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); + } -/** + /** * mpv_command_node() equivalent. Returns QVariant() on error (and * unfortunately, the same on success). * * @deprecated use command() instead */ -static inline QVariant command_variant(mpv_handle *ctx, const QVariant &args) -{ - node_builder node(args); - mpv_node res; - if (mpv_command_node(ctx, node.node(), &res) < 0) - return QVariant(); - node_autofree f(&res); - return node_to_variant(&res); -} + static inline QVariant command_variant(mpv_handle* ctx, const QVariant& args) + { + node_builder node(args); + mpv_node res; + if (mpv_command_node(ctx, node.node(), &res) < 0) + return QVariant(); + node_autofree f(&res); + return node_to_variant(&res); + } -/** + /** * This is used to return error codes wrapped in QVariant for functions which * return QVariant. * * You can use get_error() or is_error() to extract the error status from a * QVariant value. */ -struct ErrorReturn -{ - /** + struct ErrorReturn { + /** * enum mpv_error value (or a value outside of it if ABI was extended) */ - int error; + int error; - ErrorReturn() : error(0) {} - explicit ErrorReturn(int err) : error(err) {} -}; + ErrorReturn() + : error(0) + { + } + explicit ErrorReturn(int err) + : error(err) + { + } + }; -/** + /** * Return the mpv error code packed into a QVariant, or 0 (success) if it's not * an error value. * * @return error code (<0) or success (>=0) */ -static inline int get_error(const QVariant &v) -{ - if (!v.canConvert()) - return 0; - return v.value().error; -} + static inline int get_error(const QVariant& v) + { + if (!v.canConvert()) + return 0; + return v.value().error; + } -/** + /** * Return whether the QVariant carries a mpv error code. */ -static inline bool is_error(const QVariant &v) -{ - return get_error(v) < 0; -} + static inline bool is_error(const QVariant& v) + { + return get_error(v) < 0; + } -/** + /** * Return the given property as mpv_node converted to QVariant, or QVariant() * on error. * * @param name the property name * @return the property value, or an ErrorReturn with the error code */ -static inline QVariant get_property(mpv_handle *ctx, const QString &name) -{ - mpv_node node; - int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node); - if (err < 0) - return QVariant::fromValue(ErrorReturn(err)); - node_autofree f(&node); - return node_to_variant(&node); -} + static inline QVariant get_property(mpv_handle* ctx, const QString& name) + { + mpv_node node; + int err = mpv_get_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, &node); + if (err < 0) + return QVariant::fromValue(ErrorReturn(err)); + node_autofree f(&node); + return node_to_variant(&node); + } -/** + /** * Set the given property as mpv_node converted from the QVariant argument. * * @return mpv error code (<0 on error, >= 0 on success) */ -static inline int set_property(mpv_handle *ctx, const QString &name, - const QVariant &v) -{ - node_builder node(v); - return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); -} + static inline int set_property(mpv_handle* ctx, const QString& name, + const QVariant& v) + { + node_builder node(v); + return mpv_set_property(ctx, name.toUtf8().data(), MPV_FORMAT_NODE, node.node()); + } -/** + /** * mpv_command_node() equivalent. * * @param args command arguments, with args[0] being the command name as string * @return the property value, or an ErrorReturn with the error code */ -static inline QVariant command(mpv_handle *ctx, const QVariant &args) -{ - node_builder node(args); - mpv_node res; - int err = mpv_command_node(ctx, node.node(), &res); - if (err < 0) - return QVariant::fromValue(ErrorReturn(err)); - node_autofree f(&res); - return node_to_variant(&res); -} - + static inline QVariant command(mpv_handle* ctx, const QVariant& args) + { + node_builder node(args); + mpv_node res; + int err = mpv_command_node(ctx, node.node(), &res); + if (err < 0) + return QVariant::fromValue(ErrorReturn(err)); + node_autofree f(&res); + return node_to_variant(&res); + } } } diff --git a/src/registerTypes.cpp b/src/registerTypes.cpp index a7f08f2..88f3316 100644 --- a/src/registerTypes.cpp +++ b/src/registerTypes.cpp @@ -9,30 +9,30 @@ #include "utils.hpp" #include -void registerTypes() { - QSettings settings; +void registerTypes() +{ + QSettings settings; + qmlRegisterUncreatableMetaObject( + Enums::staticMetaObject, "player", 1, 0, "Enums", "Error: only enums"); + qRegisterMetaType("Enums.PlayStatus"); + qRegisterMetaType("Enums.VolumeStatus"); + qRegisterMetaType("Enums.Backends"); + qRegisterMetaType("Enums.Commands"); + qmlRegisterType("player", 1, 0, "Process"); - qmlRegisterUncreatableMetaObject( - Enums::staticMetaObject, "player", 1, 0, "Enums", "Error: only enums"); - qRegisterMetaType("Enums.PlayStatus"); - qRegisterMetaType("Enums.VolumeStatus"); - qRegisterMetaType("Enums.Backends"); - qRegisterMetaType("Enums.Commands"); - qmlRegisterType("player", 1, 0, "Process"); + qmlRegisterType("player", 1, 0, "QMLDebugger"); + qmlRegisterType("player", 1, 0, "ThumbnailCache"); - qmlRegisterType("player", 1, 0, "QMLDebugger"); - qmlRegisterType("player", 1, 0, "ThumbnailCache"); - - qmlRegisterType("player", 1, 0, "Utils"); + qmlRegisterType("player", 1, 0, "Utils"); #ifndef DISABLE_MPV_RENDER_API - if (settings.value("Backend/fbo", true).toBool()) { - qmlRegisterType("player", 1, 0, "PlayerBackend"); - } else { - qmlRegisterType("player", 1, 0, "PlayerBackend"); - } + if (settings.value("Backend/fbo", true).toBool()) { + qmlRegisterType("player", 1, 0, "PlayerBackend"); + } else { + qmlRegisterType("player", 1, 0, "PlayerBackend"); + } #else - qmlRegisterType("player", 1, 0, "PlayerBackend"); + qmlRegisterType("player", 1, 0, "PlayerBackend"); #endif } \ No newline at end of file diff --git a/src/setenv_mingw.hpp b/src/setenv_mingw.hpp index b547c64..e47044c 100644 --- a/src/setenv_mingw.hpp +++ b/src/setenv_mingw.hpp @@ -36,53 +36,52 @@ #include #include -int -setenv(const char* var, const char* value, int overwrite) +int setenv(const char* var, const char* value, int overwrite) { - /* Core implementation for both setenv() and unsetenv() functions; + /* Core implementation for both setenv() and unsetenv() functions; * at the outset, assume that the requested operation may fail. */ - int retval = -1; + int retval = -1; - /* The specified "var" name MUST be non-NULL, not a zero-length + /* The specified "var" name MUST be non-NULL, not a zero-length * string, and must not include any '=' character. */ - if (var && *var && (strchr(var, '=') == NULL)) { - /* A properly named variable may be added to, removed from, + if (var && *var && (strchr(var, '=') == NULL)) { + /* A properly named variable may be added to, removed from, * or modified within the environment, ONLY if "overwrite" * mode is enabled, OR if the named variable does not yet * exist... */ - if (overwrite || getenv(var) == NULL) { - /* ... in which cases, we convert the specified name and + if (overwrite || getenv(var) == NULL) { + /* ... in which cases, we convert the specified name and * value into the appropriate form for use with putenv(), * (noting that we accept a NULL "value" as equivalent to * a zero-length string, which renders putenv() as the * equivalent of unsetenv()). */ - const char* fmt = "%s=%s"; - const char* val = value ? value : ""; - char buf[1 + snprintf(NULL, 0, fmt, var, val)]; - snprintf(buf, sizeof(buf), fmt, var, val); - if ((retval = putenv(buf)) != 0) - /* + const char* fmt = "%s=%s"; + const char* val = value ? value : ""; + char buf[1 + snprintf(NULL, 0, fmt, var, val)]; + snprintf(buf, sizeof(buf), fmt, var, val); + if ((retval = putenv(buf)) != 0) + /* * If putenv() returns non-zero, indicating failure, the * most probable explanation is that there wasn't enough * free memory; ensure that errno is set accordingly. */ - errno = ENOMEM; - } else - /* The named variable already exists, and overwrite mode + errno = ENOMEM; + } else + /* The named variable already exists, and overwrite mode * was not enabled; there is nothing to be done. */ - retval = 0; - } else - /* The specified environment variable name was invalid. + retval = 0; + } else + /* The specified environment variable name was invalid. */ - errno = EINVAL; + errno = EINVAL; - /* Succeed or fail, "retval" has now been set to indicate the + /* Succeed or fail, "retval" has now been set to indicate the * appropriate status for return. */ - return retval; + return retval; } diff --git a/src/utils.cpp b/src/utils.cpp index abea03d..6c7808a 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,19 +1,19 @@ #include "utils.hpp" +#include "logger.h" +#include "spdlog/logger.h" #include #include #include #include #include #include -#include "logger.h" -#include "spdlog/logger.h" #if (defined(__linux__) || defined(__FreeBSD__)) && ENABLE_X11 +#include // IWYU pragma: keep #include // IWYU pragma: keep #include // IWYU pragma: keep #include // IWYU pragma: keep #include // IWYU pragma: keep -#include // IWYU pragma: keep #undef Bool #endif @@ -23,130 +23,121 @@ namespace Utils { QString getPlatformName() { - QGuiApplication* qapp = - qobject_cast(QCoreApplication::instance()); - return qapp->platformName(); + QGuiApplication* qapp = qobject_cast(QCoreApplication::instance()); + return qapp->platformName(); } -void -launchAboutQt() +void launchAboutQt() { - QApplication* qapp = - qobject_cast(QCoreApplication::instance()); - qapp->aboutQt(); + QApplication* qapp = qobject_cast(QCoreApplication::instance()); + qapp->aboutQt(); } void updateAppImage() { - QString program = - QProcessEnvironment::systemEnvironment().value("APPDIR", "") + - "/usr/bin/appimageupdatetool"; - QProcess updater; - updater.setProcessChannelMode(QProcess::ForwardedChannels); - updater.start(program, - QStringList() << QProcessEnvironment::systemEnvironment().value( - "APPIMAGE", "")); - updater.waitForFinished(); - qApp->exit(); + QString program = QProcessEnvironment::systemEnvironment().value("APPDIR", "") + "/usr/bin/appimageupdatetool"; + QProcess updater; + updater.setProcessChannelMode(QProcess::ForwardedChannels); + updater.start(program, + QStringList() << QProcessEnvironment::systemEnvironment().value( + "APPIMAGE", "")); + updater.waitForFinished(); + qApp->exit(); } // https://www.youtube.com/watch?v=nXaxk27zwlk&feature=youtu.be&t=56m34s inline int fast_mod(const int input, const int ceil) { - return input >= ceil ? input % ceil : input; + return input >= ceil ? input % ceil : input; } QString createTimestamp(const int seconds) { - const int s = fast_mod(seconds, 60); - const int m = fast_mod(seconds, 3600) / 60; - const int h = fast_mod(seconds, 86400) / 3600; + const int s = fast_mod(seconds, 60); + const int m = fast_mod(seconds, 3600) / 60; + const int h = fast_mod(seconds, 86400) / 3600; - if (h > 0) { - return QString::asprintf("%02d:%02d:%02d", h, m, s); - } else { - return QString::asprintf("%02d:%02d", m, s); - } + if (h > 0) { + return QString::asprintf("%02d:%02d:%02d", h, m, s); + } else { + return QString::asprintf("%02d:%02d", m, s); + } } -void -SetScreensaver(WId wid, bool on) +void SetScreensaver(WId wid, bool on) { - QProcess xdgScreensaver; - xdgScreensaver.setProcessChannelMode(QProcess::ForwardedChannels); - if (on) { - utilsLogger->info("Enabling screensaver."); - xdgScreensaver.start("xdg-screensaver", - QStringList() << "resume" << QString::number(wid)); - } else { - utilsLogger->info("Disabling screensaver."); - xdgScreensaver.start("xdg-screensaver", - QStringList() << "suspend" << QString::number(wid)); - } - xdgScreensaver.waitForFinished(); + QProcess xdgScreensaver; + xdgScreensaver.setProcessChannelMode(QProcess::ForwardedChannels); + if (on) { + utilsLogger->info("Enabling screensaver."); + xdgScreensaver.start("xdg-screensaver", + QStringList() << "resume" << QString::number(wid)); + } else { + utilsLogger->info("Disabling screensaver."); + xdgScreensaver.start("xdg-screensaver", + QStringList() << "suspend" << QString::number(wid)); + } + xdgScreensaver.waitForFinished(); } -void -SetDPMS(bool on) +void SetDPMS(bool on) { #if defined(__linux__) || defined(__FreeBSD__) - if (getPlatformName() != "xcb") { - return; - } - QProcess xsetProcess; - xsetProcess.setProcessChannelMode(QProcess::ForwardedChannels); - if (on) { - utilsLogger->info("Enabled DPMS."); - xsetProcess.start("xset", - QStringList() << "s" - << "on" - << "+dpms"); - } else { - utilsLogger->info("Disabled DPMS."); - xsetProcess.start("xset", - QStringList() << "s" - << "off" - << "-dpms"); - } - xsetProcess.waitForFinished(); + if (getPlatformName() != "xcb") { + return; + } + QProcess xsetProcess; + xsetProcess.setProcessChannelMode(QProcess::ForwardedChannels); + if (on) { + utilsLogger->info("Enabled DPMS."); + xsetProcess.start("xset", + QStringList() << "s" + << "on" + << "+dpms"); + } else { + utilsLogger->info("Disabled DPMS."); + xsetProcess.start("xset", + QStringList() << "s" + << "off" + << "-dpms"); + } + xsetProcess.waitForFinished(); #else - utilsLogger->error("Can't set DPMS for platform: {}", - getPlatformName().toUtf8().constData()); + utilsLogger->error("Can't set DPMS for platform: {}", + getPlatformName().toUtf8().constData()); #endif } -void -AlwaysOnTop(WId wid, bool on) +void AlwaysOnTop(WId wid, bool on) { #if (defined(__linux__) || defined(__FreeBSD__)) && ENABLE_X11 - Display* display = QX11Info::display(); - XEvent event; - event.xclient.type = ClientMessage; - event.xclient.serial = 0; - event.xclient.send_event = true; - event.xclient.display = display; - event.xclient.window = wid; - event.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", False); - event.xclient.format = 32; + Display* display = QX11Info::display(); + XEvent event; + event.xclient.type = ClientMessage; + event.xclient.serial = 0; + event.xclient.send_event = true; + event.xclient.display = display; + event.xclient.window = wid; + event.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", False); + event.xclient.format = 32; - event.xclient.data.l[0] = on; - event.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_ABOVE", False); - event.xclient.data.l[2] = 0; - event.xclient.data.l[3] = 0; - event.xclient.data.l[4] = 0; + event.xclient.data.l[0] = on; + event.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_ABOVE", False); + event.xclient.data.l[2] = 0; + event.xclient.data.l[3] = 0; + event.xclient.data.l[4] = 0; - XSendEvent(display, - DefaultRootWindow(display), - False, - SubstructureRedirectMask | SubstructureNotifyMask, - &event); + XSendEvent(display, + DefaultRootWindow(display), + False, + SubstructureRedirectMask | SubstructureNotifyMask, + &event); #else - utilsLogger->error("Can't set on top for platform: {}", - getPlatformName().toUtf8().constData()); + utilsLogger->error("Can't set on top for platform: {}", + getPlatformName().toUtf8().constData()); #endif } } - diff --git a/src/utils.hpp b/src/utils.hpp index a75b742..d7e32f3 100644 --- a/src/utils.hpp +++ b/src/utils.hpp @@ -6,34 +6,27 @@ namespace Utils { Q_NAMESPACE -void -SetDPMS(bool on); -void -SetScreensaver(WId wid, bool on); -void -AlwaysOnTop(WId wid, bool on); +void SetDPMS(bool on); +void SetScreensaver(WId wid, bool on); +void AlwaysOnTop(WId wid, bool on); QString createTimestamp(int seconds); -void -launchAboutQt(); -void -updateAppImage(); +void launchAboutQt(); +void updateAppImage(); } -class UtilsClass : public QObject -{ - Q_OBJECT +class UtilsClass : public QObject { + Q_OBJECT public slots: - void SetDPMS(bool on) { Utils::SetDPMS(on); }; - void AlwaysOnTop(WId wid, bool on) { Utils::AlwaysOnTop(wid, on); }; - void launchAboutQt() { Utils::launchAboutQt(); }; - void updateAppImage() { Utils::updateAppImage(); }; + void SetDPMS(bool on) { Utils::SetDPMS(on); }; + void AlwaysOnTop(WId wid, bool on) { Utils::AlwaysOnTop(wid, on); }; + void launchAboutQt() { Utils::launchAboutQt(); }; + void updateAppImage() { Utils::updateAppImage(); }; - - QString createTimestamp(int seconds) - { - return Utils::createTimestamp(seconds); - }; + QString createTimestamp(int seconds) + { + return Utils::createTimestamp(seconds); + }; }; #endif