Skip to content

Commit

Permalink
Backport pull request jellyfin#8191 from jellyfin/release-10.8.z
Browse files Browse the repository at this point in the history
fix: remove Virtual episodes when their physical counterpart exists

Authored-by: cvium <clausvium@gmail.com>

Merged-by: Bond-009 <bond.009@outlook.com>

Original-merge: 77a007a
  • Loading branch information
joshuaboniface committed Aug 1, 2022
1 parent d5ea136 commit b3675eb
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 6 deletions.
8 changes: 2 additions & 6 deletions MediaBrowser.Controller/Entities/TV/Series.cs
Original file line number Diff line number Diff line change
Expand Up @@ -263,14 +263,10 @@ public IEnumerable<BaseItem> GetEpisodes(User user, DtoOptions options)
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { BaseItemKind.Episode, BaseItemKind.Season },
OrderBy = new[] { (ItemSortBy.SortName, SortOrder.Ascending) },
DtoOptions = options
DtoOptions = options,
IsMissing = user?.DisplayMissingEpisodes
};

if (!user.DisplayMissingEpisodes)
{
query.IsMissing = false;
}

var allItems = LibraryManager.GetItemList(query);

var allSeriesEpisodes = allItems.OfType<Episode>().ToList();
Expand Down
57 changes: 57 additions & 0 deletions MediaBrowser.Providers/TV/SeriesMetadataService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
Expand Down Expand Up @@ -40,6 +41,7 @@ protected override async Task AfterMetadataRefresh(Series item, MetadataRefreshO
{
await base.AfterMetadataRefresh(item, refreshOptions, cancellationToken).ConfigureAwait(false);

RemoveObsoleteEpisodes(item);
RemoveObsoleteSeasons(item);
await FillInMissingSeasonsAsync(item, cancellationToken).ConfigureAwait(false);
}
Expand Down Expand Up @@ -121,6 +123,61 @@ private void RemoveObsoleteSeasons(Series series)
}
}

private void RemoveObsoleteEpisodes(Series series)
{
var episodes = series.GetEpisodes(null, new DtoOptions()).OfType<Episode>().ToList();
var numberOfEpisodes = episodes.Count;
// TODO: O(n^2), but can it be done faster without overcomplicating it?
for (var i = 0; i < numberOfEpisodes; i++)
{
var currentEpisode = episodes[i];
// The outer loop only examines virtual episodes
if (!currentEpisode.IsVirtualItem)
{
continue;
}

// Virtual episodes without an episode number are practically orphaned and should be deleted
if (!currentEpisode.IndexNumber.HasValue)
{
DeleteEpisode(currentEpisode);
continue;
}

for (var j = i + 1; j < numberOfEpisodes; j++)
{
var comparisonEpisode = episodes[j];
// The inner loop is only for "physical" episodes
if (comparisonEpisode.IsVirtualItem
|| currentEpisode.ParentIndexNumber != comparisonEpisode.ParentIndexNumber
|| !comparisonEpisode.ContainsEpisodeNumber(currentEpisode.IndexNumber.Value))
{
continue;
}

DeleteEpisode(currentEpisode);
break;
}
}
}

private void DeleteEpisode(Episode episode)
{
Logger.LogInformation(
"Removing virtual episode S{SeasonNumber}E{EpisodeNumber} in series {SeriesName}",
episode.ParentIndexNumber,
episode.IndexNumber,
episode.SeriesName);

LibraryManager.DeleteItem(
episode,
new DeleteOptions
{
DeleteFileLocation = true
},
false);
}

/// <summary>
/// Creates seasons for all episodes that aren't in a season folder.
/// If no season number can be determined, a dummy season will be created.
Expand Down

0 comments on commit b3675eb

Please sign in to comment.