Skip to content

Commit 78b060b

Browse files
committed
refactor: split video player into separate components
1 parent 5fc6463 commit 78b060b

File tree

3 files changed

+258
-155
lines changed

3 files changed

+258
-155
lines changed

package/contents/ui/FadePlayer.qml

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
import QtQuick
2+
import QtQuick.Layouts
3+
import QtMultimedia
4+
import org.kde.plasma.components as PlasmaComponents
5+
import org.kde.kirigami as Kirigami
6+
import "code/utils.js" as Utils
7+
8+
Item {
9+
id: root
10+
property var currentSource
11+
property real volume: 1.0
12+
property bool muted: true
13+
property int playbackRate: 1
14+
property int fillMode: VideoOutput.PreserveAspectCrop
15+
property int loops: MediaPlayer.Infinite
16+
property bool crossfadeEnabled: false
17+
property int targetCrossfadeDuration: 1000
18+
property bool multipleVideos: false
19+
property int lastVideoPosition: 0
20+
property bool restoreLastPosition: true
21+
property bool debugEnabled: false
22+
23+
property int position
24+
25+
// Crossfade must not be longer than the shortest video or the fade becomes glitchy
26+
// we don't know the length until a video gets played, so the crossfade duration
27+
// will decrease below the configured duration if needed as videos get played
28+
property int crossfadeMinDuration: parseInt(Math.max(Math.min(videoPlayer1.actualDuration, videoPlayer2.actualDuration) / 3, 1))
29+
property int crossfadeDuration: Math.min(root.targetCrossfadeDuration, crossfadeMinDuration)
30+
31+
property bool primaryPlayer: true
32+
property VideoPlayer player: primaryPlayer ? videoPlayer1 : videoPlayer2
33+
34+
function play() {
35+
player.play();
36+
}
37+
function pause() {
38+
player.pause();
39+
}
40+
function stop() {
41+
player.stop();
42+
}
43+
function next() {
44+
setNextSource();
45+
primaryPlayer = true;
46+
videoPlayer2.pause();
47+
videoPlayer1.opacity = 1;
48+
videoPlayer1.playerSource = root.currentSource;
49+
videoPlayer1.play();
50+
}
51+
signal setNextSource
52+
53+
VideoPlayer {
54+
id: videoPlayer1
55+
anchors.fill: parent
56+
property var playerSource: root.currentSource
57+
property int actualDuration: duration / playbackRate
58+
playbackRate: playerSource.playbackRate || root.playbackRate
59+
source: playerSource.filename ?? ""
60+
loops: (root.multipleVideos) ? 1 : root.crossfadeEnabled ? 1 : MediaPlayer.Infinite
61+
muted: root.muted
62+
z: 2
63+
opacity: 1
64+
playerId: 1
65+
onPositionChanged: {
66+
if (!root.primaryPlayer) {
67+
return;
68+
}
69+
root.position = position;
70+
71+
if ((position / playbackRate) > actualDuration - root.crossfadeDuration) {
72+
if (root.crossfadeEnabled) {
73+
root.setNextSource();
74+
if (root.debugEnabled) {
75+
console.log("player1 fading out");
76+
}
77+
opacity = 0;
78+
root.primaryPlayer = false;
79+
videoPlayer2.playerSource = root.currentSource;
80+
videoPlayer2.play();
81+
}
82+
}
83+
}
84+
onMediaStatusChanged: {
85+
if (mediaStatus == MediaPlayer.EndOfMedia) {
86+
if (root.crossfadeEnabled)
87+
return;
88+
root.setNextSource();
89+
source = playerSource.filename ?? "";
90+
videoPlayer1.play();
91+
}
92+
93+
if (mediaStatus == MediaPlayer.LoadedMedia && seekable) {
94+
if (!root.position)
95+
return;
96+
if (root.position < duration) {
97+
position = root.lastVideoPosition;
98+
}
99+
root.restoreLastPosition = false;
100+
}
101+
}
102+
onPlayingChanged: {
103+
if (playing) {
104+
if (videoPlayer1.opacity === 0) {
105+
opacity = 1;
106+
}
107+
if (root.debugEnabled) {
108+
console.log("Player 1 playing");
109+
}
110+
}
111+
}
112+
Behavior on opacity {
113+
NumberAnimation {
114+
duration: root.crossfadeDuration
115+
}
116+
}
117+
}
118+
119+
VideoPlayer {
120+
id: videoPlayer2
121+
loops: 1
122+
property var playerSource: Utils.createVideo("")
123+
property int actualDuration: duration / playbackRate
124+
playbackRate: playerSource.playbackRate || root.playbackRate
125+
source: playerSource.filename ?? ""
126+
anchors.fill: parent
127+
muted: root.muted
128+
z: 1
129+
playerId: 2
130+
onPositionChanged: {
131+
if (root.primaryPlayer) {
132+
return;
133+
}
134+
root.position = position;
135+
136+
if ((position / playbackRate) > actualDuration - root.crossfadeDuration) {
137+
if (root.debugEnabled) {
138+
console.log("player1 fading in");
139+
}
140+
videoPlayer1.opacity = 1;
141+
root.setNextSource();
142+
root.primaryPlayer = true;
143+
videoPlayer1.playerSource = root.currentSource;
144+
videoPlayer1.play();
145+
}
146+
}
147+
onPlayingChanged: {
148+
if (playing && root.debugEnabled) {
149+
console.log("player2 playing");
150+
}
151+
}
152+
}
153+
154+
ColumnLayout {
155+
visible: root.debugEnabled
156+
z: 2
157+
Item {
158+
Layout.preferredWidth: 1
159+
Layout.preferredHeight: 100
160+
}
161+
Kirigami.AbstractCard {
162+
Layout.margins: Kirigami.Units.largeSpacing
163+
contentItem: ColumnLayout {
164+
id: content
165+
PlasmaComponents.Label {
166+
text: root.player.source
167+
}
168+
PlasmaComponents.Label {
169+
text: "id " + root.player.playerId
170+
}
171+
PlasmaComponents.Label {
172+
text: "media status " + root.player.mediaStatus
173+
}
174+
PlasmaComponents.Label {
175+
text: "playing " + root.player.playing
176+
}
177+
PlasmaComponents.Label {
178+
text: "position " + root.player.position
179+
}
180+
PlasmaComponents.Label {
181+
text: "duration " + root.player.duration
182+
}
183+
}
184+
}
185+
}
186+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import QtQuick
2+
import QtMultimedia
3+
4+
Item {
5+
id: root
6+
property url source
7+
property real volume: 1.0
8+
property bool muted: true
9+
property int playbackRate: 1
10+
property int fillMode: VideoOutput.PreserveAspectCrop
11+
property int loops: MediaPlayer.Infinite
12+
property int position: 0
13+
property int actualDuration: player.duration / playbackRate
14+
property int playerId: 0
15+
16+
readonly property alias mediaStatus: player.mediaStatus
17+
readonly property alias playing: player.playing
18+
readonly property alias seekable: player.seekable
19+
readonly property alias duration: player.duration
20+
21+
function play() {
22+
player.play();
23+
}
24+
function pause() {
25+
player.pause();
26+
}
27+
function stop() {
28+
player.stop();
29+
}
30+
31+
VideoOutput {
32+
id: videoOutput
33+
fillMode: root.fillMode
34+
anchors.fill: parent
35+
}
36+
37+
AudioOutput {
38+
id: audioOutput
39+
muted: root.muted
40+
volume: root.opacity * root.volume
41+
}
42+
43+
MediaPlayer {
44+
id: player
45+
source: root.source
46+
videoOutput: videoOutput
47+
audioOutput: audioOutput
48+
playbackRate: root.playbackRate
49+
loops: root.loops
50+
onPositionChanged: position => {
51+
root.position = position;
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)