mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-06-09 09:18:46 +01:00
Version-aware playback tracking
This commit is contained in:
@@ -229,7 +229,7 @@ namespace Emby.Server.Implementations.Library
|
||||
list.Add(source);
|
||||
}
|
||||
|
||||
return SortMediaSources(list).ToArray();
|
||||
return SortMediaSources(list, item.Id).ToArray();
|
||||
}
|
||||
|
||||
/// <inheritdoc />>
|
||||
@@ -540,24 +540,32 @@ namespace Emby.Server.Implementations.Library
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources)
|
||||
private static IEnumerable<MediaSourceInfo> SortMediaSources(IEnumerable<MediaSourceInfo> sources, Guid preferredItemId = default)
|
||||
{
|
||||
return sources.OrderBy(i =>
|
||||
{
|
||||
if (i.VideoType.HasValue && i.VideoType.Value == VideoType.VideoFile)
|
||||
// The source belonging to the queried item sorts first so it stays the default that gets played.
|
||||
var preferredId = preferredItemId.IsEmpty()
|
||||
? null
|
||||
: preferredItemId.ToString("N", CultureInfo.InvariantCulture);
|
||||
|
||||
return sources
|
||||
.OrderByDescending(i => preferredId is not null && string.Equals(i.Id, preferredId, StringComparison.OrdinalIgnoreCase))
|
||||
.ThenBy(i =>
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (i.VideoType.HasValue && i.VideoType.Value == VideoType.VideoFile)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}).ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
|
||||
.ThenByDescending(i =>
|
||||
{
|
||||
var stream = i.VideoStream;
|
||||
return 1;
|
||||
})
|
||||
.ThenBy(i => i.Video3DFormat.HasValue ? 1 : 0)
|
||||
.ThenByDescending(i =>
|
||||
{
|
||||
var stream = i.VideoStream;
|
||||
|
||||
return stream?.Width ?? 0;
|
||||
})
|
||||
.Where(i => i.Type != MediaSourceType.Placeholder);
|
||||
return stream?.Width ?? 0;
|
||||
})
|
||||
.Where(i => i.Type != MediaSourceType.Placeholder);
|
||||
}
|
||||
|
||||
public async Task<Tuple<LiveStreamResponse, IDirectStreamProvider>> OpenLiveStreamInternal(LiveStreamRequest request, CancellationToken cancellationToken)
|
||||
|
||||
@@ -385,5 +385,41 @@ namespace Emby.Server.Implementations.Library
|
||||
|
||||
return playedToCompletion;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void ResetPlaybackStreamSelections(User user, BaseItem item)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(user);
|
||||
ArgumentNullException.ThrowIfNull(item);
|
||||
|
||||
using var dbContext = _repository.CreateDbContext();
|
||||
var rows = dbContext.UserData
|
||||
.Where(e => e.ItemId == item.Id && e.UserId == user.Id
|
||||
&& (e.AudioStreamIndex != null || e.SubtitleStreamIndex != null))
|
||||
.ToList();
|
||||
|
||||
if (rows.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
row.AudioStreamIndex = null;
|
||||
row.SubtitleStreamIndex = null;
|
||||
}
|
||||
|
||||
dbContext.SaveChanges();
|
||||
|
||||
var cacheKey = GetCacheKey(user.InternalId, item.Id);
|
||||
if (_cache.TryGet(cacheKey, out var cached))
|
||||
{
|
||||
cached.AudioStreamIndex = null;
|
||||
cached.SubtitleStreamIndex = null;
|
||||
_cache.AddOrUpdate(cacheKey, cached);
|
||||
}
|
||||
|
||||
item.UserData = dbContext.UserData.Where(e => e.ItemId == item.Id).AsNoTracking().ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -725,6 +725,31 @@ namespace Emby.Server.Implementations.Session
|
||||
return item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resolves the item whose user data (playback position, played status) should be updated
|
||||
/// for a playback report. When an alternate version is played the client reports the displayed
|
||||
/// item as <c>ItemId</c> and the played version as <c>MediaSourceId</c>.
|
||||
/// </summary>
|
||||
/// <param name="libraryItem">The now playing (displayed) item.</param>
|
||||
/// <param name="mediaSourceId">The reported media source id.</param>
|
||||
/// <returns>The item to track progress against.</returns>
|
||||
private BaseItem GetProgressItem(BaseItem libraryItem, string mediaSourceId)
|
||||
{
|
||||
if (libraryItem is Video libraryVideo
|
||||
&& !string.IsNullOrEmpty(mediaSourceId)
|
||||
&& Guid.TryParse(mediaSourceId, out var mediaSourceItemId)
|
||||
&& !mediaSourceItemId.Equals(libraryVideo.Id))
|
||||
{
|
||||
var versionItem = libraryVideo.GetAlternateVersion(mediaSourceItemId);
|
||||
if (versionItem is not null)
|
||||
{
|
||||
return versionItem;
|
||||
}
|
||||
}
|
||||
|
||||
return libraryItem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Used to report that playback has started for an item.
|
||||
/// </summary>
|
||||
@@ -756,9 +781,10 @@ namespace Emby.Server.Implementations.Session
|
||||
|
||||
if (libraryItem is not null)
|
||||
{
|
||||
var progressItem = GetProgressItem(libraryItem, info.MediaSourceId);
|
||||
foreach (var user in users)
|
||||
{
|
||||
OnPlaybackStart(user, libraryItem);
|
||||
OnPlaybackStart(user, progressItem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -890,9 +916,10 @@ namespace Emby.Server.Implementations.Session
|
||||
// only update saved user data on actual check-ins, not automated ones
|
||||
if (libraryItem is not null && !isAutomated)
|
||||
{
|
||||
var progressItem = GetProgressItem(libraryItem, info.MediaSourceId);
|
||||
foreach (var user in users)
|
||||
{
|
||||
OnPlaybackProgress(user, libraryItem, info);
|
||||
OnPlaybackProgress(user, progressItem, info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -952,6 +979,17 @@ namespace Emby.Server.Implementations.Session
|
||||
if (changed)
|
||||
{
|
||||
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackProgress, CancellationToken.None);
|
||||
|
||||
if (data.Played == true && item is Video playedVideo)
|
||||
{
|
||||
playedVideo.PropagatePlayedState(user, true);
|
||||
}
|
||||
}
|
||||
|
||||
if ((!user.RememberAudioSelections && data.AudioStreamIndex.HasValue)
|
||||
|| (!user.RememberSubtitleSelections && data.SubtitleStreamIndex.HasValue))
|
||||
{
|
||||
_userDataManager.ResetPlaybackStreamSelections(user, item);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1083,9 +1121,10 @@ namespace Emby.Server.Implementations.Session
|
||||
|
||||
if (libraryItem is not null)
|
||||
{
|
||||
var progressItem = GetProgressItem(libraryItem, info.MediaSourceId);
|
||||
foreach (var user in users)
|
||||
{
|
||||
playedToCompletion = OnPlaybackStopped(user, libraryItem, info.PositionTicks, info.Failed);
|
||||
playedToCompletion = OnPlaybackStopped(user, progressItem, info.PositionTicks, info.Failed);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1138,6 +1177,12 @@ namespace Emby.Server.Implementations.Session
|
||||
|
||||
_userDataManager.SaveUserData(user, item, data, UserDataSaveReason.PlaybackFinished, CancellationToken.None);
|
||||
|
||||
// A completed version marks all of its alternate versions played; positions stay per-version.
|
||||
if (data.Played == true && item is Video playedVideo)
|
||||
{
|
||||
playedVideo.PropagatePlayedState(user, true);
|
||||
}
|
||||
|
||||
return playedToCompletion;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user