import QtQuick 2.0 import QtQuick.Controls 2.3 import QtQuick.Window 2.2 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 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 } 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 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 } } 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.hypot(xStart, 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 } } }