Skip to content

Workshop library is not rescanned after a subscription — new items only show up after a manual refresh #85

Description

@wehrwolfmann

Summary

When a Workshop item is subscribed while the app is running, the Steam client downloads it into steamapps/workshop/content/431960/<id>, but the wallpaper does not appear in the library until a rescan is triggered by hand. Nothing observes the Workshop directory, so the subscription stays invisible until the user finds the Refresh button, uses the tray entry, or restarts.

I would like to suggest a hook for this: steamapps/workshop/appworkshop_431960.acf — the same file the plugin already reads since #84. Filing an issue rather than a PR because the scan trigger lives in the daemon rather than in the Lua plugin, and I would rather agree on the shape before touching it. Happy to move this to the waywallen repository if that is the better place for it.

What I observed

waywallen 0.2.6 (org.waywallen.waywallen.Daemon1.Version), plugin org.waywallen.open-wallpaper-engine 0.2.0, daemon running with --no-ui --no-tray, KDE Plasma / Arch.

  1. With the daemon running, I subscribed to a Workshop item (3625095088, 96 KB) in the Steam client.
  2. About 200 s later the Steam client created content/431960/3625095088 and, 0.2 s after that, rewrote steamapps/workshop/appworkshop_431960.acf. I polled both every 200 ms; the record's WorkshopItemDetails block for the item carries subscribedby, so it is exactly the shape source: read Workshop subscriptions from Steam's own record #84 reads.
    One detail worth noting: the .acf inode changed across that write (6865340 → 6866042). Steam replaces the file, it does not write into the existing one.
  3. I then left the daemon alone for roughly 15 minutes. The library still held the same 39 items, and select * from item where path like '%3625095088%' against waywallen-v2.db returned nothing.
  4. One manual rescan — org.waywallen.waywallen.Daemon1.Rescan over D-Bus, which is the same control::rescan entry point as the tray's "Rescan wallpapers" — made it appear at once: 39 → 40 items, display_name = 几何绘图 | GeoDraw v2.0, resource = .../3625095088/assets.json, external_id = 3625095088.

The same happens with a directory placed into content/431960 by hand: invisible for as long as I waited, present immediately after a rescan. So this is about the scan trigger and not about anything item-specific.

Expected: once Steam has finished downloading a subscribed item, it turns up in the library on its own.

Where a rescan is triggered today

Every path into control::refresh_sources I could find is an explicit user or lifecycle action, and there is no filesystem watching anywhere — Cargo.toml pulls in no notify/inotify crate, and src/ contains no direct inotify use either:

  • daemon startup — src/main.rs:588
  • the WallpaperScan request, i.e. the wallpaper page's "Refresh" action (ui/qml/page/WallpaperPage.qml:293-299) — src/ws_server.rs:1930-1939
  • library add / remove — src/ws_server.rs:2821, src/ws_server.rs:2846
  • auto-detect registering a library — src/control.rs:1521
  • plugin install/refresh — src/control.rs:708
  • tray "Rescan wallpapers" — src/tray/dbusmenu.rs:169-170 and :348
  • D-Bus Rescan — src/dbus_iface.rs:120-121

Req::SubscriptionSet (src/ws_server.rs:2572) returns as soon as the plugin accepts the mutation and schedules nothing afterwards, which is sensible on its own — at that moment Steam has not downloaded anything yet, so an immediate scan would find nothing. But nothing picks the item up later either. And a subscription made in the Steam client or in a browser never passes through that handler at all.

(Line numbers are against waywallen main, e5626c1.)

Suggestion: build on the file #84 already reads

One approach that works: treat steamapps/workshop/appworkshop_431960.acf as the "Steam finished something" signal and run a library refresh when it changes.

It fits well because the plugin already depends on that exact file — ACF_REL in wallpaper_engine/workshop.lua:10, read per Steam root by fold_root (workshop.lua:103-105), folded once per scan by workshop.refresh (workshop.lua:134, called from source.lua:64). The "is this a real subscription" question and the "has something changed" question would then come from the same source of truth, and the roots to watch are the ones source.lua:59-60 already derives from ctx.libraries(). In the run above the record was rewritten 0.2 s after the content directory appeared, so by the time it changes, the files are on disk and a scan finds them.

Things that turned out to matter:

  • The file is replaced, not modified. The inode changed across the subscription, so a watch pinned to the file itself goes deaf after the first event. Watching the containing steamapps/workshop/ directory survives the replacement.
  • It may not exist yet. A machine that has never downloaded a Workshop item for 431960 has no appworkshop_431960.acf; fold_root already treats a missing file as "no record" (workshop.lua:105). Watching the directory also covers the file being created later.
  • One record per Steam library. Each registered root has its own copy, which is why workshop.refresh folds over all of them — the watch set should follow the same list and be re-armed when libraries are added or removed.
  • Collapse bursts into a single scan. A full refresh_sources per raw event would be wasteful, so a short debounce plus skipping while scan_in_progress is already set (src/control.rs:1618) keeps it to one scan per burst.

A polling variant — re-reading the record every couple of seconds instead of watching it — is also viable and adds no dependency; reading one small KeyValues file is far cheaper than a full library scan. That is enough to cover subscriptions made anywhere, including in the Steam client.

If this direction looks right to you, I am glad to prepare the patch; I just did not want to push a daemon-side change without asking first.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions