[Backend] Make most of the commands be handled by C++.
This commit is contained in:
parent
68fd98e69f
commit
f436f77ba5
|
@ -23,7 +23,7 @@ include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
|||
|
||||
set(SOURCES
|
||||
src/main.cpp
|
||||
src/mpvobject.cpp
|
||||
src/MpvPlayerBackend.cpp
|
||||
src/filesavedialog.cpp
|
||||
src/fileopendialog.cpp
|
||||
)
|
||||
|
|
|
@ -2,7 +2,7 @@ TARGET = KittehPlayer
|
|||
|
||||
TEMPLATE = app
|
||||
QT += qml quickcontrols2 widgets core-private gui-private
|
||||
SOURCES += src/main.cpp src/mpvobject.cpp src/filesavedialog.cpp src/fileopendialog.cpp
|
||||
SOURCES += src/main.cpp src/MpvPlayerBackend.cpp src/filesavedialog.cpp src/fileopendialog.cpp
|
||||
|
||||
CONFIG += release
|
||||
#CONFIG+=qtquickcompiler
|
||||
|
@ -29,7 +29,7 @@ unix {
|
|||
|
||||
INSTALLS += target
|
||||
|
||||
HEADERS += src/mpvobject.h src/filesavedialog.h src/fileopendialog.h
|
||||
HEADERS += src/MpvPlayerBackend.h src/filesavedialog.h src/fileopendialog.h
|
||||
|
||||
|
||||
DISTFILES += KittehPlayer.desktop KittehPlayer.png README.md LICENSE.txt
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
#include <stdexcept>
|
||||
#include <clocale>
|
||||
|
||||
#include "mpvobject.h"
|
||||
#include "MpvPlayerBackend.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QQuickWindow>
|
||||
|
@ -15,12 +15,12 @@ namespace
|
|||
|
||||
void wakeup(void *ctx)
|
||||
{
|
||||
QMetaObject::invokeMethod((MpvObject*)ctx, "on_mpv_events", Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod((MpvPlayerBackend*)ctx, "on_mpv_events", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void on_mpv_redraw(void *ctx)
|
||||
{
|
||||
MpvObject::on_update(ctx);
|
||||
MpvPlayerBackend::on_update(ctx);
|
||||
}
|
||||
|
||||
static void *get_proc_address_mpv(void *ctx, const char *name)
|
||||
|
@ -38,11 +38,11 @@ static void *get_proc_address_mpv(void *ctx, const char *name)
|
|||
|
||||
class MpvRenderer : public QQuickFramebufferObject::Renderer
|
||||
{
|
||||
MpvObject *obj;
|
||||
MpvPlayerBackend *obj;
|
||||
|
||||
public:
|
||||
|
||||
MpvRenderer(MpvObject *new_obj)
|
||||
MpvRenderer(MpvPlayerBackend *new_obj)
|
||||
: obj{new_obj}
|
||||
{
|
||||
}
|
||||
|
@ -99,7 +99,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
MpvObject::MpvObject(QQuickItem * parent)
|
||||
MpvPlayerBackend::MpvPlayerBackend(QQuickItem * parent)
|
||||
: QQuickFramebufferObject(parent), mpv{mpv_create()}, mpv_gl(nullptr)
|
||||
{
|
||||
|
||||
|
@ -140,12 +140,12 @@ MpvObject::MpvObject(QQuickItem * parent)
|
|||
if (mpv_initialize(mpv) < 0)
|
||||
throw std::runtime_error("could not initialize mpv context");
|
||||
|
||||
connect(this, &MpvObject::onUpdate, this, &MpvObject::doUpdate,
|
||||
connect(this, &MpvPlayerBackend::onUpdate, this, &MpvPlayerBackend::doUpdate,
|
||||
Qt::QueuedConnection);
|
||||
|
||||
}
|
||||
|
||||
MpvObject::~MpvObject()
|
||||
MpvPlayerBackend::~MpvPlayerBackend()
|
||||
{
|
||||
if (mpv_gl)
|
||||
{
|
||||
|
@ -155,47 +155,101 @@ MpvObject::~MpvObject()
|
|||
mpv_terminate_destroy(mpv);
|
||||
}
|
||||
|
||||
void MpvObject::on_update(void *ctx)
|
||||
void MpvPlayerBackend::on_update(void *ctx)
|
||||
{
|
||||
MpvObject *self = (MpvObject *)ctx;
|
||||
MpvPlayerBackend *self = (MpvPlayerBackend *)ctx;
|
||||
emit self->onUpdate();
|
||||
}
|
||||
|
||||
// connected to onUpdate(); signal makes sure it runs on the GUI thread
|
||||
void MpvObject::doUpdate()
|
||||
void MpvPlayerBackend::doUpdate()
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
QVariant MpvObject::getProperty(const QString &name) const
|
||||
QVariant MpvPlayerBackend::getProperty(const QString &name) const
|
||||
{
|
||||
return mpv::qt::get_property_variant(mpv, name);
|
||||
}
|
||||
|
||||
|
||||
void MpvObject::command(const QVariant& params)
|
||||
void MpvPlayerBackend::command(const QVariant& params)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, params);
|
||||
}
|
||||
|
||||
void MpvObject::setProperty(const QString& name, const QVariant& value)
|
||||
void MpvPlayerBackend::setProperty(const QString& name, const QVariant& value)
|
||||
{
|
||||
mpv::qt::set_property_variant(mpv, name, value);
|
||||
}
|
||||
|
||||
void MpvObject::setOption(const QString& name, const QVariant& value)
|
||||
void MpvPlayerBackend::setOption(const QString& name, const QVariant& value)
|
||||
{
|
||||
mpv::qt::set_option_variant(mpv, name, value);
|
||||
}
|
||||
|
||||
void MpvObject::launchAboutQt()
|
||||
void MpvPlayerBackend::launchAboutQt()
|
||||
{
|
||||
QApplication *qapp = qobject_cast<QApplication *>(QCoreApplication::instance());
|
||||
qapp->aboutQt();
|
||||
}
|
||||
|
||||
void MpvObject::on_mpv_events()
|
||||
void MpvPlayerBackend::togglePlayPause()
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "cycle" << "pause");
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::toggleMute()
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "cycle" << "mute");
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::nextAudioTrack()
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "cycle" << "audio");
|
||||
}
|
||||
void MpvPlayerBackend::nextSubtitleTrack()
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "cycle" << "sub");
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::nextVideoTrack()
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "cycle" << "video");
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::prevPlaylistItem()
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "playlist-prev");
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::nextPlaylistItem()
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "playlist-next" << "force");
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::loadFile(const QVariant &filename)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "loadfile" << filename);
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::setVolume(const QVariant &volume)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "set" << "volume" << volume);
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::addVolume(const QVariant &volume)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "add" << "volume" << volume);
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::seek(const QVariant &seekTime)
|
||||
{
|
||||
mpv::qt::command_variant(mpv, QVariantList() << "seek" << seekTime);
|
||||
}
|
||||
|
||||
void MpvPlayerBackend::on_mpv_events()
|
||||
{
|
||||
while (mpv) {
|
||||
mpv_event *event = mpv_wait_event(mpv, 0);
|
||||
|
@ -208,7 +262,7 @@ void MpvObject::on_mpv_events()
|
|||
|
||||
}
|
||||
|
||||
void MpvObject::handle_mpv_event(mpv_event *event)
|
||||
void MpvPlayerBackend::handle_mpv_event(mpv_event *event)
|
||||
{
|
||||
switch (event->event_id) {
|
||||
case MPV_EVENT_PROPERTY_CHANGE: {
|
||||
|
@ -268,10 +322,10 @@ void MpvObject::handle_mpv_event(mpv_event *event)
|
|||
}
|
||||
}
|
||||
|
||||
QQuickFramebufferObject::Renderer *MpvObject::createRenderer() const
|
||||
QQuickFramebufferObject::Renderer *MpvPlayerBackend::createRenderer() const
|
||||
{
|
||||
window()->setPersistentOpenGLContext(true);
|
||||
window()->setPersistentSceneGraph(true);
|
||||
|
||||
return new MpvRenderer(const_cast<MpvObject *>(this));
|
||||
return new MpvRenderer(const_cast<MpvPlayerBackend *>(this));
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
#ifndef MPVOBJECT_H
|
||||
#define MPVOBJECT_H
|
||||
#ifndef MpvPlayerBackend_H
|
||||
#define MpvPlayerBackend_H
|
||||
|
||||
#include <mpv/client.h>
|
||||
#include <mpv/render_gl.h>
|
||||
|
@ -13,7 +13,7 @@
|
|||
|
||||
class MpvRenderer;
|
||||
|
||||
class MpvObject : public QQuickFramebufferObject
|
||||
class MpvPlayerBackend : public QQuickFramebufferObject
|
||||
{
|
||||
Q_OBJECT
|
||||
mpv_handle *mpv;
|
||||
|
@ -24,13 +24,24 @@ class MpvObject : public QQuickFramebufferObject
|
|||
public:
|
||||
static void on_update(void *ctx);
|
||||
|
||||
MpvObject(QQuickItem * parent = 0);
|
||||
virtual ~MpvObject();
|
||||
MpvPlayerBackend(QQuickItem * parent = 0);
|
||||
virtual ~MpvPlayerBackend();
|
||||
virtual Renderer *createRenderer() const;
|
||||
|
||||
|
||||
public slots:
|
||||
void launchAboutQt();
|
||||
void togglePlayPause();
|
||||
void toggleMute();
|
||||
void nextAudioTrack();
|
||||
void nextVideoTrack();
|
||||
void nextSubtitleTrack();
|
||||
void prevPlaylistItem();
|
||||
void nextPlaylistItem();
|
||||
void setVolume(const QVariant& volume);
|
||||
void addVolume(const QVariant& volume);
|
||||
void loadFile(const QVariant& filename);
|
||||
void seek(const QVariant& seekTime);
|
||||
void command(const QVariant& params);
|
||||
void setProperty(const QString& name, const QVariant& value);
|
||||
void setOption(const QString& name, const QVariant& value);
|
|
@ -10,7 +10,7 @@
|
|||
#include <QProcessEnvironment>
|
||||
#include "fileopendialog.h"
|
||||
#include "filesavedialog.h"
|
||||
#include "mpvobject.h"
|
||||
#include "MpvPlayerBackend.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include "setenv_mingw.hpp"
|
||||
|
@ -48,17 +48,12 @@ setenv("QT_QPA_PLATFORMTHEME", "gtk3", 0);
|
|||
setenv("PATH", newpath.toUtf8().constData(), 1);
|
||||
|
||||
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
||||
qmlRegisterType<MpvObject>("player", 1, 0, "MpvObject");
|
||||
qmlRegisterType<MpvPlayerBackend>("player", 1, 0, "PlayerBackend");
|
||||
qmlRegisterType<FileOpenDialog>("player", 1, 0, "FileOpenDialog");
|
||||
qmlRegisterType<FileSaveDialog>("player", 1, 0, "FileSaveDialog");
|
||||
|
||||
std::setlocale(LC_NUMERIC, "C");
|
||||
|
||||
/*QQuickView *view = new QQuickView();
|
||||
view->setResizeMode(QQuickView::SizeRootObjectToView);
|
||||
view->setSource(QUrl("qrc:///player/main.qml"));
|
||||
view->show();*/
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
#ifdef QRC_SOURCE_PATH
|
||||
RuntimeQML *rt = new RuntimeQML(&engine, QRC_SOURCE_PATH"/qml.qrc");
|
||||
|
|
|
@ -77,7 +77,7 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
|
||||
MpvObject {
|
||||
PlayerBackend {
|
||||
id: player
|
||||
anchors.fill: parent
|
||||
width: parent.width
|
||||
|
@ -157,7 +157,7 @@ ApplicationWindow {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
player.command(["loadfile", argument])
|
||||
player.loadFile(argument)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -307,7 +307,7 @@ ApplicationWindow {
|
|||
title: translate.getTranslation("URL_FILE_PATH", i18n.language)
|
||||
standardButtons: StandardButton.Cancel | StandardButton.Open
|
||||
onAccepted: {
|
||||
player.command(["loadfile", pathText.text])
|
||||
player.loadFile("loadfile")
|
||||
pathText.text = ""
|
||||
}
|
||||
TextField {
|
||||
|
@ -348,7 +348,7 @@ ApplicationWindow {
|
|||
cursorShape: controlsBar.visible ? Qt.ArrowCursor : Qt.BlankCursor
|
||||
onClicked: {
|
||||
if (appearance.clickToPause) {
|
||||
player.command(["cycle", "pause"])
|
||||
player.togglePlayPause()
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
|
@ -515,35 +515,35 @@ ApplicationWindow {
|
|||
Action {
|
||||
text: translate.getTranslation("PLAY_PAUSE", i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["cycle", "pause"])
|
||||
player.togglePlayPause()
|
||||
}
|
||||
shortcut: String(keybinds.playPause)
|
||||
}
|
||||
Action {
|
||||
text: translate.getTranslation("REWIND_10S", i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["seek", "-10"])
|
||||
player.seek("-10")
|
||||
}
|
||||
shortcut: keybinds.rewind10
|
||||
}
|
||||
Action {
|
||||
text: translate.getTranslation("FORWARD_10S", i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["seek", "10"])
|
||||
player.seek("10")
|
||||
}
|
||||
shortcut: keybinds.forward10
|
||||
}
|
||||
Action {
|
||||
text: translate.getTranslation("FORWARD_5S", i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["seek", "-5"])
|
||||
player.seek("-5")
|
||||
}
|
||||
shortcut: keybinds.rewind5
|
||||
}
|
||||
Action {
|
||||
text: translate.getTranslation("FORWARD_5S", i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["seek", "5"])
|
||||
player.seek("5")
|
||||
}
|
||||
shortcut: keybinds.forward5
|
||||
}
|
||||
|
@ -613,7 +613,7 @@ ApplicationWindow {
|
|||
text: translate.getTranslation("CYCLE_AUDIO_TRACK",
|
||||
i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["cycle", "audio"])
|
||||
player.nextAudioTrack()
|
||||
}
|
||||
shortcut: keybinds.cycleAudio
|
||||
}
|
||||
|
@ -621,7 +621,7 @@ ApplicationWindow {
|
|||
text: translate.getTranslation("INCREASE_VOLUME",
|
||||
i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["add", "volume", "2"])
|
||||
player.addVolume("2")
|
||||
}
|
||||
shortcut: keybinds.increaseVolume
|
||||
}
|
||||
|
@ -629,14 +629,14 @@ ApplicationWindow {
|
|||
text: translate.getTranslation("DECREASE_VOLUME",
|
||||
i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["add", "volume", "-2"])
|
||||
player.addVolume("-2")
|
||||
}
|
||||
shortcut: keybinds.decreaseVolume
|
||||
}
|
||||
Action {
|
||||
text: translate.getTranslation("MUTE_VOLUME", i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["cycle", "mute"])
|
||||
player.toggleMute()
|
||||
}
|
||||
shortcut: keybinds.mute
|
||||
}
|
||||
|
@ -658,7 +658,7 @@ ApplicationWindow {
|
|||
Action {
|
||||
text: translate.getTranslation("CYCLE_VIDEO", i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["cycle", "video"])
|
||||
player.nextVideoTrack()
|
||||
}
|
||||
shortcut: keybinds.cycleVideo
|
||||
}
|
||||
|
@ -680,18 +680,10 @@ ApplicationWindow {
|
|||
text: translate.getTranslation("CYCLE_SUB_TRACK",
|
||||
i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["cycle", "sub"])
|
||||
player.nextSubtitleTrack()
|
||||
}
|
||||
shortcut: keybinds.cycleSub
|
||||
}
|
||||
Action {
|
||||
text: translate.getTranslation("CYCLE_SUB_TRACK_BACKWARDS",
|
||||
i18n.language)
|
||||
onTriggered: {
|
||||
player.command(["cycle", "sub", "down"])
|
||||
}
|
||||
shortcut: keybinds.cycleSubBackwards
|
||||
}
|
||||
Action {
|
||||
text: translate.getTranslation("TOGGLE_MPV_SUBS",
|
||||
i18n.language)
|
||||
|
@ -1147,7 +1139,7 @@ ApplicationWindow {
|
|||
visible: false
|
||||
width: 0
|
||||
onClicked: {
|
||||
player.command(["playlist-prev"])
|
||||
player.prevPlaylistItem()
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
|
@ -1164,7 +1156,7 @@ ApplicationWindow {
|
|||
anchors.bottom: parent.bottom
|
||||
anchors.left: playlistPrevButton.right
|
||||
onClicked: {
|
||||
player.command(["cycle", "pause"])
|
||||
player.togglePlayPause()
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
|
@ -1181,7 +1173,7 @@ ApplicationWindow {
|
|||
anchors.bottom: parent.bottom
|
||||
anchors.left: playPauseButton.right
|
||||
onClicked: {
|
||||
player.command(["playlist-next", "force"])
|
||||
player.prevPlaylistItem()
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
|
@ -1198,7 +1190,7 @@ ApplicationWindow {
|
|||
anchors.bottom: parent.bottom
|
||||
anchors.left: playlistNextButton.right
|
||||
onClicked: {
|
||||
player.command(["cycle", "mute"])
|
||||
player.toggleMute()
|
||||
}
|
||||
background: Rectangle {
|
||||
color: "transparent"
|
||||
|
@ -1223,8 +1215,8 @@ ApplicationWindow {
|
|||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
onMoved: {
|
||||
player.command(["set", "volume", Math.round(
|
||||
volumeBar.value).toString()])
|
||||
player.setVolume(Math.round(
|
||||
volumeBar.value).toString())
|
||||
}
|
||||
|
||||
handle: Rectangle {
|
||||
|
|
|
@ -30,7 +30,6 @@ var translations = {
|
|||
CYCLE_VIDEO: "Cycle video",
|
||||
SUBTITLES: "Subtitles",
|
||||
CYCLE_SUB_TRACK: "Cycle Subtitle Track",
|
||||
CYCLE_SUB_TRACK_BACKWARDS: "Cycle Subtitle Track Backwards",
|
||||
CYCLE_AUDIO_TRACK: "Cycle Audio Track",
|
||||
TOGGLE_MPV_SUBS: "Toggle MPV Subtitles",
|
||||
VIEW: "View",
|
||||
|
@ -73,7 +72,6 @@ var translations = {
|
|||
CYCLE_VIDEO: "Siguiente vídeo",
|
||||
SUBTITLES: "Subtítulos",
|
||||
CYCLE_SUB_TRACK: "Siguientes subtítulos",
|
||||
CYCLE_SUB_TRACK_BACKWARDS: "Subtítulos previos",
|
||||
CYCLE_AUDIO_TRACK: "Siguiente pista de audio",
|
||||
TOGGLE_MPV_SUBS: "Activar subtítulos MPV",
|
||||
VIEW: "Ver",
|
||||
|
@ -116,7 +114,6 @@ var translations = {
|
|||
CYCLE_VIDEO: "Wechsle Video",
|
||||
SUBTITLES: "Untertitel",
|
||||
CYCLE_SUB_TRACK: "Wechsle Untertitel Spur",
|
||||
CYCLE_SUB_TRACK_BACKWARDS: "Wechsle Untertitel Spur Rückwärts",
|
||||
CYCLE_AUDIO_TRACK: "Cycle Audio Track",
|
||||
TOGGLE_MPV_SUBS: "Schalte MPV Untertitel An/Aus",
|
||||
VIEW: "Ansicht",
|
||||
|
@ -159,7 +156,6 @@ var translations = {
|
|||
CYCLE_VIDEO: "Changer de vidéo",
|
||||
SUBTITLES: "Sous-titres",
|
||||
CYCLE_SUB_TRACK: "Changer de piste de sous-titres",
|
||||
CYCLE_SUB_TRACK_BACKWARDS: "Changer de piste de sous-titres pour la précédente",
|
||||
CYCLE_AUDIO_TRACK: "Basculer de piste audio",
|
||||
TOGGLE_MPV_SUBS: "Basculer les sous-titres MPV",
|
||||
VIEW: "Voir",
|
||||
|
@ -202,7 +198,6 @@ var translations = {
|
|||
CYCLE_VIDEO: "Ripeti video",
|
||||
SUBTITLES: "Sottotitoli",
|
||||
CYCLE_SUB_TRACK: "Ripeti traccia sottotitoli",
|
||||
CYCLE_SUB_TRACK_BACKWARDS: "Ripeti traccia sottotili al contrario",
|
||||
CYCLE_AUDIO_TRACK: "Ripeti traccia audio",
|
||||
TOGGLE_MPV_SUBS: "Attiva MPV Sottotitoli",
|
||||
VIEW: "Vedi",
|
||||
|
@ -245,7 +240,6 @@ var translations = {
|
|||
CYCLE_VIDEO: "Зациклить воспроизведение видео",
|
||||
SUBTITLES: "Субтитры",
|
||||
CYCLE_SUB_TRACK: "Зациклить дорожку субтитров",
|
||||
CYCLE_SUB_TRACK_BACKWARDS: "Зациклить дорожку субтитров в обратную сторону",
|
||||
CYCLE_AUDIO_TRACK: "Зациклить аудиодорожку",
|
||||
TOGGLE_MPV_SUBS: "Переключить MPV-субтитры",
|
||||
VIEW: "Вид",
|
||||
|
@ -288,7 +282,6 @@ var translations = {
|
|||
CYCLE_VIDEO: "Sirkuler Gjennom Videospor",
|
||||
SUBTITLES: "Undertekster",
|
||||
CYCLE_SUB_TRACK: "Sirkuler Gjennom Undertekster",
|
||||
CYCLE_SUB_TRACK_BACKWARDS: "Sirkuler Gjennom Undertekster (Baklengs)",
|
||||
CYCLE_AUDIO_TRACK: "Sirkuler Gjennom Lydspor",
|
||||
TOGGLE_MPV_SUBS: "Skru Av/På MPV Undertekster",
|
||||
VIEW: "Vis",
|
||||
|
|
Loading…
Reference in a new issue