Skip to content

Commit 6e2e649

Browse files
feat: allow setting custom playback speed per video
By default all videos will follow the global playback speed closes: #71 Co-authored-by: Arthur Lobo <[email protected]>
1 parent 49b7e57 commit 6e2e649

File tree

3 files changed

+86
-20
lines changed

3 files changed

+86
-20
lines changed

package/contents/ui/code/utils.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
function parseCompat(cfgStr) {
22
try {
3-
return videosConfig = JSON.parse(cfgStr)
3+
return (videosConfig = JSON.parse(cfgStr).map((video) => {
4+
video.playbackRate = video.playbackRate ?? 0.0;
5+
return video;
6+
}));
47
} catch (e) {
58
console.log("Possibly old config, parsing as multi-line string", e)
69
const lines = cfgStr.trim().split("\n");
@@ -22,11 +25,13 @@ function createVideo(filename) {
2225
this.enabled = true;
2326
this.duration = 0;
2427
this.customDuration = 0;
28+
this.playbackRate = 0.0;
2529
return {
2630
"filename": this.filename,
2731
"enabled": this.enabled,
2832
"duration": this.duration,
29-
"customDuration": this.customDuration
33+
"customDuration": this.customDuration,
34+
"playbackRate": this.playbackRate,
3035
}
3136
}
3237

package/contents/ui/config.qml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,16 @@ Kirigami.FormLayout {
199199
Utils.updateConfig()
200200
}
201201
}
202+
Button {
203+
icon.name: "preferences-other"
204+
enabled: true
205+
onClicked: {
206+
dialogPlaybackRateSpeed.value = videosConfig[modelData].playbackRate
207+
videoConfig.filename = videosConfig[modelData].filename
208+
videoConfig.index = index
209+
videoConfig.open()
210+
}
211+
}
202212
}
203213
Button{
204214
icon.name: "edit-delete-remove"
@@ -689,6 +699,53 @@ Kirigami.FormLayout {
689699
}
690700
}
691701

702+
Kirigami.Dialog {
703+
id: videoConfig
704+
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
705+
title: i18n("Video Settings")
706+
padding: Kirigami.Units.largeSpacing
707+
708+
property int index
709+
property real speed
710+
property string filename: ""
711+
712+
Kirigami.FormLayout {
713+
RowLayout {
714+
Kirigami.FormData.label: i18n("Playback speed:")
715+
Slider {
716+
id: dialogPlaybackRateSpeed
717+
from: 0
718+
to: 2
719+
value: videoConfig.speed
720+
onValueChanged: {
721+
videoConfig.speed = value
722+
}
723+
}
724+
Label {
725+
text: parseFloat(dialogPlaybackRateSpeed.value).toFixed(2)
726+
font.features: { "tnum": 1 }
727+
}
728+
Button {
729+
icon.name: "edit-undo-symbolic"
730+
flat: true
731+
onClicked: {
732+
dialogPlaybackRateSpeed.value = 0.0
733+
}
734+
ToolTip.text: i18n("Reset to default")
735+
ToolTip.visible: hovered
736+
}
737+
Kirigami.ContextualHelpButton {
738+
toolTipText: i18n("A value other than 0.0 overrides the global Playback speed for this video.")
739+
}
740+
}
741+
}
742+
743+
onAccepted: {
744+
videosConfig[index].playbackRate = speed
745+
Utils.updateConfig()
746+
}
747+
}
748+
692749
Component.onCompleted: {
693750
let candidate = root.parent;
694751
while (candidate) {

package/contents/ui/main.qml

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ WallpaperItem {
3636
property string videoUrls: main.configuration.VideoUrls
3737
property var videosConfig: Utils.parseCompat(videoUrls)
3838
property int currentVideoIndex: main.configuration.LastVideoIndex < videosConfig.length ? main.configuration.LastVideoIndex : 0
39-
property string currentSource: videosConfig[currentVideoIndex].filename
39+
property var currentSource: videosConfig.length > 0 ? videosConfig[currentVideoIndex] : Utils.createVideo("")
4040
property int pauseBatteryLevel: main.configuration.PauseBatteryLevel
4141
property bool shouldPlay: {
4242
if (lockScreenMode) {
@@ -116,7 +116,7 @@ WallpaperItem {
116116
// Crossfade must not be longer than the shortest video or the fade becomes glitchy
117117
// we don't know the length until a video gets played, so the crossfade duration
118118
// will decrease below the configured duration if needed as videos get played
119-
property int crossfadeMinDuration: parseInt(Math.max(Math.min(player1.duration, player2.duration) / 3, 1) )
119+
property int crossfadeMinDuration: parseInt(Math.max(Math.min(player1.actualDuration, player2.actualDuration) / 3, 1) )
120120
property int crossfadeDuration: Math.min(main.configuration.CrossfadeDuration, crossfadeMinDuration)
121121
property bool crossfadeEnabled: main.configuration.CrossfadeEnabled
122122
property bool tick: true
@@ -171,12 +171,13 @@ WallpaperItem {
171171
// console.error(videoUrls);
172172
if (videosConfig.length == 0) {
173173
main.stop()
174+
main.currentSource.filename = ""
174175
} else {
175176
nextVideo()
176177
tick = true
177178
player2.pause()
178179
videoOutput.opacity = 1
179-
player1.source = currentSource
180+
player1.playerSource = currentSource
180181
player1.play()
181182
}
182183
}
@@ -224,15 +225,15 @@ WallpaperItem {
224225
}
225226

226227
function nextVideo() {
227-
printLog("- Video ended " + currentVideoIndex + ": " + currentSource)
228+
printLog("- Video ended " + currentVideoIndex + ": " + currentSource.filename)
228229
currentVideoIndex = (currentVideoIndex + 1) % videosConfig.length
229230
if (randomMode && currentVideoIndex === 0) {
230231
const shuffledVideos = Utils.shuffleArray(videosConfig)
231-
currentSource = shuffledVideos[currentVideoIndex].filename || ''
232+
currentSource = shuffledVideos[currentVideoIndex]
232233
} else {
233-
currentSource = videosConfig[currentVideoIndex].filename || ''
234+
currentSource = videosConfig[currentVideoIndex]
234235
}
235-
printLog("- Next " + currentVideoIndex + ": " + currentSource)
236+
printLog("- Next " + currentVideoIndex + ": " + currentSource.filename)
236237
}
237238

238239
Rectangle {
@@ -280,24 +281,26 @@ WallpaperItem {
280281

281282
MediaPlayer {
282283
id: player1
283-
source: currentSource
284+
property var playerSource: main.currentSource
285+
property int actualDuration: duration / playbackRate
286+
source: playerSource.filename ?? ""
284287
videoOutput: videoOutput
285288
audioOutput: audioOutput
286-
playbackRate: main.playbackRate
289+
playbackRate: playerSource.playbackRate || main.playbackRate
287290
loops: (videosConfig.length > 1) ?
288291
1 : crossfadeEnabled ?
289292
1 : MediaPlayer.Infinite
290293
onPositionChanged: (position) => {
291294
main.lastVideoPosition = position
292295
if (!tick) return
293296
// BUG This doesn't seem to work the first time???
294-
if (position > duration - crossfadeDuration) {
297+
if ((position / playbackRate) > actualDuration - crossfadeDuration) {
295298
if (crossfadeEnabled) {
296299
nextVideo()
297300
printLog("player1 fading out");
298301
videoOutput.opacity = 0
299302
tick = false
300-
player2.source = currentSource
303+
player2.playerSource = currentSource
301304
volumeOutput2 = 1
302305
player2.play()
303306
}
@@ -307,7 +310,7 @@ WallpaperItem {
307310
if (status == MediaPlayer.EndOfMedia) {
308311
if (crossfadeEnabled) return
309312
nextVideo()
310-
source = currentSource
313+
playerSource = currentSource
311314
play()
312315
}
313316
if (status == MediaPlayer.LoadedMedia && player1.seekable) {
@@ -331,20 +334,23 @@ WallpaperItem {
331334

332335
MediaPlayer {
333336
id: player2
337+
property var playerSource: main.currentSource
338+
property int actualDuration: duration / playbackRate
339+
source: playerSource.filename ?? ""
334340
videoOutput: videoOutput2
335341
audioOutput: audioOutput2
336-
playbackRate: main.playbackRate
342+
playbackRate: playerSource.playbackRate || main.playbackRate
337343
loops: 1
338344
onPositionChanged: (position) => {
339345
main.lastVideoPosition = position
340346
if (tick) return
341-
if (position > duration - crossfadeDuration) {
347+
if ((position / playbackRate) > actualDuration - crossfadeDuration) {
342348
printLog("player1 fading in");
343349
videoOutput.opacity = 1
344350
nextVideo()
345351
tick = true
346352
volumeOutput2 = 0
347-
player1.source = currentSource
353+
player1.playerSource = currentSource
348354
player1.play()
349355
}
350356
}
@@ -402,8 +408,6 @@ WallpaperItem {
402408
function stop() {
403409
player1.stop()
404410
player2.stop()
405-
player1.source = ""
406-
player2.source = ""
407411
}
408412

409413
function updateState() {
@@ -510,7 +514,7 @@ WallpaperItem {
510514
tick = true
511515
player2.pause()
512516
videoOutput.opacity = 1
513-
player1.source = currentSource
517+
player1.playerSource = currentSource
514518
player1.play()
515519
}
516520
},

0 commit comments

Comments
 (0)