Skip to content

Commit

Permalink
fix: respect map as specials tag
Browse files Browse the repository at this point in the history
The custom tag `"shokofin/map as specials"` should generally be applied to OVAs where you want the episodes to show up as specials and not normal episodes, but the system will allow you to set the tag on _any_ series and it should Just Work™. It can also be combined with the season merging, or used outside it in shoko groups.
  • Loading branch information
revam committed Nov 10, 2024
1 parent f3f25b8 commit dd9d1ce
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 6 deletions.
27 changes: 23 additions & 4 deletions Shokofin/API/Info/SeasonInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,20 @@ public class SeasonInfo
/// </summary>
public readonly IReadOnlyDictionary<string, RelationType> RelationMap;

public SeasonInfo(Series series, SeriesType? customType, IEnumerable<string> extraIds, DateTime? earliestImportedAt, DateTime? lastImportedAt, List<EpisodeInfo> episodes, List<Role> cast, List<Relation> relations, string[] genres, string[] tags, string[] productionLocations, string? contentRating)
public SeasonInfo(
Series series,
SeriesType? customType,
IEnumerable<string> extraIds,
DateTime? earliestImportedAt,
DateTime? lastImportedAt,
List<EpisodeInfo> episodes,
List<Role> cast,
List<Relation> relations,
string[] genres,
string[] tags,
string[] productionLocations,
IReadOnlyDictionary<string, IReadOnlyDictionary<string, ResolvedTag>> tagSeriesMap,
string? contentRating)
{
var seriesId = series.IDs.Shoko.ToString();
var studios = cast
Expand Down Expand Up @@ -138,13 +151,19 @@ public SeasonInfo(Series series, SeriesType? customType, IEnumerable<string> ext
.ThenBy(episode => episode.AniDB.EpisodeNumber)
.ToList();

// Take note of any episodes which should be mapped as specials.
var mappedAsSpecials = episodes
.Where(episode => episode.AniDB.Type is EpisodeType.Normal or EpisodeType.Other && tagSeriesMap[episode.Shoko.IDs.ParentSeries.ToString()].ContainsKey("/custom user tags/shokofin/map as specials"))
.ToHashSet();

// Iterate over the episodes once and store some values for later use.
int index = 0;
int lastNormalEpisode = -1;
foreach (var episode in episodes) {
if (episode.Shoko.IsHidden)
continue;
switch (episode.AniDB.Type) {
var episodeType = mappedAsSpecials.Contains(episode) ? EpisodeType.Special : episode.AniDB.Type;
switch (episodeType) {
case EpisodeType.Normal:
episodesList.Add(episode);
lastNormalEpisode = index;
Expand All @@ -159,15 +178,15 @@ public SeasonInfo(Series series, SeriesType? customType, IEnumerable<string> ext
if (episode.ExtraType != null) {
extrasList.Add(episode);
}
else if (episode.AniDB.Type == EpisodeType.Special) {
else if (episodeType is EpisodeType.Special) {
specialsList.Add(episode);
if (lastNormalEpisode == -1) {
specialsBeforeEpisodes.Add(episode.Id);
}
else {
var previousEpisode = episodes
.GetRange(lastNormalEpisode, index - lastNormalEpisode)
.FirstOrDefault(e => e.AniDB.Type == EpisodeType.Normal);
.FirstOrDefault(e => e.AniDB.Type is EpisodeType.Normal && !mappedAsSpecials.Contains(e));
if (previousEpisode != null)
specialsAnchorDictionary[episode] = previousEpisode;
}
Expand Down
11 changes: 9 additions & 2 deletions Shokofin/API/ShokoAPIManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,7 @@ private async Task<SeasonInfo> CreateSeasonInfo(Series series)
var genresTasks = detailsIds.Select(id => GetGenresForSeries(id));
var tagsTasks = detailsIds.Select(id => GetTagsForSeries(id));
var productionLocationsTasks = detailsIds.Select(id => GetProductionLocations(id));
var namespacedTagsTasks = detailsIds.Select(id => GetNamespacedTagsForSeries(id));

// Await the tasks in order.
var cast = (await Task.WhenAll(castTasks))
Expand All @@ -781,16 +782,22 @@ private async Task<SeasonInfo> CreateSeasonInfo(Series series)
.OrderBy(g => g)
.Distinct()
.ToArray();
var namespacedTags = (await Task.WhenAll(namespacedTagsTasks))
.Select((t, i) => (t, i))
.ToDictionary(t => detailsIds[t.i], (t) => t.t);

// Create the season info using the merged details.
seasonInfo = new SeasonInfo(series, customSeriesType, extraIds, earliestImportedAt, lastImportedAt, episodes, cast, relations, genres, tags, productionLocations, contentRating);
seasonInfo = new SeasonInfo(series, customSeriesType, extraIds, earliestImportedAt, lastImportedAt, episodes, cast, relations, genres, tags, productionLocations, namespacedTags, contentRating);
} else {
var cast = await APIClient.GetSeriesCast(seriesId).ConfigureAwait(false);
var relations = await APIClient.GetSeriesRelations(seriesId).ConfigureAwait(false);
var genres = await GetGenresForSeries(seriesId).ConfigureAwait(false);
var tags = await GetTagsForSeries(seriesId).ConfigureAwait(false);
var productionLocations = await GetProductionLocations(seriesId).ConfigureAwait(false);
seasonInfo = new SeasonInfo(series, customSeriesType, extraIds, earliestImportedAt, lastImportedAt, episodes, cast, relations, genres, tags, productionLocations, contentRating);
var namespacedTags = new Dictionary<string, IReadOnlyDictionary<string, ResolvedTag>>() {
{ seriesId, await GetNamespacedTagsForSeries(seriesId).ConfigureAwait(false) },
};
seasonInfo = new SeasonInfo(series, customSeriesType, extraIds, earliestImportedAt, lastImportedAt, episodes, cast, relations, genres, tags, productionLocations, namespacedTags, contentRating);
}

foreach (var episode in episodes)
Expand Down

0 comments on commit dd9d1ce

Please sign in to comment.