feat: add subtitle track selection, secondary subtitle and delay
This commit is contained in:
1 parent
1eb67b829c
commit
48cac86d60
5 files changed
+320
-4
No files matched your search
+53
-2
@@ -65,6 +65,27 @@ Kirigami.Page {
|
||||
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)))
|
||||
}
|
||||
|
||||
function isSubtitleFile(url: url): bool {
|
||||
return /\.(srt|ass|ssa|vtt|sub|sup)$/i.test(url.toString())
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialog
|
||||
@@ -111,8 +132,14 @@ Kirigami.Page {
|
||||
DropArea {
|
||||
anchors.fill: parent
|
||||
onDropped: drop => {
|
||||
if (drop.hasUrls) {
|
||||
mpv.loadFile(drop.urls[0])
|
||||
if (!drop.hasUrls) {
|
||||
return
|
||||
}
|
||||
const url = drop.urls[0]
|
||||
if (mpv.fileLoaded && page.isSubtitleFile(url)) {
|
||||
mpv.addSubtitleFile(url)
|
||||
} else {
|
||||
mpv.loadFile(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -165,6 +192,30 @@ Kirigami.Page {
|
||||
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
|
||||
x: subtitleButton.width - width
|
||||
y: -height - Kirigami.Units.smallSpacing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls as QQC2
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.ki18n
|
||||
|
||||
QQC2.Popup {
|
||||
id: popup
|
||||
|
||||
required property MpvItem mpv
|
||||
|
||||
// [{ id, text }] with "Off" first, shared by both track pickers.
|
||||
readonly property var trackModel: [{ id: 0, text: KI18n.i18nc("@item:inlistbox no subtitle", "Off") }]
|
||||
.concat(mpv.subtitleTracks.map(track => ({ id: track.id, text: trackLabel(track) })))
|
||||
|
||||
function trackLabel(track: var): string {
|
||||
const name = track.title || (track.external ? track.externalFilename.split("/").pop() : "")
|
||||
const lang = track.lang ? `[${track.lang}]` : ""
|
||||
const description = [name, lang].filter(part => part.length > 0).join(" ")
|
||||
return KI18n.i18nc("@item:inlistbox subtitle track number and description", "%1: %2",
|
||||
track.id, description || KI18n.i18nc("@item:inlistbox", "Untitled"))
|
||||
}
|
||||
|
||||
function indexOfTrack(id: int): int {
|
||||
return Math.max(0, trackModel.findIndex(entry => entry.id === id))
|
||||
}
|
||||
|
||||
padding: Kirigami.Units.largeSpacing
|
||||
|
||||
contentItem: Kirigami.FormLayout {
|
||||
QQC2.ComboBox {
|
||||
id: primaryCombo
|
||||
Kirigami.FormData.label: KI18n.i18nc("@label:listbox", "Subtitle:")
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 16
|
||||
model: popup.trackModel
|
||||
textRole: "text"
|
||||
valueRole: "id"
|
||||
onActivated: popup.mpv.subtitleId = currentValue
|
||||
|
||||
// Always reflect what mpv actually selected, also after the user picks an entry.
|
||||
Binding on currentIndex {
|
||||
value: popup.indexOfTrack(popup.mpv.subtitleId)
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ComboBox {
|
||||
id: secondaryCombo
|
||||
Kirigami.FormData.label: KI18n.i18nc("@label:listbox", "Secondary subtitle:")
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 16
|
||||
model: popup.trackModel
|
||||
textRole: "text"
|
||||
valueRole: "id"
|
||||
onActivated: popup.mpv.secondarySubtitleId = currentValue
|
||||
|
||||
Binding on currentIndex {
|
||||
value: popup.indexOfTrack(popup.mpv.secondarySubtitleId)
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: KI18n.i18nc("@label:spinbox", "Delay:")
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
QQC2.SpinBox {
|
||||
id: delaySpin
|
||||
from: -600000
|
||||
to: 600000
|
||||
stepSize: 100
|
||||
editable: true
|
||||
textFromValue: (value, locale) => KI18n.i18nc("@item:valuesuffix milliseconds", "%1 ms", value)
|
||||
valueFromText: (text, locale) => parseInt(text) || 0
|
||||
onValueModified: popup.mpv.subtitleDelay = value / 1000
|
||||
|
||||
Binding on value {
|
||||
value: Math.round(popup.mpv.subtitleDelay * 1000)
|
||||
restoreMode: Binding.RestoreNone
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.ToolButton {
|
||||
enabled: popup.mpv.subtitleDelay !== 0
|
||||
icon.name: "edit-undo"
|
||||
text: KI18n.i18nc("@action:button", "Reset delay")
|
||||
display: QQC2.AbstractButton.IconOnly
|
||||
onClicked: popup.mpv.subtitleDelay = 0
|
||||
|
||||
QQC2.ToolTip.text: text
|
||||
QQC2.ToolTip.visible: hovered
|
||||
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
icon.name: "add-subtitle"
|
||||
text: KI18n.i18nc("@action:button", "Load Subtitle File…")
|
||||
onClicked: subtitleDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: subtitleDialog
|
||||
title: KI18n.i18nc("@title:window", "Load Subtitle")
|
||||
currentFolder: {
|
||||
const url = popup.mpv.currentUrl.toString()
|
||||
return url.substring(0, url.lastIndexOf("/"))
|
||||
}
|
||||
nameFilters: [
|
||||
KI18n.i18nc("@item:inlistbox file filter", "Subtitle files (*.srt *.ass *.ssa *.vtt *.sub *.sup)"),
|
||||
KI18n.i18nc("@item:inlistbox file filter", "All files (*)")
|
||||
]
|
||||
onAccepted: popup.mpv.addSubtitleFile(selectedFile)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user