Skip to content

Commit

Permalink
feat: add support for merging side/main stories
Browse files Browse the repository at this point in the history
Added **EXPERIMENTAL** support for merging side/main stories… making the existing merge logic even more complex in the process.
  • Loading branch information
revam committed Feb 4, 2025
1 parent f9c7351 commit 23c25d1
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 56 deletions.
151 changes: 95 additions & 56 deletions Shokofin/API/ShokoAPIManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -172,15 +172,16 @@ seriesType is not SeriesType.Unknown
else if (tags.ContainsKey("/tmdb structure"))
seriesSettings.StructureType = SeriesStructureType.TMDB_SeriesAndMovies;

if (tags.ContainsKey("/no merge")) {
seriesSettings.MergeOverride |= SeriesMergingOverride.NoMerge;
}
else {
if (tags.ContainsKey("/merge forward"))
seriesSettings.MergeOverride |= SeriesMergingOverride.MergeForward;
if (tags.ContainsKey("/merge backward"))
seriesSettings.MergeOverride |= SeriesMergingOverride.MergeBackward;
}
if (tags.ContainsKey("/no merge"))
seriesSettings.MergeOverride = SeriesMergingOverride.NoMerge;
else if (tags.ContainsKey("/merge with main story"))
seriesSettings.MergeOverride = SeriesMergingOverride.MergeWithMainStory;
else if (tags.ContainsKey("/merge forward") && tags.ContainsKey("/merge backward"))
seriesSettings.MergeOverride = SeriesMergingOverride.MergeForward | SeriesMergingOverride.MergeBackward;
else if (tags.ContainsKey("/merge forward"))
seriesSettings.MergeOverride = SeriesMergingOverride.MergeForward;
else if (tags.ContainsKey("/merge backward"))
seriesSettings.MergeOverride = SeriesMergingOverride.MergeBackward;

if (tags.ContainsKey("/episodes as specials")) {
seriesSettings.EpisodesAsSpecials = true;
Expand Down Expand Up @@ -1267,12 +1268,17 @@ private async Task<SeasonInfo> CreateSeasonInfo(ShokoSeries series) {
var currentDate = currentSeries.AniDB.AirDate.Value;
var currentRelations = relations;
var currentConfig = seriesConfig;
var groupId = currentSeries.IDs.ParentGroup;
while (currentRelations.Count > 0) {
foreach (var prequelRelation in currentRelations.Where(relation => relation.Type is RelationType.Prequel && relation.RelatedIDs.Shoko.HasValue)) {
foreach (
var prequelRelation in currentRelations
.Where(relation => relation.Type is RelationType.Prequel or RelationType.MainStory && relation.RelatedIDs.Shoko.HasValue)
.OrderBy(relation => relation.Type is RelationType.Prequel)
) {
if (await ApiClient.GetShokoSeries(prequelRelation.RelatedIDs.Shoko!.Value.ToString()).ConfigureAwait(false) is not { } prequelSeries)
continue;

if (prequelSeries.IDs.ParentGroup != currentSeries.IDs.ParentGroup)
if (prequelSeries.IDs.ParentGroup != groupId)
continue;

var prequelConfig = await GetSeriesConfiguration(prequelSeries.Id).ConfigureAwait(false);
Expand All @@ -1285,10 +1291,14 @@ private async Task<SeasonInfo> CreateSeasonInfo(ShokoSeries series) {
if (!config.EXPERIMENTAL_MergeSeasonsTypes.Contains(prequelConfig.TypeOverride ?? prequelSeries.AniDB.Type))
continue;

if (prequelSeries.AniDB.AirDate is not { } prequelDate || prequelDate > currentDate)
if (prequelSeries.AniDB.AirDate is not { } prequelDate || (prequelRelation.Type is RelationType.Prequel && prequelDate > currentDate))
continue;

var mergeOverride = currentConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeBackward) || prequelConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeForward);
var mergeOverride = (
prequelRelation.Type is RelationType.Prequel && (currentConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeBackward) || prequelConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeForward))
) || (
prequelRelation.Type is RelationType.MainStory && currentConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeWithMainStory)
);
if (!mergeOverride) {
if (maxDaysThreshold is -1)
continue;
Expand All @@ -1311,6 +1321,10 @@ private async Task<SeasonInfo> CreateSeasonInfo(ShokoSeries series) {
goto continuePrequelWhileLoop;
}

// We only want to merge main/side stories if the override is set.
if (prequelRelation.Type is RelationType.MainStory)
continue;

if (string.IsNullOrEmpty(adjustedPrequelMainTitle)) {
if (string.Equals(adjustedMainTitle, prequelMainTitle, StringComparison.InvariantCultureIgnoreCase)) {
currentSeries = prequelSeries;
Expand Down Expand Up @@ -1345,63 +1359,88 @@ private async Task<SeasonInfo> CreateSeasonInfo(ShokoSeries series) {
goto logAndReturn;
}

while (currentRelations.Count > 0) {
foreach (var sequelRelation in currentRelations.Where(relation => relation.Type == RelationType.Sequel && relation.RelatedIDs.Shoko.HasValue)) {
if (await ApiClient.GetShokoSeries(sequelRelation.RelatedIDs.Shoko!.Value.ToString()).ConfigureAwait(false) is not { } sequelSeries)
continue;

if (sequelSeries.IDs.ParentGroup != currentSeries.IDs.ParentGroup)
continue;
var storyStack = new Stack<(string adjustedMainTitle, DateTime currentDate, SeriesConfiguration currentConfig, IReadOnlyList<Relation> currentRelations, int relationOffset)>([
(adjustedMainTitle, currentDate, currentConfig, currentRelations, 0)
]);
while (storyStack.Count > 0) {
(adjustedMainTitle, currentDate, currentConfig, currentRelations, var relationOffset) = storyStack.Pop();
while (currentRelations.Count > 0) {
foreach (
var sequelRelation in currentRelations
.Where(relation => relation.Type is RelationType.Sequel or RelationType.SideStory && relation.RelatedIDs.Shoko.HasValue)
.OrderBy(relation => relation.Type is RelationType.Sequel)
.Skip(relationOffset)
) {
relationOffset++;
if (await ApiClient.GetShokoSeries(sequelRelation.RelatedIDs.Shoko!.Value.ToString()).ConfigureAwait(false) is not { } sequelSeries)
continue;

var sequelConfig = await GetSeriesConfiguration(sequelSeries.Id).ConfigureAwait(false);
if (sequelConfig.MergeOverride is SeriesMergingOverride.NoMerge)
continue;
if (sequelSeries.IDs.ParentGroup != groupId)
continue;

if (sequelConfig.StructureType is not SeriesStructureType.Shoko_Groups)
continue;
var sequelConfig = await GetSeriesConfiguration(sequelSeries.Id).ConfigureAwait(false);
if (sequelConfig.MergeOverride is SeriesMergingOverride.NoMerge)
continue;

if (!config.EXPERIMENTAL_MergeSeasonsTypes.Contains(sequelConfig.TypeOverride ?? sequelSeries.AniDB.Type))
continue;
if (sequelConfig.StructureType is not SeriesStructureType.Shoko_Groups)
continue;

if (sequelSeries.AniDB.AirDate is not { } sequelDate || sequelDate < currentDate)
continue;
if (!config.EXPERIMENTAL_MergeSeasonsTypes.Contains(sequelConfig.TypeOverride ?? sequelSeries.AniDB.Type))
continue;

var mergeOverride = currentConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeForward) || sequelConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeBackward);
if (!mergeOverride) {
if (maxDaysThreshold < 0)
if (sequelSeries.AniDB.AirDate is not { } sequelDate || (sequelRelation.Type is RelationType.Sequel && sequelDate < currentDate))
continue;

if (maxDaysThreshold > 0) {
var deltaDays = (int)Math.Floor((sequelDate - currentDate).TotalDays);
if (deltaDays > maxDaysThreshold)
var mergeOverride = (
sequelRelation.Type is RelationType.Sequel && (currentConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeForward) || sequelConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeBackward))
) || (
sequelRelation.Type is RelationType.SideStory && sequelConfig.MergeOverride.HasFlag(SeriesMergingOverride.MergeWithMainStory)
);
if (!mergeOverride) {
if (maxDaysThreshold < 0)
continue;

if (maxDaysThreshold > 0) {
var deltaDays = (int)Math.Floor((sequelDate - currentDate).TotalDays);
if (deltaDays > maxDaysThreshold)
continue;
}
}
}

var sequelMainTitle = sequelSeries.AniDB.Titles.First(title => title.Type == TitleType.Main).Value;
var adjustedSequelMainTitle = AdjustMainTitle(sequelMainTitle);
if (mergeOverride) {
adjustedMainTitle = adjustedSequelMainTitle ?? sequelMainTitle;
extraIds.Add(sequelSeries.Id);
currentSeries = sequelSeries;
currentDate = sequelDate;
currentRelations = await ApiClient.GetRelationsForShokoSeries(sequelSeries.Id).ConfigureAwait(false);
currentConfig = sequelConfig;
goto continueSequelWhileLoop;
}
var sequelMainTitle = sequelSeries.AniDB.Titles.First(title => title.Type == TitleType.Main).Value;
var adjustedSequelMainTitle = AdjustMainTitle(sequelMainTitle);
if (mergeOverride) {
// If we're about to enter a side story, then push the main story on the stack at the next relation index.
if (sequelRelation.Type is RelationType.SideStory)
storyStack.Push((adjustedMainTitle, currentDate, currentConfig, currentRelations, relationOffset));

// Re-focus on the sequel when overriding.
adjustedMainTitle = adjustedSequelMainTitle ?? sequelMainTitle;
extraIds.Add(sequelSeries.Id);
currentDate = sequelDate;
currentRelations = await ApiClient.GetRelationsForShokoSeries(sequelSeries.Id).ConfigureAwait(false);
currentConfig = sequelConfig;
goto continueSequelWhileLoop;
}

if (string.IsNullOrEmpty(adjustedSequelMainTitle))
continue;
// We only want to merge main/side stories if the override is set.
if (sequelRelation.Type is RelationType.SideStory)
continue;

if (string.Equals(adjustedMainTitle, adjustedSequelMainTitle, StringComparison.InvariantCultureIgnoreCase)) {
extraIds.Add(sequelSeries.Id);
currentDate = sequelDate;
currentRelations = await ApiClient.GetRelationsForShokoSeries(sequelSeries.Id).ConfigureAwait(false);
goto continueSequelWhileLoop;
if (string.IsNullOrEmpty(adjustedSequelMainTitle))
continue;

if (string.Equals(adjustedMainTitle, adjustedSequelMainTitle, StringComparison.InvariantCultureIgnoreCase)) {
extraIds.Add(sequelSeries.Id);
currentDate = sequelDate;
currentRelations = await ApiClient.GetRelationsForShokoSeries(sequelSeries.Id).ConfigureAwait(false);
currentConfig = sequelConfig;
goto continueSequelWhileLoop;
}
}
break;
continueSequelWhileLoop: continue;
}
break;
continueSequelWhileLoop: continue;
}

logAndReturn:
Expand Down
1 change: 1 addition & 0 deletions Shokofin/Configuration/SeriesMergingOverride.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ public enum SeriesMergingOverride {
NoMerge = 1,
MergeForward = 2,
MergeBackward = 4,
MergeWithMainStory = 8,
}

0 comments on commit 23c25d1

Please sign in to comment.