253 lines
8.3 KiB
QML
253 lines
8.3 KiB
QML
import QtQuick
|
|
import QtQuick.Controls as QQC2
|
|
import QtQuick.Dialogs
|
|
import QtQuick.Layouts
|
|
import org.kde.kirigami as Kirigami
|
|
import org.kde.ki18n
|
|
|
|
Kirigami.Page {
|
|
id: page
|
|
|
|
property url initialUrl
|
|
readonly property alias mediaTitle: mpv.mediaTitle
|
|
readonly property alias translator: translator
|
|
|
|
signal aboutRequested()
|
|
signal settingsRequested()
|
|
signal fullScreenToggled()
|
|
|
|
title: KI18n.i18nc("@title", "Player")
|
|
padding: 0
|
|
|
|
actions: [
|
|
Kirigami.Action {
|
|
icon.name: "document-open"
|
|
text: KI18n.i18nc("@action", "Open File…")
|
|
shortcut: StandardKey.Open
|
|
onTriggered: fileDialog.open()
|
|
},
|
|
Kirigami.Action {
|
|
icon.name: "view-fullscreen"
|
|
text: KI18n.i18nc("@action", "Full Screen")
|
|
displayHint: Kirigami.DisplayHint.IconOnly
|
|
shortcut: "F"
|
|
onTriggered: page.fullScreenToggled()
|
|
},
|
|
Kirigami.Action {
|
|
icon.name: "configure"
|
|
text: KI18n.i18nc("@action", "Settings")
|
|
displayHint: Kirigami.DisplayHint.AlwaysHide
|
|
shortcut: StandardKey.Preferences
|
|
onTriggered: page.settingsRequested()
|
|
},
|
|
Kirigami.Action {
|
|
icon.name: "help-about"
|
|
text: KI18n.i18nc("@action", "About QtPotato")
|
|
displayHint: Kirigami.DisplayHint.AlwaysHide
|
|
onTriggered: page.aboutRequested()
|
|
}
|
|
]
|
|
|
|
Component.onCompleted: {
|
|
if (page.initialUrl.toString().length > 0) {
|
|
mpv.loadFile(page.initialUrl)
|
|
}
|
|
}
|
|
|
|
function formatTime(totalSeconds: real): string {
|
|
const s = Math.max(0, Math.floor(totalSeconds))
|
|
const h = Math.floor(s / 3600)
|
|
const m = Math.floor((s % 3600) / 60)
|
|
const sec = String(s % 60).padStart(2, "0")
|
|
return h > 0 ? `${h}:${String(m).padStart(2, "0")}:${sec}` : `${m}:${sec}`
|
|
}
|
|
|
|
Shortcut {
|
|
sequence: "Space"
|
|
onActivated: mpv.togglePause()
|
|
}
|
|
Shortcut {
|
|
sequence: "Left"
|
|
onActivated: mpv.seek(mpv.position - 5)
|
|
}
|
|
Shortcut {
|
|
sequence: "Right"
|
|
onActivated: mpv.seek(mpv.position + 5)
|
|
}
|
|
// Same keys as mpv: z shows subtitles earlier, x later.
|
|
Shortcut {
|
|
sequence: "Z"
|
|
enabled: mpv.fileLoaded
|
|
onActivated: page.nudgeSubtitleDelay(-0.1)
|
|
}
|
|
Shortcut {
|
|
sequence: "X"
|
|
enabled: mpv.fileLoaded
|
|
onActivated: page.nudgeSubtitleDelay(0.1)
|
|
}
|
|
|
|
function nudgeSubtitleDelay(seconds: real) {
|
|
const delay = mpv.subtitleDelay + seconds
|
|
mpv.subtitleDelay = delay
|
|
mpv.showText(KI18n.i18nc("@info:osd", "Subtitle delay: %1 ms", Math.round(delay * 1000)))
|
|
}
|
|
|
|
TranslationService {
|
|
id: translator
|
|
onTranslated: (subtitleUrl, fromCache) => {
|
|
mpv.showSecondarySubtitleFile(subtitleUrl,
|
|
KI18n.i18nc("@item:inlistbox subtitle track, %1 is a language", "%1 (translated)", QtPotatoSettings.targetLanguage))
|
|
mpv.showText(fromCache
|
|
? KI18n.i18nc("@info:osd", "Loaded the saved translation")
|
|
: KI18n.i18nc("@info:osd", "Translation ready"))
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: mpv
|
|
// A translation belongs to the file it was started for.
|
|
function onCurrentUrlChanged() {
|
|
translator.cancel()
|
|
}
|
|
}
|
|
|
|
function isSubtitleFile(url: url): bool {
|
|
return /\.(srt|ass|ssa|vtt|sub|sup)$/i.test(url.toString())
|
|
}
|
|
|
|
FileDialog {
|
|
id: fileDialog
|
|
title: KI18n.i18nc("@title:window", "Open Video")
|
|
nameFilters: [
|
|
KI18n.i18nc("@item:inlistbox file filter", "Video files (*.mkv *.mp4 *.webm *.avi *.mov *.m4v *.ts *.flv *.wmv)"),
|
|
KI18n.i18nc("@item:inlistbox file filter", "All files (*)")
|
|
]
|
|
onAccepted: mpv.loadFile(selectedFile)
|
|
}
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
spacing: 0
|
|
|
|
MpvItem {
|
|
id: mpv
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: "black"
|
|
visible: !mpv.fileLoaded
|
|
z: -1
|
|
}
|
|
|
|
Kirigami.PlaceholderMessage {
|
|
anchors.centerIn: parent
|
|
width: parent.width - Kirigami.Units.gridUnit * 4
|
|
visible: !mpv.fileLoaded
|
|
icon.name: "video-symbolic"
|
|
text: KI18n.i18nc("@info:placeholder", "No video open")
|
|
explanation: KI18n.i18nc("@info:placeholder", "Open a file or drop one here")
|
|
helpfulAction: page.actions[0]
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: mpv.togglePause()
|
|
onDoubleClicked: page.fullScreenToggled()
|
|
}
|
|
|
|
DropArea {
|
|
anchors.fill: parent
|
|
onDropped: drop => {
|
|
if (!drop.hasUrls) {
|
|
return
|
|
}
|
|
const url = drop.urls[0]
|
|
if (mpv.fileLoaded && page.isSubtitleFile(url)) {
|
|
mpv.addSubtitleFile(url)
|
|
} else {
|
|
mpv.loadFile(url)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
QQC2.ToolBar {
|
|
Layout.fillWidth: true
|
|
position: QQC2.ToolBar.Footer
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
spacing: Kirigami.Units.smallSpacing
|
|
|
|
QQC2.ToolButton {
|
|
enabled: mpv.fileLoaded
|
|
focusPolicy: Qt.NoFocus
|
|
icon.name: mpv.paused || !mpv.fileLoaded ? "media-playback-start" : "media-playback-pause"
|
|
text: mpv.paused || !mpv.fileLoaded ? KI18n.i18nc("@action:button", "Play") : KI18n.i18nc("@action:button", "Pause")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
onClicked: mpv.togglePause()
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
}
|
|
|
|
QQC2.Label {
|
|
text: page.formatTime(seekBar.pressed ? seekBar.value : mpv.position)
|
|
font.features: { "tnum": 1 }
|
|
}
|
|
|
|
QQC2.Slider {
|
|
id: seekBar
|
|
Layout.fillWidth: true
|
|
enabled: mpv.fileLoaded && mpv.duration > 0
|
|
focusPolicy: Qt.NoFocus
|
|
from: 0
|
|
to: Math.max(mpv.duration, 1)
|
|
onMoved: mpv.seek(value)
|
|
|
|
// Follow playback, but not while the user is dragging the handle.
|
|
Binding on value {
|
|
value: mpv.position
|
|
when: !seekBar.pressed
|
|
restoreMode: Binding.RestoreNone
|
|
}
|
|
}
|
|
|
|
QQC2.Label {
|
|
text: page.formatTime(mpv.duration)
|
|
font.features: { "tnum": 1 }
|
|
}
|
|
|
|
QQC2.ToolButton {
|
|
id: subtitleButton
|
|
enabled: mpv.fileLoaded
|
|
focusPolicy: Qt.NoFocus
|
|
checkable: true
|
|
checked: subtitlePopup.visible
|
|
icon.name: "media-view-subtitles-symbolic"
|
|
text: KI18n.i18nc("@action:button", "Subtitles")
|
|
display: QQC2.AbstractButton.IconOnly
|
|
onClicked: subtitlePopup.visible ? subtitlePopup.close() : subtitlePopup.open()
|
|
|
|
QQC2.ToolTip.text: text
|
|
QQC2.ToolTip.visible: hovered && !subtitlePopup.visible
|
|
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
|
|
|
SubtitlePopup {
|
|
id: subtitlePopup
|
|
objectName: "subtitlePopup"
|
|
mpv: mpv
|
|
translator: translator
|
|
onSettingsRequested: page.settingsRequested()
|
|
x: subtitleButton.width - width
|
|
y: -height - Kirigami.Units.smallSpacing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|