Skip to content

feat: merge cover and lyrics extraction into single TagLib pass with concurrent processing#720

Open
kt286 wants to merge 1 commit into
linuxdeepin:masterfrom
kt286:feat/concurrent-cover-lyrics-extraction
Open

feat: merge cover and lyrics extraction into single TagLib pass with concurrent processing#720
kt286 wants to merge 1 commit into
linuxdeepin:masterfrom
kt286:feat/concurrent-cover-lyrics-extraction

Conversation

@kt286

@kt286 kt286 commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

…concurrent processing

  • Add parseMetaCoverAndLyrics() to extract cover art and lyrics in one TagLib file-open, reducing redundant I/O
  • Fall back to FFmpeg for cover extraction when TagLib APIC frame is empty
  • Add QtConcurrent-based concurrent cover processing in DBOperate for batch imports
  • Conditionally link MPRIS only on Linux, add Qt6::Concurrent module

feat: 将封面和歌词提取合并为单次 TagLib 读取,并支持并发处理

  • 新增 parseMetaCoverAndLyrics() 方法,在一次 TagLib 文件打开中同时提取封面和歌词,减少重复 I/O
  • 当 TagLib APIC 帧为空时,回退到 FFmpeg 提取封面
  • 在 DBOperate 中添加基于 QtConcurrent 的并发封面处理,用于批量导入
  • 仅在 Linux 上链接 MPRIS,添加 Qt6::Concurrent 模块

Summary by Sourcery

Unify cover art and lyrics extraction into a single metadata pass and process pending covers concurrently during batch imports, while updating build configuration for conditional MPRIS linking and QtConcurrent support.

New Features:

  • Add a combined metadata extractor that reads cover art and lyrics in a single TagLib file-open and falls back to FFmpeg when no embedded cover is found.
  • Introduce concurrent batch processing for cover and lyrics extraction during media imports using QtConcurrent.

Enhancements:

  • Cache and reuse extracted cover art and lyrics on disk, including a default cover when none can be found.
  • Refine logging and metadata handling around cover and lyrics parsing for improved observability.

Build:

  • Link Qt6::Concurrent and include the Concurrent module in the libdmusic build.
  • Link the MPRIS library conditionally only on Linux platforms.

@deepin-ci-robot

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: kt286

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

@deepin-ci-robot

Copy link
Copy Markdown

Hi @kt286. Thanks for your PR.

I'm waiting for a linuxdeepin member to verify that this patch is reasonable to test. If it is, they should reply with /ok-to-test on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should join the org to skip this step.

Once the patch is verified, the new status will be reflected by the ok-to-test label.

I understand the commands that are listed here.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@kt286 kt286 changed the title feat: merge cover and lyrics extraction into single TagLib pass with … feat: merge cover and lyrics extraction into single TagLib pass with concurrent processing Jul 21, 2026
@sourcery-ai

sourcery-ai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a new AudioAnalysis::parseMetaCoverAndLyrics() routine that extracts cover art and lyrics in a single TagLib pass with FFmpeg fallback for covers, and wires it into DBOperate via QtConcurrent-based parallel batch processing while updating build configuration to link QtConcurrent and MPRIS conditionally on Linux.

Sequence diagram for parallel cover and lyrics extraction via parseMetaCoverAndLyrics

sequenceDiagram
    participant DBOperate
    participant QtConcurrent
    participant AudioAnalysis
    participant TagLib
    participant FFmpeg

    DBOperate->>DBOperate: slotImportMetas(urls, metaHash)
    DBOperate->>DBOperate: collect pendingCovers
    DBOperate->>DBOperate: processPendingCovers(pendingCovers)

    DBOperate->>QtConcurrent: map(pendingCovers, parseMetaCoverAndLyrics)
    activate QtConcurrent

    loop for each MediaMeta in pendingCovers (in parallel)
        QtConcurrent->>AudioAnalysis: parseMetaCoverAndLyrics(meta)
        activate AudioAnalysis
        AudioAnalysis->>AudioAnalysis: check cache for cover/lyrics
        alt [not fully cached]
            AudioAnalysis->>TagLib: open MPEG::File(path)
            alt [TagLib file valid]
                AudioAnalysis->>TagLib: read APIC frame (cover)
                AudioAnalysis->>TagLib: read SYLT/USLT frames (lyrics)
                alt [cover not found via TagLib]
                    AudioAnalysis->>FFmpeg: avformat_open_input(path)
                    AudioAnalysis->>FFmpeg: scan streams for attached_pic
                    FFmpeg-->>AudioAnalysis: cover image data
                end
            end
        end
        AudioAnalysis-->>QtConcurrent: meta updated (coverUrl, hasimage, lyricPath)
        deactivate AudioAnalysis
    end

    QtConcurrent-->>DBOperate: all metas updated
    deactivate QtConcurrent

    loop batches of size 8
        DBOperate->>DBOperate: emit signalMetaCoverReady(meta) for each in batch
    end
    DBOperate->>DBOperate: emit signalCoverBatchFinished()
Loading

File-Level Changes

Change Details Files
Add merged cover+lyrics extraction using a single TagLib MPEG file-open with FFmpeg-based cover fallback and default-cover handling.
  • Introduce AudioAnalysis::parseMetaCoverAndLyrics(DMusic::MediaMeta &meta) to prepare cache directories, check cached cover/lyrics, and early-return when both are present.
  • Use TagLib::MPEG::File to open the file once (with platform-specific path handling) and, if valid, read ID3v2 APIC frames for cover art, writing a scaled JPEG to the cache and updating meta fields.
  • Extract lyrics from ID3v2 SYLT (synchronized) frames first, falling back to USLT (unsynchronized) frames, and persist them as .lrc files in the lyrics cache directory.
  • If TagLib does not provide a cover and FFmpeg playback engine is enabled, dynamically resolve FFmpeg symbols, scan attached pictures, and cache a scaled cover image.
  • When neither TagLib nor FFmpeg yields a cover, assign a default cover image path; log progress and clear TagLib resources at the end.
src/libdmusic/core/audioanalysis.cpp
src/libdmusic/core/audioanalysis.h
Switch DBOperate cover/lyrics extraction after import to parallel batch processing via QtConcurrent while keeping per-meta UI update semantics.
  • Replace the sequential loop that called AudioAnalysis::parseMetaCover() and parseMetaLyrics() with a conditional call to a new processPendingCovers() helper when pendingCovers is non-empty.
  • Implement DBOperate::processPendingCovers() to use QtConcurrent::map over the QListDMusic::MediaMeta and invoke AudioAnalysis::parseMetaCoverAndLyrics() for each item, waiting for completion.
  • After parallel processing, emit signalMetaCoverReady() for each meta in small batches to avoid overwhelming UI updates and log batch processing start/end.
  • Declare processPendingCovers() as a private helper, and include and in dboperate.h to support the new concurrency usage.
src/libdmusic/core/dboperate.cpp
src/libdmusic/core/dboperate.h
Update build configuration to enable QtConcurrent and make MPRIS linkage Linux-only.
  • Add Concurrent to the QT_MODULES list so the library links against Qt6::Concurrent.
  • Adjust target_link_libraries to always link Qt6::Concurrent alongside the existing Qt modules.
  • Remove unconditional linkage of PkgConfig::MPRIS from TARGET_LIBS wiring and wrap MPRIS linkage in an if(LINUX) guard so it is only linked on Linux.
  • Leave TagLib, DTK, ICU, and FFMPEG link settings intact while cleaning up duplicate PkgConfig::TAGLIB/DTK entries.
src/libdmusic/CMakeLists.txt
Refresh copyright years in core libdmusic headers and sources.
  • Extend copyright ranges from 20202020/2021 to 20202026 in audioanalysis.cpp/.h and dboperate.cpp/.h headers.
src/libdmusic/core/audioanalysis.cpp
src/libdmusic/core/audioanalysis.h
src/libdmusic/core/dboperate.cpp
src/libdmusic/core/dboperate.h

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

@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 left some high level feedback:

  • The cache directory setup in parseMetaCoverAndLyrics() mixes manual cdUp()/mkdir() for images with mkpath() for lyrics; consider using QDir().mkpath() consistently for both to avoid fragile parent-directory assumptions and simplify the logic.
  • In the FFmpeg fallback path inside parseMetaCoverAndLyrics(), the dynamic symbol resolution (DynamicLibraries::instance()->resolve) runs for every file and now potentially in parallel; consider caching these function pointers once at a higher level or as static locals to avoid repeated, concurrent lookups on the same symbols.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The cache directory setup in parseMetaCoverAndLyrics() mixes manual cdUp()/mkdir() for images with mkpath() for lyrics; consider using QDir().mkpath() consistently for both to avoid fragile parent-directory assumptions and simplify the logic.
- In the FFmpeg fallback path inside parseMetaCoverAndLyrics(), the dynamic symbol resolution (DynamicLibraries::instance()->resolve) runs for every file and now potentially in parallel; consider caching these function pointers once at a higher level or as static locals to avoid repeated, concurrent lookups on the same symbols.

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.

@kt286
kt286 force-pushed the feat/concurrent-cover-lyrics-extraction branch from 05dd233 to 18410af Compare July 22, 2026 02:38
…concurrent processing

- Add parseMetaCoverAndLyrics() to extract cover art and lyrics in one TagLib file-open, reducing redundant I/O
- Fall back to FFmpeg for cover extraction when TagLib APIC frame is empty
- Add QtConcurrent-based concurrent cover processing in DBOperate for batch imports
- Conditionally link MPRIS only on Linux, add Qt6::Concurrent module

feat: 将封面和歌词提取合并为单次 TagLib 读取,并支持并发处理

- 新增 parseMetaCoverAndLyrics() 方法,在一次 TagLib 文件打开中同时提取封面和歌词,减少重复 I/O
- 当 TagLib APIC 帧为空时,回退到 FFmpeg 提取封面
- 在 DBOperate 中添加基于 QtConcurrent 的并发封面处理,用于批量导入
- 仅在 Linux 上链接 MPRIS,添加 Qt6::Concurrent 模块
@lzwind
lzwind force-pushed the feat/concurrent-cover-lyrics-extraction branch from 18410af to f89ca6c Compare July 23, 2026 05:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants