Refactor metadata handling in PLib video library manager to improve video matching accuracy. Introduce new properties for tracking when metadata was applied and from which source. Update LibraryService and related components to utilize these properties, ensuring videos without synopses are correctly identified as matched. Enhance UI to display the library's own thumbnail alongside candidate matches, improving user decision-making. Revise README.md to reflect these changes and clarify metadata application processes.

This commit is contained in:
Leonid Pershin
2026-08-10 11:29:58 +03:00
parent 4539a97d16
commit 70611c2051
20 changed files with 1387 additions and 513 deletions
+41 -5
View File
@@ -39,21 +39,57 @@ public sealed class MetadataScanTests
}
[Fact]
public async Task Videos_that_already_have_a_description_are_skipped_by_default()
public async Task Videos_a_match_was_already_applied_to_are_skipped_by_default()
{
var described = Video("описанное", hash: 1);
described.Describe("уже есть");
_videos.Seed(described, Video("голое", hash: 2));
var done = Video("размеченное", hash: 1);
done.MarkMetadataApplied("StashDB");
_videos.Seed(done, Video("голое", hash: 2));
Answer(_source, []);
(await CollectAsync()).OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(1);
// Asking again for everything is what the switch is for.
var all = await CollectAsync(new MetadataScanRequest(OnlyWithoutDescription: false));
var all = await CollectAsync(new MetadataScanRequest(OnlyWithoutMetadata: false));
all.OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(2);
}
[Fact]
public async Task A_match_with_no_synopsis_still_counts_as_applied()
{
var video = Video("видео", hash: 1);
_videos.Seed(video);
// stash-box scenes often carry a title, performers and tags but no details at all.
Answer(_source, [Match("Название") with { Description = null }]);
await CollectAsync(new MetadataScanRequest(ApplyUnambiguous: true));
video.Description.ShouldBeNull();
video.MetadataAppliedAt.ShouldNotBeNull();
video.MetadataSource.ShouldBe("StashDB");
// Judging "already done" by the description sent these back on every single run.
var again = await CollectAsync(new MetadataScanRequest(ApplyUnambiguous: true));
again.OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(0);
}
[Fact]
public async Task A_result_carries_our_own_poster_frame_for_the_user_to_compare_against()
{
var video = Video("видео", hash: 1);
video.AttachThumbnail(@"C:\cache\ours.jpg");
_videos.Seed(video);
Answer(_source, [Match("Название")]);
var matched = (await CollectAsync()).OfType<MetadataScanEvent.Matched>().Single();
// Deciding whether a proposal is the same video is done by eye, and there is nothing
// to judge against if only the proposals have pictures.
matched.VideoThumbnailPath.ShouldBe(@"C:\cache\ours.jpg");
}
[Fact]
public async Task A_single_candidate_is_written_only_when_the_run_was_told_it_may()
{