Skip to content

Commit 73fc437

Browse files
committed
feat: alternative playback speed
refs: #76
1 parent 9acb60d commit 73fc437

6 files changed

Lines changed: 178 additions & 22 deletions

File tree

package/contents/config/main.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@
8181
<entry name="EffectsShowBlur" type="String">
8282
<default></default>
8383
</entry>
84+
<entry name="EffectsAlternativeSpeed" type="String">
85+
<default></default>
86+
</entry>
8487
<entry name="BlurAnimationDuration" type="Int">
8588
<default>300</default>
8689
</entry>
@@ -139,5 +142,17 @@
139142
<entry name="FillBlurRadius" type="Int">
140143
<default>64</default>
141144
</entry>
145+
<!-- Alternative playback rate
146+
0: when existing a maximized or full-screen windows
147+
1: when a visible window is active
148+
2: when a window is visible
149+
3: never
150+
-->
151+
<entry name="AlternativePlaybackRateMode" type="Int">
152+
<default>3</default>
153+
</entry>
154+
<entry name="AlternativePlaybackRate" type="Double">
155+
<default>0.5</default>
156+
</entry>
142157
</group>
143158
</kcfg>

package/contents/ui/FadePlayer.qml

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ Item {
2424
property bool resumeLastVideo: true
2525
property int fillBlurRadius: 32
2626
property bool fillBlur: true
27+
property real alternativePlaybackRateGlobal: 0.5
28+
property bool useAlternativePlaybackRate: false
2729

2830
// Crossfade must not be longer than the shortest video or the fade becomes glitchy
2931
// we don't know the length until a video gets played, so the crossfade duration
@@ -100,7 +102,12 @@ Item {
100102
anchors.fill: parent
101103
property var playerSource: root.currentSource
102104
property int actualDuration: duration / playbackRate
103-
playbackRate: playerSource.playbackRate || root.playbackRate
105+
playbackRate: {
106+
if (root.useAlternativePlaybackRate) {
107+
return playerSource.alternativePlaybackRate || root.alternativePlaybackRateGlobal;
108+
}
109+
return playerSource.playbackRate || root.playbackRate;
110+
}
104111
source: playerSource.filename ?? ""
105112
volume: root.volume
106113
muted: root.muted
@@ -190,7 +197,12 @@ Item {
190197
anchors.fill: parent
191198
property var playerSource: Utils.createVideo("")
192199
property int actualDuration: duration / playbackRate
193-
playbackRate: playerSource.playbackRate || root.playbackRate
200+
playbackRate: {
201+
if (root.useAlternativePlaybackRate) {
202+
return playerSource.alternativePlaybackRate || root.alternativePlaybackRateGlobal;
203+
}
204+
return playerSource.playbackRate || root.playbackRate;
205+
}
194206
source: playerSource.filename ?? ""
195207
volume: root.volume
196208
muted: root.muted

package/contents/ui/VideosModel.qml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ Item {
2626
"duration": 0,
2727
"customDuration": 0,
2828
"playbackRate": 0.0,
29+
"alternativePlaybackRate": 0.0,
2930
"loop": false
3031
});
3132
updated();

package/contents/ui/code/utils.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ function parseCompat(cfgStr) {
33
try {
44
JSON.parse(cfgStr).forEach((video) => {
55
video.playbackRate = video.playbackRate ?? 0.0;
6+
video.alternativePlaybackRate = video.alternativePlaybackRate ?? 0.0;
67
videos.push(video);
78
});
89
} catch (e) {
@@ -24,6 +25,7 @@ function createVideo(filename) {
2425
"duration": 0,
2526
"customDuration": 0,
2627
"playbackRate": 0.0,
28+
"alternativePlaybackRate": 0.0,
2729
"loop": false
2830
};
2931
}

package/contents/ui/config.qml

Lines changed: 117 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ ColumnLayout {
3737
property var parentLayout
3838
property alias cfg_FillMode: videoFillMode.currentValue
3939
property alias cfg_PauseMode: pauseModeCombo.currentValue
40+
property alias cfg_AlternativePlaybackRateMode: alternativePlaybackRateMode.currentValue
4041
property alias cfg_BackgroundColor: colorButton.color
4142
property alias cfg_PauseBatteryLevel: pauseBatteryLevel.value
4243
property alias cfg_BatteryPausesVideo: batteryPausesVideoCheckBox.checked
@@ -55,10 +56,12 @@ ColumnLayout {
5556
property alias cfg_EffectsPauseVideo: effectsPauseVideoInput.text
5657
property alias cfg_EffectsShowBlur: effectsShowBlurInput.text
5758
property alias cfg_EffectsHideBlur: effectsHideBlurInput.text
59+
property alias cfg_EffectsAlternativeSpeed: effectsAlternativeSpeedInput.text
5860
property alias cfg_BlurAnimationDuration: blurAnimationDurationSpinBox.value
5961
property alias cfg_CrossfadeEnabled: crossfadeEnabledCheckbox.checked
6062
property alias cfg_CrossfadeDuration: crossfadeDurationSpinBox.value
6163
property real cfg_PlaybackRate
64+
property real cfg_AlternativePlaybackRate
6265
property alias cfg_Volume: volumeSlider.value
6366
property alias cfg_RandomMode: randomModeCheckbox.checked
6467
property alias cfg_ResumeLastVideo: resumeLastVideoCheckbox.checked
@@ -166,6 +169,7 @@ ColumnLayout {
166169
duration: item.duration,
167170
customDuration: item.customDuration,
168171
playbackRate: item.playbackRate,
172+
alternativePlaybackRate: item.alternativePlaybackRate,
169173
loop: item.loop
170174
});
171175
}
@@ -336,9 +340,6 @@ ColumnLayout {
336340
valueFromText: function (text, locale) {
337341
return Number.fromLocaleString(locale, reExtractNum.exec(text)[1]);
338342
}
339-
onDisplayTextChanged: {
340-
value = Number.fromLocaleString(Qt.locale(), reExtractNum.exec(displayText)[1]);
341-
}
342343
}
343344
Button {
344345
visible: root.cfg_FillBlurRadius > 64
@@ -418,9 +419,6 @@ ColumnLayout {
418419
valueFromText: function (text, locale) {
419420
return Number.fromLocaleString(locale, reExtractNum.exec(text)[1]);
420421
}
421-
onDisplayTextChanged: {
422-
value = Number.fromLocaleString(Qt.locale(), reExtractNum.exec(displayText)[1]);
423-
}
424422
}
425423
SpinBox {
426424
id: wallpaperTimerMinutes
@@ -442,9 +440,6 @@ ColumnLayout {
442440
valueFromText: function (text, locale) {
443441
return parseInt(text);
444442
}
445-
onDisplayTextChanged: {
446-
value = Number.fromLocaleString(Qt.locale(), reExtractNum.exec(displayText)[1]);
447-
}
448443
}
449444
SpinBox {
450445
id: wallpaperTimerSeconds
@@ -466,9 +461,6 @@ ColumnLayout {
466461
valueFromText: function (text, locale) {
467462
return Number.fromLocaleString(locale, reExtractNum.exec(text)[0]);
468463
}
469-
onDisplayTextChanged: {
470-
value = Number.fromLocaleString(Qt.locale(), reExtractNum.exec(displayText)[1]);
471-
}
472464
}
473465
}
474466

@@ -488,7 +480,7 @@ ColumnLayout {
488480
Kirigami.FormData.label: i18n("Speed:")
489481
visible: root.currentTab === 1
490482
Components.DoubleSpinBox {
491-
id: playbackRateSlider
483+
id: playbackRateGlobal
492484
from: 0.01 * multiplier
493485
to: 2 * multiplier
494486
value: root.cfg_PlaybackRate * multiplier
@@ -497,7 +489,7 @@ ColumnLayout {
497489
"tnum": 1
498490
}
499491
onValueModified: {
500-
root.cfg_PlaybackRate = value / playbackRateSlider.multiplier;
492+
root.cfg_PlaybackRate = value / playbackRateGlobal.multiplier;
501493
}
502494
}
503495
Button {
@@ -541,9 +533,6 @@ ColumnLayout {
541533
valueFromText: function (text, locale) {
542534
return Number.fromLocaleString(locale, reExtractNum.exec(text)[1]);
543535
}
544-
onDisplayTextChanged: {
545-
value = Number.fromLocaleString(Qt.locale(), reExtractNum.exec(displayText)[1]);
546-
}
547536
}
548537
Button {
549538
icon.name: "dialog-information-symbolic"
@@ -640,6 +629,21 @@ ColumnLayout {
640629
id: blurRadiusSpinBox
641630
from: 0
642631
to: 145
632+
font.features: {
633+
"tnum": 1
634+
}
635+
readonly property regexp reExtractNum: /\D*?(-?\d*\.?\d*)\D*$/
636+
637+
validator: RegularExpressionValidator {
638+
regularExpression: blurRadiusSpinBox.reExtractNum
639+
}
640+
641+
textFromValue: function (value, locale) {
642+
return i18n("%1px", value);
643+
}
644+
valueFromText: function (text, locale) {
645+
return Number.fromLocaleString(locale, reExtractNum.exec(text)[1]);
646+
}
643647
}
644648
Button {
645649
visible: blurRadiusSpinBox.visible && root.cfg_BlurRadius > 64
@@ -661,6 +665,76 @@ ColumnLayout {
661665
from: 0
662666
to: 9999
663667
stepSize: 100
668+
font.features: {
669+
"tnum": 1
670+
}
671+
672+
readonly property regexp reExtractNum: /\D*?(-?\d*\.?\d*)\D*$/
673+
674+
validator: RegularExpressionValidator {
675+
regularExpression: blurAnimationDurationSpinBox.reExtractNum
676+
}
677+
678+
textFromValue: function (value, locale) {
679+
return i18nc("animation duration", "%1ms", value);
680+
}
681+
valueFromText: function (text, locale) {
682+
return Number.fromLocaleString(locale, reExtractNum.exec(text)[1]);
683+
}
684+
}
685+
}
686+
687+
ComboBox {
688+
id: alternativePlaybackRateMode
689+
Kirigami.FormData.label: i18n("Alternative speed:")
690+
model: [
691+
{
692+
text: i18n("Maximized or full-screen windows"),
693+
value: Enum.PauseMode.MaximizedOrFullScreen
694+
},
695+
{
696+
text: i18n("Active window"),
697+
value: Enum.PauseMode.ActiveWindowPresent
698+
},
699+
{
700+
text: i18n("At least one window is visible"),
701+
value: Enum.PauseMode.WindowVisible
702+
},
703+
{
704+
text: i18n("Never"),
705+
value: Enum.PauseMode.Never
706+
}
707+
]
708+
textRole: "text"
709+
valueRole: "value"
710+
visible: !root.isLockScreenSettings && root.currentTab === 1
711+
}
712+
713+
RowLayout {
714+
visible: root.currentTab === 1 && root.cfg_AlternativePlaybackRateMode !== 3
715+
Label {
716+
text: i18n("Speed:")
717+
}
718+
Components.DoubleSpinBox {
719+
from: 0.01 * multiplier
720+
to: 2 * multiplier
721+
value: root.cfg_AlternativePlaybackRate * multiplier
722+
stepSize: 0.01 * multiplier
723+
font.features: {
724+
"tnum": 1
725+
}
726+
onValueModified: {
727+
root.cfg_AlternativePlaybackRate = value / multiplier;
728+
}
729+
}
730+
Button {
731+
icon.name: "edit-undo-symbolic"
732+
flat: true
733+
onClicked: {
734+
root.cfg_AlternativePlaybackRate = 1.0;
735+
}
736+
ToolTip.text: i18n("Reset to default")
737+
ToolTip.visible: hovered
664738
}
665739
}
666740

@@ -686,9 +760,6 @@ ColumnLayout {
686760
valueFromText: function (text, locale) {
687761
return Number.fromLocaleString(locale, reExtractNum.exec(text)[1]);
688762
}
689-
onDisplayTextChanged: {
690-
value = Number.fromLocaleString(Qt.locale(), reExtractNum.exec(displayText)[1]);
691-
}
692763
}
693764
CheckBox {
694765
id: batteryPausesVideoCheckBox
@@ -779,6 +850,14 @@ ColumnLayout {
779850
model: effects.loadedEffects
780851
}
781852

853+
Components.CheckableValueListView {
854+
id: effectsAlternativeSpeedInput
855+
Layout.maximumWidth: 400
856+
Kirigami.FormData.label: i18n("Alternative speed in:")
857+
visible: root.currentTab === 2
858+
model: effects.loadedEffects
859+
}
860+
782861
Label {
783862
text: i18n("Currently enabled and <u><strong><font color='%1'>active</font></strong></u> Desktop Effects:", Kirigami.Theme.positiveTextColor)
784863
visible: root.currentTab === 2
@@ -976,6 +1055,7 @@ ColumnLayout {
9761055
required property string filename
9771056
required property bool enabled
9781057
required property real playbackRate
1058+
required property real alternativePlaybackRate
9791059
required property bool loop
9801060
implicitWidth: ListView.view.width
9811061
implicitHeight: delegate.height
@@ -1062,6 +1142,23 @@ ColumnLayout {
10621142
ToolTip.visible: hovered
10631143
ToolTip.text: i18n("Playback speed for this video. Minimum accepted is 0.01, set to 0.0 to ignore this setting.")
10641144
}
1145+
1146+
Components.DoubleSpinBox {
1147+
from: 0
1148+
to: 2 * multiplier
1149+
value: itemDelegate.alternativePlaybackRate * multiplier
1150+
stepSize: 0.01 * multiplier
1151+
enabled: itemDelegate.enabled
1152+
font.features: {
1153+
"tnum": 1
1154+
}
1155+
onValueModified: {
1156+
videosModel.updateItem(itemDelegate.index, "alternativePlaybackRate", value / multiplier);
1157+
}
1158+
ToolTip.delay: 1000
1159+
ToolTip.visible: hovered
1160+
ToolTip.text: i18n("Alternative playback speed for this video. Minimum accepted is 0.01, set to 0.0 to ignore this setting.")
1161+
}
10651162
Button {
10661163
icon.name: "media-repeat-single-symbolic"
10671164
checkable: true

package/contents/ui/main.qml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,10 @@ WallpaperItem {
118118
property var activeEffects: effectsModel.activeEffects
119119
property var effectsHideBlur: main.configuration.EffectsHideBlur.split(",").filter(Boolean)
120120
property var effectsShowBlur: main.configuration.EffectsShowBlur.split(",").filter(Boolean)
121+
property var effectsAlternativeSpeed: main.configuration.EffectsAlternativeSpeed.split(",").filter(Boolean)
121122
property bool effectHideBlur: effectsHideBlur.some(item => activeEffects.includes(item))
122123
property bool effectShowBlur: effectsShowBlur.some(item => activeEffects.includes(item))
124+
property bool effectAlternativeSpeed: effectsAlternativeSpeed.some(item => activeEffects.includes(item))
123125

124126
property var effectsPauseVideo: main.configuration.EffectsPauseVideo.split(",").filter(Boolean)
125127
property var effectsPlayVideo: main.configuration.EffectsPlayVideo.split(",").filter(Boolean)
@@ -167,6 +169,31 @@ WallpaperItem {
167169
}
168170
return mute;
169171
}
172+
property bool useAlternativePlaybackRate: {
173+
if (lockScreenMode) {
174+
return false;
175+
}
176+
177+
if (effectAlternativeSpeed) {
178+
return true;
179+
}
180+
181+
let r = false;
182+
switch (main.configuration.AlternativePlaybackRateMode) {
183+
case Enum.PauseMode.MaximizedOrFullScreen:
184+
r = windowModel.maximizedExists;
185+
break;
186+
case Enum.PauseMode.ActiveWindowPresent:
187+
r = windowModel.activeExists;
188+
break;
189+
case Enum.PauseMode.WindowVisible:
190+
r = windowModel.visibleExists;
191+
break;
192+
case Enum.PauseMode.Never:
193+
r = false;
194+
}
195+
return r;
196+
}
170197

171198
function getVideos() {
172199
return Utils.parseCompat(videoUrls).filter(video => video.enabled);
@@ -271,6 +298,8 @@ WallpaperItem {
271298
fillBlurRadius: main.configuration.FillBlurRadius
272299
volume: main.volume
273300
playbackRate: main.playbackRate
301+
useAlternativePlaybackRate: main.useAlternativePlaybackRate
302+
alternativePlaybackRateGlobal: main.configuration.AlternativePlaybackRate
274303
resumeLastVideo: main.configuration.ResumeLastVideo
275304
}
276305
}

0 commit comments

Comments
 (0)