Refactor metadata application logic in PLib video library manager to utilize a set of sources instead of a single source, improving the accuracy of metadata retrieval. Update tests to validate new behavior, ensuring that previously answered sources are not queried again and that multiple applications of the same source are recorded correctly. Revise README.md to reflect these changes and clarify the new metadata handling processes.
This commit is contained in:
@@ -183,9 +183,15 @@ dotnet test
|
|||||||
**Признак «уже размечено» — отдельная отметка, а не наличие описания.** Сначала прогон
|
**Признак «уже размечено» — отдельная отметка, а не наличие описания.** Сначала прогон
|
||||||
пропускал видео с непустым `Description`, и это ломалось на источниках, у которых есть
|
пропускал видео с непустым `Description`, и это ломалось на источниках, у которых есть
|
||||||
название, актёры и теги, но нет синопсиса: такие видео возвращались на каждом запуске.
|
название, актёры и теги, но нет синопсиса: такие видео возвращались на каждом запуске.
|
||||||
Теперь применение ставит `MetadataAppliedAt` и имя источника, а миграция проставляет
|
Хранится **набор** источников (`VideoMetadataSources`), а не последний применённый.
|
||||||
отметку тем, у кого описание уже есть, — иначе первый же запуск после обновления предложил
|
Одного поля хватало ровно до второго применения, после которого первый источник
|
||||||
бы заново всё, что уже было сделано.
|
забывался и предлагался заново. Набор же отвечает на вопрос, который прогон и задаёт, —
|
||||||
|
«этот источник уже высказался?», — и делает осмысленным режим «источники, которых ещё
|
||||||
|
не спрашивали»: у видео уже есть ответы PornDB, вопрос в том, что добавит StashDB.
|
||||||
|
Спрашивать того, кто уже ответил, значит потратить запрос, чтобы услышать написанное.
|
||||||
|
Режимов три: только неразмеченные, недостающие источники, всё заново.
|
||||||
|
Миграция переносит старую одиночную колонку в набор первой строкой, и делает это до
|
||||||
|
удаления колонок — сгенерированный вариант удалял их первыми и терял всё разом.
|
||||||
- **Картинки скачиваются один раз и по размеру.** `images` в stash-box есть у сцены,
|
- **Картинки скачиваются один раз и по размеру.** `images` в stash-box есть у сцены,
|
||||||
у актёра и у студии; у тега такого поля нет вовсе, поэтому там карточка показывает первую
|
у актёра и у студии; у тега такого поля нет вовсе, поэтому там карточка показывает первую
|
||||||
букву — это норма, а не отсутствие данных. Обложка сцены нужна там, где выбирают из
|
букву — это норма, а не отсутствие данных. Обложка сцены нужна там, где выбирают из
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ public sealed class MetadataScanTests
|
|||||||
(await CollectAsync()).OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(1);
|
(await CollectAsync()).OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(1);
|
||||||
|
|
||||||
// Asking again for everything is what the switch is for.
|
// Asking again for everything is what the switch is for.
|
||||||
var all = await CollectAsync(new MetadataScanRequest(OnlyWithoutMetadata: false));
|
var all = await CollectAsync(new MetadataScanRequest(MetadataScanScope.Everything));
|
||||||
all.OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(2);
|
all.OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,8 +66,8 @@ public sealed class MetadataScanTests
|
|||||||
await CollectAsync(new MetadataScanRequest(ApplyUnambiguous: true));
|
await CollectAsync(new MetadataScanRequest(ApplyUnambiguous: true));
|
||||||
|
|
||||||
video.Description.ShouldBeNull();
|
video.Description.ShouldBeNull();
|
||||||
video.MetadataAppliedAt.ShouldNotBeNull();
|
video.HasMetadata.ShouldBeTrue();
|
||||||
video.MetadataSource.ShouldBe("StashDB");
|
video.AppliedSources.ShouldHaveSingleItem().SourceName.ShouldBe("StashDB");
|
||||||
|
|
||||||
// Judging "already done" by the description sent these back on every single run.
|
// Judging "already done" by the description sent these back on every single run.
|
||||||
var again = await CollectAsync(new MetadataScanRequest(ApplyUnambiguous: true));
|
var again = await CollectAsync(new MetadataScanRequest(ApplyUnambiguous: true));
|
||||||
@@ -148,6 +148,66 @@ public sealed class MetadataScanTests
|
|||||||
events.OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(3);
|
events.OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task A_new_source_can_be_asked_about_videos_the_old_one_already_answered()
|
||||||
|
{
|
||||||
|
var video = Video("видео", hash: 1);
|
||||||
|
video.MarkMetadataApplied("PornDb");
|
||||||
|
_videos.Seed(video);
|
||||||
|
|
||||||
|
var fresh = new MetadataSourceOptions { Name = "StashDB", Endpoint = "https://stashdb/graphql" };
|
||||||
|
var known = new MetadataSourceOptions { Name = "PornDb", Endpoint = "https://porndb/graphql" };
|
||||||
|
|
||||||
|
Answer(fresh, [Match("Из нового источника")]);
|
||||||
|
Answer(known, [Match("Из старого источника")]);
|
||||||
|
|
||||||
|
var events = await CollectAsync(
|
||||||
|
new MetadataScanRequest(MetadataScanScope.MissingSources),
|
||||||
|
known,
|
||||||
|
fresh);
|
||||||
|
|
||||||
|
// The point of the mode: the source that already had its say is not asked to repeat
|
||||||
|
// itself, and only what the new one adds comes back.
|
||||||
|
var matched = events.OfType<MetadataScanEvent.Matched>().ShouldHaveSingleItem();
|
||||||
|
matched.Matches.ShouldHaveSingleItem().Title.ShouldBe("Из нового источника");
|
||||||
|
|
||||||
|
await _provider.DidNotReceive().FindByPerceptualHashAsync(
|
||||||
|
known,
|
||||||
|
Arg.Any<ulong>(),
|
||||||
|
Arg.Any<CancellationToken>());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task A_video_every_source_has_already_answered_is_left_out_of_that_mode()
|
||||||
|
{
|
||||||
|
var video = Video("видео", hash: 1);
|
||||||
|
video.MarkMetadataApplied("StashDB");
|
||||||
|
_videos.Seed(video);
|
||||||
|
|
||||||
|
Answer(_source, [Match("Название")]);
|
||||||
|
|
||||||
|
var events = await CollectAsync(new MetadataScanRequest(MetadataScanScope.MissingSources));
|
||||||
|
|
||||||
|
events.OfType<MetadataScanEvent.Completed>().Single().Processed.ShouldBe(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Applying_the_same_source_twice_records_it_once()
|
||||||
|
{
|
||||||
|
var video = Video("видео", hash: 1);
|
||||||
|
_videos.Seed(video);
|
||||||
|
|
||||||
|
video.MarkMetadataApplied("StashDB");
|
||||||
|
video.MarkMetadataApplied("stashdb");
|
||||||
|
|
||||||
|
// Free text the user typed: "PornDb" and "PornDB" are plainly the same source to
|
||||||
|
// everyone but a byte comparison.
|
||||||
|
video.AppliedSources.ShouldHaveSingleItem();
|
||||||
|
video.HasMetadataFrom("STASHDB").ShouldBeTrue();
|
||||||
|
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
private void Answer(MetadataSourceOptions source, IReadOnlyList<VideoMetadataMatch> matches) =>
|
private void Answer(MetadataSourceOptions source, IReadOnlyList<VideoMetadataMatch> matches) =>
|
||||||
_provider.FindByPerceptualHashAsync(source, Arg.Any<ulong>(), Arg.Any<CancellationToken>())
|
_provider.FindByPerceptualHashAsync(source, Arg.Any<ulong>(), Arg.Any<CancellationToken>())
|
||||||
.Returns(matches);
|
.Returns(matches);
|
||||||
|
|||||||
Reference in New Issue
Block a user