Review of "Live LockPaper" version 1

Details Page Preview

A GNOME Shell extension that lets you use any video as your lock screen background and desktop wallpaper. This is a fork of Live Lock Screen by nick-redwill, extended with additional features. Features: • Video lock screen and desktop wallpaper playback • Multi-video playlists with random or sequential playback • Per-monitor video configuration for multi-display setups • Auto FPS detection for smooth playback • Blur and brightness effects with password prompt customization • Battery optimization (separate controls for lockscreen and wallpaper) • Panel quick controls for play/pause, next video, and settings • Dynamic panel icons that change based on wallpaper/lockscreen state • Sleep/wake handling for proper video management during system suspend • Pause when hidden modes to save resources • GTK4 renderer for high-performance playback (with legacy appsink fallback) Note: Requires GStreamer plugins and ffmpeg (for thumbnail generation). See the README on GitHub for installation instructions. Based on Live Lock Screen by nick-redwill (https://github.com/nick-redwill/LiveLockScreen)

Extension Homepage
https://github.com/DeLuca21/LiveLockPaper

No comments.

FAQ

Files

Note: Binary files aren't shown on the web site. To see all files, please download the extension zipfile.

Shexli (experimental) error 2 warning 13

Shexli found 15 issues that may need reviewer attention.

EGO-A-004 warning

extension files should not contain excessive ungated console logging

File contains 13 ungated console.log/warn/error calls (threshold: 5).

No excessive logging

  • core/pipeline.js:60
    console.error(`[Pipeline:${this._name}] init: video file not found: ${this._videoPath}`)
  • core/pipeline.js:295
    console.error('Pipeline init failed: ', e.message)
  • core/pipeline.js:444
    console.error(`[Pipeline:${this._name}] Error in frame processing: ${e.message}`)
  • core/pipeline.js:101
    console.log(`[Pipeline:${this._name}] GPU colour conversion: ✓ (glupload → glcolorconvert → gldownload)`)
  • core/pipeline.js:103
    console.log(`[Pipeline:${this._name}] GPU colour conversion: ✗ GL elements not available, falling back to CPU videoconvert`)
  • core/pipeline.js:159
    console.log(`[Pipeline:${this._name}] Warning: failed to link ${binElements[i].name} → ${binElements[i + 1].name}`)
  • core/pipeline.js:268
    console.log(`[Pipeline:${this._name}] Initialized: ${this._framerate} fps (${interval.toFixed(1)}ms${scaleInfo}${staggerInfo}), polling: ${deliveryLabel}, priority: ${prioLabel}, hwdec: ${hwLabel}, gpu-cc: ${gpuLabel}`)
  • core/pipeline.js:274
    console.log(`[Pipeline:${this._name}] Stagger delay elapsed, starting frame timer`)
  • core/pipeline.js:328
    console.log(`[Pipeline:${this._name}] HW decoder ranks boosted: ${boosted.join(', ')}`)
  • core/pipeline.js:330
    console.log(`[Pipeline:${this._name}] No hardware decoders found on this system`)

EGO-X-002 warning

extensions should not use synchronous subprocess APIs in shell code

Shell code should avoid synchronous subprocess APIs like `GLib.spawn_command_line_sync()` and `GLib.spawn_sync()`.

Complete Examples

  • core/player_process.js:271
    GLib.spawn_command_line_sync(`kill ${pid}`)

EGO-C49-003 error

extensions targeting GNOME 49 must not call maximize or unmaximize with Meta.MaximizeFlags

This extension explicitly targets GNOME Shell 49 but still passes `Meta.MaximizeFlags` to `maximize()` or `unmaximize()`.

Meta.Window

  • core/player_process.js:162
    win.unmaximize(Meta.MaximizeFlags.BOTH)

EGO-A-004 warning

extension files should not contain excessive ungated console logging

File contains 120 ungated console.log/warn/error calls (threshold: 5).

No excessive logging

  • extension.js:147
    console.error('[LiveLockPaper] Failed to initialize GStreamer')
  • extension.js:205
    console.error(`[LiveLockPaper] Activation failed: ${e.message}\n${e.stack}`)
  • extension.js:210
    console.error(`[LiveLockPaper] _deferredInit failed: ${e.message}\n${e.stack}`)
  • extension.js:254
    console.error(`[LiveLockPaper] _fullTeardown failed: ${e.message}\n${e.stack}`)
  • extension.js:279
    console.error(`[LiveLockPaper] _onSessionModeChanged crashed: ${e.message}\n${e.stack}`)
  • extension.js:1108
    console.error('[LockScreen] screenShield._dialog not available after retries')
  • extension.js:1128
    console.error(`[LockScreen] _enableLockScreen failed: ${e.message}\n${e.stack}`)
  • extension.js:1523
    console.error(`[LockScreen:GTK4] Failed to spawn player: ${e.message}`)
  • extension.js:1609
    console.error(`[LockScreen:GTK4] ${err}`)
  • extension.js:2096
    console.error(`[LockScreen] Pipeline init/play failed: ${e.message}`)

EGO-A-004 warning

extension files should not contain excessive ungated console logging

File contains 85 ungated console.log/warn/error calls (threshold: 5).

No excessive logging

  • prefs.js:820
    console.log('Could not migrate video path:', e)
  • prefs.js:848
    console.log(`[Video] Error opening video ${row._videoPath}:`, e)
  • prefs.js:974
    console.log('Error unpacking metadata:', e)
  • prefs.js:988
    console.log(`[Metadata] Raw metadata for ${path}: ${metaStr}`)
  • prefs.js:996
    console.log(`[Metadata] Extracting variant type: ${variantType}`)
  • prefs.js:999
    console.log(`[Metadata] Extracted int value: ${result}`)
  • prefs.js:1012
    console.log(`[Metadata] Could not extract value from variant:`, e2)
  • prefs.js:1019
    console.log(`[Metadata] Extracted via get_int32: ${result}`)
  • prefs.js:1040
    console.log(`[Metadata] Extracted values for ${path}:`, { width, height, fps, duration, playCount })
  • prefs.js:1061
    console.log(`[Metadata] Displaying duration: ${minutes}:${seconds.toString().padStart(2, '0')} for ${path}`)

EGO-P-006 warning

unnecessary build and translation artifacts should not be shipped

Package contains files that often should not be shipped for review.

Don't include unnecessary files

  • screenshots/lockscreen-clock.png
    screenshots/lockscreen-clock.png

EGO-P-006 warning

unnecessary build and translation artifacts should not be shipped

Compiled GSettings schemas should not be shipped for 45+ packages.

Don't include unnecessary files

  • schemas/gschemas.compiled
    schemas/gschemas.compiled

EGO-P-004 error

GSettings schema XML filename must match schema id

GSettings schema filename must match `<schema-id>.gschema.xml`.

GSettings Schemas

  • schemas/org.gnome.shell.extensions.live-lockscreen.gschema.xml
    id='org.gnome.shell.extensions.live-lockpaper-deluca21' path='/org/gnome/shell/extensions/live-lockpaper-deluca21/'

EGO-P-007 warning

JavaScript files should be reachable from extension.js or prefs.js

Some JavaScript files are not reachable from `extension.js` or `prefs.js` imports.

Don't include unnecessary files

  • external/command_handler.js
  • external/pipeline.js
  • external/player.js
  • external/run.js

EGO-L-002 warning

objects created by extension should be destroyed in disable()

Objects assigned in `enable()` are missing matching `.destroy()` calls in `disable()` or its helper methods.

Destroy all objects

  • extension.js:598
            this._menuGtkRendererSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Use GTK4 renderer',
                !this._settings.get_boolean(Keys.DEBUG_USE_GTK4_SINK)
            )
  • extension.js:583
                this._menuLockscreenDisableOnBatterySwitch = new PopupMenu.PopupSwitchMenuItem(
                    'Disable on battery',
                    this._settings.get_boolean(Keys.LOCKSCREEN_DISABLE_ON_BATTERY)
                )
  • extension.js:508
            this._menuLockscreenEnabledSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Lock screen video',
                this._settings.get_boolean(Keys.LOCKSCREEN_ENABLED)
            )
  • extension.js:505
    this._menuLockscreenGroup = new PopupMenu.PopupSubMenuMenuItem('Lock screen', true)
  • extension.js:554
            this._menuLsBlurSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Blur',
                (this._settings.get_int(Keys.BLUR_RADIUS) ?? 0) > 0
            )
  • extension.js:573
            this._menuLsGrayscaleSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Grayscale effect',
                this._settings.get_boolean(Keys.PROMPT_GRAYSCALE)
            )
  • extension.js:535
            this._menuLsMuteSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Mute lockscreen audio',
                this._settings.get_int(Keys.AUDIO_VOLUME) === 0
            )
  • extension.js:526
            this._menuLsPerMonitorSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Per-monitor videos',
                this._settings.get_boolean(Keys.LOCKSCREEN_PER_MONITOR)
            )
  • extension.js:517
            this._menuLsRandomOrderSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Random order',
                this._settings.get_boolean(Keys.VIDEO_RANDOM_ORDER)
            )
  • extension.js:453
            this._menuMuteSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Mute wallpaper audio',
                this._settings.get_int(Keys.WALLPAPER_VOLUME) === 0
            )
  • extension.js:393
    this._menuNextItem = new PopupMenu.PopupMenuItem('Next Video')
  • extension.js:433
    this._menuPauseWhenHiddenItem = new PopupMenu.PopupMenuItem('')
  • extension.js:422
            this._menuPerMonitorSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Per-monitor videos',
                this._settings.get_boolean(Keys.WALLPAPER_PER_MONITOR)
            )
  • extension.js:386
    this._menuPlayPauseItem = new PopupMenu.PopupMenuItem('Pause Wallpaper')
  • extension.js:413
            this._menuRandomOrderSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Random order',
                this._settings.get_boolean(Keys.WALLPAPER_RANDOM_ORDER)
            )
  • extension.js:609
    this._menuRestartItem = new PopupMenu.PopupMenuItem('Restart Wallpaper')
  • extension.js:617
    this._menuSettingsItem = new PopupMenu.PopupMenuItem('Open Settings')
  • extension.js:472
            this._menuWallpaperBlurSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Wallpaper blur',
                (this._settings.get_int(Keys.WALLPAPER_BLUR_RADIUS) ?? 0) > 0
            )
  • extension.js:492
                this._menuWallpaperDisableOnBatterySwitch = new PopupMenu.PopupSwitchMenuItem(
                    'Disable on battery',
                    this._settings.get_boolean(Keys.WALLPAPER_DISABLE_ON_BATTERY)
                )
  • extension.js:404
            this._menuWallpaperEnabledSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Wallpaper enabled',
                this._settings.get_boolean(Keys.WALLPAPER_ENABLED)
            )
  • extension.js:401
    this._menuWallpaperGroup = new PopupMenu.PopupSubMenuMenuItem('Wallpaper', true)
  • extension.js:379
            this._panelIcon = new St.Icon({
                icon_name: 'preferences-desktop-wallpaper-symbolic',
                fallback_icon_name: 'image-x-generic-symbolic',
                style_class: 'system-status-icon',
            })

EGO-L-005 warning

owned object references should be released in disable()

Owned references that are cleaned up in `disable()` should also be released with `null` or `undefined`.

Destroy all objects

  • extension.js:505
    this._menuLockscreenGroup = new PopupMenu.PopupSubMenuMenuItem('Lock screen', true)
  • extension.js:554
            this._menuLsBlurSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Blur',
                (this._settings.get_int(Keys.BLUR_RADIUS) ?? 0) > 0
            )
  • extension.js:535
            this._menuLsMuteSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Mute lockscreen audio',
                this._settings.get_int(Keys.AUDIO_VOLUME) === 0
            )
  • extension.js:526
            this._menuLsPerMonitorSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Per-monitor videos',
                this._settings.get_boolean(Keys.LOCKSCREEN_PER_MONITOR)
            )
  • extension.js:517
            this._menuLsRandomOrderSwitch = new PopupMenu.PopupSwitchMenuItem(
                'Random order',
                this._settings.get_boolean(Keys.VIDEO_RANDOM_ORDER)
            )
  • extension.js:401
    this._menuWallpaperGroup = new PopupMenu.PopupSubMenuMenuItem('Wallpaper', true)

EGO-L-003 warning

signals connected by extension should be disconnected in disable()

Signals assigned in `enable()` are missing matching disconnect calls in `disable()` or its helper methods.

Disconnect all signals

  • extension.js:558
            this._menuLsBlurId = this._menuLsBlurSwitch.connect('toggled', (_item, state) => {
                const current = this._settings.get_int(Keys.BLUR_RADIUS);
                if (!state) {
                    if (current > 0)
                        this._panelLsBlurRestoreRadius = current;
                    th
  • extension.js:539
            this._menuLsMuteId = this._menuLsMuteSwitch.connect('toggled', (_item, state) => {
                const current = this._settings.get_int(Keys.AUDIO_VOLUME);
                if (state) {
                    if (current > 0)
                        this._panelLsMuteRestoreVolume = current;
                    th
  • extension.js:530
            this._menuLsPerMonitorId = this._menuLsPerMonitorSwitch.connect('toggled', (_item, state) => {
                this._settings.set_boolean(Keys.LOCKSCREEN_PER_MONITOR, state);
            })
  • extension.js:521
            this._menuLsRandomOrderId = this._menuLsRandomOrderSwitch.connect('toggled', (_item, state) => {
                this._settings.set_boolean(Keys.VIDEO_RANDOM_ORDER, state);
            })

EGO-L-004 warning

main loop sources should be removed in disable()

Main loop sources assigned in `enable()` are missing matching removals in `disable()` or its helper methods.

Remove main loop sources

  • extension.js:2754
                GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
                    this._setupPauseWhenHidden();
                    this._wpPauseWhenHiddenReady = true;
                    return GLib.SOURCE_REMOVE;
                })
  • extension.js:2908
                            GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
                                this._setupPauseWhenHiddenSubprocess();
                                this._wpPauseWhenHiddenReady = true;
                                return GLib.SOURCE_REMOVE;
                            })
  • extension.js:3150
                    GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1000, () => {
                        if (this._wpPlayerProcess) {
                            // Subprocess mode
                            this._setupPauseWhenHiddenSubprocess();
                        } else if (this._wpPipelines && this._wpPipelines.leng
  • extension.js:3204
                        GLib.timeout_add(GLib.PRIORITY_DEFAULT, 500, () => {
                            this._refreshGtkHelperWindowHints('settings-restart-500ms');
                            this._debugDumpWindowSnapshot('settings-restart-500ms');
                            // Only verify windows if using GTK4 
  • extension.js:3217
                            GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1500, () => {
                                // Double-check we're still not in a restart
                                if (this._wpRestartInFlight || this._wpRestartTimeout || !this._active) {
                                    return GLib.SOU
  • extension.js:3745
                GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => {
                    if (hasPipelines) this._checkDesktopVisibility();
                    if (hasSubprocess) {
                        this._checkDesktopVisibilitySubprocess();
                    }
                    return GLib.SOURCE_REMOVE;
             
  • extension.js:3755
                GLib.timeout_add(GLib.PRIORITY_DEFAULT, 900, () => {
                    if (Main.sessionMode.currentMode !== 'user')
                        return GLib.SOURCE_REMOVE;
                    if (hasSubprocess && this._wpPlayerProcess) {
                        this._wpPlayerProcess.play();
                   
  • extension.js:3771
                GLib.timeout_add(GLib.PRIORITY_DEFAULT, 600, () => {
                    if (Main.sessionMode.currentMode !== 'user')
                        return GLib.SOURCE_REMOVE;
                    this._refreshGtkHelperWindowHints('post-resume-600ms');
                    return GLib.SOURCE_REMOVE;
                

EGO-L-007 warning

main loop sources should be removed before being recreated

Main loop sources should be removed before creating a new source on the same field.

Remove main loop sources

  • extension.js:83
                this._deferredInitId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, delay, () => {
                    this._deferredInitId = null;
                    if (!this._active) return GLib.SOURCE_REMOVE;
                    this._deferredInit();
                    return GLib.SOURCE_REMOVE;
                })
  • extension.js:3046
            this._wpVisibilityCheckId = GLib.timeout_add(
                GLib.PRIORITY_DEFAULT,
                300,
                () => {
                    this._checkDesktopVisibilitySubprocess();
                    return GLib.SOURCE_CONTINUE;
                }
            )

EGO-M-008 warning

extensions using unlock-dialog must document it in disable() comments

Extensions using `unlock-dialog` should document the reason in `disable()` comments.

Session Modes

  • extension.js:94
        disable() {
            const mode = Main.sessionMode.currentMode;
            console.log(`[LiveLockPaper] disable() called, sessionMode=${mode}`);
    
            this._active = false;
    
            // Cancel pending deferred init (the main debounce mechanism)
            if (this._deferredInitId) {
                GLib.

All Versions

Version Status
1 Unreviewed