Automatic video version choice based on user preferences and max bitrate
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
2024-12-03 01:25:59 +01:00
parent 7dbc17ec14
commit 5cc359bd2c

View File

@@ -183,7 +183,7 @@ public sealed class CinemaMediaSourceManager : IMediaSourceManager
switch (video)
{
case CinemaMovie movie: items = GetVideoVersionsEnumerate<CinemaMovie>(csId!, movie, videoPrimary!, ver.Versions); break;
case CinemaEpisode episode: items = GetVideoVersionsEnumerate<CinemaEpisode>(csId!, episode, videoPrimary!, ver.Versions); break;
case CinemaEpisode episode: items = GetVideoVersionsEnumerate<CinemaEpisode>(csId!, episode, videoPrimary!, SortVersionsByPreferences(user, ver.Versions)); break;
default: throw new NotSupportedException(string.Format("BaseItem type '{0}' not supported in CinemaMediaSources.", video.GetType().Name));
}
@@ -192,6 +192,11 @@ public sealed class CinemaMediaSourceManager : IMediaSourceManager
// Note: Also makes sure BaseItems exist for each version
foreach (var i in items)
result.Add(GetVersionInfo(i, ver.Versions[idx++].Meta, thisServerBaseUri));
// For episodes we must choose the audio/subtitle automatically as there is no UI for it
if (video is CinemaEpisode)
foreach (MediaSourceInfo i in result)
ForceByPreferences(user, i);
}
break;
}
@@ -221,6 +226,88 @@ public sealed class CinemaMediaSourceManager : IMediaSourceManager
#endregion
/// <summary>
/// Sorts media verions by preferences as episodes currently do not support multiple versions
/// and the first one gets played.
/// </summary>
private static VersionEntry[] SortVersionsByPreferences(User user, VersionEntry[] items)
{
if (user == null)
// We have no info about the user so nothing to sort
return items;
double? bitrateLimit = IsWebshareFreeAccount ? WebshareFreeBitrate : null;
string? audioLang = FixLangToIso(user.AudioLanguagePreference);
string? subtitleLang = FixLangToIso(user.SubtitleLanguagePreference);
int[] scores = new int[items.Length];
for (int i = 0; i < items.Length; i++)
{
var m = items[i].Meta;
int score = 0;
StreamVideo? video = m.video?.FirstOrDefault();
if (video != null)
{
score += video.height / 100; // range [1..30]
score += video.hasHdr ? 3 : 0; // HDR is plus but resolution ust rule
}
if (m.subtitles != null && subtitleLang != null)
foreach (StreamSubtitle j in m.subtitles)
if (j.language == subtitleLang)
{
score += 4; // proper subtitles are preferred but much better resulution still rules
break;
}
if (m.audio != null && audioLang != null)
foreach (StreamAudio j in m.audio)
if (j.language == audioLang)
{
score += 25; // proper audio channel overrides resolution and other minor bonuses
break;
}
if (bitrateLimit != null && video != null)
{
double bitRate = (m.size ?? 0.0) * 8 / (video.duration ?? 1.0);
score += bitRate / (1 + BitrateMargin) < bitrateLimit.Value ? 50 : 0; // without proper bitrate it is not playable at all
}
scores[i] = score;
}
Array.Sort(scores, items);
Array.Reverse(items);
return items;
}
private static void ForceByPreferences(User user, MediaSourceInfo item) {
if (user == null)
// We have no info about the user so nothing to force
return;
string? audioLang = FixLangToIso(user.AudioLanguagePreference);
string? subtitleLang = FixLangToIso(user.SubtitleLanguagePreference);
foreach (var i in item.MediaStreams) {
if (i.Type == MediaStreamType.Audio && audioLang != null)
i.IsForced = audioLang == i.Language;
else if (i.Type == MediaStreamType.Subtitle && subtitleLang != null)
i.IsForced = subtitleLang == i.Language;
}
}
private static string? FixLangToIso(string? nonIso)
{
switch (nonIso)
{
case "cze": return "cs";
case "eng": return "en";
default: return nonIso;
}
}
/// <summary>
/// Gets stream metadata for a video version (ie. for Movie or Episode).
/// </summary>
@@ -294,7 +381,8 @@ public sealed class CinemaMediaSourceManager : IMediaSourceManager
primary = _libraryManager.GetItemById(primaryId);
if (primary == null
|| !CinemaQueryExtensions.IsCinemaExternalId(primary.ExternalId)
|| string.IsNullOrEmpty(primary.ExternalId)) {
|| string.IsNullOrEmpty(primary.ExternalId))
{
// Not a Stream Cinema video
csId = null;
return Task.FromResult<VersionSetEntry>(default);