Skip to content

Commit

Permalink
Merge pull request #205 from h3llrais3r/history-episode-import
Browse files Browse the repository at this point in the history
  • Loading branch information
crobibero committed May 30, 2023
2 parents e1ce6b4 + fc7ab88 commit 404a102
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 49 deletions.
48 changes: 47 additions & 1 deletion Trakt/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -441,12 +441,23 @@ public static TraktMovieWatchedHistory FindMatch(Movie item, IEnumerable<TraktMo
/// </summary>
/// <param name="item">The <see cref="BaseItem"/>.</param>
/// <param name="results">>The <see cref="IEnumerable{TraktEpisodeWatchedHistory}"/>.</param>
/// <returns>TraktMovieWatchedHistory.</returns>
/// <returns>TraktEpisodeWatchedHistory.</returns>
public static TraktEpisodeWatchedHistory FindMatch(Episode item, IEnumerable<TraktEpisodeWatchedHistory> results)
{
return results.FirstOrDefault(i => IsMatch(item, i.Episode));
}

/// <summary>
/// Gets all watched history matches for an episode.
/// </summary>
/// <param name="item">The <see cref="BaseItem"/>.</param>
/// <param name="results">>The <see cref="IEnumerable{TraktEpisodeWatchedHistory}"/>.</param>
/// <returns>IEnumerable{TraktEpisodeWatchedHistory}.</returns>
public static IEnumerable<TraktEpisodeWatchedHistory> FindAllMatches(Episode item, IEnumerable<TraktEpisodeWatchedHistory> results)
{
return results.Where(i => IsMatch(item, i)).AsEnumerable();
}

/// <summary>
/// Checks if a <see cref="BaseItem"/> matches a <see cref="TraktMovie"/>.
/// </summary>
Expand Down Expand Up @@ -539,4 +550,39 @@ public static bool IsMatch(Episode item, TraktEpisode episode)

return false;
}

/// <summary>
/// Checks if a <see cref="Episode"/> matches a <see cref="TraktEpisodeWatchedHistory"/>.
/// </summary>
/// <param name="item">The <see cref="Episode"/>.</param>
/// <param name="episodeHistory">The <see cref="TraktEpisodeWatchedHistory"/>.</param>
/// <returns><see cref="bool"/> indicating if the <see cref="Episode"/> matches a <see cref="TraktEpisodeWatchedHistory"/>.</returns>
public static bool IsMatch(Episode item, TraktEpisodeWatchedHistory episodeHistory)
{
// Match by provider id's
if (IsMatch(item, episodeHistory.Episode))
{
return true;
}

// Match by show, season and episode number if there isn't any provider id in common
// If there was a common provider id between the item and the trakt episode (f.e. both have tvdb id), you shouldn't check anymore by season/number
if (!HasAnyProviderTvIdInCommon(item, episodeHistory.Episode)
&& IsMatch(item.Series, episodeHistory.Show)
&& item.GetSeasonNumber() == episodeHistory.Episode.Season
&& item.ContainsEpisodeNumber(episodeHistory.Episode.Number))
{
return true;
}

return false;
}

private static bool HasAnyProviderTvIdInCommon(Episode item, TraktEpisode traktEpisode)
{
return (item.HasProviderId(MetadataProvider.Tvdb) && traktEpisode.Ids.Tvdb != null)
|| (item.HasProviderId(MetadataProvider.Imdb) && traktEpisode.Ids.Imdb != null)
|| (item.HasProviderId(MetadataProvider.Tmdb) && traktEpisode.Ids.Tmdb != null)
|| (item.HasProviderId(MetadataProvider.TvRage) && traktEpisode.Ids.Tvrage != null);
}
}
66 changes: 18 additions & 48 deletions Trakt/ScheduledTasks/SyncFromTraktTask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities;
Expand Down Expand Up @@ -109,7 +110,7 @@ public async Task ExecuteAsync(IProgress<double> progress, CancellationToken can
}
}

private async Task SyncTraktDataForUser(Jellyfin.Data.Entities.User user, double currentProgress, IProgress<double> progress, double percentPerUser, CancellationToken cancellationToken)
private async Task SyncTraktDataForUser(User user, double currentProgress, IProgress<double> progress, double percentPerUser, CancellationToken cancellationToken)
{
var traktUser = UserHelper.GetTraktUser(user, true);

Expand Down Expand Up @@ -319,77 +320,45 @@ private async Task SyncTraktDataForUser(Jellyfin.Data.Entities.User user, double
{
cancellationToken.ThrowIfCancellationRequested();
var matchedWatchedShow = Extensions.FindMatch(episode.Series, traktWatchedShows);
var matchedWatchedEpisodeHistory = Extensions.FindMatch(episode, traktWatchedEpisodesHistory);
var matchedPausedEpisode = Extensions.FindMatch(episode, traktPausedEpisodes);
var userData = _userDataManager.GetUserData(user.Id, episode);
bool changed = false;
bool episodeWatched = false;

if (!traktUser.SkipWatchedImportFromTrakt && matchedWatchedShow != null)
{
var matchedWatchedSeason = matchedWatchedShow.Seasons.FirstOrDefault(tSeason => tSeason.Number == episode.GetSeasonNumber());

// Keep track of the shows rewatch cycles
DateTime? tLastReset = null;
if (DateTime.TryParse(matchedWatchedShow.ResetAt, out var resetValue))
{
tLastReset = resetValue;
}

// Fallback procedure to find match by using episode history
if (matchedWatchedSeason == null && matchedWatchedEpisodeHistory != null)
{
// Find watched season via history match
_logger.LogDebug("Using history to match season for user {User} for {Data}", user.Username, GetVerboseEpisodeData(episode));
matchedWatchedSeason = matchedWatchedShow.Seasons.FirstOrDefault(tSeason => tSeason.Number == matchedWatchedEpisodeHistory.Episode.Season);
}
var matchedWatchedEpisodeHistory = Extensions.FindAllMatches(episode, traktWatchedEpisodesHistory);

// If it's not a match then it means trakt.tv doesn't know about the season, leave the watched state alone and move on
if (matchedWatchedSeason != null)
// Check if match is found in history
if (matchedWatchedEpisodeHistory != null && matchedWatchedEpisodeHistory.Any())
{
// Check for matching episodes including multi-episode entities
var matchedWatchedEpisode = matchedWatchedSeason.Episodes.FirstOrDefault(x => episode.ContainsEpisodeNumber(x.Number));

// Fallback procedure to find match by using episode history
if (matchedWatchedEpisode == null && matchedWatchedEpisodeHistory != null)
{
// Find watched season and episode via history match
_logger.LogDebug("Using history to match season and episode for user {User} for {Data}", user.Username, GetVerboseEpisodeData(episode));
matchedWatchedEpisode = matchedWatchedSeason.Episodes.FirstOrDefault(tEpisode => tEpisode.Number == matchedWatchedEpisodeHistory.Episode.Number);
}

// Fallback procedure to find match by using episode history, without checking the season (episode can belong to different season)
if (matchedWatchedEpisode == null && matchedWatchedEpisodeHistory != null)
{
// Find watched episode via history match
_logger.LogDebug("Using history to match episode for user {User} for {Data}", user.Username, GetVerboseEpisodeData(episode));
foreach (var season in matchedWatchedShow.Seasons)
{
matchedWatchedEpisode = season.Episodes.FirstOrDefault(tEpisode => tEpisode.Number == matchedWatchedEpisodeHistory.Episode.Number);
if (matchedWatchedEpisode != null)
{
break; // stop when found in a season
}
}
}
// History is ordered with last watched first, so take the first one
var lastWatchedEpisodeHistory = matchedWatchedEpisodeHistory.FirstOrDefault();

// Prepend a check if the matched episode is on a rewatch cycle and
// discard it if the last play date was before the reset date
if (matchedWatchedEpisode != null
if (lastWatchedEpisodeHistory != null
&& tLastReset != null
&& DateTime.TryParse(matchedWatchedEpisode.LastWatchedAt, out var lastPlayedValue)
&& DateTime.TryParse(lastWatchedEpisodeHistory.WatchedAt, out var lastPlayedValue)
&& lastPlayedValue < tLastReset)
{
matchedWatchedEpisode = null;
lastWatchedEpisodeHistory = null;
}

if (matchedWatchedEpisode != null)
if (lastWatchedEpisodeHistory != null)
{
_logger.LogDebug("Episode is in watched list of user {User}: {Data}", user.Username, GetVerboseEpisodeData(episode));
_logger.LogDebug("Episode is in watched history list of user {User}: {Data}", user.Username, GetVerboseEpisodeData(episode));

episodeWatched = true;
DateTime? tLastPlayed = null;
if (DateTime.TryParse(matchedWatchedEpisode.LastWatchedAt, out var lastWatchedValue))
if (DateTime.TryParse(lastWatchedEpisodeHistory.WatchedAt, out var lastWatchedValue))
{
tLastPlayed = lastWatchedValue;
}
Expand Down Expand Up @@ -427,17 +396,18 @@ private async Task SyncTraktDataForUser(Jellyfin.Data.Entities.User user, double
}

// Keep the highest play count
if (userData.PlayCount < matchedWatchedEpisode.Plays)
var playCount = matchedWatchedEpisodeHistory.Count();
if (userData.PlayCount < playCount)
{
_logger.LogDebug("Adjusting episode's play count to match a higher remote value (remote: {Remote} | local: {Local}) for user {User} locally: {Data}", matchedWatchedEpisode.Plays, userData.PlayCount, user.Username, GetVerboseEpisodeData(episode));
userData.PlayCount = matchedWatchedEpisode.Plays;
_logger.LogDebug("Adjusting episode's play count to match a higher remote value (remote: {Remote} | local: {Local}) for user {User} locally: {Data}", playCount, userData.PlayCount, user.Username, GetVerboseEpisodeData(episode));
userData.PlayCount = playCount;
changed = true;
}
}
}
else
{
_logger.LogDebug("No season data found for user {User} for {Data}", user.Username, GetVerboseEpisodeData(episode));
_logger.LogDebug("No episode history data found for user {User} for {Data}", user.Username, GetVerboseEpisodeData(episode));
}
}
else
Expand Down

0 comments on commit 404a102

Please sign in to comment.