Render the ice as long videos and play them as wallpapers

render_wallpaper.py drives headless Firefox frame by frame (four at a time,
resuming where it stopped), then ffmpeg. Thirty seconds at 30 fps is exactly
one plasma cycle, so frame 900 meets frame 0 and the loop has no seam.

plasma-plugin/dev.emil.video is a minimal Plasma 6 video wallpaper: one file
per screen, filling the screen, looping. H.264 is encoded Baseline because
Qt here decodes through openh264, which rejects High profile and leaves a
frozen frame; a new source also has to be played explicitly.

The #shot hash learned info=0 to hide the on-screen hint for renders.
This commit is contained in:
emil28092005
2026-10-31 01:22:16 +03:00
parent 3f638a286a
commit 92e8b287a3
7 changed files with 193 additions and 0 deletions
@@ -0,0 +1,58 @@
import QtQuick
import QtMultimedia
import org.kde.plasma.plasmoid
// Plays one video file, filling the screen and looping for ever. Plasma keeps a
// separate configuration per screen, so every monitor gets its own video.
WallpaperItem {
id: root
readonly property var activeConfiguration: {
if (typeof wallpaper !== "undefined" && wallpaper && wallpaper.configuration)
return wallpaper.configuration
return typeof configuration !== "undefined" ? configuration : null
}
function option(name, fallback) {
if (!activeConfiguration)
return fallback
const value = activeConfiguration[name]
return value === undefined ? fallback : value
}
readonly property string videoPath: String(option("videoPath", ""))
// shortcut: keeps decoding behind fullscreen windows too, add window tracking
// (as the shader wallpaper plugin does) if the cost ever shows up in a game
readonly property bool shouldPlay: videoPath.length > 0 && root.visible
Rectangle {
anchors.fill: parent
color: "#03111d"
}
VideoOutput {
id: output
anchors.fill: parent
fillMode: VideoOutput.PreserveAspectCrop
}
MediaPlayer {
id: player
videoOutput: output
source: root.videoPath.length > 0 ? "file://" + root.videoPath : ""
loops: MediaPlayer.Infinite
playbackRate: Number(root.option("playbackRate", 1.0))
// Some containers do not restart on their own when they run out of frames.
onMediaStatusChanged: {
if (mediaStatus === MediaPlayer.EndOfMedia) {
position = 0
play()
}
}
onErrorOccurred: console.warn("video wallpaper:", error, errorString)
}
Component.onCompleted: player.play()
}