Skip to content

feat(): adds Media field IsMissing for persistent media#551

Open
BossRighteous wants to merge 4 commits intoZaparooProject:mainfrom
BossRighteous:media-persistence-simple
Open

feat(): adds Media field IsMissing for persistent media#551
BossRighteous wants to merge 4 commits intoZaparooProject:mainfrom
BossRighteous:media-persistence-simple

Conversation

@BossRighteous
Copy link
Contributor

This PR takes advantage of all of the existing scan state routines and adds a simple IsMissing flag to Media.

Instead of truncating tables in scanning, we can selectively mark Media.IsMissing = 1 as an inexpensive query before scan.
The AddMediaPath routine then performs an insert with ON CONFLICT DO UPDATE SET IsMissing = 0 to allow existing records to update on conflict or insert as new.

Truncate operations can be moved to a new UI, and records may be pivoted from the WHERE Media.IsMissing = 1 as a starting point for orphans.

I was able to scan from scratch, rescan, and selective rescan with file modifications. Please test accordingly on your side as well.

Copilot AI review requested due to automatic review settings March 7, 2026 22:04
@BossRighteous BossRighteous changed the title adds Media field IsMissing for persistent media feat(): adds Media field IsMissing for persistent media Mar 7, 2026
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a persistent Media.IsMissing flag intended to support rescans without truncating media tables, by marking existing media as missing before scanning and clearing the flag when media is encountered again.

Changes:

  • Add IsMissing column to Media (migration + Go struct/interface updates).
  • Replace full/selective truncation in the media scanner with “mark missing” maintenance operations.
  • Add UPSERT-style behavior and batch inserter support intended to clear IsMissing on conflict.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
pkg/database/database.go Adds IsMissing to database.Media and new methods to MediaDBI.
pkg/database/mediadb/migrations/20260304011734_media_ismissing.sql Adds the IsMissing column to the Media table.
pkg/database/mediadb/sql_media.go Updates Media insert SQL to include IsMissing and attempts UPSERT behavior.
pkg/database/mediadb/sql_maintenance.go Adds SQL helpers to mark media missing (all or per-system).
pkg/database/mediadb/mediadb.go Wires new maintenance methods + adds conflict clause support to batch insert for Media.
pkg/database/mediadb/batch_inserter.go Extends batch inserter to append a custom onConflict clause.
pkg/database/mediadb/batch_inserter_test.go Updates test call site for new batch inserter signature.
pkg/database/mediascanner/mediascanner.go Switches scanner prep from truncation to “mark missing” and adjusts scan-state seeding flow.
pkg/database/mediascanner/indexing_pipeline.go Sets IsMissing=0 on inserts; attempts to clear missing on “found” media.
pkg/testing/helpers/db_mocks.go Adds mock methods for the new maintenance API.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

return handleCancellation(
ctx, db, "Media indexing cancelled during selective scan state population",
)
}
Copy link
Member

Choose a reason for hiding this comment

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

Since data isn't truncated anymore, PopulateScanStateForSelectiveIndexing leaves TitleIDs/MediaIDs maps empty — so AddMediaPath will re-insert titles with new DBIDs each rescan. MediaTitles doesn't have a unique constraint so they'd duplicate. Is that a problem or is this handled somewhere I'm missing?

IsMissing: 0,
})
// Don't process tags if found
return titleIndex, mediaIndex, nil
Copy link
Member

Choose a reason for hiding this comment

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

Related to the above — since MediaIDs is empty, this else branch never actually runs right now. Every path takes the !ok branch and does a fresh insert (which upserts via ON CONFLICT). So the early return skipping tags is dead code until the maps are populated. Worth keeping in mind when addressing the maps question.

return 0, fmt.Errorf("failed to truncate database: %w", err)
}
log.Info().Msg("database truncation completed")*/
err := db.MarkAllMediaMissing()
Copy link
Member

Choose a reason for hiding this comment

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

What's the plan for rows that stay IsMissing=1 after a scan finishes? Right now search queries don't filter on it, so removed files would still show up in results. Is the idea to add a cleanup step later, filter at query time, or leave them for a future orphan management UI?

Copy link
Member

Choose a reason for hiding this comment

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

@BossRighteous we agreed that there really wouldn't be that many and it can be worried about later right?

Fix unchecked error return from InsertMedia, variable shadowing,
stale comments, import shadowing, and test mock expectations.
Remove commented-out truncation code blocks.
@sentry
Copy link

sentry bot commented Mar 8, 2026

Fix gosec G118 (unused context cancel), G117 (secret pattern), G703
(path traversal), noctx (httptest.NewRequest), and prealloc warnings.
Fixes GO-2026-4601 (net/url IPv6 parsing) and GO-2026-4602 (os.Root
FileInfo escape) flagged by CI govulncheck.
Comment on lines +33 to +41
const insertMediaSQL = `INSERT INTO Media
(DBID, MediaTitleDBID, SystemDBID, Path, IsMissing)
VALUES (?, ?, ?, ?, ?)
ON CONFLICT (SystemDBID, Path)
DO UPDATE SET
IsMissing = 0
ON CONFLICT (DBID)
DO UPDATE SET
IsMissing = 0;`
Copy link

Choose a reason for hiding this comment

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

Bug: During a selective rescan, the ON CONFLICT clause for Media updates IsMissing but fails to update MediaTitleDBID, leaving a stale reference and creating orphaned records.
Severity: HIGH

Suggested Fix

Modify the ON CONFLICT (SystemDBID, Path) clause in the InsertMedia function. The DO UPDATE statement should also update the MediaTitleDBID to the new value, for example: ... DO UPDATE SET IsMissing = 0, MediaTitleDBID = excluded.MediaTitleDBID.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: pkg/database/mediadb/sql_media.go#L33-L41

Potential issue: During a selective rescan, the logic in `AddMediaPath` creates a new
`MediaTitle` record for a file, but the subsequent `InsertMedia` call uses an `ON
CONFLICT` clause that only updates the `IsMissing` flag on the existing `Media` row. It
fails to update the `MediaTitleDBID` to point to the newly created title record. This
results in the `Media` row retaining a stale reference to an old `MediaTitle`, leading
to incorrect metadata associations and orphaned `MediaTitle` entries in the database.

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.

3 participants