Skip to content

feat(display): support video wallpaper thumbnail in monitor schematic#3323

Open
deepin-wm wants to merge 1 commit into
linuxdeepin:masterfrom
deepin-wm:feat/display-video-wallpaper-thumbnail
Open

feat(display): support video wallpaper thumbnail in monitor schematic#3323
deepin-wm wants to merge 1 commit into
linuxdeepin:masterfrom
deepin-wm:feat/display-video-wallpaper-thumbnail

Conversation

@deepin-wm

@deepin-wm deepin-wm commented Jun 29, 2026

Copy link
Copy Markdown

Summary

When the wallpaper is a video file, resolve its thumbnail (from metadata.json, ffmpegthumbnailer cache, or async generation) and pass the thumbnail path to QML instead of the video path, so the monitor schematic can display dynamic wallpaper thumbnails correctly.

Changes

  1. Add resolveVideoThumbnail() to DisplayWorker for video wallpaper
  2. Resolve thumbnail from metadata.json, ffmpegthumbnailer cache, or async generation via QtConcurrent
  3. Add isVideoFile() helper and WallpaperSourceTypeVideo constant
  4. Handle video wallpaper in both Wayland and X11 code paths
  5. Add Qt6::Concurrent and libffmpegthumbnailer CMake dependencies

Test Plan

  1. Test monitor schematic display with static wallpaper
  2. Test monitor schematic display with video wallpaper
  3. Verify thumbnail generation for various video formats (mp4, mov, avi, webm, mkv)
  4. Test multi-monitor setup with different wallpaper types
  5. Verify async thumbnail generation completes and updates display

Fixes: https://pms.uniontech.com/bug-view-367369.html

Summary by Sourcery

Support using thumbnails for video wallpapers in the monitor schematic instead of video files, including async thumbnail generation and caching.

New Features:

  • Add support for resolving and displaying video wallpaper thumbnails for monitors in both Wayland and X11 paths.

Enhancements:

  • Introduce helper utilities and state tracking to identify video wallpapers and map them to generated thumbnails.

Build:

  • Add Qt Concurrent and libffmpegthumbnailer as dependencies for display plugin to enable asynchronous video thumbnail generation.

@deepin-ci-robot

Copy link
Copy Markdown

Skipping CI for Draft Pull Request.
If you want CI signal for your change, please convert it to an actual PR.
You can still manually trigger a test run with /test all

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: deepin-wm

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@sourcery-ai

sourcery-ai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Reviewer's Guide

Support video wallpapers in the monitor schematic by resolving and displaying thumbnail images instead of video file paths, with asynchronous thumbnail generation and caching for both Wayland and X11 paths.

Sequence diagram for asynchronous video wallpaper thumbnail resolution

sequenceDiagram
    participant DisplayWorker
    participant Monitor
    participant QtConcurrent
    participant FfmpegThumbnailer

    DisplayWorker->>DisplayWorker: updateMonitorWallpaper(mon)
    DisplayWorker->>DisplayWorker: resolveVideoThumbnail(videoPath, mon)
    alt metadata.json or cache hit
        DisplayWorker->>Monitor: setWallpaper(thumbnailPath)
    else no cached thumbnail
        DisplayWorker->>DisplayWorker: m_videoWallpaperMonitors[videoPath] = monitor
        DisplayWorker->>QtConcurrent: QtConcurrent::run(lambda(videoPath, cachePath))
        QtConcurrent->>FfmpegThumbnailer: generateThumbnail(videoPath, Png, cachePath)
        FfmpegThumbnailer-->>DisplayWorker: videoThumbnailReady(videoPath, cachePath)
        DisplayWorker->>Monitor: setWallpaper(thumbnailPath)
        DisplayWorker->>DisplayWorker: m_videoWallpaperMonitors.remove(videoPath)
    end
Loading

File-Level Changes

Change Details Files
Resolve and use video wallpaper thumbnails instead of raw video paths when setting monitor wallpapers.
  • Introduce isVideoFile() helper and videoSuffixes list to detect video wallpaper paths.
  • Add WallpaperSourceTypeVideo constant to match Wayland wallpaper sourceType values.
  • Update updateMonitorWallpaper() to detect video wallpapers and call resolveVideoThumbnail() before setting the wallpaper.
  • Handle video wallpaper in Wayland output initialization (onOutputWallpaperReady) by resolving thumbnails for video-source wallpapers.
  • Handle video wallpaper changes in onWallpaperChanged() by resolving thumbnails when sourceType indicates a video source.
src/plugin-display/operation/private/displayworker.cpp
Implement thumbnail resolution, caching, and asynchronous generation for video wallpapers.
  • Add resolveVideoThumbnail() helper that first tries metadata.json in SysLiveWallpaperDir for pre-defined thumbnails.
  • If no metadata thumbnail is found, derive a cache path under QStandardPaths::CacheLocation/live-wallpaper-thumbnails using an MD5 of the video path.
  • Return existing cached thumbnail if present, otherwise track the monitor associated with the video path in m_videoWallpaperMonitors.
  • Use QtConcurrent::run with ffmpegthumbnailer::VideoThumbnailer to asynchronously generate a PNG thumbnail into the cache path.
  • Emit videoThumbnailReady(videoPath, thumbnailPath) when generation succeeds (or an empty path on failure), and handle it to update the associated monitor’s wallpaper and clear the tracking map.
src/plugin-display/operation/private/displayworker.cpp
src/plugin-display/operation/private/displayworker.h
Wire up state tracking and signals needed to update monitors when async thumbnail generation completes.
  • Add QMap<QString, QPointer> m_videoWallpaperMonitors to track monitors associated with pending video thumbnail generation.
  • Connect DisplayWorker::videoThumbnailReady to a lambda in the constructor that looks up the monitor by videoPath, applies the thumbnailPath if non-empty, and removes the entry from m_videoWallpaperMonitors.
  • Use QPointer guard in the QtConcurrent::run lambda to safely emit videoThumbnailReady from the worker thread only if DisplayWorker still exists.
src/plugin-display/operation/private/displayworker.cpp
src/plugin-display/operation/private/displayworker.h
Add build dependencies for concurrent execution and ffmpegthumbnailer in the display plugin.
  • Require the Qt Concurrent component in the find_package(${QT_NS} ...) call for the display plugin.
  • Add a pkg-config check for libffmpegthumbnailer (FFMPEGTHUMBNAILER) and link it via PkgConfig::FFMPEGTHUMBNAILER.
  • Include ${QT_NS}::Concurrent in Display_Libraries and the target_link_libraries list so QtConcurrent is available.
src/plugin-display/CMakeLists.txt

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@mhduiy mhduiy marked this pull request as ready for review June 29, 2026 09:50

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • Consider caching and reusing the parsed metadata.json for SysLiveWallpaperDir instead of re-reading and reparsing it on every resolveVideoThumbnail() call to avoid repeated disk IO and JSON parsing.
  • isVideoFile() relies purely on file suffix with a hardcoded list; you may want to centralize/extend this (e.g., via QMimeType or a configurable list) to avoid missing supported formats or duplicating logic elsewhere.
  • resolveVideoThumbnail() recreates the cache directory and computes the cache path on each call; factoring out cache directory initialization and cache name generation into helpers can simplify the function and reduce repeated work.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider caching and reusing the parsed metadata.json for SysLiveWallpaperDir instead of re-reading and reparsing it on every resolveVideoThumbnail() call to avoid repeated disk IO and JSON parsing.
- isVideoFile() relies purely on file suffix with a hardcoded list; you may want to centralize/extend this (e.g., via QMimeType or a configurable list) to avoid missing supported formats or duplicating logic elsewhere.
- resolveVideoThumbnail() recreates the cache directory and computes the cache path on each call; factoring out cache directory initialization and cache name generation into helpers can simplify the function and reduce repeated work.

## Individual Comments

### Comment 1
<location path="src/plugin-display/operation/private/displayworker.cpp" line_range="1393-1396" />
<code_context>
+        }
+    }
+
+    QString cacheDir = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/live-wallpaper-thumbnails";
+    QDir().mkpath(cacheDir);
+    QString cacheName = QString(QCryptographicHash::hash(videoPath.toUtf8(), QCryptographicHash::Md5).toHex()) + ".png";
+    QString cachePath = cacheDir + "/" + cacheName;
+
+    if (QFile::exists(cachePath)) {
</code_context>
<issue_to_address>
**issue:** Cache directory handling doesn’t check for failure and may behave unexpectedly if the location is empty or invalid.

If `QStandardPaths::writableLocation(CacheLocation)` returns an empty string or a path that can’t be created, `QDir().mkpath(cacheDir)` will fail silently, and `cachePath` may point to an unusable or unintended location. Please verify that `cacheDir` is non-empty and that `mkpath` returns true before using it, and fall back to `videoPath` or another safe default if the cache location isn’t usable.
</issue_to_address>

### Comment 2
<location path="src/plugin-display/operation/private/displayworker.cpp" line_range="1402-1406" />
<code_context>
+        return cachePath;
+    }
+
+    if (m_videoWallpaperMonitors.contains(videoPath)) {
+        return videoPath;
+    }
+
+    m_videoWallpaperMonitors[videoPath] = monitor;
+
+    QPointer<DisplayWorker> guard(this);
</code_context>
<issue_to_address>
**issue (bug_risk):** Video thumbnail mapping only supports a single monitor per video path, which may break multi-monitor setups.

Keying `m_videoWallpaperMonitors` by `videoPath` causes later assignments to overwrite earlier ones when the same video is used on multiple monitors, so only one monitor gets updated when thumbnail generation completes. If sharing video wallpapers across monitors is intended, map each `videoPath` to multiple monitors (e.g. a `QList<QPointer<Monitor>>` or similar collection) instead.
</issue_to_address>

### Comment 3
<location path="src/plugin-display/operation/private/displayworker.cpp" line_range="1363" />
<code_context>
     m_model->setIsConcatScreenMode(m_displayInter->isConcatScreenEnabled());
 }
+
+QString DisplayWorker::resolveVideoThumbnail(const QString &videoPath, Monitor *monitor)
+{
+    QDir liveDir(SysLiveWallpaperDir);
</code_context>
<issue_to_address>
**issue (complexity):** Consider refactoring the new video wallpaper handling into shared helpers that hide thumbnail resolution details and isolate state management around `m_videoWallpaperMonitors`.

The added video handling is functionally fine but does increase complexity in three separate call sites and in `resolveVideoThumbnail`. You can keep the feature intact while reducing cognitive load by:

1. Centralizing “wallpaper vs video thumbnail” resolution
2. Splitting `resolveVideoThumbnail` into smaller helpers (metadata lookup vs async generation)
3. Narrowing where `m_videoWallpaperMonitors` is touched

### 1. Centralize wallpaper resolution

Instead of branching in each caller, introduce a small helper that hides video-specific logic:

```cpp
// in DisplayWorker.h
private:
    QString resolveWallpaperForMonitor(Monitor *mon,
                                       const QString &fileSource,
                                       uint32_t sourceType = 0);

// in DisplayWorker.cpp
QString DisplayWorker::resolveWallpaperForMonitor(Monitor *mon,
                                                  const QString &fileSource,
                                                  uint32_t sourceType)
{
    const QString path = fileSource.isEmpty()
        ? m_displayInter->GetCurrentWorkspaceBackgroundForMonitor(mon->name())
        : fileSource;

    const bool isVideo = (sourceType == WallpaperSourceTypeVideo) || isVideoFile(path);
    return isVideo ? resolveVideoThumbnail(path, mon) : path;
}
```

Then use this helper in the three call sites:

```cpp
void DisplayWorker::updateMonitorWallpaper(Monitor *mon)
{
    mon->setWallpaper(resolveWallpaperForMonitor(mon, QString{}));
}

void DisplayWorker::onOutputWallpaperReady(WQt::Output *output)
{
    ...
    if (wp) {
        connect(...);
        if (wp->sourceType() == WallpaperSourceTypeVideo && !wp->fileSource().isEmpty()) {
            for (auto it = m_wl_monitors.cbegin(); it != m_wl_monitors.cend(); ++it) {
                if (it.key()->name() == output->name()) {
                    it.key()->setWallpaper(resolveWallpaperForMonitor(it.key(),
                                                                       wp->fileSource(),
                                                                       wp->sourceType()));
                    break;
                }
            }
        }
    }
}

void DisplayWorker::onWallpaperChanged(const QString &fileSource,
                                       uint32_t sourceType,
                                       uint32_t role)
{
    Q_UNUSED(role);
    ...
    for (auto it = m_wl_monitors.cbegin(); it != m_wl_monitors.cend(); ++it) {
        if (it.key()->name() == outputName) {
            it.key()->setWallpaper(resolveWallpaperForMonitor(it.key(),
                                                              fileSource,
                                                              sourceType));
            break;
        }
    }
}
```

This keeps the existing methods mostly unaware of video-specific details.

### 2. Split `resolveVideoThumbnail` responsibilities

`resolveVideoThumbnail` currently mixes JSON parsing, cache management, async job scheduling, and monitor bookkeeping. Splitting into tiny helpers makes it easier to follow and test:

```cpp
// in DisplayWorker.h
private:
    QString findLiveWallpaperThumbnail(const QString &videoPath) const;
    QString ensureThumbnailCachePath(const QString &videoPath) const;
    void scheduleThumbnailGeneration(const QString &videoPath,
                                     const QString &cachePath,
                                     Monitor *monitor);
```

Implementation:

```cpp
QString DisplayWorker::findLiveWallpaperThumbnail(const QString &videoPath) const
{
    QDir liveDir(SysLiveWallpaperDir);
    if (!liveDir.exists())
        return {};

    QFile metaFile(liveDir.absoluteFilePath("metadata.json"));
    if (!metaFile.open(QIODevice::ReadOnly))
        return {};

    QJsonParseError parseErr;
    const QJsonDocument doc = QJsonDocument::fromJson(metaFile.readAll(), &parseErr);
    if (parseErr.error != QJsonParseError::NoError || !doc.isArray())
        return {};

    for (const auto &entry : doc.array()) {
        const QJsonObject obj = entry.toObject();
        const QString videoAbsPath = liveDir.absoluteFilePath(obj.value("path").toString());
        if (videoAbsPath == videoPath) {
            const QString thumbnailRel = obj.value("thumbnail").toString();
            const QString thumbAbsPath = liveDir.absoluteFilePath(thumbnailRel);
            if (!thumbnailRel.isEmpty() && QFile::exists(thumbAbsPath))
                return thumbAbsPath;
            break;
        }
    }
    return {};
}

QString DisplayWorker::ensureThumbnailCachePath(const QString &videoPath) const
{
    const QString cacheDir =
        QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
        + "/live-wallpaper-thumbnails";
    QDir().mkpath(cacheDir);

    const QString cacheName =
        QString(QCryptographicHash::hash(videoPath.toUtf8(),
                                         QCryptographicHash::Md5).toHex())
        + ".png";
    return cacheDir + "/" + cacheName;
}

void DisplayWorker::scheduleThumbnailGeneration(const QString &videoPath,
                                                const QString &cachePath,
                                                Monitor *monitor)
{
    m_videoWallpaperMonitors[videoPath] = monitor;

    QPointer<DisplayWorker> guard(this);
    (void)QtConcurrent::run([guard, videoPath, cachePath]() {
        ffmpegthumbnailer::VideoThumbnailer thumbnailer(480, false, true, 8, false);
        thumbnailer.setThumbnailSize(480, -1);
        thumbnailer.setSeekTime("00:00:01");

        try {
            thumbnailer.generateThumbnail(videoPath.toStdString(),
                                          Png,
                                          cachePath.toStdString());
        } catch (const std::exception &e) {
            qCWarning(DdcDisplayWorker) << "Failed to generate video thumbnail:" << e.what();
            if (guard)
                Q_EMIT guard->videoThumbnailReady(videoPath, {});
            return;
        }

        if (guard && QFile::exists(cachePath))
            Q_EMIT guard->videoThumbnailReady(videoPath, cachePath);
    });
}
```

Now `resolveVideoThumbnail` becomes a simple orchestration method:

```cpp
QString DisplayWorker::resolveVideoThumbnail(const QString &videoPath, Monitor *monitor)
{
    if (const QString liveThumb = findLiveWallpaperThumbnail(videoPath); !liveThumb.isEmpty())
        return liveThumb;

    const QString cachePath = ensureThumbnailCachePath(videoPath);
    if (QFile::exists(cachePath))
        return cachePath;

    if (!m_videoWallpaperMonitors.contains(videoPath))
        scheduleThumbnailGeneration(videoPath, cachePath, monitor);

    // fall back to video path until thumbnail is ready
    return videoPath;
}
```

This keeps filesystem/JSON logic, cache naming, and async job management clearly separated.

### 3. Narrow `m_videoWallpaperMonitors` usage

With the above split, `m_videoWallpaperMonitors` is only touched in:

- `scheduleThumbnailGeneration` (insert)
- the `videoThumbnailReady` lambda in the constructor (lookup + remove)

No other code paths need to know about this map, making the monitor update flow easier to track and reducing mutable state spread inside the class.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/plugin-display/operation/private/displayworker.cpp
Comment thread src/plugin-display/operation/private/displayworker.cpp Outdated
m_model->setIsConcatScreenMode(m_displayInter->isConcatScreenEnabled());
}

QString DisplayWorker::resolveVideoThumbnail(const QString &videoPath, Monitor *monitor)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider refactoring the new video wallpaper handling into shared helpers that hide thumbnail resolution details and isolate state management around m_videoWallpaperMonitors.

The added video handling is functionally fine but does increase complexity in three separate call sites and in resolveVideoThumbnail. You can keep the feature intact while reducing cognitive load by:

  1. Centralizing “wallpaper vs video thumbnail” resolution
  2. Splitting resolveVideoThumbnail into smaller helpers (metadata lookup vs async generation)
  3. Narrowing where m_videoWallpaperMonitors is touched

1. Centralize wallpaper resolution

Instead of branching in each caller, introduce a small helper that hides video-specific logic:

// in DisplayWorker.h
private:
    QString resolveWallpaperForMonitor(Monitor *mon,
                                       const QString &fileSource,
                                       uint32_t sourceType = 0);

// in DisplayWorker.cpp
QString DisplayWorker::resolveWallpaperForMonitor(Monitor *mon,
                                                  const QString &fileSource,
                                                  uint32_t sourceType)
{
    const QString path = fileSource.isEmpty()
        ? m_displayInter->GetCurrentWorkspaceBackgroundForMonitor(mon->name())
        : fileSource;

    const bool isVideo = (sourceType == WallpaperSourceTypeVideo) || isVideoFile(path);
    return isVideo ? resolveVideoThumbnail(path, mon) : path;
}

Then use this helper in the three call sites:

void DisplayWorker::updateMonitorWallpaper(Monitor *mon)
{
    mon->setWallpaper(resolveWallpaperForMonitor(mon, QString{}));
}

void DisplayWorker::onOutputWallpaperReady(WQt::Output *output)
{
    ...
    if (wp) {
        connect(...);
        if (wp->sourceType() == WallpaperSourceTypeVideo && !wp->fileSource().isEmpty()) {
            for (auto it = m_wl_monitors.cbegin(); it != m_wl_monitors.cend(); ++it) {
                if (it.key()->name() == output->name()) {
                    it.key()->setWallpaper(resolveWallpaperForMonitor(it.key(),
                                                                       wp->fileSource(),
                                                                       wp->sourceType()));
                    break;
                }
            }
        }
    }
}

void DisplayWorker::onWallpaperChanged(const QString &fileSource,
                                       uint32_t sourceType,
                                       uint32_t role)
{
    Q_UNUSED(role);
    ...
    for (auto it = m_wl_monitors.cbegin(); it != m_wl_monitors.cend(); ++it) {
        if (it.key()->name() == outputName) {
            it.key()->setWallpaper(resolveWallpaperForMonitor(it.key(),
                                                              fileSource,
                                                              sourceType));
            break;
        }
    }
}

This keeps the existing methods mostly unaware of video-specific details.

2. Split resolveVideoThumbnail responsibilities

resolveVideoThumbnail currently mixes JSON parsing, cache management, async job scheduling, and monitor bookkeeping. Splitting into tiny helpers makes it easier to follow and test:

// in DisplayWorker.h
private:
    QString findLiveWallpaperThumbnail(const QString &videoPath) const;
    QString ensureThumbnailCachePath(const QString &videoPath) const;
    void scheduleThumbnailGeneration(const QString &videoPath,
                                     const QString &cachePath,
                                     Monitor *monitor);

Implementation:

QString DisplayWorker::findLiveWallpaperThumbnail(const QString &videoPath) const
{
    QDir liveDir(SysLiveWallpaperDir);
    if (!liveDir.exists())
        return {};

    QFile metaFile(liveDir.absoluteFilePath("metadata.json"));
    if (!metaFile.open(QIODevice::ReadOnly))
        return {};

    QJsonParseError parseErr;
    const QJsonDocument doc = QJsonDocument::fromJson(metaFile.readAll(), &parseErr);
    if (parseErr.error != QJsonParseError::NoError || !doc.isArray())
        return {};

    for (const auto &entry : doc.array()) {
        const QJsonObject obj = entry.toObject();
        const QString videoAbsPath = liveDir.absoluteFilePath(obj.value("path").toString());
        if (videoAbsPath == videoPath) {
            const QString thumbnailRel = obj.value("thumbnail").toString();
            const QString thumbAbsPath = liveDir.absoluteFilePath(thumbnailRel);
            if (!thumbnailRel.isEmpty() && QFile::exists(thumbAbsPath))
                return thumbAbsPath;
            break;
        }
    }
    return {};
}

QString DisplayWorker::ensureThumbnailCachePath(const QString &videoPath) const
{
    const QString cacheDir =
        QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
        + "/live-wallpaper-thumbnails";
    QDir().mkpath(cacheDir);

    const QString cacheName =
        QString(QCryptographicHash::hash(videoPath.toUtf8(),
                                         QCryptographicHash::Md5).toHex())
        + ".png";
    return cacheDir + "/" + cacheName;
}

void DisplayWorker::scheduleThumbnailGeneration(const QString &videoPath,
                                                const QString &cachePath,
                                                Monitor *monitor)
{
    m_videoWallpaperMonitors[videoPath] = monitor;

    QPointer<DisplayWorker> guard(this);
    (void)QtConcurrent::run([guard, videoPath, cachePath]() {
        ffmpegthumbnailer::VideoThumbnailer thumbnailer(480, false, true, 8, false);
        thumbnailer.setThumbnailSize(480, -1);
        thumbnailer.setSeekTime("00:00:01");

        try {
            thumbnailer.generateThumbnail(videoPath.toStdString(),
                                          Png,
                                          cachePath.toStdString());
        } catch (const std::exception &e) {
            qCWarning(DdcDisplayWorker) << "Failed to generate video thumbnail:" << e.what();
            if (guard)
                Q_EMIT guard->videoThumbnailReady(videoPath, {});
            return;
        }

        if (guard && QFile::exists(cachePath))
            Q_EMIT guard->videoThumbnailReady(videoPath, cachePath);
    });
}

Now resolveVideoThumbnail becomes a simple orchestration method:

QString DisplayWorker::resolveVideoThumbnail(const QString &videoPath, Monitor *monitor)
{
    if (const QString liveThumb = findLiveWallpaperThumbnail(videoPath); !liveThumb.isEmpty())
        return liveThumb;

    const QString cachePath = ensureThumbnailCachePath(videoPath);
    if (QFile::exists(cachePath))
        return cachePath;

    if (!m_videoWallpaperMonitors.contains(videoPath))
        scheduleThumbnailGeneration(videoPath, cachePath, monitor);

    // fall back to video path until thumbnail is ready
    return videoPath;
}

This keeps filesystem/JSON logic, cache naming, and async job management clearly separated.

3. Narrow m_videoWallpaperMonitors usage

With the above split, m_videoWallpaperMonitors is only touched in:

  • scheduleThumbnailGeneration (insert)
  • the videoThumbnailReady lambda in the constructor (lookup + remove)

No other code paths need to know about this map, making the monitor update flow easier to track and reducing mutable state spread inside the class.

1. Add resolveVideoThumbnail() to DisplayWorker for video wallpaper
2. Resolve thumbnail from metadata.json, ffmpegthumbnailer cache,
   or async generation via QtConcurrent
3. Add isVideoFile() helper and WallpaperSourceTypeVideo constant
4. Handle video wallpaper in both Wayland and X11 code paths
5. Add Qt6::Concurrent and libffmpegthumbnailer CMake dependencies

Log: Monitor schematic now shows video wallpaper thumbnails correctly

Influence:
1. Test monitor schematic display with static wallpaper
2. Test monitor schematic display with video wallpaper
3. Verify thumbnail generation for various video formats
4. Test multi-monitor setup with different wallpaper types
5. Verify async thumbnail generation completes and updates display

feat(display): 显示器示意图支持视频壁纸缩略图

1. 在 DisplayWorker 中添加 resolveVideoThumbnail() 方法
2. 从 metadata.json、ffmpegthumbnailer 缓存或异步生成获取缩略图
3. 添加 isVideoFile() 辅助函数和 WallpaperSourceTypeVideo 常量
4. 在 Wayland 和 X11 两条路径中处理视频壁纸
5. 添加 Qt6::Concurrent 和 libffmpegthumbnailer CMake 依赖

Log: 显示器示意图可正常显示视频壁纸缩略图

Influence:
1. 测试静态壁纸下显示器示意图的显示
2. 测试视频壁纸下显示器示意图的显示
3. 验证各种视频格式的缩略图生成
4. 测试多显示器不同壁纸类型的配置
5. 验证异步缩略图生成完成后显示更新

PMS: BUG-367369
@deepin-wm deepin-wm force-pushed the feat/display-video-wallpaper-thumbnail branch from 3a12fdf to aa06497 Compare June 29, 2026 10:08
@deepin-bot

deepin-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 6.1.98
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #3328

@deepin-bot

deepin-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

TAG Bot

New tag: 6.1.99
DISTRIBUTION: unstable
Suggest: synchronizing this PR through rebase #3348

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants