diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml
index 2529d80994..de1590c746 100644
--- a/.github/workflows/automation.yml
+++ b/.github/workflows/automation.yml
@@ -20,7 +20,7 @@ jobs:
with:
project: Current Release
action: delete
- repo-token: ${{ secrets.GH_TOKEN }}
+ repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add to 'Release Next' project
uses: alex-page/github-project-automation-plus@v0.7.1
@@ -29,7 +29,7 @@ jobs:
with:
project: Release Next
column: In progress
- repo-token: ${{ secrets.GH_TOKEN }}
+ repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add to 'Current Release' project
uses: alex-page/github-project-automation-plus@v0.7.1
@@ -38,7 +38,7 @@ jobs:
with:
project: Current Release
column: In progress
- repo-token: ${{ secrets.GH_TOKEN }}
+ repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Check number of comments from the team member
if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER'
@@ -52,7 +52,7 @@ jobs:
with:
project: Issue Triage for Main Repo
column: Needs triage
- repo-token: ${{ secrets.GH_TOKEN }}
+ repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add issue to triage project
uses: alex-page/github-project-automation-plus@v0.7.1
@@ -61,4 +61,4 @@ jobs:
with:
project: Issue Triage for Main Repo
column: Pending response
- repo-token: ${{ secrets.GH_TOKEN }}
+ repo-token: ${{ secrets.JF_BOT_TOKEN }}
diff --git a/.github/workflows/merge-conflicts.yml b/.github/workflows/merge-conflicts.yml
index ce808617a1..1b04eab467 100644
--- a/.github/workflows/merge-conflicts.yml
+++ b/.github/workflows/merge-conflicts.yml
@@ -14,4 +14,4 @@ jobs:
- uses: eps1lon/actions-label-merge-conflict@v2.0.1
with:
dirtyLabel: 'merge conflict'
- repoToken: ${{ secrets.GH_TOKEN }}
+ repoToken: ${{ secrets.JF_BOT_TOKEN }}
diff --git a/.github/workflows/rebase.yml b/.github/workflows/rebase.yml
index 3172ec0d9f..3eec9fa033 100644
--- a/.github/workflows/rebase.yml
+++ b/.github/workflows/rebase.yml
@@ -11,17 +11,17 @@ jobs:
- name: Notify as seen
uses: peter-evans/create-or-update-comment@v1.4.5
with:
- token: ${{ secrets.GH_TOKEN }}
+ token: ${{ secrets.JF_BOT_TOKEN }}
comment-id: ${{ github.event.comment.id }}
reactions: '+1'
- name: Checkout the latest code
uses: actions/checkout@v2
with:
- token: ${{ secrets.GH_TOKEN }}
+ token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0
- name: Automatic Rebase
uses: cirrus-actions/rebase@1.4
env:
- GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
+ GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
diff --git a/Emby.Naming/Audio/AudioFileParser.cs b/Emby.Naming/Audio/AudioFileParser.cs
index 8b47dd12e4..af4aa0059f 100644
--- a/Emby.Naming/Audio/AudioFileParser.cs
+++ b/Emby.Naming/Audio/AudioFileParser.cs
@@ -1,7 +1,7 @@
using System;
using System.IO;
-using System.Linq;
using Emby.Naming.Common;
+using MediaBrowser.Common.Extensions;
namespace Emby.Naming.Audio
{
@@ -18,8 +18,8 @@ namespace Emby.Naming.Audio
/// True if file at path is audio file.
public static bool IsAudioFile(string path, NamingOptions options)
{
- var extension = Path.GetExtension(path);
- return options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
+ var extension = Path.GetExtension(path.AsSpan());
+ return options.AudioFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase);
}
}
}
diff --git a/Emby.Naming/Emby.Naming.csproj b/Emby.Naming/Emby.Naming.csproj
index 63116f3680..3224ff4129 100644
--- a/Emby.Naming/Emby.Naming.csproj
+++ b/Emby.Naming/Emby.Naming.csproj
@@ -23,11 +23,12 @@
-
+
-
+
+
diff --git a/Emby.Naming/Video/ExtraResolver.cs b/Emby.Naming/Video/ExtraResolver.cs
index 1d3b36a1ad..f9d06c09be 100644
--- a/Emby.Naming/Video/ExtraResolver.cs
+++ b/Emby.Naming/Video/ExtraResolver.cs
@@ -29,72 +29,75 @@ namespace Emby.Naming.Video
/// Path to file.
/// Returns object.
public ExtraResult GetExtraInfo(string path)
- {
- return _options.VideoExtraRules
- .Select(i => GetExtraInfo(path, i))
- .FirstOrDefault(i => i.ExtraType != null) ?? new ExtraResult();
- }
-
- private ExtraResult GetExtraInfo(string path, ExtraRule rule)
{
var result = new ExtraResult();
- if (rule.MediaType == MediaType.Audio)
+ for (var i = 0; i < _options.VideoExtraRules.Length; i++)
{
- if (!AudioFileParser.IsAudioFile(path, _options))
+ var rule = _options.VideoExtraRules[i];
+ if (rule.MediaType == MediaType.Audio)
+ {
+ if (!AudioFileParser.IsAudioFile(path, _options))
+ {
+ continue;
+ }
+ }
+ else if (rule.MediaType == MediaType.Video)
+ {
+ if (!new VideoResolver(_options).IsVideoFile(path))
+ {
+ continue;
+ }
+ }
+
+ var pathSpan = path.AsSpan();
+ if (rule.RuleType == ExtraRuleType.Filename)
+ {
+ var filename = Path.GetFileNameWithoutExtension(pathSpan);
+
+ if (filename.Equals(rule.Token, StringComparison.OrdinalIgnoreCase))
+ {
+ result.ExtraType = rule.ExtraType;
+ result.Rule = rule;
+ }
+ }
+ else if (rule.RuleType == ExtraRuleType.Suffix)
+ {
+ var filename = Path.GetFileNameWithoutExtension(pathSpan);
+
+ if (filename.Contains(rule.Token, StringComparison.OrdinalIgnoreCase))
+ {
+ result.ExtraType = rule.ExtraType;
+ result.Rule = rule;
+ }
+ }
+ else if (rule.RuleType == ExtraRuleType.Regex)
+ {
+ var filename = Path.GetFileName(path);
+
+ var regex = new Regex(rule.Token, RegexOptions.IgnoreCase);
+
+ if (regex.IsMatch(filename))
+ {
+ result.ExtraType = rule.ExtraType;
+ result.Rule = rule;
+ }
+ }
+ else if (rule.RuleType == ExtraRuleType.DirectoryName)
+ {
+ var directoryName = Path.GetFileName(Path.GetDirectoryName(pathSpan));
+ if (directoryName.Equals(rule.Token, StringComparison.OrdinalIgnoreCase))
+ {
+ result.ExtraType = rule.ExtraType;
+ result.Rule = rule;
+ }
+ }
+
+ if (result.ExtraType != null)
{
return result;
}
}
- else if (rule.MediaType == MediaType.Video)
- {
- if (!new VideoResolver(_options).IsVideoFile(path))
- {
- return result;
- }
- }
-
- if (rule.RuleType == ExtraRuleType.Filename)
- {
- var filename = Path.GetFileNameWithoutExtension(path);
-
- if (string.Equals(filename, rule.Token, StringComparison.OrdinalIgnoreCase))
- {
- result.ExtraType = rule.ExtraType;
- result.Rule = rule;
- }
- }
- else if (rule.RuleType == ExtraRuleType.Suffix)
- {
- var filename = Path.GetFileNameWithoutExtension(path);
-
- if (filename.IndexOf(rule.Token, StringComparison.OrdinalIgnoreCase) > 0)
- {
- result.ExtraType = rule.ExtraType;
- result.Rule = rule;
- }
- }
- else if (rule.RuleType == ExtraRuleType.Regex)
- {
- var filename = Path.GetFileName(path);
-
- var regex = new Regex(rule.Token, RegexOptions.IgnoreCase);
-
- if (regex.IsMatch(filename))
- {
- result.ExtraType = rule.ExtraType;
- result.Rule = rule;
- }
- }
- else if (rule.RuleType == ExtraRuleType.DirectoryName)
- {
- var directoryName = Path.GetFileName(Path.GetDirectoryName(path));
- if (string.Equals(directoryName, rule.Token, StringComparison.OrdinalIgnoreCase))
- {
- result.ExtraType = rule.ExtraType;
- result.Rule = rule;
- }
- }
return result;
}
diff --git a/Emby.Naming/Video/VideoResolver.cs b/Emby.Naming/Video/VideoResolver.cs
index 79a6da8f7b..27e73208c6 100644
--- a/Emby.Naming/Video/VideoResolver.cs
+++ b/Emby.Naming/Video/VideoResolver.cs
@@ -1,8 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.IO;
-using System.Linq;
using Emby.Naming.Common;
+using MediaBrowser.Common.Extensions;
namespace Emby.Naming.Video
{
@@ -59,15 +59,15 @@ namespace Emby.Naming.Video
}
bool isStub = false;
- string? container = null;
+ ReadOnlySpan container = ReadOnlySpan.Empty;
string? stubType = null;
if (!isDirectory)
{
- var extension = Path.GetExtension(path);
+ var extension = Path.GetExtension(path.AsSpan());
// Check supported extensions
- if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
+ if (!_options.VideoFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{
// It's not supported. Check stub extensions
if (!StubResolver.TryResolveFile(path, _options, out stubType))
@@ -86,9 +86,7 @@ namespace Emby.Naming.Video
var extraResult = new ExtraResolver(_options).GetExtraInfo(path);
- var name = isDirectory
- ? Path.GetFileName(path)
- : Path.GetFileNameWithoutExtension(path);
+ var name = Path.GetFileNameWithoutExtension(path);
int? year = null;
@@ -107,7 +105,7 @@ namespace Emby.Naming.Video
return new VideoFileInfo(
path: path,
- container: container,
+ container: container.IsEmpty ? null : container.ToString(),
isStub: isStub,
name: name,
year: year,
@@ -126,8 +124,8 @@ namespace Emby.Naming.Video
/// True if is video file.
public bool IsVideoFile(string path)
{
- var extension = Path.GetExtension(path);
- return _options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
+ var extension = Path.GetExtension(path.AsSpan());
+ return _options.VideoFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase);
}
///
@@ -137,8 +135,8 @@ namespace Emby.Naming.Video
/// True if is video file stub.
public bool IsStubFile(string path)
{
- var extension = Path.GetExtension(path);
- return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
+ var extension = Path.GetExtension(path.AsSpan());
+ return _options.StubFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase);
}
///
diff --git a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
index 8c756a7f4c..c331a61124 100644
--- a/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
+++ b/Emby.Server.Implementations/Data/BaseSqliteRepository.cs
@@ -181,11 +181,9 @@ namespace Emby.Server.Implementations.Data
foreach (var row in connection.Query("PRAGMA table_info(" + table + ")"))
{
- if (row[1].SQLiteType != SQLiteType.Null)
+ if (row.TryGetString(1, out var columnName))
{
- var name = row[1].ToString();
-
- columnNames.Add(name);
+ columnNames.Add(columnName);
}
}
diff --git a/Emby.Server.Implementations/Data/SqliteExtensions.cs b/Emby.Server.Implementations/Data/SqliteExtensions.cs
index a04d630887..a8f3feb589 100644
--- a/Emby.Server.Implementations/Data/SqliteExtensions.cs
+++ b/Emby.Server.Implementations/Data/SqliteExtensions.cs
@@ -3,6 +3,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using SQLitePCL.pretty;
@@ -96,21 +97,43 @@ namespace Emby.Server.Implementations.Data
DateTimeStyles.None).ToUniversalTime();
}
- public static DateTime? TryReadDateTime(this IResultSetValue result)
+ public static bool TryReadDateTime(this IReadOnlyList reader, int index, out DateTime result)
{
- var dateText = result.ToString();
+ var item = reader[index];
+ if (item.IsDbNull())
+ {
+ result = default;
+ return false;
+ }
+
+ var dateText = item.ToString();
if (DateTime.TryParseExact(dateText, _datetimeFormats, DateTimeFormatInfo.InvariantInfo, DateTimeStyles.None, out var dateTimeResult))
{
- return dateTimeResult.ToUniversalTime();
+ result = dateTimeResult.ToUniversalTime();
+ return true;
}
- return null;
+ result = default;
+ return false;
}
- public static bool IsDBNull(this IReadOnlyList result, int index)
+ public static bool TryGetGuid(this IReadOnlyList reader, int index, out Guid result)
{
- return result[index].SQLiteType == SQLiteType.Null;
+ var item = reader[index];
+ if (item.IsDbNull())
+ {
+ result = default;
+ return false;
+ }
+
+ result = item.ReadGuidFromBlob();
+ return true;
+ }
+
+ public static bool IsDbNull(this IResultSetValue result)
+ {
+ return result.SQLiteType == SQLiteType.Null;
}
public static string GetString(this IReadOnlyList result, int index)
@@ -118,14 +141,48 @@ namespace Emby.Server.Implementations.Data
return result[index].ToString();
}
+ public static bool TryGetString(this IReadOnlyList reader, int index, out string result)
+ {
+ result = null;
+ var item = reader[index];
+ if (item.IsDbNull())
+ {
+ return false;
+ }
+
+ result = item.ToString();
+ return true;
+ }
+
public static bool GetBoolean(this IReadOnlyList result, int index)
{
return result[index].ToBool();
}
- public static int GetInt32(this IReadOnlyList result, int index)
+ public static bool TryGetBoolean(this IReadOnlyList reader, int index, out bool result)
{
- return result[index].ToInt();
+ var item = reader[index];
+ if (item.IsDbNull())
+ {
+ result = default;
+ return false;
+ }
+
+ result = item.ToBool();
+ return true;
+ }
+
+ public static bool TryGetInt32(this IReadOnlyList reader, int index, out int result)
+ {
+ var item = reader[index];
+ if (item.IsDbNull())
+ {
+ result = default;
+ return false;
+ }
+
+ result = item.ToInt();
+ return true;
}
public static long GetInt64(this IReadOnlyList result, int index)
@@ -133,9 +190,43 @@ namespace Emby.Server.Implementations.Data
return result[index].ToInt64();
}
- public static float GetFloat(this IReadOnlyList result, int index)
+ public static bool TryGetInt64(this IReadOnlyList reader, int index, out long result)
{
- return result[index].ToFloat();
+ var item = reader[index];
+ if (item.IsDbNull())
+ {
+ result = default;
+ return false;
+ }
+
+ result = item.ToInt64();
+ return true;
+ }
+
+ public static bool TryGetSingle(this IReadOnlyList reader, int index, out float result)
+ {
+ var item = reader[index];
+ if (item.IsDbNull())
+ {
+ result = default;
+ return false;
+ }
+
+ result = item.ToFloat();
+ return true;
+ }
+
+ public static bool TryGetDouble(this IReadOnlyList reader, int index, out double result)
+ {
+ var item = reader[index];
+ if (item.IsDbNull())
+ {
+ result = default;
+ return false;
+ }
+
+ result = item.ToDouble();
+ return true;
}
public static Guid GetGuid(this IReadOnlyList result, int index)
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 835eb06924..b3d8860a9e 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -1332,27 +1332,23 @@ namespace Emby.Server.Implementations.Data
if (queryHasStartDate)
{
- if (!reader.IsDBNull(index))
+ if (item is IHasStartDate hasStartDate && reader.TryReadDateTime(index, out var startDate))
{
- if (item is IHasStartDate hasStartDate)
- {
- hasStartDate.StartDate = reader[index].ReadDateTime();
- }
+ hasStartDate.StartDate = startDate;
}
index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryReadDateTime(index++, out var endDate))
{
- item.EndDate = reader[index].TryReadDateTime();
+ item.EndDate = endDate;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ var channelId = reader[index];
+ if (!channelId.IsDbNull())
{
- if (!Utf8Parser.TryParse(reader[index].ToBlob(), out Guid value, out _, standardFormat: 'N'))
+ if (!Utf8Parser.TryParse(channelId.ToBlob(), out Guid value, out _, standardFormat: 'N'))
{
var str = reader.GetString(index);
Logger.LogWarning("{ChannelId} isn't in the expected format", str);
@@ -1368,33 +1364,25 @@ namespace Emby.Server.Implementations.Data
{
if (item is IHasProgramAttributes hasProgramAttributes)
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetBoolean(index++, out var isMovie))
{
- hasProgramAttributes.IsMovie = reader.GetBoolean(index);
+ hasProgramAttributes.IsMovie = isMovie;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetBoolean(index++, out var isSeries))
{
- hasProgramAttributes.IsSeries = reader.GetBoolean(index);
+ hasProgramAttributes.IsSeries = isSeries;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var episodeTitle))
{
- hasProgramAttributes.EpisodeTitle = reader.GetString(index);
+ hasProgramAttributes.EpisodeTitle = episodeTitle;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetBoolean(index++, out var isRepeat))
{
- hasProgramAttributes.IsRepeat = reader.GetBoolean(index);
+ hasProgramAttributes.IsRepeat = isRepeat;
}
-
- index++;
}
else
{
@@ -1402,242 +1390,190 @@ namespace Emby.Server.Implementations.Data
}
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetSingle(index++, out var communityRating))
{
- item.CommunityRating = reader.GetFloat(index);
+ item.CommunityRating = communityRating;
}
- index++;
-
if (HasField(query, ItemFields.CustomRating))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var customRating))
{
- item.CustomRating = reader.GetString(index);
+ item.CustomRating = customRating;
}
-
- index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetInt32(index++, out var indexNumber))
{
- item.IndexNumber = reader.GetInt32(index);
+ item.IndexNumber = indexNumber;
}
- index++;
-
if (HasField(query, ItemFields.Settings))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetBoolean(index++, out var isLocked))
{
- item.IsLocked = reader.GetBoolean(index);
+ item.IsLocked = isLocked;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var preferredMetadataLanguage))
{
- item.PreferredMetadataLanguage = reader.GetString(index);
+ item.PreferredMetadataLanguage = preferredMetadataLanguage;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var preferredMetadataCountryCode))
{
- item.PreferredMetadataCountryCode = reader.GetString(index);
+ item.PreferredMetadataCountryCode = preferredMetadataCountryCode;
}
-
- index++;
}
if (HasField(query, ItemFields.Width))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetInt32(index++, out var width))
{
- item.Width = reader.GetInt32(index);
+ item.Width = width;
}
-
- index++;
}
if (HasField(query, ItemFields.Height))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetInt32(index++, out var height))
{
- item.Height = reader.GetInt32(index);
+ item.Height = height;
}
-
- index++;
}
if (HasField(query, ItemFields.DateLastRefreshed))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryReadDateTime(index++, out var dateLastRefreshed))
{
- item.DateLastRefreshed = reader[index].ReadDateTime();
+ item.DateLastRefreshed = dateLastRefreshed;
}
-
- index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var name))
{
- item.Name = reader.GetString(index);
+ item.Name = name;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var restorePath))
{
- item.Path = RestorePath(reader.GetString(index));
+ item.Path = RestorePath(restorePath);
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryReadDateTime(index++, out var premiereDate))
{
- item.PremiereDate = reader[index].TryReadDateTime();
+ item.PremiereDate = premiereDate;
}
- index++;
-
if (HasField(query, ItemFields.Overview))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var overview))
{
- item.Overview = reader.GetString(index);
+ item.Overview = overview;
}
-
- index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetInt32(index++, out var parentIndexNumber))
{
- item.ParentIndexNumber = reader.GetInt32(index);
+ item.ParentIndexNumber = parentIndexNumber;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetInt32(index++, out var productionYear))
{
- item.ProductionYear = reader.GetInt32(index);
+ item.ProductionYear = productionYear;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var officialRating))
{
- item.OfficialRating = reader.GetString(index);
+ item.OfficialRating = officialRating;
}
- index++;
-
if (HasField(query, ItemFields.SortName))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var forcedSortName))
{
- item.ForcedSortName = reader.GetString(index);
+ item.ForcedSortName = forcedSortName;
}
-
- index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetInt64(index++, out var runTimeTicks))
{
- item.RunTimeTicks = reader.GetInt64(index);
+ item.RunTimeTicks = runTimeTicks;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetInt64(index++, out var size))
{
- item.Size = reader.GetInt64(index);
+ item.Size = size;
}
- index++;
-
if (HasField(query, ItemFields.DateCreated))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryReadDateTime(index++, out var dateCreated))
{
- item.DateCreated = reader[index].ReadDateTime();
+ item.DateCreated = dateCreated;
}
-
- index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryReadDateTime(index++, out var dateModified))
{
- item.DateModified = reader[index].ReadDateTime();
+ item.DateModified = dateModified;
}
- index++;
-
- item.Id = reader.GetGuid(index);
- index++;
+ item.Id = reader.GetGuid(index++);
if (HasField(query, ItemFields.Genres))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var genres))
{
- item.Genres = reader.GetString(index).Split('|', StringSplitOptions.RemoveEmptyEntries);
+ item.Genres = genres.Split('|', StringSplitOptions.RemoveEmptyEntries);
}
-
- index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetGuid(index++, out var parentId))
{
- item.ParentId = reader.GetGuid(index);
+ item.ParentId = parentId;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var audioString))
{
- if (Enum.TryParse(reader.GetString(index), true, out ProgramAudio audio))
+ // TODO Span overload coming in the future https://github.com/dotnet/runtime/issues/1916
+ if (Enum.TryParse(audioString, true, out ProgramAudio audio))
{
item.Audio = audio;
}
}
- index++;
-
// TODO: Even if not needed by apps, the server needs it internally
// But get this excluded from contexts where it is not needed
if (hasServiceName)
{
if (item is LiveTvChannel liveTvChannel)
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index, out var serviceName))
{
- liveTvChannel.ServiceName = reader.GetString(index);
+ liveTvChannel.ServiceName = serviceName;
}
}
index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetBoolean(index++, out var isInMixedFolder))
{
- item.IsInMixedFolder = reader.GetBoolean(index);
+ item.IsInMixedFolder = isInMixedFolder;
}
- index++;
-
if (HasField(query, ItemFields.DateLastSaved))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryReadDateTime(index++, out var dateLastSaved))
{
- item.DateLastSaved = reader[index].ReadDateTime();
+ item.DateLastSaved = dateLastSaved;
}
-
- index++;
}
if (HasField(query, ItemFields.Settings))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var lockedFields))
{
IEnumerable GetLockedFields(string s)
{
@@ -1650,37 +1586,31 @@ namespace Emby.Server.Implementations.Data
}
}
- item.LockedFields = GetLockedFields(reader.GetString(index)).ToArray();
+ item.LockedFields = GetLockedFields(lockedFields).ToArray();
}
-
- index++;
}
if (HasField(query, ItemFields.Studios))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var studios))
{
- item.Studios = reader.GetString(index).Split('|', StringSplitOptions.RemoveEmptyEntries);
+ item.Studios = studios.Split('|', StringSplitOptions.RemoveEmptyEntries);
}
-
- index++;
}
if (HasField(query, ItemFields.Tags))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var tags))
{
- item.Tags = reader.GetString(index).Split('|', StringSplitOptions.RemoveEmptyEntries);
+ item.Tags = tags.Split('|', StringSplitOptions.RemoveEmptyEntries);
}
-
- index++;
}
if (hasTrailerTypes)
{
if (item is Trailer trailer)
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index, out var trailerTypes))
{
IEnumerable GetTrailerTypes(string s)
{
@@ -1693,7 +1623,7 @@ namespace Emby.Server.Implementations.Data
}
}
- trailer.TrailerTypes = GetTrailerTypes(reader.GetString(index)).ToArray();
+ trailer.TrailerTypes = GetTrailerTypes(trailerTypes).ToArray();
}
}
@@ -1702,19 +1632,17 @@ namespace Emby.Server.Implementations.Data
if (HasField(query, ItemFields.OriginalTitle))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var originalTitle))
{
- item.OriginalTitle = reader.GetString(index);
+ item.OriginalTitle = originalTitle;
}
-
- index++;
}
if (item is Video video)
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index, out var primaryVersionId))
{
- video.PrimaryVersionId = reader.GetString(index);
+ video.PrimaryVersionId = primaryVersionId;
}
}
@@ -1722,40 +1650,34 @@ namespace Emby.Server.Implementations.Data
if (HasField(query, ItemFields.DateLastMediaAdded))
{
- if (item is Folder folder && !reader.IsDBNull(index))
+ if (item is Folder folder && reader.TryReadDateTime(index, out var dateLastMediaAdded))
{
- folder.DateLastMediaAdded = reader[index].TryReadDateTime();
+ folder.DateLastMediaAdded = dateLastMediaAdded;
}
index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var album))
{
- item.Album = reader.GetString(index);
+ item.Album = album;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetSingle(index++, out var criticRating))
{
- item.CriticRating = reader.GetFloat(index);
+ item.CriticRating = criticRating;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetBoolean(index++, out var isVirtualItem))
{
- item.IsVirtualItem = reader.GetBoolean(index);
+ item.IsVirtualItem = isVirtualItem;
}
- index++;
-
if (item is IHasSeries hasSeriesName)
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index, out var seriesName))
{
- hasSeriesName.SeriesName = reader.GetString(index);
+ hasSeriesName.SeriesName = seriesName;
}
}
@@ -1765,15 +1687,15 @@ namespace Emby.Server.Implementations.Data
{
if (item is Episode episode)
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index, out var seasonName))
{
- episode.SeasonName = reader.GetString(index);
+ episode.SeasonName = seasonName;
}
index++;
- if (!reader.IsDBNull(index))
+ if (reader.TryGetGuid(index, out var seasonId))
{
- episode.SeasonId = reader.GetGuid(index);
+ episode.SeasonId = seasonId;
}
}
else
@@ -1789,9 +1711,9 @@ namespace Emby.Server.Implementations.Data
{
if (hasSeries != null)
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetGuid(index, out var seriesId))
{
- hasSeries.SeriesId = reader.GetGuid(index);
+ hasSeries.SeriesId = seriesId;
}
}
@@ -1800,56 +1722,48 @@ namespace Emby.Server.Implementations.Data
if (HasField(query, ItemFields.PresentationUniqueKey))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var presentationUniqueKey))
{
- item.PresentationUniqueKey = reader.GetString(index);
+ item.PresentationUniqueKey = presentationUniqueKey;
}
-
- index++;
}
if (HasField(query, ItemFields.InheritedParentalRatingValue))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetInt32(index++, out var parentalRating))
{
- item.InheritedParentalRatingValue = reader.GetInt32(index);
+ item.InheritedParentalRatingValue = parentalRating;
}
-
- index++;
}
if (HasField(query, ItemFields.ExternalSeriesId))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var externalSeriesId))
{
- item.ExternalSeriesId = reader.GetString(index);
+ item.ExternalSeriesId = externalSeriesId;
}
-
- index++;
}
if (HasField(query, ItemFields.Taglines))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var tagLine))
{
- item.Tagline = reader.GetString(index);
+ item.Tagline = tagLine;
}
-
- index++;
}
- if (item.ProviderIds.Count == 0 && !reader.IsDBNull(index))
+ if (item.ProviderIds.Count == 0 && reader.TryGetString(index, out var providerIds))
{
- DeserializeProviderIds(reader.GetString(index), item);
+ DeserializeProviderIds(providerIds, item);
}
index++;
if (query.DtoOptions.EnableImages)
{
- if (item.ImageInfos.Length == 0 && !reader.IsDBNull(index))
+ if (item.ImageInfos.Length == 0 && reader.TryGetString(index, out var imageInfos))
{
- item.ImageInfos = DeserializeImages(reader.GetString(index));
+ item.ImageInfos = DeserializeImages(imageInfos);
}
index++;
@@ -1857,72 +1771,62 @@ namespace Emby.Server.Implementations.Data
if (HasField(query, ItemFields.ProductionLocations))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var productionLocations))
{
- item.ProductionLocations = reader.GetString(index).Split('|', StringSplitOptions.RemoveEmptyEntries).ToArray();
+ item.ProductionLocations = productionLocations.Split('|', StringSplitOptions.RemoveEmptyEntries);
}
-
- index++;
}
if (HasField(query, ItemFields.ExtraIds))
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var extraIds))
{
- item.ExtraIds = SplitToGuids(reader.GetString(index));
+ item.ExtraIds = SplitToGuids(extraIds);
}
-
- index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetInt32(index++, out var totalBitrate))
{
- item.TotalBitrate = reader.GetInt32(index);
+ item.TotalBitrate = totalBitrate;
}
- index++;
-
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var extraTypeString))
{
- if (Enum.TryParse(reader.GetString(index), true, out ExtraType extraType))
+ if (Enum.TryParse(extraTypeString, true, out ExtraType extraType))
{
item.ExtraType = extraType;
}
}
- index++;
-
if (hasArtistFields)
{
- if (item is IHasArtist hasArtists && !reader.IsDBNull(index))
+ if (item is IHasArtist hasArtists && reader.TryGetString(index, out var artists))
{
- hasArtists.Artists = reader.GetString(index).Split('|', StringSplitOptions.RemoveEmptyEntries);
+ hasArtists.Artists = artists.Split('|', StringSplitOptions.RemoveEmptyEntries);
}
index++;
- if (item is IHasAlbumArtist hasAlbumArtists && !reader.IsDBNull(index))
+ if (item is IHasAlbumArtist hasAlbumArtists && reader.TryGetString(index, out var albumArtists))
{
- hasAlbumArtists.AlbumArtists = reader.GetString(index).Split('|', StringSplitOptions.RemoveEmptyEntries);
+ hasAlbumArtists.AlbumArtists = albumArtists.Split('|', StringSplitOptions.RemoveEmptyEntries);
}
index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index++, out var externalId))
{
- item.ExternalId = reader.GetString(index);
+ item.ExternalId = externalId;
}
- index++;
-
if (HasField(query, ItemFields.SeriesPresentationUniqueKey))
{
if (hasSeries != null)
{
- if (!reader.IsDBNull(index))
+ if (reader.TryGetString(index, out var seriesPresentationUniqueKey))
{
- hasSeries.SeriesPresentationUniqueKey = reader.GetString(index);
+ hasSeries.SeriesPresentationUniqueKey = seriesPresentationUniqueKey;
}
}
@@ -1931,21 +1835,19 @@ namespace Emby.Server.Implementations.Data
if (enableProgramAttributes)
{
- if (item is LiveTvProgram program && !reader.IsDBNull(index))
+ if (item is LiveTvProgram program && reader.TryGetString(index, out var showId))
{
- program.ShowId = reader.GetString(index);
+ program.ShowId = showId;
}
index++;
}
- if (!reader.IsDBNull(index))
+ if (reader.TryGetGuid(index, out var ownerId))
{
- item.OwnerId = reader.GetGuid(index);
+ item.OwnerId = ownerId;
}
- index++;
-
return item;
}
@@ -2032,14 +1934,14 @@ namespace Emby.Server.Implementations.Data
StartPositionTicks = reader.GetInt64(0)
};
- if (!reader.IsDBNull(1))
+ if (reader.TryGetString(1, out var chapterName))
{
- chapter.Name = reader.GetString(1);
+ chapter.Name = chapterName;
}
- if (!reader.IsDBNull(2))
+ if (reader.TryGetString(2, out var imagePath))
{
- chapter.ImagePath = reader.GetString(2);
+ chapter.ImagePath = imagePath;
if (!string.IsNullOrEmpty(chapter.ImagePath))
{
@@ -2054,9 +1956,9 @@ namespace Emby.Server.Implementations.Data
}
}
- if (!reader.IsDBNull(3))
+ if (reader.TryReadDateTime(3, out var imageDateModified))
{
- chapter.ImageDateModified = reader[3].ReadDateTime();
+ chapter.ImageDateModified = imageDateModified;
}
return chapter;
@@ -3228,12 +3130,8 @@ namespace Emby.Server.Implementations.Data
foreach (var row in statement.ExecuteQuery())
{
var id = row.GetGuid(0);
- string path = null;
- if (!row.IsDBNull(1))
- {
- path = row.GetString(1);
- }
+ row.TryGetString(1, out var path);
list.Add(new Tuple(id, path));
}
@@ -4427,7 +4325,7 @@ namespace Emby.Server.Implementations.Data
whereClauses.Add(string.Join(" AND ", excludeIds));
}
- if (query.ExcludeProviderIds.Count > 0)
+ if (query.ExcludeProviderIds != null && query.ExcludeProviderIds.Count > 0)
{
var excludeIds = new List();
@@ -4457,7 +4355,7 @@ namespace Emby.Server.Implementations.Data
}
}
- if (query.HasAnyProviderId.Count > 0)
+ if (query.HasAnyProviderId != null && query.HasAnyProviderId.Count > 0)
{
var hasProviderIds = new List();
@@ -5327,9 +5225,9 @@ AND Type = @InternalPersonType)");
{
foreach (var row in statement.ExecuteQuery())
{
- if (!row.IsDBNull(0))
+ if (row.TryGetString(0, out var result))
{
- list.Add(row.GetString(0));
+ list.Add(result);
}
}
}
@@ -5603,7 +5501,7 @@ AND Type = @InternalPersonType)");
return result;
}
- private ItemCounts GetItemCounts(IReadOnlyList reader, int countStartColumn, string[] typesToCount)
+ private static ItemCounts GetItemCounts(IReadOnlyList reader, int countStartColumn, string[] typesToCount)
{
var counts = new ItemCounts();
@@ -5612,51 +5510,43 @@ AND Type = @InternalPersonType)");
return counts;
}
- var typeString = reader.IsDBNull(countStartColumn) ? null : reader.GetString(countStartColumn);
-
- if (string.IsNullOrWhiteSpace(typeString))
+ if (!reader.TryGetString(countStartColumn, out var typeString))
{
return counts;
}
- var allTypes = typeString.Split('|', StringSplitOptions.RemoveEmptyEntries)
- .ToLookup(x => x);
-
- foreach (var type in allTypes)
+ foreach (var typeName in typeString.AsSpan().Split('|'))
{
- var value = type.Count();
- var typeName = type.Key;
-
- if (string.Equals(typeName, typeof(Series).FullName, StringComparison.OrdinalIgnoreCase))
+ if (typeName.Equals(typeof(Series).FullName, StringComparison.OrdinalIgnoreCase))
{
- counts.SeriesCount = value;
+ counts.SeriesCount++;
}
- else if (string.Equals(typeName, typeof(Episode).FullName, StringComparison.OrdinalIgnoreCase))
+ else if (typeName.Equals(typeof(Episode).FullName, StringComparison.OrdinalIgnoreCase))
{
- counts.EpisodeCount = value;
+ counts.EpisodeCount++;
}
- else if (string.Equals(typeName, typeof(Movie).FullName, StringComparison.OrdinalIgnoreCase))
+ else if (typeName.Equals(typeof(Movie).FullName, StringComparison.OrdinalIgnoreCase))
{
- counts.MovieCount = value;
+ counts.MovieCount++;
}
- else if (string.Equals(typeName, typeof(MusicAlbum).FullName, StringComparison.OrdinalIgnoreCase))
+ else if (typeName.Equals(typeof(MusicAlbum).FullName, StringComparison.OrdinalIgnoreCase))
{
- counts.AlbumCount = value;
+ counts.AlbumCount++;
}
- else if (string.Equals(typeName, typeof(MusicArtist).FullName, StringComparison.OrdinalIgnoreCase))
+ else if (typeName.Equals(typeof(MusicArtist).FullName, StringComparison.OrdinalIgnoreCase))
{
- counts.ArtistCount = value;
+ counts.ArtistCount++;
}
- else if (string.Equals(typeName, typeof(Audio).FullName, StringComparison.OrdinalIgnoreCase))
+ else if (typeName.Equals(typeof(Audio).FullName, StringComparison.OrdinalIgnoreCase))
{
- counts.SongCount = value;
+ counts.SongCount++;
}
- else if (string.Equals(typeName, typeof(Trailer).FullName, StringComparison.OrdinalIgnoreCase))
+ else if (typeName.Equals(typeof(Trailer).FullName, StringComparison.OrdinalIgnoreCase))
{
- counts.TrailerCount = value;
+ counts.TrailerCount++;
}
- counts.ItemCount += value;
+ counts.ItemCount++;
}
return counts;
@@ -5850,19 +5740,19 @@ AND Type = @InternalPersonType)");
Name = reader.GetString(1)
};
- if (!reader.IsDBNull(2))
+ if (reader.TryGetString(2, out var role))
{
- item.Role = reader.GetString(2);
+ item.Role = role;
}
- if (!reader.IsDBNull(3))
+ if (reader.TryGetString(3, out var type))
{
- item.Type = reader.GetString(3);
+ item.Type = type;
}
- if (!reader.IsDBNull(4))
+ if (reader.TryGetInt32(4, out var sortOrder))
{
- item.SortOrder = reader.GetInt32(4);
+ item.SortOrder = sortOrder;
}
return item;
@@ -6058,150 +5948,150 @@ AND Type = @InternalPersonType)");
item.Type = Enum.Parse(reader[2].ToString(), true);
- if (reader[3].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(3, out var codec))
{
- item.Codec = reader[3].ToString();
+ item.Codec = codec;
}
- if (reader[4].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(4, out var language))
{
- item.Language = reader[4].ToString();
+ item.Language = language;
}
- if (reader[5].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(5, out var channelLayout))
{
- item.ChannelLayout = reader[5].ToString();
+ item.ChannelLayout = channelLayout;
}
- if (reader[6].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(6, out var profile))
{
- item.Profile = reader[6].ToString();
+ item.Profile = profile;
}
- if (reader[7].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(7, out var aspectRatio))
{
- item.AspectRatio = reader[7].ToString();
+ item.AspectRatio = aspectRatio;
}
- if (reader[8].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(8, out var path))
{
- item.Path = RestorePath(reader[8].ToString());
+ item.Path = RestorePath(path);
}
item.IsInterlaced = reader.GetBoolean(9);
- if (reader[10].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetInt32(10, out var bitrate))
{
- item.BitRate = reader.GetInt32(10);
+ item.BitRate = bitrate;
}
- if (reader[11].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetInt32(11, out var channels))
{
- item.Channels = reader.GetInt32(11);
+ item.Channels = channels;
}
- if (reader[12].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetInt32(12, out var sampleRate))
{
- item.SampleRate = reader.GetInt32(12);
+ item.SampleRate = sampleRate;
}
item.IsDefault = reader.GetBoolean(13);
item.IsForced = reader.GetBoolean(14);
item.IsExternal = reader.GetBoolean(15);
- if (reader[16].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetInt32(16, out var width))
{
- item.Width = reader.GetInt32(16);
+ item.Width = width;
}
- if (reader[17].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetInt32(17, out var height))
{
- item.Height = reader.GetInt32(17);
+ item.Height = height;
}
- if (reader[18].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetSingle(18, out var averageFrameRate))
{
- item.AverageFrameRate = reader.GetFloat(18);
+ item.AverageFrameRate = averageFrameRate;
}
- if (reader[19].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetSingle(19, out var realFrameRate))
{
- item.RealFrameRate = reader.GetFloat(19);
+ item.RealFrameRate = realFrameRate;
}
- if (reader[20].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetSingle(20, out var level))
{
- item.Level = reader.GetFloat(20);
+ item.Level = level;
}
- if (reader[21].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(21, out var pixelFormat))
{
- item.PixelFormat = reader[21].ToString();
+ item.PixelFormat = pixelFormat;
}
- if (reader[22].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetInt32(22, out var bitDepth))
{
- item.BitDepth = reader.GetInt32(22);
+ item.BitDepth = bitDepth;
}
- if (reader[23].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetBoolean(23, out var isAnamorphic))
{
- item.IsAnamorphic = reader.GetBoolean(23);
+ item.IsAnamorphic = isAnamorphic;
}
- if (reader[24].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetInt32(24, out var refFrames))
{
- item.RefFrames = reader.GetInt32(24);
+ item.RefFrames = refFrames;
}
- if (reader[25].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(25, out var codecTag))
{
- item.CodecTag = reader.GetString(25);
+ item.CodecTag = codecTag;
}
- if (reader[26].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(26, out var comment))
{
- item.Comment = reader.GetString(26);
+ item.Comment = comment;
}
- if (reader[27].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(27, out var nalLengthSize))
{
- item.NalLengthSize = reader.GetString(27);
+ item.NalLengthSize = nalLengthSize;
}
- if (reader[28].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetBoolean(28, out var isAVC))
{
- item.IsAVC = reader[28].ToBool();
+ item.IsAVC = isAVC;
}
- if (reader[29].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(29, out var title))
{
- item.Title = reader[29].ToString();
+ item.Title = title;
}
- if (reader[30].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(30, out var timeBase))
{
- item.TimeBase = reader[30].ToString();
+ item.TimeBase = timeBase;
}
- if (reader[31].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(31, out var codecTimeBase))
{
- item.CodecTimeBase = reader[31].ToString();
+ item.CodecTimeBase = codecTimeBase;
}
- if (reader[32].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(32, out var colorPrimaries))
{
- item.ColorPrimaries = reader[32].ToString();
+ item.ColorPrimaries = colorPrimaries;
}
- if (reader[33].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(33, out var colorSpace))
{
- item.ColorSpace = reader[33].ToString();
+ item.ColorSpace = colorSpace;
}
- if (reader[34].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(34, out var colorTransfer))
{
- item.ColorTransfer = reader[34].ToString();
+ item.ColorTransfer = colorTransfer;
}
if (item.Type == MediaStreamType.Subtitle)
@@ -6357,29 +6247,29 @@ AND Type = @InternalPersonType)");
Index = reader[1].ToInt()
};
- if (reader[2].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(2, out var codec))
{
- item.Codec = reader[2].ToString();
+ item.Codec = codec;
}
- if (reader[2].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(3, out var codecTag))
{
- item.CodecTag = reader[3].ToString();
+ item.CodecTag = codecTag;
}
- if (reader[4].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(4, out var comment))
{
- item.Comment = reader[4].ToString();
+ item.Comment = comment;
}
- if (reader[6].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(5, out var fileName))
{
- item.FileName = reader[5].ToString();
+ item.FileName = fileName;
}
- if (reader[6].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetString(6, out var mimeType))
{
- item.MimeType = reader[6].ToString();
+ item.MimeType = mimeType;
}
return item;
diff --git a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
index 6574db6078..e0ebd0a6cf 100644
--- a/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteUserDataRepository.cs
@@ -355,9 +355,9 @@ namespace Emby.Server.Implementations.Data
userData.Key = reader[0].ToString();
// userData.UserId = reader[1].ReadGuidFromBlob();
- if (reader[2].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetDouble(2, out var rating))
{
- userData.Rating = reader[2].ToDouble();
+ userData.Rating = rating;
}
userData.Played = reader[3].ToBool();
@@ -365,19 +365,19 @@ namespace Emby.Server.Implementations.Data
userData.IsFavorite = reader[5].ToBool();
userData.PlaybackPositionTicks = reader[6].ToInt64();
- if (reader[7].SQLiteType != SQLiteType.Null)
+ if (reader.TryReadDateTime(7, out var lastPlayedDate))
{
- userData.LastPlayedDate = reader[7].TryReadDateTime();
+ userData.LastPlayedDate = lastPlayedDate;
}
- if (reader[8].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetInt32(8, out var audioStreamIndex))
{
- userData.AudioStreamIndex = reader[8].ToInt();
+ userData.AudioStreamIndex = audioStreamIndex;
}
- if (reader[9].SQLiteType != SQLiteType.Null)
+ if (reader.TryGetInt32(9, out var subtitleStreamIndex))
{
- userData.SubtitleStreamIndex = reader[9].ToInt();
+ userData.SubtitleStreamIndex = subtitleStreamIndex;
}
return userData;
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 8ea98f454f..14f6f565c4 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 6e688693be..16050185fb 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -165,13 +165,13 @@ namespace Emby.Server.Implementations.Library.Resolvers
protected void SetVideoType(Video video, VideoFileInfo videoInfo)
{
- var extension = Path.GetExtension(video.Path);
- video.VideoType = string.Equals(extension, ".iso", StringComparison.OrdinalIgnoreCase) ||
- string.Equals(extension, ".img", StringComparison.OrdinalIgnoreCase) ?
- VideoType.Iso :
- VideoType.VideoFile;
+ var extension = Path.GetExtension(video.Path.AsSpan());
+ video.VideoType = extension.Equals(".iso", StringComparison.OrdinalIgnoreCase)
+ || extension.Equals(".img", StringComparison.OrdinalIgnoreCase)
+ ? VideoType.Iso
+ : VideoType.VideoFile;
- video.IsShortcut = string.Equals(extension, ".strm", StringComparison.OrdinalIgnoreCase);
+ video.IsShortcut = extension.Equals(".strm", StringComparison.OrdinalIgnoreCase);
video.IsPlaceHolder = videoInfo.IsStub;
if (videoInfo.IsStub)
@@ -193,11 +193,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
{
if (video.VideoType == VideoType.Iso)
{
- if (video.Path.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1)
+ if (video.Path.Contains("dvd", StringComparison.OrdinalIgnoreCase))
{
video.IsoType = IsoType.Dvd;
}
- else if (video.Path.IndexOf("bluray", StringComparison.OrdinalIgnoreCase) != -1)
+ else if (video.Path.Contains("bluray", StringComparison.OrdinalIgnoreCase))
{
video.IsoType = IsoType.BluRay;
}
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
index 142c59542a..32245f899e 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/RecordingHelper.cs
@@ -6,7 +6,7 @@ using MediaBrowser.Controller.LiveTv;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
- internal class RecordingHelper
+ internal static class RecordingHelper
{
public static DateTime GetStartTime(TimerInfo timer)
{
@@ -70,17 +70,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private static string GetDateString(DateTime date)
{
- date = date.ToLocalTime();
-
- return string.Format(
- CultureInfo.InvariantCulture,
- "{0}_{1}_{2}_{3}_{4}_{5}",
- date.Year.ToString("0000", CultureInfo.InvariantCulture),
- date.Month.ToString("00", CultureInfo.InvariantCulture),
- date.Day.ToString("00", CultureInfo.InvariantCulture),
- date.Hour.ToString("00", CultureInfo.InvariantCulture),
- date.Minute.ToString("00", CultureInfo.InvariantCulture),
- date.Second.ToString("00", CultureInfo.InvariantCulture));
+ return date.ToLocalTime().ToString("yyyy_MM_dd_HH_mm_ss", CultureInfo.InvariantCulture);
}
}
}
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
index 324109bcfe..bbac6e0551 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs
@@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
{
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
- using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(model.LineupURL, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(model.LineupURL ?? model.BaseURL + "/lineup.json", HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
var lineup = await JsonSerializer.DeserializeAsync>(stream, _jsonOptions, cancellationToken)
.ConfigureAwait(false) ?? new List();
diff --git a/Emby.Server.Implementations/Localization/Core/bg-BG.json b/Emby.Server.Implementations/Localization/Core/bg-BG.json
index 9db3b50d91..bc25531d30 100644
--- a/Emby.Server.Implementations/Localization/Core/bg-BG.json
+++ b/Emby.Server.Implementations/Localization/Core/bg-BG.json
@@ -39,7 +39,7 @@
"MixedContent": "Смесено съдържание",
"Movies": "Филми",
"Music": "Музика",
- "MusicVideos": "Музикални клипове",
+ "MusicVideos": "Музикални видеа",
"NameInstallFailed": "{0} не можа да се инсталира",
"NameSeasonNumber": "Сезон {0}",
"NameSeasonUnknown": "Неразпознат сезон",
@@ -62,7 +62,7 @@
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
"Photos": "Снимки",
"Playlists": "Списъци",
- "Plugin": "Приставка",
+ "Plugin": "Добавка",
"PluginInstalledWithName": "{0} е инсталиранa",
"PluginUninstalledWithName": "{0} е деинсталиранa",
"PluginUpdatedWithName": "{0} е обновенa",
@@ -116,5 +116,7 @@
"TasksMaintenanceCategory": "Поддръжка",
"Undefined": "Неопределено",
"Forced": "Принудително",
- "Default": "По подразбиране"
+ "Default": "По подразбиране",
+ "TaskCleanActivityLogDescription": "Изтрива записите в дневника с активност по стари от конфигурираната възраст.",
+ "TaskCleanActivityLog": "Изчисти дневника с активност"
}
diff --git a/Emby.Server.Implementations/Localization/Core/bn.json b/Emby.Server.Implementations/Localization/Core/bn.json
index f0e42a052b..c3fbe24082 100644
--- a/Emby.Server.Implementations/Localization/Core/bn.json
+++ b/Emby.Server.Implementations/Localization/Core/bn.json
@@ -1,7 +1,7 @@
{
"DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
"DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
- "Collections": "কলেক্শন",
+ "Collections": "সংগ্রহ",
"ChapterNameValue": "অধ্যায় {0}",
"Channels": "চ্যানেল",
"CameraImageUploadedFrom": "{0} থেকে একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে",
diff --git a/Jellyfin.Api/Controllers/DynamicHlsController.cs b/Jellyfin.Api/Controllers/DynamicHlsController.cs
index b4154b361e..9f3216de34 100644
--- a/Jellyfin.Api/Controllers/DynamicHlsController.cs
+++ b/Jellyfin.Api/Controllers/DynamicHlsController.cs
@@ -28,7 +28,6 @@ using MediaBrowser.Model.Net;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;
@@ -545,7 +544,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] EncodingContext? context,
[FromQuery] Dictionary streamOptions)
{
- var cancellationTokenSource = new CancellationTokenSource();
+ using var cancellationTokenSource = new CancellationTokenSource();
var streamingRequest = new VideoRequestDto
{
Id = itemId,
@@ -710,7 +709,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] EncodingContext? context,
[FromQuery] Dictionary streamOptions)
{
- var cancellationTokenSource = new CancellationTokenSource();
+ using var cancellationTokenSource = new CancellationTokenSource();
var streamingRequest = new StreamingRequestDto
{
Id = itemId,
@@ -1138,7 +1137,7 @@ namespace Jellyfin.Api.Controllers
var isHlsInFmp4 = string.Equals(segmentContainer, "mp4", StringComparison.OrdinalIgnoreCase);
var hlsVersion = isHlsInFmp4 ? "7" : "3";
- var builder = new StringBuilder();
+ var builder = new StringBuilder(128);
builder.AppendLine("#EXTM3U")
.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD")
@@ -1191,7 +1190,7 @@ namespace Jellyfin.Api.Controllers
throw new ArgumentException("StartTimeTicks is not allowed.");
}
- var cancellationTokenSource = new CancellationTokenSource();
+ using var cancellationTokenSource = new CancellationTokenSource();
var cancellationToken = cancellationTokenSource.Token;
using var state = await StreamingHelpers.GetStreamingState(
@@ -1208,7 +1207,7 @@ namespace Jellyfin.Api.Controllers
_deviceManager,
_transcodingJobHelper,
TranscodingJobType,
- cancellationTokenSource.Token)
+ cancellationToken)
.ConfigureAwait(false);
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
@@ -1227,7 +1226,7 @@ namespace Jellyfin.Api.Controllers
}
var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath);
- await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
+ await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false);
var released = false;
var startTranscoding = false;
@@ -1323,24 +1322,28 @@ namespace Jellyfin.Api.Controllers
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
}
- private double[] GetSegmentLengths(StreamState state)
+ private static double[] GetSegmentLengths(StreamState state)
+ => GetSegmentLengthsInternal(state.RunTimeTicks ?? 0, state.SegmentLength);
+
+ internal static double[] GetSegmentLengthsInternal(long runtimeTicks, int segmentlength)
{
- var result = new List();
+ var segmentLengthTicks = TimeSpan.FromSeconds(segmentlength).Ticks;
+ var wholeSegments = runtimeTicks / segmentLengthTicks;
+ var remainingTicks = runtimeTicks % segmentLengthTicks;
- var ticks = state.RunTimeTicks ?? 0;
-
- var segmentLengthTicks = TimeSpan.FromSeconds(state.SegmentLength).Ticks;
-
- while (ticks > 0)
+ var segmentsLen = wholeSegments + (remainingTicks == 0 ? 0 : 1);
+ var segments = new double[segmentsLen];
+ for (int i = 0; i < wholeSegments; i++)
{
- var length = ticks >= segmentLengthTicks ? segmentLengthTicks : ticks;
-
- result.Add(TimeSpan.FromTicks(length).TotalSeconds);
-
- ticks -= length;
+ segments[i] = segmentlength;
}
- return result.ToArray();
+ if (remainingTicks != 0)
+ {
+ segments[^1] = TimeSpan.FromTicks(remainingTicks).TotalSeconds;
+ }
+
+ return segments;
}
private string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding, int startNumber)
@@ -1376,18 +1379,13 @@ namespace Jellyfin.Api.Controllers
}
else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase))
{
- var outputFmp4HeaderArg = string.Empty;
- var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
- if (isWindows)
+ var outputFmp4HeaderArg = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) switch
{
// on Windows, the path of fmp4 header file needs to be configured
- outputFmp4HeaderArg = " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"";
- }
- else
- {
+ true => " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"",
// on Linux/Unix, ffmpeg generate fmp4 header file to m3u8 output folder
- outputFmp4HeaderArg = " -hls_fmp4_init_filename \"" + outputFileNameWithoutExtension + "-1" + outputExtension + "\"";
- }
+ false => " -hls_fmp4_init_filename \"" + outputFileNameWithoutExtension + "-1" + outputExtension + "\""
+ };
segmentFormat = "fmp4" + outputFmp4HeaderArg;
}
diff --git a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
index 2420779633..6fec42d444 100644
--- a/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
+++ b/Jellyfin.Api/Helpers/TranscodingJobHelper.cs
@@ -22,7 +22,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.Http;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.Helpers
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index c10c34b59a..eb9fc4f14c 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -15,7 +15,7 @@
-
+
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index 2c6a176b69..d24c73526a 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -27,13 +27,13 @@
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
index 775edafc2d..688ff71682 100644
--- a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
+++ b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
@@ -5,6 +5,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using Microsoft.AspNetCore.Http;
@@ -207,7 +208,7 @@ namespace Jellyfin.Server.Implementations.Security
auth = httpReq.Request.Headers[HeaderNames.Authorization];
}
- return GetAuthorization(auth);
+ return auth.Count > 0 ? GetAuthorization(auth[0]) : null;
}
///
@@ -224,7 +225,7 @@ namespace Jellyfin.Server.Implementations.Security
auth = httpReq.Headers[HeaderNames.Authorization];
}
- return GetAuthorization(auth);
+ return auth.Count > 0 ? GetAuthorization(auth[0]) : null;
}
///
@@ -232,43 +233,38 @@ namespace Jellyfin.Server.Implementations.Security
///
/// The authorization header.
/// Dictionary{System.StringSystem.String}.
- private Dictionary? GetAuthorization(string? authorizationHeader)
+ private Dictionary? GetAuthorization(ReadOnlySpan authorizationHeader)
{
- if (authorizationHeader == null)
+ var firstSpace = authorizationHeader.IndexOf(' ');
+
+ // There should be at least two parts
+ if (firstSpace == -1)
{
return null;
}
- var parts = authorizationHeader.Split(' ', 2);
+ var name = authorizationHeader[..firstSpace];
- // There should be at least to parts
- if (parts.Length != 2)
+ if (!name.Equals("MediaBrowser", StringComparison.OrdinalIgnoreCase)
+ && !name.Equals("Emby", StringComparison.OrdinalIgnoreCase))
{
return null;
}
- var acceptedNames = new[] { "MediaBrowser", "Emby" };
-
- // It has to be a digest request
- if (!acceptedNames.Contains(parts[0], StringComparer.OrdinalIgnoreCase))
- {
- return null;
- }
-
- // Remove uptil the first space
- authorizationHeader = parts[1];
- parts = authorizationHeader.Split(',');
+ authorizationHeader = authorizationHeader[(firstSpace + 1)..];
var result = new Dictionary(StringComparer.OrdinalIgnoreCase);
- foreach (var item in parts)
+ foreach (var item in authorizationHeader.Split(','))
{
- var param = item.Trim().Split('=', 2);
+ var trimmedItem = item.Trim();
+ var firstEqualsSign = trimmedItem.IndexOf('=');
- if (param.Length == 2)
+ if (firstEqualsSign > 0)
{
- var value = NormalizeValue(param[1].Trim('"'));
- result[param[0]] = value;
+ var key = trimmedItem[..firstEqualsSign].ToString();
+ var value = NormalizeValue(trimmedItem[(firstEqualsSign + 1)..].Trim('"').ToString());
+ result[key] = value;
}
}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index 3496cabe8e..f83de7ac83 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -38,8 +38,8 @@
-
-
+
+
diff --git a/Jellyfin.sln b/Jellyfin.sln
index 8626a4b1ba..9fbd9d266e 100644
--- a/Jellyfin.sln
+++ b/Jellyfin.sln
@@ -81,6 +81,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "te
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Integration.Tests", "tests\Jellyfin.Server.Integration.Tests\Jellyfin.Server.Integration.Tests.csproj", "{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Providers.Tests", "tests\Jellyfin.Providers.Tests\Jellyfin.Providers.Tests.csproj", "{A964008C-2136-4716-B6CB-B3426C22320A}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -223,6 +225,10 @@ Global
{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {A964008C-2136-4716-B6CB-B3426C22320A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {A964008C-2136-4716-B6CB-B3426C22320A}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {A964008C-2136-4716-B6CB-B3426C22320A}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {A964008C-2136-4716-B6CB-B3426C22320A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -240,6 +246,7 @@ Global
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
+ {A964008C-2136-4716-B6CB-B3426C22320A} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE}
diff --git a/MediaBrowser.Common/Extensions/EnumerableExtensions.cs b/MediaBrowser.Common/Extensions/EnumerableExtensions.cs
new file mode 100644
index 0000000000..2b8a6c395b
--- /dev/null
+++ b/MediaBrowser.Common/Extensions/EnumerableExtensions.cs
@@ -0,0 +1,51 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Common.Extensions
+{
+ ///
+ /// Static extensions for the interface.
+ ///
+ public static class EnumerableExtensions
+ {
+ ///
+ /// Determines whether the value is contained in the source collection.
+ ///
+ /// An instance of the interface.
+ /// The value to look for in the collection.
+ /// The string comparison.
+ /// A value indicating whether the value is contained in the collection.
+ /// The source is null.
+ public static bool Contains(this IEnumerable source, ReadOnlySpan value, StringComparison stringComparison)
+ {
+ if (source == null)
+ {
+ throw new ArgumentNullException(nameof(source));
+ }
+
+ if (source is IList list)
+ {
+ int len = list.Count;
+ for (int i = 0; i < len; i++)
+ {
+ if (value.Equals(list[i], stringComparison))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ foreach (string element in source)
+ {
+ if (value.Equals(element, stringComparison))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs
index 26c64e0da6..26a936be0c 100644
--- a/MediaBrowser.Controller/Channels/Channel.cs
+++ b/MediaBrowser.Controller/Channels/Channel.cs
@@ -84,7 +84,7 @@ namespace MediaBrowser.Controller.Channels
internal static bool IsChannelVisible(BaseItem channelItem, User user)
{
- var channel = ChannelManager.GetChannel(channelItem.ChannelId.ToString(""));
+ var channel = ChannelManager.GetChannel(channelItem.ChannelId.ToString(string.Empty));
return channel.IsVisible(user);
}
diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs
index 4c5626338e..49be897ef3 100644
--- a/MediaBrowser.Controller/Channels/IChannelManager.cs
+++ b/MediaBrowser.Controller/Channels/IChannelManager.cs
@@ -51,32 +51,47 @@ namespace MediaBrowser.Controller.Channels
/// Gets the channels internal.
///
/// The query.
+ /// The channels.
QueryResult GetChannelsInternal(ChannelQuery query);
///
/// Gets the channels.
///
/// The query.
+ /// The channels.
QueryResult GetChannels(ChannelQuery query);
///
- /// Gets the latest media.
+ /// Gets the latest channel items.
///
+ /// The item query.
+ /// The cancellation token.
+ /// The latest channels.
Task> GetLatestChannelItems(InternalItemsQuery query, CancellationToken cancellationToken);
///
- /// Gets the latest media.
+ /// Gets the latest channel items.
///
+ /// The item query.
+ /// The cancellation token.
+ /// The latest channels.
Task> GetLatestChannelItemsInternal(InternalItemsQuery query, CancellationToken cancellationToken);
///
/// Gets the channel items.
///
+ /// The query.
+ /// The cancellation token.
+ /// The channel items.
Task> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken);
///
- /// Gets the channel items internal.
+ /// Gets the channel items.
///
+ /// The query.
+ /// The progress to report to.
+ /// The cancellation token.
+ /// The channel items.
Task> GetChannelItemsInternal(InternalItemsQuery query, IProgress progress, CancellationToken cancellationToken);
///
@@ -84,9 +99,14 @@ namespace MediaBrowser.Controller.Channels
///
/// The item.
/// The cancellation token.
- /// Task{IEnumerable{MediaSourceInfo}}.
+ /// The item media sources.
IEnumerable GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken);
+ ///
+ /// Whether the item supports media probe.
+ ///
+ /// The item.
+ /// Whether media probe should be enabled.
bool EnableMediaProbe(BaseItem item);
}
}
diff --git a/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs b/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs
new file mode 100644
index 0000000000..a2dc5682dd
--- /dev/null
+++ b/MediaBrowser.Controller/Channels/IDisableMediaSourceDisplay.cs
@@ -0,0 +1,8 @@
+#pragma warning disable CS1591
+
+namespace MediaBrowser.Controller.Channels
+{
+ public interface IDisableMediaSourceDisplay
+ {
+ }
+}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/IHasFolderAttributes.cs b/MediaBrowser.Controller/Channels/IHasFolderAttributes.cs
new file mode 100644
index 0000000000..47277a8cc8
--- /dev/null
+++ b/MediaBrowser.Controller/Channels/IHasFolderAttributes.cs
@@ -0,0 +1,9 @@
+#pragma warning disable CS1591
+
+namespace MediaBrowser.Controller.Channels
+{
+ public interface IHasFolderAttributes
+ {
+ string[] Attributes { get; }
+ }
+}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs b/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs
index 589295543c..eeaa6b622e 100644
--- a/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs
+++ b/MediaBrowser.Controller/Channels/IRequiresMediaInfoCallback.cs
@@ -1,5 +1,3 @@
-#pragma warning disable CS1591
-
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
@@ -7,11 +5,17 @@ using MediaBrowser.Model.Dto;
namespace MediaBrowser.Controller.Channels
{
+ ///
+ /// The channel requires a media info callback.
+ ///
public interface IRequiresMediaInfoCallback
{
///
/// Gets the channel item media information.
///
+ /// The channel item id.
+ /// The cancellation token.
+ /// The enumerable of media source info.
Task> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken);
}
}
diff --git a/MediaBrowser.Controller/Channels/ISearchableChannel.cs b/MediaBrowser.Controller/Channels/ISearchableChannel.cs
index b58446fc43..b87943a6ed 100644
--- a/MediaBrowser.Controller/Channels/ISearchableChannel.cs
+++ b/MediaBrowser.Controller/Channels/ISearchableChannel.cs
@@ -5,7 +5,6 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
-using MediaBrowser.Controller.Entities;
namespace MediaBrowser.Controller.Channels
{
@@ -19,35 +18,4 @@ namespace MediaBrowser.Controller.Channels
/// Task{IEnumerable{ChannelItemInfo}}.
Task> Search(ChannelSearchInfo searchInfo, CancellationToken cancellationToken);
}
-
- public interface ISupportsLatestMedia
- {
- ///
- /// Gets the latest media.
- ///
- /// The request.
- /// The cancellation token.
- /// Task{IEnumerable{ChannelItemInfo}}.
- Task> GetLatestMedia(ChannelLatestMediaSearch request, CancellationToken cancellationToken);
- }
-
- public interface ISupportsDelete
- {
- bool CanDelete(BaseItem item);
-
- Task DeleteItem(string id, CancellationToken cancellationToken);
- }
-
- public interface IDisableMediaSourceDisplay
- {
- }
-
- public interface ISupportsMediaProbe
- {
- }
-
- public interface IHasFolderAttributes
- {
- string[] Attributes { get; }
- }
}
diff --git a/MediaBrowser.Controller/Channels/ISupportsDelete.cs b/MediaBrowser.Controller/Channels/ISupportsDelete.cs
new file mode 100644
index 0000000000..204054374e
--- /dev/null
+++ b/MediaBrowser.Controller/Channels/ISupportsDelete.cs
@@ -0,0 +1,15 @@
+#pragma warning disable CS1591
+
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Channels
+{
+ public interface ISupportsDelete
+ {
+ bool CanDelete(BaseItem item);
+
+ Task DeleteItem(string id, CancellationToken cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/ISupportsLatestMedia.cs b/MediaBrowser.Controller/Channels/ISupportsLatestMedia.cs
new file mode 100644
index 0000000000..dbba7cba2d
--- /dev/null
+++ b/MediaBrowser.Controller/Channels/ISupportsLatestMedia.cs
@@ -0,0 +1,21 @@
+#nullable disable
+
+#pragma warning disable CS1591
+
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Channels
+{
+ public interface ISupportsLatestMedia
+ {
+ ///
+ /// Gets the latest media.
+ ///
+ /// The request.
+ /// The cancellation token.
+ /// The latest media.
+ Task> GetLatestMedia(ChannelLatestMediaSearch request, CancellationToken cancellationToken);
+ }
+}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs b/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs
new file mode 100644
index 0000000000..2682de51c4
--- /dev/null
+++ b/MediaBrowser.Controller/Channels/ISupportsMediaProbe.cs
@@ -0,0 +1,8 @@
+#pragma warning disable CS1591
+
+namespace MediaBrowser.Controller.Channels
+{
+ public interface ISupportsMediaProbe
+ {
+ }
+}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs
index 152c653dc4..45cd08173a 100644
--- a/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs
+++ b/MediaBrowser.Controller/Channels/InternalChannelFeatures.cs
@@ -30,7 +30,7 @@ namespace MediaBrowser.Controller.Channels
public List ContentTypes { get; set; }
///
- /// Represents the maximum number of records the channel allows retrieving at a time.
+ /// Gets or sets the maximum number of records the channel allows retrieving at a time.
///
public int? MaxPageSize { get; set; }
@@ -41,7 +41,7 @@ namespace MediaBrowser.Controller.Channels
public List DefaultSortFields { get; set; }
///
- /// Indicates if a sort ascending/descending toggle is supported or not.
+ /// Gets or sets a value indicating whether a sort ascending/descending toggle is supported or not.
///
public bool SupportsSortOrderToggle { get; set; }
diff --git a/MediaBrowser.Controller/Dto/IDtoService.cs b/MediaBrowser.Controller/Dto/IDtoService.cs
index 7f4bbead0f..61d7962357 100644
--- a/MediaBrowser.Controller/Dto/IDtoService.cs
+++ b/MediaBrowser.Controller/Dto/IDtoService.cs
@@ -36,11 +36,17 @@ namespace MediaBrowser.Controller.Dto
/// The options.
/// The user.
/// The owner.
+ /// The of .
IReadOnlyList GetBaseItemDtos(IReadOnlyList items, DtoOptions options, User user = null, BaseItem owner = null);
///
/// Gets the item by name dto.
///
+ /// The item.
+ /// The dto options.
+ /// The list of tagged items.
+ /// The user.
+ /// The item dto.
BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List taggedItems, User user = null);
}
}
diff --git a/MediaBrowser.Controller/Entities/AggregateFolder.cs b/MediaBrowser.Controller/Entities/AggregateFolder.cs
index e365bfda1f..533130fc8e 100644
--- a/MediaBrowser.Controller/Entities/AggregateFolder.cs
+++ b/MediaBrowser.Controller/Entities/AggregateFolder.cs
@@ -22,6 +22,8 @@ namespace MediaBrowser.Controller.Entities
///
public class AggregateFolder : Folder
{
+ private bool _requiresRefresh;
+
public AggregateFolder()
{
PhysicalLocationsList = Array.Empty();
@@ -85,8 +87,6 @@ namespace MediaBrowser.Controller.Entities
}
}
- private bool _requiresRefresh;
-
public override bool RequiresRefresh()
{
var changed = base.RequiresRefresh() || _requiresRefresh;
@@ -106,11 +106,11 @@ namespace MediaBrowser.Controller.Entities
return changed;
}
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
ClearCache();
- var changed = base.BeforeMetadataRefresh(replaceAllMetdata) || _requiresRefresh;
+ var changed = base.BeforeMetadataRefresh(replaceAllMetadata) || _requiresRefresh;
_requiresRefresh = false;
return changed;
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
index b07c3eed13..0928a8073b 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
@@ -208,9 +208,9 @@ namespace MediaBrowser.Controller.Entities.Audio
///
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
///
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
if (IsAccessedByName)
{
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
index b07d47ffdd..a682a2e58c 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
@@ -38,7 +38,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public override bool IsDisplayedAsFolder => true;
///
- /// Returns the folder containing the item.
+ /// Gets the folder containing the item.
/// If the item is a folder, it returns the folder itself.
///
/// The containing folder path.
@@ -106,9 +106,9 @@ namespace MediaBrowser.Controller.Entities.Audio
///
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
///
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
var newPath = GetRebasedPath();
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 238c98982c..6e46b4cec8 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -92,7 +92,8 @@ namespace MediaBrowser.Controller.Entities
public const string ShortsFolderName = "shorts";
public const string FeaturettesFolderName = "featurettes";
- public static readonly string[] AllExtrasTypesFolderNames = {
+ public static readonly string[] AllExtrasTypesFolderNames =
+ {
ExtrasFolderName,
BehindTheScenesFolderName,
DeletedScenesFolderName,
@@ -177,7 +178,7 @@ namespace MediaBrowser.Controller.Entities
public virtual bool AlwaysScanInternalMetadataPath => false;
///
- /// Gets a value indicating whether this instance is in mixed folder.
+ /// Gets or sets a value indicating whether this instance is in mixed folder.
///
/// true if this instance is in mixed folder; otherwise, false.
[JsonIgnore]
@@ -244,7 +245,7 @@ namespace MediaBrowser.Controller.Entities
public ProgramAudio? Audio { get; set; }
///
- /// Return the id that should be used to key display prefs for this item.
+ /// Gets the id that should be used to key display prefs for this item.
/// Default is based on the type for everything except actual generic folders.
///
/// The display prefs id.
@@ -280,7 +281,7 @@ namespace MediaBrowser.Controller.Entities
}
///
- /// Returns the folder containing the item.
+ /// Gets the folder containing the item.
/// If the item is a folder, it returns the folder itself.
///
[JsonIgnore]
@@ -305,8 +306,11 @@ namespace MediaBrowser.Controller.Entities
public string ServiceName { get; set; }
///
- /// If this content came from an external service, the id of the content on that service.
+ /// Gets or sets the external id.
///
+ ///
+ /// If this content came from an external service, the id of the content on that service.
+ ///
[JsonIgnore]
public string ExternalId { get; set; }
@@ -330,7 +334,7 @@ namespace MediaBrowser.Controller.Entities
}
///
- /// Gets or sets the type of the location.
+ /// Gets the type of the location.
///
/// The type of the location.
[JsonIgnore]
@@ -449,8 +453,11 @@ namespace MediaBrowser.Controller.Entities
}
///
- /// This is just a helper for convenience.
+ /// Gets the primary image path.
///
+ ///
+ /// This is just a helper for convenience.
+ ///
/// The primary image path.
[JsonIgnore]
public string PrimaryImagePath => this.GetImagePath(ImageType.Primary);
@@ -541,7 +548,7 @@ namespace MediaBrowser.Controller.Entities
public DateTime DateLastRefreshed { get; set; }
///
- /// The logger.
+ /// Gets or sets the logger.
///
public static ILogger Logger { get; set; }
@@ -621,7 +628,7 @@ namespace MediaBrowser.Controller.Entities
private Guid[] _themeVideoIds;
///
- /// Gets the name of the sort.
+ /// Gets or sets the name of the sort.
///
/// The name of the sort.
[JsonIgnore]
@@ -848,7 +855,7 @@ namespace MediaBrowser.Controller.Entities
}
///
- /// When the item first debuted. For movies this could be premiere date, episodes would be first aired
+ /// Gets or sets the date that the item first debuted. For movies this could be premiere date, episodes would be first aired.
///
/// The premiere date.
[JsonIgnore]
@@ -945,7 +952,7 @@ namespace MediaBrowser.Controller.Entities
public int? ProductionYear { get; set; }
///
- /// If the item is part of a series, this is it's number in the series.
+ /// Gets or sets the index number. If the item is part of a series, this is it's number in the series.
/// This could be episode number, album track number, etc.
///
/// The index number.
@@ -953,7 +960,7 @@ namespace MediaBrowser.Controller.Entities
public int? IndexNumber { get; set; }
///
- /// For an episode this could be the season number, or for a song this could be the disc number.
+ /// Gets or sets the parent index number. For an episode this could be the season number, or for a song this could be the disc number.
///
/// The parent index number.
[JsonIgnore]
@@ -1017,9 +1024,9 @@ namespace MediaBrowser.Controller.Entities
}
// if (!user.IsParentalScheduleAllowed())
- //{
+ // {
// return PlayAccess.None;
- //}
+ // }
return PlayAccess.Full;
}
@@ -2645,7 +2652,9 @@ namespace MediaBrowser.Controller.Entities
///
/// This is called before any metadata refresh and returns true if changes were made.
///
- public virtual bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ /// Whether to replace all metadata.
+ /// true if the item has change, else false.
+ public virtual bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
_sortName = null;
diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs
index a86da29ce4..d0fb3997d5 100644
--- a/MediaBrowser.Controller/Entities/CollectionFolder.cs
+++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs
@@ -29,30 +29,45 @@ namespace MediaBrowser.Controller.Entities
public class CollectionFolder : Folder, ICollectionFolder
{
private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
- public static IXmlSerializer XmlSerializer { get; set; }
-
- public static IServerApplicationHost ApplicationHost { get; set; }
+ private static readonly Dictionary _libraryOptions = new Dictionary();
+ private bool _requiresRefresh;
+ ///
+ /// Initializes a new instance of the class.
+ ///
public CollectionFolder()
{
PhysicalLocationsList = Array.Empty();
PhysicalFolderIds = Array.Empty();
}
+ public static IXmlSerializer XmlSerializer { get; set; }
+
+ public static IServerApplicationHost ApplicationHost { get; set; }
+
[JsonIgnore]
public override bool SupportsPlayedStatus => false;
[JsonIgnore]
public override bool SupportsInheritedParentImages => false;
+ public string CollectionType { get; set; }
+
+ ///
+ /// Gets the item's children.
+ ///
+ ///
+ /// Our children are actually just references to the ones in the physical root...
+ ///
+ /// The actual children.
+ [JsonIgnore]
+ public override IEnumerable Children => GetActualChildren();
+
public override bool CanDelete()
{
return false;
}
- public string CollectionType { get; set; }
-
- private static readonly Dictionary LibraryOptions = new Dictionary();
public LibraryOptions GetLibraryOptions()
{
return GetLibraryOptions(Path);
@@ -106,12 +121,12 @@ namespace MediaBrowser.Controller.Entities
public static LibraryOptions GetLibraryOptions(string path)
{
- lock (LibraryOptions)
+ lock (_libraryOptions)
{
- if (!LibraryOptions.TryGetValue(path, out var options))
+ if (!_libraryOptions.TryGetValue(path, out var options))
{
options = LoadLibraryOptions(path);
- LibraryOptions[path] = options;
+ _libraryOptions[path] = options;
}
return options;
@@ -120,9 +135,9 @@ namespace MediaBrowser.Controller.Entities
public static void SaveLibraryOptions(string path, LibraryOptions options)
{
- lock (LibraryOptions)
+ lock (_libraryOptions)
{
- LibraryOptions[path] = options;
+ _libraryOptions[path] = options;
var clone = JsonSerializer.Deserialize(JsonSerializer.SerializeToUtf8Bytes(options, _jsonOptions), _jsonOptions);
foreach (var mediaPath in clone.PathInfos)
@@ -139,15 +154,18 @@ namespace MediaBrowser.Controller.Entities
public static void OnCollectionFolderChange()
{
- lock (LibraryOptions)
+ lock (_libraryOptions)
{
- LibraryOptions.Clear();
+ _libraryOptions.Clear();
}
}
///
- /// Allow different display preferences for each collection folder.
+ /// Gets the display preferences id.
///
+ ///
+ /// Allow different display preferences for each collection folder.
+ ///
/// The display prefs id.
[JsonIgnore]
public override Guid DisplayPreferencesId => Id;
@@ -155,21 +173,20 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public override string[] PhysicalLocations => PhysicalLocationsList;
+ public string[] PhysicalLocationsList { get; set; }
+
+ public Guid[] PhysicalFolderIds { get; set; }
+
public override bool IsSaveLocalMetadataEnabled()
{
return true;
}
- public string[] PhysicalLocationsList { get; set; }
-
- public Guid[] PhysicalFolderIds { get; set; }
-
protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
{
return CreateResolveArgs(directoryService, true).FileSystemChildren;
}
- private bool _requiresRefresh;
public override bool RequiresRefresh()
{
var changed = base.RequiresRefresh() || _requiresRefresh;
@@ -201,9 +218,9 @@ namespace MediaBrowser.Controller.Entities
return changed;
}
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var changed = base.BeforeMetadataRefresh(replaceAllMetdata) || _requiresRefresh;
+ var changed = base.BeforeMetadataRefresh(replaceAllMetadata) || _requiresRefresh;
_requiresRefresh = false;
return changed;
}
@@ -312,13 +329,6 @@ namespace MediaBrowser.Controller.Entities
return Task.CompletedTask;
}
- ///
- /// Our children are actually just references to the ones in the physical root...
- ///
- /// The actual children.
- [JsonIgnore]
- public override IEnumerable Children => GetActualChildren();
-
public IEnumerable GetActualChildren()
{
return GetPhysicalFolders(true).SelectMany(c => c.Children);
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index a59f5c6e4b..29d837c143 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -37,6 +37,11 @@ namespace MediaBrowser.Controller.Entities
///
public class Folder : BaseItem
{
+ public Folder()
+ {
+ LinkedChildren = Array.Empty();
+ }
+
public static IUserViewManager UserViewManager { get; set; }
///
@@ -50,11 +55,6 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public DateTime? DateLastMediaAdded { get; set; }
- public Folder()
- {
- LinkedChildren = Array.Empty();
- }
-
[JsonIgnore]
public override bool SupportsThemeMedia => true;
@@ -86,6 +86,85 @@ namespace MediaBrowser.Controller.Entities
[JsonIgnore]
public virtual bool SupportsDateLastMediaAdded => false;
+ [JsonIgnore]
+ public override string FileNameWithoutExtension
+ {
+ get
+ {
+ if (IsFileProtocol)
+ {
+ return System.IO.Path.GetFileName(Path);
+ }
+
+ return null;
+ }
+ }
+
+ ///
+ /// Gets the actual children.
+ ///
+ /// The actual children.
+ [JsonIgnore]
+ public virtual IEnumerable Children => LoadChildren();
+
+ ///
+ /// Gets thread-safe access to all recursive children of this folder - without regard to user.
+ ///
+ /// The recursive children.
+ [JsonIgnore]
+ public IEnumerable RecursiveChildren => GetRecursiveChildren();
+
+ [JsonIgnore]
+ protected virtual bool SupportsShortcutChildren => false;
+
+ protected virtual bool FilterLinkedChildrenPerUser => false;
+
+ [JsonIgnore]
+ protected override bool SupportsOwnedItems => base.SupportsOwnedItems || SupportsShortcutChildren;
+
+ [JsonIgnore]
+ public virtual bool SupportsUserDataFromChildren
+ {
+ get
+ {
+ // These are just far too slow.
+ if (this is ICollectionFolder)
+ {
+ return false;
+ }
+
+ if (this is UserView)
+ {
+ return false;
+ }
+
+ if (this is UserRootFolder)
+ {
+ return false;
+ }
+
+ if (this is Channel)
+ {
+ return false;
+ }
+
+ if (SourceType != SourceType.Library)
+ {
+ return false;
+ }
+
+ if (this is IItemByName)
+ {
+ if (this is not IHasDualAccess hasDualAccess || hasDualAccess.IsAccessedByName)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
public override bool CanDelete()
{
if (IsRoot)
@@ -108,20 +187,6 @@ namespace MediaBrowser.Controller.Entities
return baseResult;
}
- [JsonIgnore]
- public override string FileNameWithoutExtension
- {
- get
- {
- if (IsFileProtocol)
- {
- return System.IO.Path.GetFileName(Path);
- }
-
- return null;
- }
- }
-
protected override bool IsAllowTagFilterEnforced()
{
if (this is ICollectionFolder)
@@ -137,9 +202,6 @@ namespace MediaBrowser.Controller.Entities
return true;
}
- [JsonIgnore]
- protected virtual bool SupportsShortcutChildren => false;
-
///
/// Adds the child.
///
@@ -169,20 +231,6 @@ namespace MediaBrowser.Controller.Entities
LibraryManager.CreateItem(item, this);
}
- ///
- /// Gets the actual children.
- ///
- /// The actual children.
- [JsonIgnore]
- public virtual IEnumerable Children => LoadChildren();
-
- ///
- /// thread-safe access to all recursive children of this folder - without regard to user.
- ///
- /// The recursive children.
- [JsonIgnore]
- public IEnumerable RecursiveChildren => GetRecursiveChildren();
-
public override bool IsVisible(User user)
{
if (this is ICollectionFolder && !(this is BasePluginFolder))
@@ -1428,8 +1476,6 @@ namespace MediaBrowser.Controller.Entities
return list;
}
- protected virtual bool FilterLinkedChildrenPerUser => false;
-
public bool ContainsLinkedChildByItemId(Guid itemId)
{
var linkedChildren = LinkedChildren;
@@ -1530,9 +1576,6 @@ namespace MediaBrowser.Controller.Entities
.Where(i => i.Item2 != null);
}
- [JsonIgnore]
- protected override bool SupportsOwnedItems => base.SupportsOwnedItems || SupportsShortcutChildren;
-
protected override async Task RefreshedOwnedItems(MetadataRefreshOptions options, List fileSystemChildren, CancellationToken cancellationToken)
{
var changesFound = false;
@@ -1696,51 +1739,6 @@ namespace MediaBrowser.Controller.Entities
return !IsPlayed(user);
}
- [JsonIgnore]
- public virtual bool SupportsUserDataFromChildren
- {
- get
- {
- // These are just far too slow.
- if (this is ICollectionFolder)
- {
- return false;
- }
-
- if (this is UserView)
- {
- return false;
- }
-
- if (this is UserRootFolder)
- {
- return false;
- }
-
- if (this is Channel)
- {
- return false;
- }
-
- if (SourceType != SourceType.Library)
- {
- return false;
- }
-
- var iItemByName = this as IItemByName;
- if (iItemByName != null)
- {
- var hasDualAccess = this as IHasDualAccess;
- if (hasDualAccess == null || hasDualAccess.IsAccessedByName)
- {
- return false;
- }
- }
-
- return true;
- }
- }
-
public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, DtoOptions fields)
{
if (!SupportsUserDataFromChildren)
diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs
index 310c0c9ec7..698643b446 100644
--- a/MediaBrowser.Controller/Entities/Genre.cs
+++ b/MediaBrowser.Controller/Entities/Genre.cs
@@ -16,6 +16,23 @@ namespace MediaBrowser.Controller.Entities
///
public class Genre : BaseItem, IItemByName
{
+ ///
+ /// Gets the folder containing the item.
+ /// If the item is a folder, it returns the folder itself.
+ ///
+ /// The containing folder path.
+ [JsonIgnore]
+ public override string ContainingFolderPath => Path;
+
+ [JsonIgnore]
+ public override bool IsDisplayedAsFolder => true;
+
+ [JsonIgnore]
+ public override bool SupportsAncestors => false;
+
+ [JsonIgnore]
+ public override bool SupportsPeople => false;
+
public override List GetUserDataKeys()
{
var list = base.GetUserDataKeys();
@@ -34,20 +51,6 @@ namespace MediaBrowser.Controller.Entities
return 1;
}
- ///
- /// Gets the folder containing the item.
- /// If the item is a folder, it returns the folder itself.
- ///
- /// The containing folder path.
- [JsonIgnore]
- public override string ContainingFolderPath => Path;
-
- [JsonIgnore]
- public override bool IsDisplayedAsFolder => true;
-
- [JsonIgnore]
- public override bool SupportsAncestors => false;
-
public override bool IsSaveLocalMetadataEnabled()
{
return true;
@@ -72,9 +75,6 @@ namespace MediaBrowser.Controller.Entities
return LibraryManager.GetItemList(query);
}
- [JsonIgnore]
- public override bool SupportsPeople => false;
-
public static string GetPath(string name)
{
return GetPath(name, true);
@@ -107,12 +107,10 @@ namespace MediaBrowser.Controller.Entities
return base.RequiresRefresh();
}
- ///
- /// This is called before any metadata refresh and returns true or false indicating if changes were made.
- ///
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ ///
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
var newPath = GetRebasedPath();
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
diff --git a/MediaBrowser.Controller/Entities/IHasSeries.cs b/MediaBrowser.Controller/Entities/IHasSeries.cs
index 64d769d5ba..5f774bbdee 100644
--- a/MediaBrowser.Controller/Entities/IHasSeries.cs
+++ b/MediaBrowser.Controller/Entities/IHasSeries.cs
@@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Entities
public interface IHasSeries
{
///
- /// Gets the name of the series.
+ /// Gets or sets the name of the series.
///
/// The name of the series.
string SeriesName { get; set; }
diff --git a/MediaBrowser.Controller/Entities/IHasShares.cs b/MediaBrowser.Controller/Entities/IHasShares.cs
new file mode 100644
index 0000000000..bdde744a37
--- /dev/null
+++ b/MediaBrowser.Controller/Entities/IHasShares.cs
@@ -0,0 +1,11 @@
+#nullable disable
+
+#pragma warning disable CS1591
+
+namespace MediaBrowser.Controller.Entities
+{
+ public interface IHasShares
+ {
+ Share[] Shares { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index c060210296..75fea365ba 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -1,5 +1,3 @@
-#nullable disable
-
#pragma warning disable CS1591
using System;
@@ -20,9 +18,9 @@ namespace MediaBrowser.Controller.Entities
public int? Limit { get; set; }
- public User User { get; set; }
+ public User? User { get; set; }
- public BaseItem SimilarTo { get; set; }
+ public BaseItem? SimilarTo { get; set; }
public bool? IsFolder { get; set; }
@@ -58,23 +56,23 @@ namespace MediaBrowser.Controller.Entities
public bool? CollapseBoxSetItems { get; set; }
- public string NameStartsWithOrGreater { get; set; }
+ public string? NameStartsWithOrGreater { get; set; }
- public string NameStartsWith { get; set; }
+ public string? NameStartsWith { get; set; }
- public string NameLessThan { get; set; }
+ public string? NameLessThan { get; set; }
- public string NameContains { get; set; }
+ public string? NameContains { get; set; }
- public string MinSortName { get; set; }
+ public string? MinSortName { get; set; }
- public string PresentationUniqueKey { get; set; }
+ public string? PresentationUniqueKey { get; set; }
- public string Path { get; set; }
+ public string? Path { get; set; }
- public string Name { get; set; }
+ public string? Name { get; set; }
- public string Person { get; set; }
+ public string? Person { get; set; }
public Guid[] PersonIds { get; set; }
@@ -82,7 +80,7 @@ namespace MediaBrowser.Controller.Entities
public Guid[] ExcludeItemIds { get; set; }
- public string AdjacentTo { get; set; }
+ public string? AdjacentTo { get; set; }
public string[] PersonTypes { get; set; }
@@ -182,13 +180,13 @@ namespace MediaBrowser.Controller.Entities
public Guid ParentId { get; set; }
- public string ParentType { get; set; }
+ public string? ParentType { get; set; }
public Guid[] AncestorIds { get; set; }
public Guid[] TopParentIds { get; set; }
- public BaseItem Parent
+ public BaseItem? Parent
{
set
{
@@ -213,9 +211,9 @@ namespace MediaBrowser.Controller.Entities
public SeriesStatus[] SeriesStatuses { get; set; }
- public string ExternalSeriesId { get; set; }
+ public string? ExternalSeriesId { get; set; }
- public string ExternalId { get; set; }
+ public string? ExternalId { get; set; }
public Guid[] AlbumIds { get; set; }
@@ -223,9 +221,9 @@ namespace MediaBrowser.Controller.Entities
public Guid[] ExcludeArtistIds { get; set; }
- public string AncestorWithPresentationUniqueKey { get; set; }
+ public string? AncestorWithPresentationUniqueKey { get; set; }
- public string SeriesPresentationUniqueKey { get; set; }
+ public string? SeriesPresentationUniqueKey { get; set; }
public bool GroupByPresentationUniqueKey { get; set; }
@@ -235,7 +233,7 @@ namespace MediaBrowser.Controller.Entities
public bool ForceDirect { get; set; }
- public Dictionary ExcludeProviderIds { get; set; }
+ public Dictionary? ExcludeProviderIds { get; set; }
public bool EnableGroupByMetadataKey { get; set; }
@@ -253,13 +251,13 @@ namespace MediaBrowser.Controller.Entities
public int MinSimilarityScore { get; set; }
- public string HasNoAudioTrackWithLanguage { get; set; }
+ public string? HasNoAudioTrackWithLanguage { get; set; }
- public string HasNoInternalSubtitleTrackWithLanguage { get; set; }
+ public string? HasNoInternalSubtitleTrackWithLanguage { get; set; }
- public string HasNoExternalSubtitleTrackWithLanguage { get; set; }
+ public string? HasNoExternalSubtitleTrackWithLanguage { get; set; }
- public string HasNoSubtitleTrackWithLanguage { get; set; }
+ public string? HasNoSubtitleTrackWithLanguage { get; set; }
public bool? IsDeadArtist { get; set; }
@@ -283,12 +281,10 @@ namespace MediaBrowser.Controller.Entities
ExcludeInheritedTags = Array.Empty();
ExcludeItemIds = Array.Empty();
ExcludeItemTypes = Array.Empty();
- ExcludeProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase);
ExcludeTags = Array.Empty();
GenreIds = Array.Empty();
Genres = Array.Empty();
GroupByPresentationUniqueKey = true;
- HasAnyProviderId = new Dictionary(StringComparer.OrdinalIgnoreCase);
ImageTypes = Array.Empty();
IncludeItemTypes = Array.Empty();
ItemIds = Array.Empty();
@@ -309,32 +305,33 @@ namespace MediaBrowser.Controller.Entities
Years = Array.Empty();
}
- public InternalItemsQuery(User user)
+ public InternalItemsQuery(User? user)
: this()
{
- SetUser(user);
+ if (user != null)
+ {
+ SetUser(user);
+ }
}
public void SetUser(User user)
{
- if (user != null)
+ MaxParentalRating = user.MaxParentalAgeRating;
+
+ if (MaxParentalRating.HasValue)
{
- MaxParentalRating = user.MaxParentalAgeRating;
-
- if (MaxParentalRating.HasValue)
- {
- BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems)
- .Where(i => i != UnratedItem.Other.ToString())
- .Select(e => Enum.Parse(e, true)).ToArray();
- }
-
- ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags);
-
- User = user;
+ string other = UnratedItem.Other.ToString();
+ BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems)
+ .Where(i => i != other)
+ .Select(e => Enum.Parse(e, true)).ToArray();
}
+
+ ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags);
+
+ User = user;
}
- public Dictionary HasAnyProviderId { get; set; }
+ public Dictionary? HasAnyProviderId { get; set; }
public Guid[] AlbumArtistIds { get; set; }
@@ -356,8 +353,8 @@ namespace MediaBrowser.Controller.Entities
public int? MinWidth { get; set; }
- public string SearchTerm { get; set; }
+ public string? SearchTerm { get; set; }
- public string SeriesTimerId { get; set; }
+ public string? SeriesTimerId { get; set; }
}
}
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index 64d60c2e90..b54bbf5eb9 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -144,9 +144,9 @@ namespace MediaBrowser.Controller.Entities.Movies
}
///
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
if (!ProductionYear.HasValue)
{
diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs
index 4e622ba01b..237ad5198c 100644
--- a/MediaBrowser.Controller/Entities/MusicVideo.cs
+++ b/MediaBrowser.Controller/Entities/MusicVideo.cs
@@ -36,9 +36,9 @@ namespace MediaBrowser.Controller.Entities
return info;
}
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
if (!ProductionYear.HasValue)
{
diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs
index d9ff55362c..913f76d3bd 100644
--- a/MediaBrowser.Controller/Entities/Person.cs
+++ b/MediaBrowser.Controller/Entities/Person.cs
@@ -50,7 +50,7 @@ namespace MediaBrowser.Controller.Entities
}
///
- /// Returns the folder containing the item.
+ /// Gets the folder containing the item.
/// If the item is a folder, it returns the folder itself.
///
/// The containing folder path.
@@ -67,6 +67,9 @@ namespace MediaBrowser.Controller.Entities
return true;
}
+ ///
+ /// Gets a value indicating whether to enable alpha numeric sorting.
+ ///
[JsonIgnore]
public override bool EnableAlphaNumericSorting => false;
@@ -126,9 +129,9 @@ namespace MediaBrowser.Controller.Entities
///
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
///
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
var newPath = GetRebasedPath();
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
diff --git a/MediaBrowser.Controller/Entities/Share.cs b/MediaBrowser.Controller/Entities/Share.cs
index 7e4ec18307..64f446eef2 100644
--- a/MediaBrowser.Controller/Entities/Share.cs
+++ b/MediaBrowser.Controller/Entities/Share.cs
@@ -4,11 +4,6 @@
namespace MediaBrowser.Controller.Entities
{
- public interface IHasShares
- {
- Share[] Shares { get; set; }
- }
-
public class Share
{
public string UserId { get; set; }
diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs
index ae1d10447b..6fd0a6c6c3 100644
--- a/MediaBrowser.Controller/Entities/Studio.cs
+++ b/MediaBrowser.Controller/Entities/Studio.cs
@@ -29,7 +29,7 @@ namespace MediaBrowser.Controller.Entities
}
///
- /// Returns the folder containing the item.
+ /// Gets the folder containing the item.
/// If the item is a folder, it returns the folder itself.
///
/// The containing folder path.
@@ -105,9 +105,9 @@ namespace MediaBrowser.Controller.Entities
///
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
///
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
var newPath = GetRebasedPath();
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs
index 2724bd9b3d..1b4cc7a78b 100644
--- a/MediaBrowser.Controller/Entities/TV/Episode.cs
+++ b/MediaBrowser.Controller/Entities/TV/Episode.cs
@@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.Entities.TV
public IReadOnlyList RemoteTrailerIds { get; set; }
///
- /// Gets the season in which it aired.
+ /// Gets or sets the season in which it aired.
///
/// The aired season.
public int? AirsBeforeSeasonNumber { get; set; }
@@ -44,7 +44,7 @@ namespace MediaBrowser.Controller.Entities.TV
public int? AirsBeforeEpisodeNumber { get; set; }
///
- /// This is the ending episode number for double episodes.
+ /// Gets or sets the ending episode number for double episodes.
///
/// The index number.
public int? IndexNumberEnd { get; set; }
@@ -116,7 +116,7 @@ namespace MediaBrowser.Controller.Entities.TV
}
///
- /// This Episode's Series Instance.
+ /// Gets the Episode's Series Instance.
///
/// The series.
[JsonIgnore]
@@ -261,6 +261,7 @@ namespace MediaBrowser.Controller.Entities.TV
[JsonIgnore]
public Guid SeasonId { get; set; }
+
[JsonIgnore]
public Guid SeriesId { get; set; }
@@ -318,9 +319,9 @@ namespace MediaBrowser.Controller.Entities.TV
return id;
}
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
if (!IsLocked)
{
@@ -328,7 +329,7 @@ namespace MediaBrowser.Controller.Entities.TV
{
try
{
- if (LibraryManager.FillMissingEpisodeNumbersFromPath(this, replaceAllMetdata))
+ if (LibraryManager.FillMissingEpisodeNumbersFromPath(this, replaceAllMetadata))
{
hasChanges = true;
}
diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs
index ad3e0fe8d6..5e2053dcc7 100644
--- a/MediaBrowser.Controller/Entities/TV/Season.cs
+++ b/MediaBrowser.Controller/Entities/TV/Season.cs
@@ -81,7 +81,7 @@ namespace MediaBrowser.Controller.Entities.TV
}
///
- /// This Episode's Series Instance.
+ /// Gets this Episode's Series Instance.
///
/// The series.
[JsonIgnore]
@@ -242,9 +242,9 @@ namespace MediaBrowser.Controller.Entities.TV
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
///
/// true if XXXX, false otherwise.
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
{
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index ded825abc0..44d07b4a48 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -59,8 +59,11 @@ namespace MediaBrowser.Controller.Entities.TV
public IReadOnlyList RemoteTrailerIds { get; set; }
///
- /// airdate, dvd or absolute.
+ /// Gets or sets the display order.
///
+ ///
+ /// Valid options are airdate, dvd or absolute.
+ ///
public string DisplayOrder { get; set; }
///
diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs
index b086b5906b..732b45521b 100644
--- a/MediaBrowser.Controller/Entities/Trailer.cs
+++ b/MediaBrowser.Controller/Entities/Trailer.cs
@@ -45,9 +45,9 @@ namespace MediaBrowser.Controller.Entities
return info;
}
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
if (!ProductionYear.HasValue)
{
diff --git a/MediaBrowser.Controller/Entities/UserItemData.cs b/MediaBrowser.Controller/Entities/UserItemData.cs
index f60359c014..6ab2116d73 100644
--- a/MediaBrowser.Controller/Entities/UserItemData.cs
+++ b/MediaBrowser.Controller/Entities/UserItemData.cs
@@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Entities
public const double MinLikeValue = 6.5;
///
- /// This is an interpreted property to indicate likes or dislikes
+ /// Gets or sets a value indicating whether the item is liked or not.
/// This should never be serialized.
///
/// null if [likes] contains no value, true if [likes]; otherwise, false.
diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs
index e492740ed0..2dea2e50bf 100644
--- a/MediaBrowser.Controller/Entities/UserRootFolder.cs
+++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs
@@ -23,6 +23,7 @@ namespace MediaBrowser.Controller.Entities
{
private List _childrenIds = null;
private readonly object _childIdsLock = new object();
+
protected override List LoadChildren()
{
lock (_childIdsLock)
@@ -87,10 +88,10 @@ namespace MediaBrowser.Controller.Entities
return list;
}
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
ClearCache();
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
if (string.Equals("default", Name, StringComparison.OrdinalIgnoreCase))
{
diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs
index 0dfde27667..1e6c01bf8b 100644
--- a/MediaBrowser.Controller/Entities/UserView.cs
+++ b/MediaBrowser.Controller/Entities/UserView.cs
@@ -15,13 +15,19 @@ namespace MediaBrowser.Controller.Entities
{
public class UserView : Folder, IHasCollectionType
{
- ///
+ ///
+ /// Gets or sets the view type.
+ ///
public string ViewType { get; set; }
- ///
+ ///
+ /// Gets or sets the display parent id.
+ ///
public new Guid DisplayParentId { get; set; }
- ///
+ ///
+ /// Gets or sets the user id.
+ ///
public Guid? UserId { get; set; }
public static ITVSeriesManager TVSeriesManager;
@@ -110,10 +116,10 @@ namespace MediaBrowser.Controller.Entities
return GetChildren(user, false);
}
- private static string[] UserSpecificViewTypes = new string[]
- {
- Model.Entities.CollectionType.Playlists
- };
+ private static readonly string[] UserSpecificViewTypes = new string[]
+ {
+ Model.Entities.CollectionType.Playlists
+ };
public static bool IsUserSpecific(Folder folder)
{
diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs
index 4d84a151a7..f268bc939e 100644
--- a/MediaBrowser.Controller/Entities/Year.cs
+++ b/MediaBrowser.Controller/Entities/Year.cs
@@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Entities
}
///
- /// Returns the folder containing the item.
+ /// Gets the folder containing the item.
/// If the item is a folder, it returns the folder itself.
///
/// The containing folder path.
@@ -112,11 +112,13 @@ namespace MediaBrowser.Controller.Entities
}
///
- /// This is called before any metadata refresh and returns true or false indicating if changes were made.
+ /// This is called before any metadata refresh and returns true if changes were made.
///
- public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
+ /// Whether to replace all metadata.
+ /// true if the item has change, else false.
+ public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
{
- var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
+ var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
var newPath = GetRebasedPath();
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
diff --git a/MediaBrowser.Controller/Extensions/StringExtensions.cs b/MediaBrowser.Controller/Extensions/StringExtensions.cs
index 8441a31713..48bd9522a8 100644
--- a/MediaBrowser.Controller/Extensions/StringExtensions.cs
+++ b/MediaBrowser.Controller/Extensions/StringExtensions.cs
@@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.Extensions
{
// will throw if input contains invalid unicode chars
// https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
- text = Regex.Replace(text, "([\ud800-\udbff](?![\udc00-\udfff]))|((?
/// Resolves a set of files into a list of BaseItem.
///
+ /// The list of tiles.
+ /// Instance of the interface.
+ /// The parent folder.
+ /// The library options.
+ /// The collection type.
+ /// The items resolved from the paths.
IEnumerable ResolvePaths(
IEnumerable files,
IDirectoryService directoryService,
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
index 51e56f4b5e..1a893fc2d0 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
@@ -148,7 +148,7 @@ namespace MediaBrowser.Controller.LiveTv
public bool IsNews { get; set; }
///
- /// Gets or sets a value indicating whether this instance is kids.
+ /// Gets a value indicating whether this instance is kids.
///
/// true if this instance is kids; otherwise, false.
[JsonIgnore]
diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
index a66bec11cb..e2adec0009 100644
--- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
+++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
@@ -71,7 +71,7 @@ namespace MediaBrowser.Controller.LiveTv
public override SourceType SourceType => SourceType.LiveTV;
///
- /// The start date of the program, in UTC.
+ /// Gets or sets start date of the program, in UTC.
///
[JsonIgnore]
public DateTime StartDate { get; set; }
diff --git a/MediaBrowser.Controller/LiveTv/TimerInfo.cs b/MediaBrowser.Controller/LiveTv/TimerInfo.cs
index e54dc967ca..1a2e8acb31 100644
--- a/MediaBrowser.Controller/LiveTv/TimerInfo.cs
+++ b/MediaBrowser.Controller/LiveTv/TimerInfo.cs
@@ -28,18 +28,17 @@ namespace MediaBrowser.Controller.LiveTv
public string[] Tags { get; set; }
///
- /// Id of the recording.
+ /// Gets or sets the id of the recording.
///
public string Id { get; set; }
///
/// Gets or sets the series timer identifier.
///
- /// The series timer identifier.
public string SeriesTimerId { get; set; }
///
- /// ChannelId of the recording.
+ /// Gets or sets the channelId of the recording.
///
public string ChannelId { get; set; }
@@ -52,24 +51,24 @@ namespace MediaBrowser.Controller.LiveTv
public string ShowId { get; set; }
///
- /// Name of the recording.
+ /// Gets or sets the name of the recording.
///
public string Name { get; set; }
///
- /// Description of the recording.
+ /// Gets or sets the description of the recording.
///
public string Overview { get; set; }
public string SeriesId { get; set; }
///
- /// The start date of the recording, in UTC.
+ /// Gets or sets the start date of the recording, in UTC.
///
public DateTime StartDate { get; set; }
///
- /// The end date of the recording, in UTC.
+ /// Gets or sets the end date of the recording, in UTC.
///
public DateTime EndDate { get; set; }
@@ -133,7 +132,7 @@ namespace MediaBrowser.Controller.LiveTv
public bool IsSeries { get; set; }
///
- /// Gets or sets a value indicating whether this instance is live.
+ /// Gets a value indicating whether this instance is live.
///
/// true if this instance is live; otherwise, false.
[JsonIgnore]
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index 97cb8d63ba..feb5883e58 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -596,7 +596,8 @@ namespace MediaBrowser.Controller.MediaEncoding
&& string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
&& isNvdecDecoder)
{
- arg.Append("-hwaccel_output_format cuda -autorotate 0 ");
+ // Fix for 'No decoder surfaces left' error. https://trac.ffmpeg.org/ticket/7562
+ arg.Append("-hwaccel_output_format cuda -extra_hw_frames 3 -autorotate 0 ");
}
if (state.IsVideoRequest
@@ -1070,7 +1071,6 @@ namespace MediaBrowser.Controller.MediaEncoding
else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc)
|| string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_nvenc)
{
- // following preset will be deprecated in ffmpeg 4.4, use p1~p7 instead.
switch (encodingOptions.EncoderPreset)
{
case "veryslow":
@@ -1251,7 +1251,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase)
- && profile.Contains("constrainedbaseline", StringComparison.OrdinalIgnoreCase))
+ && profile.Contains("baseline", StringComparison.OrdinalIgnoreCase))
{
profile = "constrained_baseline";
}
@@ -2933,6 +2933,7 @@ namespace MediaBrowser.Controller.MediaEncoding
return threads;
}
+
#nullable disable
public void TryStreamCopy(EncodingJobInfo state)
{
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
index 1e13382b75..bc34785eeb 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs
@@ -430,7 +430,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
///
- /// Predicts the audio sample rate that will be in the output stream.
+ /// Gets the target video level.
///
public double? TargetVideoLevel
{
@@ -453,7 +453,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
///
- /// Predicts the audio sample rate that will be in the output stream.
+ /// Gets the target video bit depth.
///
public int? TargetVideoBitDepth
{
@@ -488,7 +488,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
///
- /// Predicts the audio sample rate that will be in the output stream.
+ /// Gets the target framerate.
///
public float? TargetFramerate
{
@@ -520,7 +520,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
///
- /// Predicts the audio sample rate that will be in the output stream.
+ /// Gets the target packet length.
///
public int? TargetPacketLength
{
@@ -536,7 +536,7 @@ namespace MediaBrowser.Controller.MediaEncoding
}
///
- /// Predicts the audio sample rate that will be in the output stream.
+ /// Gets the target video profile.
///
public string TargetVideoProfile
{
@@ -700,25 +700,4 @@ namespace MediaBrowser.Controller.MediaEncoding
Progress.Report(percentComplete.Value);
}
}
-
- ///
- /// Enum TranscodingJobType.
- ///
- public enum TranscodingJobType
- {
- ///
- /// The progressive.
- ///
- Progressive,
-
- ///
- /// The HLS.
- ///
- Hls,
-
- ///
- /// The dash.
- ///
- Dash
- }
}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
index 88de5b2925..61f3bc7711 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobOptions.cs
@@ -4,59 +4,10 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using MediaBrowser.Model.Dlna;
namespace MediaBrowser.Controller.MediaEncoding
{
- public class EncodingJobOptions : BaseEncodingJobOptions
- {
- public string OutputDirectory { get; set; }
-
- public string ItemId { get; set; }
-
- public string TempDirectory { get; set; }
-
- public bool ReadInputAtNativeFramerate { get; set; }
-
- ///
- /// Gets a value indicating whether this instance has fixed resolution.
- ///
- /// true if this instance has fixed resolution; otherwise, false.
- public bool HasFixedResolution => Width.HasValue || Height.HasValue;
-
- public DeviceProfile DeviceProfile { get; set; }
-
- public EncodingJobOptions(StreamInfo info, DeviceProfile deviceProfile)
- {
- Container = info.Container;
- StartTimeTicks = info.StartPositionTicks;
- MaxWidth = info.MaxWidth;
- MaxHeight = info.MaxHeight;
- MaxFramerate = info.MaxFramerate;
- Id = info.ItemId;
- MediaSourceId = info.MediaSourceId;
- AudioCodec = info.TargetAudioCodec.FirstOrDefault();
- MaxAudioChannels = info.GlobalMaxAudioChannels;
- AudioBitRate = info.AudioBitrate;
- AudioSampleRate = info.TargetAudioSampleRate;
- DeviceProfile = deviceProfile;
- VideoCodec = info.TargetVideoCodec.FirstOrDefault();
- VideoBitRate = info.VideoBitrate;
- AudioStreamIndex = info.AudioStreamIndex;
- SubtitleMethod = info.SubtitleDeliveryMethod;
- Context = info.Context;
- TranscodingMaxAudioChannels = info.TranscodingMaxAudioChannels;
-
- if (info.SubtitleDeliveryMethod != SubtitleDeliveryMethod.External)
- {
- SubtitleStreamIndex = info.SubtitleStreamIndex;
- }
-
- StreamOptions = info.StreamOptions;
- }
- }
-
// For now until api and media encoding layers are unified
public class BaseEncodingJobOptions
{
diff --git a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
index d3260280a5..76a9fd7c74 100644
--- a/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
+++ b/MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs
@@ -20,7 +20,7 @@ namespace MediaBrowser.Controller.MediaEncoding
public interface IMediaEncoder : ITranscoderSupport
{
///
- /// The location of the discovered FFmpeg tool.
+ /// Gets location of the discovered FFmpeg tool.
///
FFmpegLocation EncoderLocation { get; }
diff --git a/MediaBrowser.Controller/MediaEncoding/TranscodingJobType.cs b/MediaBrowser.Controller/MediaEncoding/TranscodingJobType.cs
new file mode 100644
index 0000000000..66b6283719
--- /dev/null
+++ b/MediaBrowser.Controller/MediaEncoding/TranscodingJobType.cs
@@ -0,0 +1,23 @@
+namespace MediaBrowser.Controller.MediaEncoding
+{
+ ///
+ /// Enum TranscodingJobType.
+ ///
+ public enum TranscodingJobType
+ {
+ ///
+ /// The progressive.
+ ///
+ Progressive,
+
+ ///
+ /// The HLS.
+ ///
+ Hls,
+
+ ///
+ /// The dash.
+ ///
+ Dash
+ }
+}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs
index a80c116439..bb9e5da1e3 100644
--- a/MediaBrowser.Controller/Playlists/Playlist.cs
+++ b/MediaBrowser.Controller/Playlists/Playlist.cs
@@ -22,14 +22,14 @@ namespace MediaBrowser.Controller.Playlists
{
public class Playlist : Folder, IHasShares
{
- public static string[] SupportedExtensions =
- {
- ".m3u",
- ".m3u8",
- ".pls",
- ".wpl",
- ".zpl"
- };
+ public static readonly IReadOnlyList SupportedExtensions = new[]
+ {
+ ".m3u",
+ ".m3u8",
+ ".pls",
+ ".wpl",
+ ".zpl"
+ };
public Guid OwnerUserId { get; set; }
diff --git a/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs b/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs
new file mode 100644
index 0000000000..ea966c2827
--- /dev/null
+++ b/MediaBrowser.Controller/Plugins/IRunBeforeStartup.cs
@@ -0,0 +1,9 @@
+namespace MediaBrowser.Controller.Plugins
+{
+ ///
+ /// Indicates that a should be invoked as a pre-startup task.
+ ///
+ public interface IRunBeforeStartup
+ {
+ }
+}
\ No newline at end of file
diff --git a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
index b44e2531e1..6024661e15 100644
--- a/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
+++ b/MediaBrowser.Controller/Plugins/IServerEntryPoint.cs
@@ -14,13 +14,7 @@ namespace MediaBrowser.Controller.Plugins
///
/// Run the initialization for this module. This method is invoked at application start.
///
+ /// A representing the asynchronous operation.
Task RunAsync();
}
-
- ///
- /// Indicates that a should be invoked as a pre-startup task.
- ///
- public interface IRunBeforeStartup
- {
- }
}
diff --git a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
index 1152504669..2cf5367793 100644
--- a/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
+++ b/MediaBrowser.Controller/Providers/MetadataRefreshOptions.cs
@@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Providers
///
/// Gets or sets a value indicating whether all existing data should be overwritten with new data from providers
- /// when paired with MetadataRefreshMode=FullRefresh
+ /// when paired with MetadataRefreshMode=FullRefresh.
///
public bool ReplaceAllMetadata { get; set; }
diff --git a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs b/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs
deleted file mode 100644
index 3d3e44da01..0000000000
--- a/MediaBrowser.Controller/Sync/IHasDynamicAccess.cs
+++ /dev/null
@@ -1,22 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.Sync;
-
-namespace MediaBrowser.Controller.Sync
-{
- public interface IHasDynamicAccess
- {
- ///
- /// Gets the synced file information.
- ///
- /// The identifier.
- /// The target.
- /// The cancellation token.
- /// Task<SyncedFileInfo>.
- Task GetSyncedFileInfo(string id, SyncTarget target, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs b/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs
deleted file mode 100644
index b2c53365c4..0000000000
--- a/MediaBrowser.Controller/Sync/IRemoteSyncProvider.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace MediaBrowser.Controller.Sync
-{
- ///
- /// A marker interface.
- ///
- public interface IRemoteSyncProvider
- {
- }
-}
diff --git a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs b/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
deleted file mode 100644
index 3891ac0a66..0000000000
--- a/MediaBrowser.Controller/Sync/IServerSyncProvider.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System;
-using System.IO;
-using System.Threading;
-using System.Threading.Tasks;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Querying;
-using MediaBrowser.Model.Sync;
-
-namespace MediaBrowser.Controller.Sync
-{
- public interface IServerSyncProvider : ISyncProvider
- {
- ///
- /// Transfers the file.
- ///
- Task SendFile(SyncJob syncJob, string originalMediaPath, Stream inputStream, bool isMedia, string[] outputPathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken);
-
- Task> GetFiles(string[] directoryPathParts, SyncTarget target, CancellationToken cancellationToken);
- }
-
- public interface ISupportsDirectCopy
- {
- ///
- /// Sends the file.
- ///
- Task SendFile(SyncJob syncJob, string originalMediaPath, string inputPath, bool isMedia, string[] outputPathParts, SyncTarget target, IProgress progress, CancellationToken cancellationToken);
- }
-}
diff --git a/MediaBrowser.Controller/Sync/ISyncProvider.cs b/MediaBrowser.Controller/Sync/ISyncProvider.cs
deleted file mode 100644
index ea20014c79..0000000000
--- a/MediaBrowser.Controller/Sync/ISyncProvider.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using MediaBrowser.Model.Sync;
-
-namespace MediaBrowser.Controller.Sync
-{
- public interface ISyncProvider
- {
- ///
- /// Gets the name.
- ///
- /// The name.
- string Name { get; }
-
- ///
- /// Gets the synchronize targets.
- ///
- /// The user identifier.
- /// IEnumerable<SyncTarget>.
- List GetSyncTargets(string userId);
-
- ///
- /// Gets all synchronize targets.
- ///
- /// IEnumerable<SyncTarget>.
- List GetAllSyncTargets();
- }
-}
diff --git a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs b/MediaBrowser.Controller/Sync/SyncedFileInfo.cs
deleted file mode 100644
index 7eac52299b..0000000000
--- a/MediaBrowser.Controller/Sync/SyncedFileInfo.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-#nullable disable
-
-#pragma warning disable CS1591
-
-using System.Collections.Generic;
-using MediaBrowser.Model.MediaInfo;
-
-namespace MediaBrowser.Controller.Sync
-{
- public class SyncedFileInfo
- {
- public SyncedFileInfo()
- {
- RequiredHttpHeaders = new Dictionary();
- }
-
- ///
- /// Gets or sets the path.
- ///
- /// The path.
- public string Path { get; set; }
-
- public string[] PathParts { get; set; }
-
- ///
- /// Gets or sets the protocol.
- ///
- /// The protocol.
- public MediaProtocol Protocol { get; set; }
-
- ///
- /// Gets or sets the required HTTP headers.
- ///
- /// The required HTTP headers.
- public Dictionary RequiredHttpHeaders { get; set; }
-
- ///
- /// Gets or sets the identifier.
- ///
- /// The identifier.
- public string Id { get; set; }
- }
-}
diff --git a/MediaBrowser.Controller/TV/ITVSeriesManager.cs b/MediaBrowser.Controller/TV/ITVSeriesManager.cs
index 291dea04ef..e066c03fdc 100644
--- a/MediaBrowser.Controller/TV/ITVSeriesManager.cs
+++ b/MediaBrowser.Controller/TV/ITVSeriesManager.cs
@@ -6,16 +6,26 @@ using MediaBrowser.Model.Querying;
namespace MediaBrowser.Controller.TV
{
+ ///
+ /// The TV Series manager.
+ ///
public interface ITVSeriesManager
{
///
/// Gets the next up.
///
+ /// The next up query.
+ /// The dto options.
+ /// The next up items.
QueryResult GetNextUp(NextUpQuery query, DtoOptions options);
///
/// Gets the next up.
///
+ /// The next up request.
+ /// The list of parent folders.
+ /// The dto options.
+ /// The next up items.
QueryResult GetNextUp(NextUpQuery request, BaseItem[] parentsFolders, DtoOptions options);
}
}
diff --git a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
index 7ad8c24e89..31475e22f4 100644
--- a/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
+++ b/MediaBrowser.LocalMetadata/Images/LocalImageProvider.cs
@@ -466,7 +466,7 @@ namespace MediaBrowser.LocalMetadata.Images
return added;
}
- private bool AddImage(IEnumerable files, List images, string name, ImageType type)
+ private bool AddImage(List files, List images, string name, ImageType type)
{
var image = GetImage(files, name);
@@ -484,9 +484,20 @@ namespace MediaBrowser.LocalMetadata.Images
return false;
}
- private FileSystemMetadata? GetImage(IEnumerable files, string name)
+ private static FileSystemMetadata? GetImage(IReadOnlyList files, string name)
{
- return files.FirstOrDefault(i => !i.IsDirectory && string.Equals(name, _fileSystem.GetFileNameWithoutExtension(i), StringComparison.OrdinalIgnoreCase) && i.Length > 0);
+ for (var i = 0; i < files.Count; i++)
+ {
+ var file = files[i];
+ if (!file.IsDirectory
+ && file.Length > 0
+ && Path.GetFileNameWithoutExtension(file.FullName.AsSpan()).Equals(name, StringComparison.OrdinalIgnoreCase))
+ {
+ return file;
+ }
+ }
+
+ return null;
}
}
}
diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
index e9f999c6d0..b086ef07bb 100644
--- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
+++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs
@@ -15,17 +15,6 @@ namespace MediaBrowser.Providers.MediaInfo
{
private readonly ILocalizationManager _localization;
- private static readonly HashSet SubtitleExtensions = new HashSet(StringComparer.OrdinalIgnoreCase)
- {
- ".srt",
- ".ssa",
- ".ass",
- ".sub",
- ".smi",
- ".sami",
- ".vtt"
- };
-
public SubtitleResolver(ILocalizationManager localization)
{
_localization = localization;
@@ -88,6 +77,115 @@ namespace MediaBrowser.Providers.MediaInfo
return list;
}
+ public void AddExternalSubtitleStreams(
+ List streams,
+ string videoPath,
+ int startIndex,
+ string[] files)
+ {
+ var videoFileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(videoPath);
+
+ foreach (var fullName in files)
+ {
+ var extension = Path.GetExtension(fullName.AsSpan());
+ if (!IsSubtitleExtension(extension))
+ {
+ continue;
+ }
+
+ var fileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(fullName);
+
+ MediaStream mediaStream;
+
+ // The subtitle filename must either be equal to the video filename or start with the video filename followed by a dot
+ if (videoFileNameWithoutExtension.Equals(fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
+ {
+ mediaStream = new MediaStream
+ {
+ Index = startIndex++,
+ Type = MediaStreamType.Subtitle,
+ IsExternal = true,
+ Path = fullName
+ };
+ }
+ else if (fileNameWithoutExtension.Length > videoFileNameWithoutExtension.Length
+ && fileNameWithoutExtension[videoFileNameWithoutExtension.Length] == '.'
+ && fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
+ {
+ var isForced = fullName.Contains(".forced.", StringComparison.OrdinalIgnoreCase)
+ || fullName.Contains(".foreign.", StringComparison.OrdinalIgnoreCase);
+
+ var isDefault = fullName.Contains(".default.", StringComparison.OrdinalIgnoreCase);
+
+ // Support xbmc naming conventions - 300.spanish.srt
+ var languageSpan = fileNameWithoutExtension;
+ while (languageSpan.Length > 0)
+ {
+ var lastDot = languageSpan.LastIndexOf('.');
+ var currentSlice = languageSpan[lastDot..];
+ if (currentSlice.Equals(".default", StringComparison.OrdinalIgnoreCase)
+ || currentSlice.Equals(".forced", StringComparison.OrdinalIgnoreCase)
+ || currentSlice.Equals(".foreign", StringComparison.OrdinalIgnoreCase))
+ {
+ languageSpan = languageSpan[..lastDot];
+ continue;
+ }
+
+ languageSpan = languageSpan[(lastDot + 1)..];
+ break;
+ }
+
+ var language = languageSpan.ToString();
+ // Try to translate to three character code
+ // Be flexible and check against both the full and three character versions
+ var culture = _localization.FindLanguageInfo(language);
+
+ if (culture != null)
+ {
+ language = culture.ThreeLetterISOLanguageName;
+ }
+
+ mediaStream = new MediaStream
+ {
+ Index = startIndex++,
+ Type = MediaStreamType.Subtitle,
+ IsExternal = true,
+ Path = fullName,
+ Language = language,
+ IsForced = isForced,
+ IsDefault = isDefault
+ };
+ }
+ else
+ {
+ continue;
+ }
+
+ mediaStream.Codec = extension.TrimStart('.').ToString().ToLowerInvariant();
+
+ streams.Add(mediaStream);
+ }
+ }
+
+ private static bool IsSubtitleExtension(ReadOnlySpan extension)
+ {
+ return extension.Equals(".srt", StringComparison.OrdinalIgnoreCase)
+ || extension.Equals(".ssa", StringComparison.OrdinalIgnoreCase)
+ || extension.Equals(".ass", StringComparison.OrdinalIgnoreCase)
+ || extension.Equals(".sub", StringComparison.OrdinalIgnoreCase)
+ || extension.Equals(".vtt", StringComparison.OrdinalIgnoreCase)
+ || extension.Equals(".smi", StringComparison.OrdinalIgnoreCase)
+ || extension.Equals(".sami", StringComparison.OrdinalIgnoreCase);
+ }
+
+ private static ReadOnlySpan NormalizeFilenameForSubtitleComparison(string filename)
+ {
+ // Try to account for sloppy file naming
+ filename = filename.Replace("_", string.Empty, StringComparison.Ordinal);
+ filename = filename.Replace(" ", string.Empty, StringComparison.Ordinal);
+ return Path.GetFileNameWithoutExtension(filename.AsSpan());
+ }
+
private void AddExternalSubtitleStreams(
List streams,
string folder,
@@ -100,104 +198,5 @@ namespace MediaBrowser.Providers.MediaInfo
AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
}
-
- public void AddExternalSubtitleStreams(
- List streams,
- string videoPath,
- int startIndex,
- string[] files)
- {
- var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(videoPath);
- videoFileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(videoFileNameWithoutExtension);
-
- foreach (var fullName in files)
- {
- var extension = Path.GetExtension(fullName);
-
- if (!SubtitleExtensions.Contains(extension))
- {
- continue;
- }
-
- var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName);
- fileNameWithoutExtension = NormalizeFilenameForSubtitleComparison(fileNameWithoutExtension);
-
- if (!string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase) &&
- !fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase))
- {
- continue;
- }
-
- var codec = Path.GetExtension(fullName).ToLowerInvariant().TrimStart('.');
-
- if (string.Equals(codec, "txt", StringComparison.OrdinalIgnoreCase))
- {
- codec = "srt";
- }
-
- // If the subtitle file matches the video file name
- if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase))
- {
- streams.Add(new MediaStream
- {
- Index = startIndex++,
- Type = MediaStreamType.Subtitle,
- IsExternal = true,
- Path = fullName,
- Codec = codec
- });
- }
- else if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase))
- {
- var isForced = fullName.IndexOf(".forced.", StringComparison.OrdinalIgnoreCase) != -1 ||
- fullName.IndexOf(".foreign.", StringComparison.OrdinalIgnoreCase) != -1;
-
- var isDefault = fullName.IndexOf(".default.", StringComparison.OrdinalIgnoreCase) != -1;
-
- // Support xbmc naming conventions - 300.spanish.srt
- var language = fileNameWithoutExtension
- .Replace(".forced", string.Empty, StringComparison.OrdinalIgnoreCase)
- .Replace(".foreign", string.Empty, StringComparison.OrdinalIgnoreCase)
- .Replace(".default", string.Empty, StringComparison.OrdinalIgnoreCase)
- .Split('.')
- .LastOrDefault();
-
- // Try to translate to three character code
- // Be flexible and check against both the full and three character versions
- var culture = _localization.FindLanguageInfo(language);
-
- if (culture != null)
- {
- language = culture.ThreeLetterISOLanguageName;
- }
-
- streams.Add(new MediaStream
- {
- Index = startIndex++,
- Type = MediaStreamType.Subtitle,
- IsExternal = true,
- Path = fullName,
- Codec = codec,
- Language = language,
- IsForced = isForced,
- IsDefault = isDefault
- });
- }
- }
- }
-
- private string NormalizeFilenameForSubtitleComparison(string filename)
- {
- // Try to account for sloppy file naming
- filename = filename.Replace("_", string.Empty, StringComparison.Ordinal);
- filename = filename.Replace(" ", string.Empty, StringComparison.Ordinal);
-
- // can't normalize this due to languages such as pt-br
- // filename = filename.Replace("-", string.Empty);
-
- // filename = filename.Replace(".", string.Empty);
-
- return filename;
- }
}
}
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
index ce93924026..2eab95294a 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/ArtistProvider.cs
@@ -69,58 +69,52 @@ namespace MediaBrowser.Providers.Music
private IEnumerable GetResultsFromResponse(Stream stream)
{
- using (var oReader = new StreamReader(stream, Encoding.UTF8))
+ using var oReader = new StreamReader(stream, Encoding.UTF8);
+ var settings = new XmlReaderSettings()
{
- var settings = new XmlReaderSettings()
- {
- ValidationType = ValidationType.None,
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true
- };
+ ValidationType = ValidationType.None,
+ CheckCharacters = false,
+ IgnoreProcessingInstructions = true,
+ IgnoreComments = true
+ };
- using (var reader = XmlReader.Create(oReader, settings))
- {
- reader.MoveToContent();
- reader.Read();
+ using var reader = XmlReader.Create(oReader, settings);
+ reader.MoveToContent();
+ reader.Read();
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
{
- if (reader.NodeType == XmlNodeType.Element)
+ case "artist-list":
{
- switch (reader.Name)
+ if (reader.IsEmptyElement)
{
- case "artist-list":
- {
- if (reader.IsEmptyElement)
- {
- reader.Read();
- continue;
- }
-
- using (var subReader = reader.ReadSubtree())
- {
- return ParseArtistList(subReader).ToList();
- }
- }
-
- default:
- {
- reader.Skip();
- break;
- }
+ reader.Read();
+ continue;
}
+
+ using var subReader = reader.ReadSubtree();
+ return ParseArtistList(subReader).ToList();
}
- else
+
+ default:
{
- reader.Read();
+ reader.Skip();
+ break;
}
}
-
- return Enumerable.Empty();
+ }
+ else
+ {
+ reader.Read();
}
}
+
+ return Enumerable.Empty();
}
private IEnumerable ParseArtistList(XmlReader reader)
@@ -145,13 +139,11 @@ namespace MediaBrowser.Providers.Music
var mbzId = reader.GetAttribute("id");
- using (var subReader = reader.ReadSubtree())
+ using var subReader = reader.ReadSubtree();
+ var artist = ParseArtist(subReader, mbzId);
+ if (artist != null)
{
- var artist = ParseArtist(subReader, mbzId);
- if (artist != null)
- {
- yield return artist;
- }
+ yield return artist;
}
break;
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
index e5ad0f3e09..0023d59594 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
@@ -128,53 +128,49 @@ namespace MediaBrowser.Providers.Music
private IEnumerable GetResultsFromResponse(Stream stream)
{
- using (var oReader = new StreamReader(stream, Encoding.UTF8))
+ using var oReader = new StreamReader(stream, Encoding.UTF8);
+ var settings = new XmlReaderSettings()
{
- var settings = new XmlReaderSettings()
+ ValidationType = ValidationType.None,
+ CheckCharacters = false,
+ IgnoreProcessingInstructions = true,
+ IgnoreComments = true
+ };
+
+ using var reader = XmlReader.Create(oReader, settings);
+ var results = ReleaseResult.Parse(reader);
+
+ return results.Select(i =>
+ {
+ var result = new RemoteSearchResult
{
- ValidationType = ValidationType.None,
- CheckCharacters = false,
- IgnoreProcessingInstructions = true,
- IgnoreComments = true
+ Name = i.Title,
+ ProductionYear = i.Year
};
- using (var reader = XmlReader.Create(oReader, settings))
+ if (i.Artists.Count > 0)
{
- var results = ReleaseResult.Parse(reader);
-
- return results.Select(i =>
+ result.AlbumArtist = new RemoteSearchResult
{
- var result = new RemoteSearchResult
- {
- Name = i.Title,
- ProductionYear = i.Year
- };
+ SearchProviderName = Name,
+ Name = i.Artists[0].Item1
+ };
- if (i.Artists.Count > 0)
- {
- result.AlbumArtist = new RemoteSearchResult
- {
- SearchProviderName = Name,
- Name = i.Artists[0].Item1
- };
-
- result.AlbumArtist.SetProviderId(MetadataProvider.MusicBrainzArtist, i.Artists[0].Item2);
- }
-
- if (!string.IsNullOrWhiteSpace(i.ReleaseId))
- {
- result.SetProviderId(MetadataProvider.MusicBrainzAlbum, i.ReleaseId);
- }
-
- if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId))
- {
- result.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, i.ReleaseGroupId);
- }
-
- return result;
- });
+ result.AlbumArtist.SetProviderId(MetadataProvider.MusicBrainzArtist, i.Artists[0].Item2);
}
- }
+
+ if (!string.IsNullOrWhiteSpace(i.ReleaseId))
+ {
+ result.SetProviderId(MetadataProvider.MusicBrainzAlbum, i.ReleaseId);
+ }
+
+ if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId))
+ {
+ result.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, i.ReleaseGroupId);
+ }
+
+ return result;
+ });
}
///
@@ -339,10 +335,8 @@ namespace MediaBrowser.Providers.Music
continue;
}
- using (var subReader = reader.ReadSubtree())
- {
- return ParseReleaseList(subReader).ToList();
- }
+ using var subReader = reader.ReadSubtree();
+ return ParseReleaseList(subReader).ToList();
}
default:
@@ -383,13 +377,11 @@ namespace MediaBrowser.Providers.Music
var releaseId = reader.GetAttribute("id");
- using (var subReader = reader.ReadSubtree())
+ using var subReader = reader.ReadSubtree();
+ var release = ParseRelease(subReader, releaseId);
+ if (release != null)
{
- var release = ParseRelease(subReader, releaseId);
- if (release != null)
- {
- yield return release;
- }
+ yield return release;
}
break;
@@ -460,14 +452,12 @@ namespace MediaBrowser.Providers.Music
case "artist-credit":
{
- using (var subReader = reader.ReadSubtree())
- {
- var artist = ParseArtistCredit(subReader);
+ using var subReader = reader.ReadSubtree();
+ var artist = ParseArtistCredit(subReader);
- if (!string.IsNullOrEmpty(artist.Item1))
- {
- result.Artists.Add(artist);
- }
+ if (!string.IsNullOrEmpty(artist.Item1))
+ {
+ result.Artists.Add(artist);
}
break;
@@ -505,12 +495,10 @@ namespace MediaBrowser.Providers.Music
switch (reader.Name)
{
case "name-credit":
- {
- using (var subReader = reader.ReadSubtree())
- {
- return ParseArtistNameCredit(subReader);
- }
- }
+ {
+ using var subReader = reader.ReadSubtree();
+ return ParseArtistNameCredit(subReader);
+ }
default:
{
@@ -545,10 +533,8 @@ namespace MediaBrowser.Providers.Music
case "artist":
{
var id = reader.GetAttribute("id");
- using (var subReader = reader.ReadSubtree())
- {
- return ParseArtistArtistCredit(subReader, id);
- }
+ using var subReader = reader.ReadSubtree();
+ return ParseArtistArtistCredit(subReader, id);
}
default:
@@ -647,47 +633,43 @@ namespace MediaBrowser.Providers.Music
IgnoreComments = true
};
- using (var reader = XmlReader.Create(oReader, settings))
+ using var reader = XmlReader.Create(oReader, settings);
+ reader.MoveToContent();
+ reader.Read();
+
+ // Loop through each element
+ while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
- reader.MoveToContent();
- reader.Read();
-
- // Loop through each element
- while (!reader.EOF && reader.ReadState == ReadState.Interactive)
+ if (reader.NodeType == XmlNodeType.Element)
{
- if (reader.NodeType == XmlNodeType.Element)
+ switch (reader.Name)
{
- switch (reader.Name)
+ case "release-group-list":
{
- case "release-group-list":
+ if (reader.IsEmptyElement)
{
- if (reader.IsEmptyElement)
- {
- reader.Read();
- continue;
- }
-
- using (var subReader = reader.ReadSubtree())
- {
- return GetFirstReleaseGroupId(subReader);
- }
+ reader.Read();
+ continue;
}
- default:
- {
- reader.Skip();
- break;
- }
+ using var subReader = reader.ReadSubtree();
+ return GetFirstReleaseGroupId(subReader);
+ }
+
+ default:
+ {
+ reader.Skip();
+ break;
}
}
- else
- {
- reader.Read();
- }
}
-
- return null;
+ else
+ {
+ reader.Read();
+ }
}
+
+ return null;
}
private string GetFirstReleaseGroupId(XmlReader reader)
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
index bf42ceadef..e4c908a62c 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
@@ -1,6 +1,5 @@
#pragma warning disable CS1591
-using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -55,14 +54,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
return Enumerable.Empty();
}
- var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), cancellationToken).ConfigureAwait(false);
+ var language = item.GetPreferredMetadataLanguage();
+ var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), language, cancellationToken).ConfigureAwait(false);
if (personResult?.Images?.Profiles == null)
{
return Enumerable.Empty();
}
var remoteImages = new RemoteImageInfo[personResult.Images.Profiles.Count];
- var language = item.GetPreferredMetadataLanguage();
for (var i = 0; i < personResult.Images.Profiles.Count; i++)
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
index 1757c82678..6db550b1d0 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
-using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
@@ -32,7 +31,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
{
if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId))
{
- var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), cancellationToken).ConfigureAwait(false);
+ var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
if (personResult != null)
{
@@ -96,7 +95,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
if (personTmdbId > 0)
{
- var person = await _tmdbClientManager.GetPersonAsync(personTmdbId, cancellationToken).ConfigureAwait(false);
+ var person = await _tmdbClientManager.GetPersonAsync(personTmdbId, id.MetadataLanguage, cancellationToken).ConfigureAwait(false);
result.HasMetadata = true;
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
index 05e5d3ced7..79ec6139d1 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
@@ -276,11 +276,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
/// Gets a person eg. cast or crew member from the TMDb API based on its TMDb id.
///
/// The person's TMDb id.
+ /// The episode's language.
/// The cancellation token.
/// The TMDb person information or null if not found.
- public async Task GetPersonAsync(int personTmdbId, CancellationToken cancellationToken)
+ public async Task GetPersonAsync(int personTmdbId, string language, CancellationToken cancellationToken)
{
- var key = $"person-{personTmdbId.ToString(CultureInfo.InvariantCulture)}";
+ var key = $"person-{personTmdbId.ToString(CultureInfo.InvariantCulture)}-{language}";
if (_memoryCache.TryGetValue(key, out Person person))
{
return person;
@@ -290,6 +291,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
person = await _tmDbClient.GetPersonAsync(
personTmdbId,
+ TmdbUtils.NormalizeLanguage(language),
PersonMethods.TvCredits | PersonMethods.MovieCredits | PersonMethods.Images | PersonMethods.ExternalIds,
cancellationToken).ConfigureAwait(false);
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
index b4e165f82b..b713736a0f 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
@@ -148,6 +148,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
if (parts.Length == 2)
{
+ // TMDB doesn't support Switzerland (de-CH, it-CH or fr-CH) so use the language (de, it or fr) without country code
+ if (string.Equals(parts[1], "CH", StringComparison.OrdinalIgnoreCase))
+ {
+ return parts[0];
+ }
+
language = parts[0] + "-" + parts[1].ToUpperInvariant();
}
diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
index 5ec9a02cbd..f6153dd532 100644
--- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
+++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs
@@ -172,23 +172,19 @@ namespace MediaBrowser.Providers.Studios
public IEnumerable GetAvailableImages(string file)
{
- using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read))
+ using var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read);
+ using var reader = new StreamReader(fileStream);
+ var lines = new List();
+
+ foreach (var line in reader.ReadAllLines())
{
- using (var reader = new StreamReader(fileStream))
+ if (!string.IsNullOrWhiteSpace(line))
{
- var lines = new List();
-
- foreach (var line in reader.ReadAllLines())
- {
- if (!string.IsNullOrWhiteSpace(line))
- {
- lines.Add(line);
- }
- }
-
- return lines;
+ lines.Add(line);
}
}
+
+ return lines;
}
}
}
diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
index bf0c853ae9..6aacaa15de 100644
--- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
+++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs
@@ -187,48 +187,46 @@ namespace MediaBrowser.Providers.Subtitles
{
var saveInMediaFolder = libraryOptions.SaveSubtitlesWithMedia;
- using (var stream = response.Stream)
- using (var memoryStream = new MemoryStream())
+ using var stream = response.Stream;
+ using var memoryStream = new MemoryStream();
+ await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
+ memoryStream.Position = 0;
+
+ var savePaths = new List();
+ var saveFileName = Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLowerInvariant();
+
+ if (response.IsForced)
{
- await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
- memoryStream.Position = 0;
+ saveFileName += ".forced";
+ }
- var savePaths = new List();
- var saveFileName = Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLowerInvariant();
+ saveFileName += "." + response.Format.ToLowerInvariant();
- if (response.IsForced)
+ if (saveInMediaFolder)
+ {
+ var mediaFolderPath = Path.GetFullPath(Path.Combine(video.ContainingFolderPath, saveFileName));
+ // TODO: Add some error handling to the API user: return BadRequest("Could not save subtitle, bad path.");
+ if (mediaFolderPath.StartsWith(video.ContainingFolderPath, StringComparison.Ordinal))
{
- saveFileName += ".forced";
+ savePaths.Add(mediaFolderPath);
}
+ }
- saveFileName += "." + response.Format.ToLowerInvariant();
+ var internalPath = Path.GetFullPath(Path.Combine(video.GetInternalMetadataPath(), saveFileName));
- if (saveInMediaFolder)
- {
- var mediaFolderPath = Path.GetFullPath(Path.Combine(video.ContainingFolderPath, saveFileName));
- // TODO: Add some error handling to the API user: return BadRequest("Could not save subtitle, bad path.");
- if (mediaFolderPath.StartsWith(video.ContainingFolderPath, StringComparison.Ordinal))
- {
- savePaths.Add(mediaFolderPath);
- }
- }
+ // TODO: Add some error to the user: return BadRequest("Could not save subtitle, bad path.");
+ if (internalPath.StartsWith(video.GetInternalMetadataPath(), StringComparison.Ordinal))
+ {
+ savePaths.Add(internalPath);
+ }
- var internalPath = Path.GetFullPath(Path.Combine(video.GetInternalMetadataPath(), saveFileName));
-
- // TODO: Add some error to the user: return BadRequest("Could not save subtitle, bad path.");
- if (internalPath.StartsWith(video.GetInternalMetadataPath(), StringComparison.Ordinal))
- {
- savePaths.Add(internalPath);
- }
-
- if (savePaths.Count > 0)
- {
- await TrySaveToFiles(memoryStream, savePaths).ConfigureAwait(false);
- }
- else
- {
- _logger.LogError("An uploaded subtitle could not be saved because the resulting paths were invalid.");
- }
+ if (savePaths.Count > 0)
+ {
+ await TrySaveToFiles(memoryStream, savePaths).ConfigureAwait(false);
+ }
+ else
+ {
+ _logger.LogError("An uploaded subtitle could not be saved because the resulting paths were invalid.");
}
}
@@ -247,10 +245,8 @@ namespace MediaBrowser.Providers.Subtitles
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
- using (var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, true))
- {
- await stream.CopyToAsync(fs).ConfigureAwait(false);
- }
+ using var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, true);
+ await stream.CopyToAsync(fs).ConfigureAwait(false);
return;
}
diff --git a/deployment/Dockerfile.debian.amd64 b/deployment/Dockerfile.debian.amd64
index ec0321f477..0923645007 100644
--- a/deployment/Dockerfile.debian.amd64
+++ b/deployment/Dockerfile.debian.amd64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.debian.arm64 b/deployment/Dockerfile.debian.arm64
index 8fd5ddb932..eef272d5be 100644
--- a/deployment/Dockerfile.debian.arm64
+++ b/deployment/Dockerfile.debian.arm64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.debian.armhf b/deployment/Dockerfile.debian.armhf
index 14615d19fb..154481d64a 100644
--- a/deployment/Dockerfile.debian.armhf
+++ b/deployment/Dockerfile.debian.armhf
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.linux.amd64 b/deployment/Dockerfile.linux.amd64
index 1f6ca15586..171ebe3728 100644
--- a/deployment/Dockerfile.linux.amd64
+++ b/deployment/Dockerfile.linux.amd64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.linux.amd64-musl b/deployment/Dockerfile.linux.amd64-musl
index 6af5d8baf5..a0a5f69235 100644
--- a/deployment/Dockerfile.linux.amd64-musl
+++ b/deployment/Dockerfile.linux.amd64-musl
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.linux.arm64 b/deployment/Dockerfile.linux.arm64
index 15b59e29d1..ae59802cd4 100644
--- a/deployment/Dockerfile.linux.arm64
+++ b/deployment/Dockerfile.linux.arm64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.linux.armhf b/deployment/Dockerfile.linux.armhf
index 71a0fda215..236b35ce5e 100644
--- a/deployment/Dockerfile.linux.armhf
+++ b/deployment/Dockerfile.linux.armhf
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.macos b/deployment/Dockerfile.macos
index 9291bcbb94..2e80d4a6ec 100644
--- a/deployment/Dockerfile.macos
+++ b/deployment/Dockerfile.macos
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.portable b/deployment/Dockerfile.portable
index e98ba74f87..852a6b553c 100644
--- a/deployment/Dockerfile.portable
+++ b/deployment/Dockerfile.portable
@@ -15,7 +15,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.amd64 b/deployment/Dockerfile.ubuntu.amd64
index d1fd8818e6..bffeb7307e 100644
--- a/deployment/Dockerfile.ubuntu.amd64
+++ b/deployment/Dockerfile.ubuntu.amd64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.arm64 b/deployment/Dockerfile.ubuntu.arm64
index 8e79d417cf..e90da4636b 100644
--- a/deployment/Dockerfile.ubuntu.arm64
+++ b/deployment/Dockerfile.ubuntu.arm64
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.ubuntu.armhf b/deployment/Dockerfile.ubuntu.armhf
index 627caa95a8..aae262ee61 100644
--- a/deployment/Dockerfile.ubuntu.armhf
+++ b/deployment/Dockerfile.ubuntu.armhf
@@ -16,7 +16,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/deployment/Dockerfile.windows.amd64 b/deployment/Dockerfile.windows.amd64
index 5723abcae6..c847b1621d 100644
--- a/deployment/Dockerfile.windows.amd64
+++ b/deployment/Dockerfile.windows.amd64
@@ -15,7 +15,7 @@ RUN apt-get update \
# Install dotnet repository
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
-RUN wget https://download.visualstudio.microsoft.com/download/pr/5f0f07ab-cd9a-4498-a9f7-67d90d582180/2a3db6698751e6cbb93ec244cb81cc5f/dotnet-sdk-5.0.202-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
+RUN wget https://download.visualstudio.microsoft.com/download/pr/ef13f9da-46dc-4de9-a05e-5a4c20574189/be95913ebf1fb6c66833ca40060d3f65/dotnet-sdk-5.0.203-linux-x64.tar.gz -O dotnet-sdk.tar.gz \
&& mkdir -p dotnet-sdk \
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
diff --git a/jellyfin.ruleset b/jellyfin.ruleset
index 19c0a08b23..1a9f2bf96c 100644
--- a/jellyfin.ruleset
+++ b/jellyfin.ruleset
@@ -1,13 +1,6 @@
-
-
-
-
-
-
-
@@ -22,6 +15,10 @@
+
+
+
+
@@ -43,6 +40,8 @@
or pass in 'CancellationToken.None' explicitly to indicate intentionally not propagating the token -->
+
+
diff --git a/tests/Jellyfin.Api.Tests/Controllers/DynamicHlsControllerTests.cs b/tests/Jellyfin.Api.Tests/Controllers/DynamicHlsControllerTests.cs
new file mode 100644
index 0000000000..1170838156
--- /dev/null
+++ b/tests/Jellyfin.Api.Tests/Controllers/DynamicHlsControllerTests.cs
@@ -0,0 +1,58 @@
+using System;
+using System.Collections.Generic;
+using AutoFixture;
+using AutoFixture.AutoMoq;
+using Jellyfin.Api.Controllers;
+using Jellyfin.Api.Helpers;
+using Jellyfin.Api.Models.StreamingDtos;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.MediaEncoding;
+using Moq;
+using Xunit;
+
+namespace Jellyfin.Api.Tests.Controllers
+{
+ public class DynamicHlsControllerTests
+ {
+ [Theory]
+ [MemberData(nameof(GetSegmentLengths_Success_TestData))]
+ public void GetSegmentLengths_Success(long runtimeTicks, int segmentlength, double[] expected)
+ {
+ var res = DynamicHlsController.GetSegmentLengthsInternal(runtimeTicks, segmentlength);
+ Assert.Equal(expected.Length, res.Length);
+ for (int i = 0; i < expected.Length; i++)
+ {
+ Assert.Equal(expected[i], res[i]);
+ }
+ }
+
+ public static IEnumerable