mirror of https://github.com/jellyfin/jellyfin.git
Merge branch 'master' into authenticationdb-efcore
# Conflicts: # Emby.Server.Implementations/Security/AuthenticationRepository.cs # Jellyfin.Server.Implementations/Security/AuthorizationContext.cs # MediaBrowser.Controller/Devices/IDeviceManager.cs
This commit is contained in:
commit
b6446c06ee
|
@ -20,7 +20,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
project: Current Release
|
project: Current Release
|
||||||
action: delete
|
action: delete
|
||||||
repo-token: ${{ secrets.GH_TOKEN }}
|
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
|
|
||||||
- name: Add to 'Release Next' project
|
- name: Add to 'Release Next' project
|
||||||
uses: alex-page/github-project-automation-plus@v0.7.1
|
uses: alex-page/github-project-automation-plus@v0.7.1
|
||||||
|
@ -29,7 +29,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
project: Release Next
|
project: Release Next
|
||||||
column: In progress
|
column: In progress
|
||||||
repo-token: ${{ secrets.GH_TOKEN }}
|
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
|
|
||||||
- name: Add to 'Current Release' project
|
- name: Add to 'Current Release' project
|
||||||
uses: alex-page/github-project-automation-plus@v0.7.1
|
uses: alex-page/github-project-automation-plus@v0.7.1
|
||||||
|
@ -38,7 +38,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
project: Current Release
|
project: Current Release
|
||||||
column: In progress
|
column: In progress
|
||||||
repo-token: ${{ secrets.GH_TOKEN }}
|
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
|
|
||||||
- name: Check number of comments from the team member
|
- name: Check number of comments from the team member
|
||||||
if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER'
|
if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER'
|
||||||
|
@ -52,7 +52,7 @@ jobs:
|
||||||
with:
|
with:
|
||||||
project: Issue Triage for Main Repo
|
project: Issue Triage for Main Repo
|
||||||
column: Needs triage
|
column: Needs triage
|
||||||
repo-token: ${{ secrets.GH_TOKEN }}
|
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
|
|
||||||
- name: Add issue to triage project
|
- name: Add issue to triage project
|
||||||
uses: alex-page/github-project-automation-plus@v0.7.1
|
uses: alex-page/github-project-automation-plus@v0.7.1
|
||||||
|
@ -61,4 +61,4 @@ jobs:
|
||||||
with:
|
with:
|
||||||
project: Issue Triage for Main Repo
|
project: Issue Triage for Main Repo
|
||||||
column: Pending response
|
column: Pending response
|
||||||
repo-token: ${{ secrets.GH_TOKEN }}
|
repo-token: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
|
|
|
@ -14,4 +14,4 @@ jobs:
|
||||||
- uses: eps1lon/actions-label-merge-conflict@v2.0.1
|
- uses: eps1lon/actions-label-merge-conflict@v2.0.1
|
||||||
with:
|
with:
|
||||||
dirtyLabel: 'merge conflict'
|
dirtyLabel: 'merge conflict'
|
||||||
repoToken: ${{ secrets.GH_TOKEN }}
|
repoToken: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
|
|
|
@ -11,17 +11,17 @@ jobs:
|
||||||
- name: Notify as seen
|
- name: Notify as seen
|
||||||
uses: peter-evans/create-or-update-comment@v1.4.5
|
uses: peter-evans/create-or-update-comment@v1.4.5
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GH_TOKEN }}
|
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
comment-id: ${{ github.event.comment.id }}
|
comment-id: ${{ github.event.comment.id }}
|
||||||
reactions: '+1'
|
reactions: '+1'
|
||||||
|
|
||||||
- name: Checkout the latest code
|
- name: Checkout the latest code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.GH_TOKEN }}
|
token: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Automatic Rebase
|
- name: Automatic Rebase
|
||||||
uses: cirrus-actions/rebase@1.4
|
uses: cirrus-actions/rebase@1.4
|
||||||
env:
|
env:
|
||||||
GITHUB_TOKEN: ${{ secrets.GH_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using Emby.Naming.Common;
|
using Emby.Naming.Common;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
|
|
||||||
namespace Emby.Naming.Audio
|
namespace Emby.Naming.Audio
|
||||||
{
|
{
|
||||||
|
@ -18,8 +18,8 @@ namespace Emby.Naming.Audio
|
||||||
/// <returns>True if file at path is audio file.</returns>
|
/// <returns>True if file at path is audio file.</returns>
|
||||||
public static bool IsAudioFile(string path, NamingOptions options)
|
public static bool IsAudioFile(string path, NamingOptions options)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(path);
|
var extension = Path.GetExtension(path.AsSpan());
|
||||||
return options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
return options.AudioFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,11 +23,12 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="..\SharedVersion.cs" />
|
<Compile Include="../SharedVersion.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\MediaBrowser.Model\MediaBrowser.Model.csproj" />
|
<ProjectReference Include="../MediaBrowser.Common/MediaBrowser.Common.csproj" />
|
||||||
|
<ProjectReference Include="../MediaBrowser.Model/MediaBrowser.Model.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
|
|
|
@ -29,72 +29,75 @@ namespace Emby.Naming.Video
|
||||||
/// <param name="path">Path to file.</param>
|
/// <param name="path">Path to file.</param>
|
||||||
/// <returns>Returns <see cref="ExtraResult"/> object.</returns>
|
/// <returns>Returns <see cref="ExtraResult"/> object.</returns>
|
||||||
public ExtraResult GetExtraInfo(string path)
|
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();
|
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;
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using Emby.Naming.Common;
|
using Emby.Naming.Common;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
|
|
||||||
namespace Emby.Naming.Video
|
namespace Emby.Naming.Video
|
||||||
{
|
{
|
||||||
|
@ -59,15 +59,15 @@ namespace Emby.Naming.Video
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isStub = false;
|
bool isStub = false;
|
||||||
string? container = null;
|
ReadOnlySpan<char> container = ReadOnlySpan<char>.Empty;
|
||||||
string? stubType = null;
|
string? stubType = null;
|
||||||
|
|
||||||
if (!isDirectory)
|
if (!isDirectory)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(path);
|
var extension = Path.GetExtension(path.AsSpan());
|
||||||
|
|
||||||
// Check supported extensions
|
// Check supported extensions
|
||||||
if (!_options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
if (!_options.VideoFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// It's not supported. Check stub extensions
|
// It's not supported. Check stub extensions
|
||||||
if (!StubResolver.TryResolveFile(path, _options, out stubType))
|
if (!StubResolver.TryResolveFile(path, _options, out stubType))
|
||||||
|
@ -86,9 +86,7 @@ namespace Emby.Naming.Video
|
||||||
|
|
||||||
var extraResult = new ExtraResolver(_options).GetExtraInfo(path);
|
var extraResult = new ExtraResolver(_options).GetExtraInfo(path);
|
||||||
|
|
||||||
var name = isDirectory
|
var name = Path.GetFileNameWithoutExtension(path);
|
||||||
? Path.GetFileName(path)
|
|
||||||
: Path.GetFileNameWithoutExtension(path);
|
|
||||||
|
|
||||||
int? year = null;
|
int? year = null;
|
||||||
|
|
||||||
|
@ -107,7 +105,7 @@ namespace Emby.Naming.Video
|
||||||
|
|
||||||
return new VideoFileInfo(
|
return new VideoFileInfo(
|
||||||
path: path,
|
path: path,
|
||||||
container: container,
|
container: container.IsEmpty ? null : container.ToString(),
|
||||||
isStub: isStub,
|
isStub: isStub,
|
||||||
name: name,
|
name: name,
|
||||||
year: year,
|
year: year,
|
||||||
|
@ -126,8 +124,8 @@ namespace Emby.Naming.Video
|
||||||
/// <returns>True if is video file.</returns>
|
/// <returns>True if is video file.</returns>
|
||||||
public bool IsVideoFile(string path)
|
public bool IsVideoFile(string path)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(path);
|
var extension = Path.GetExtension(path.AsSpan());
|
||||||
return _options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
return _options.VideoFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -137,8 +135,8 @@ namespace Emby.Naming.Video
|
||||||
/// <returns>True if is video file stub.</returns>
|
/// <returns>True if is video file stub.</returns>
|
||||||
public bool IsStubFile(string path)
|
public bool IsStubFile(string path)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(path);
|
var extension = Path.GetExtension(path.AsSpan());
|
||||||
return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
return _options.StubFileExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -181,11 +181,9 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
foreach (var row in connection.Query("PRAGMA table_info(" + table + ")"))
|
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(columnName);
|
||||||
|
|
||||||
columnNames.Add(name);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using SQLitePCL.pretty;
|
using SQLitePCL.pretty;
|
||||||
|
|
||||||
|
@ -96,21 +97,43 @@ namespace Emby.Server.Implementations.Data
|
||||||
DateTimeStyles.None).ToUniversalTime();
|
DateTimeStyles.None).ToUniversalTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DateTime? TryReadDateTime(this IResultSetValue result)
|
public static bool TryReadDateTime(this IReadOnlyList<IResultSetValue> 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))
|
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<IResultSetValue> result, int index)
|
public static bool TryGetGuid(this IReadOnlyList<IResultSetValue> 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<IResultSetValue> result, int index)
|
public static string GetString(this IReadOnlyList<IResultSetValue> result, int index)
|
||||||
|
@ -118,14 +141,48 @@ namespace Emby.Server.Implementations.Data
|
||||||
return result[index].ToString();
|
return result[index].ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool TryGetString(this IReadOnlyList<IResultSetValue> 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<IResultSetValue> result, int index)
|
public static bool GetBoolean(this IReadOnlyList<IResultSetValue> result, int index)
|
||||||
{
|
{
|
||||||
return result[index].ToBool();
|
return result[index].ToBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int GetInt32(this IReadOnlyList<IResultSetValue> result, int index)
|
public static bool TryGetBoolean(this IReadOnlyList<IResultSetValue> 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<IResultSetValue> 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<IResultSetValue> result, int index)
|
public static long GetInt64(this IReadOnlyList<IResultSetValue> result, int index)
|
||||||
|
@ -133,9 +190,43 @@ namespace Emby.Server.Implementations.Data
|
||||||
return result[index].ToInt64();
|
return result[index].ToInt64();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float GetFloat(this IReadOnlyList<IResultSetValue> result, int index)
|
public static bool TryGetInt64(this IReadOnlyList<IResultSetValue> 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<IResultSetValue> 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<IResultSetValue> 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<IResultSetValue> result, int index)
|
public static Guid GetGuid(this IReadOnlyList<IResultSetValue> result, int index)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -355,9 +355,9 @@ namespace Emby.Server.Implementations.Data
|
||||||
userData.Key = reader[0].ToString();
|
userData.Key = reader[0].ToString();
|
||||||
// userData.UserId = reader[1].ReadGuidFromBlob();
|
// 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();
|
userData.Played = reader[3].ToBool();
|
||||||
|
@ -365,19 +365,19 @@ namespace Emby.Server.Implementations.Data
|
||||||
userData.IsFavorite = reader[5].ToBool();
|
userData.IsFavorite = reader[5].ToBool();
|
||||||
userData.PlaybackPositionTicks = reader[6].ToInt64();
|
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;
|
return userData;
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.3" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.6" />
|
||||||
<PackageReference Include="Mono.Nat" Version="3.0.1" />
|
<PackageReference Include="Mono.Nat" Version="3.0.1" />
|
||||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
|
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
|
||||||
<PackageReference Include="sharpcompress" Version="0.28.2" />
|
<PackageReference Include="sharpcompress" Version="0.28.2" />
|
||||||
|
|
|
@ -165,13 +165,13 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
||||||
|
|
||||||
protected void SetVideoType(Video video, VideoFileInfo videoInfo)
|
protected void SetVideoType(Video video, VideoFileInfo videoInfo)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(video.Path);
|
var extension = Path.GetExtension(video.Path.AsSpan());
|
||||||
video.VideoType = string.Equals(extension, ".iso", StringComparison.OrdinalIgnoreCase) ||
|
video.VideoType = extension.Equals(".iso", StringComparison.OrdinalIgnoreCase)
|
||||||
string.Equals(extension, ".img", StringComparison.OrdinalIgnoreCase) ?
|
|| extension.Equals(".img", StringComparison.OrdinalIgnoreCase)
|
||||||
VideoType.Iso :
|
? VideoType.Iso
|
||||||
VideoType.VideoFile;
|
: VideoType.VideoFile;
|
||||||
|
|
||||||
video.IsShortcut = string.Equals(extension, ".strm", StringComparison.OrdinalIgnoreCase);
|
video.IsShortcut = extension.Equals(".strm", StringComparison.OrdinalIgnoreCase);
|
||||||
video.IsPlaceHolder = videoInfo.IsStub;
|
video.IsPlaceHolder = videoInfo.IsStub;
|
||||||
|
|
||||||
if (videoInfo.IsStub)
|
if (videoInfo.IsStub)
|
||||||
|
@ -193,11 +193,11 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
||||||
{
|
{
|
||||||
if (video.VideoType == VideoType.Iso)
|
if (video.VideoType == VideoType.Iso)
|
||||||
{
|
{
|
||||||
if (video.Path.IndexOf("dvd", StringComparison.OrdinalIgnoreCase) != -1)
|
if (video.Path.Contains("dvd", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
video.IsoType = IsoType.Dvd;
|
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;
|
video.IsoType = IsoType.BluRay;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ using MediaBrowser.Controller.LiveTv;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
{
|
{
|
||||||
internal class RecordingHelper
|
internal static class RecordingHelper
|
||||||
{
|
{
|
||||||
public static DateTime GetStartTime(TimerInfo timer)
|
public static DateTime GetStartTime(TimerInfo timer)
|
||||||
{
|
{
|
||||||
|
@ -70,17 +70,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
private static string GetDateString(DateTime date)
|
private static string GetDateString(DateTime date)
|
||||||
{
|
{
|
||||||
date = date.ToLocalTime();
|
return date.ToLocalTime().ToString("yyyy_MM_dd_HH_mm_ss", CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
{
|
{
|
||||||
var model = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false);
|
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);
|
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
var lineup = await JsonSerializer.DeserializeAsync<List<Channels>>(stream, _jsonOptions, cancellationToken)
|
var lineup = await JsonSerializer.DeserializeAsync<List<Channels>>(stream, _jsonOptions, cancellationToken)
|
||||||
.ConfigureAwait(false) ?? new List<Channels>();
|
.ConfigureAwait(false) ?? new List<Channels>();
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
"MixedContent": "Смесено съдържание",
|
"MixedContent": "Смесено съдържание",
|
||||||
"Movies": "Филми",
|
"Movies": "Филми",
|
||||||
"Music": "Музика",
|
"Music": "Музика",
|
||||||
"MusicVideos": "Музикални клипове",
|
"MusicVideos": "Музикални видеа",
|
||||||
"NameInstallFailed": "{0} не можа да се инсталира",
|
"NameInstallFailed": "{0} не можа да се инсталира",
|
||||||
"NameSeasonNumber": "Сезон {0}",
|
"NameSeasonNumber": "Сезон {0}",
|
||||||
"NameSeasonUnknown": "Неразпознат сезон",
|
"NameSeasonUnknown": "Неразпознат сезон",
|
||||||
|
@ -62,7 +62,7 @@
|
||||||
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
|
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
|
||||||
"Photos": "Снимки",
|
"Photos": "Снимки",
|
||||||
"Playlists": "Списъци",
|
"Playlists": "Списъци",
|
||||||
"Plugin": "Приставка",
|
"Plugin": "Добавка",
|
||||||
"PluginInstalledWithName": "{0} е инсталиранa",
|
"PluginInstalledWithName": "{0} е инсталиранa",
|
||||||
"PluginUninstalledWithName": "{0} е деинсталиранa",
|
"PluginUninstalledWithName": "{0} е деинсталиранa",
|
||||||
"PluginUpdatedWithName": "{0} е обновенa",
|
"PluginUpdatedWithName": "{0} е обновенa",
|
||||||
|
@ -116,5 +116,7 @@
|
||||||
"TasksMaintenanceCategory": "Поддръжка",
|
"TasksMaintenanceCategory": "Поддръжка",
|
||||||
"Undefined": "Неопределено",
|
"Undefined": "Неопределено",
|
||||||
"Forced": "Принудително",
|
"Forced": "Принудително",
|
||||||
"Default": "По подразбиране"
|
"Default": "По подразбиране",
|
||||||
|
"TaskCleanActivityLogDescription": "Изтрива записите в дневника с активност по стари от конфигурираната възраст.",
|
||||||
|
"TaskCleanActivityLog": "Изчисти дневника с активност"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
|
"DeviceOnlineWithName": "{0}-এর সাথে সংযুক্ত হয়েছে",
|
||||||
"DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
|
"DeviceOfflineWithName": "{0}-এর সাথে সংযোগ বিচ্ছিন্ন হয়েছে",
|
||||||
"Collections": "কলেক্শন",
|
"Collections": "সংগ্রহ",
|
||||||
"ChapterNameValue": "অধ্যায় {0}",
|
"ChapterNameValue": "অধ্যায় {0}",
|
||||||
"Channels": "চ্যানেল",
|
"Channels": "চ্যানেল",
|
||||||
"CameraImageUploadedFrom": "{0} থেকে একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে",
|
"CameraImageUploadedFrom": "{0} থেকে একটি নতুন ক্যামেরার চিত্র আপলোড করা হয়েছে",
|
||||||
|
|
|
@ -28,7 +28,6 @@ using MediaBrowser.Model.Net;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Microsoft.Net.Http.Headers;
|
using Microsoft.Net.Http.Headers;
|
||||||
|
|
||||||
|
@ -545,7 +544,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] EncodingContext? context,
|
[FromQuery] EncodingContext? context,
|
||||||
[FromQuery] Dictionary<string, string> streamOptions)
|
[FromQuery] Dictionary<string, string> streamOptions)
|
||||||
{
|
{
|
||||||
var cancellationTokenSource = new CancellationTokenSource();
|
using var cancellationTokenSource = new CancellationTokenSource();
|
||||||
var streamingRequest = new VideoRequestDto
|
var streamingRequest = new VideoRequestDto
|
||||||
{
|
{
|
||||||
Id = itemId,
|
Id = itemId,
|
||||||
|
@ -710,7 +709,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] EncodingContext? context,
|
[FromQuery] EncodingContext? context,
|
||||||
[FromQuery] Dictionary<string, string> streamOptions)
|
[FromQuery] Dictionary<string, string> streamOptions)
|
||||||
{
|
{
|
||||||
var cancellationTokenSource = new CancellationTokenSource();
|
using var cancellationTokenSource = new CancellationTokenSource();
|
||||||
var streamingRequest = new StreamingRequestDto
|
var streamingRequest = new StreamingRequestDto
|
||||||
{
|
{
|
||||||
Id = itemId,
|
Id = itemId,
|
||||||
|
@ -1138,7 +1137,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
var isHlsInFmp4 = string.Equals(segmentContainer, "mp4", StringComparison.OrdinalIgnoreCase);
|
var isHlsInFmp4 = string.Equals(segmentContainer, "mp4", StringComparison.OrdinalIgnoreCase);
|
||||||
var hlsVersion = isHlsInFmp4 ? "7" : "3";
|
var hlsVersion = isHlsInFmp4 ? "7" : "3";
|
||||||
|
|
||||||
var builder = new StringBuilder();
|
var builder = new StringBuilder(128);
|
||||||
|
|
||||||
builder.AppendLine("#EXTM3U")
|
builder.AppendLine("#EXTM3U")
|
||||||
.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD")
|
.AppendLine("#EXT-X-PLAYLIST-TYPE:VOD")
|
||||||
|
@ -1191,7 +1190,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
throw new ArgumentException("StartTimeTicks is not allowed.");
|
throw new ArgumentException("StartTimeTicks is not allowed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
var cancellationTokenSource = new CancellationTokenSource();
|
using var cancellationTokenSource = new CancellationTokenSource();
|
||||||
var cancellationToken = cancellationTokenSource.Token;
|
var cancellationToken = cancellationTokenSource.Token;
|
||||||
|
|
||||||
using var state = await StreamingHelpers.GetStreamingState(
|
using var state = await StreamingHelpers.GetStreamingState(
|
||||||
|
@ -1208,7 +1207,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
_deviceManager,
|
_deviceManager,
|
||||||
_transcodingJobHelper,
|
_transcodingJobHelper,
|
||||||
TranscodingJobType,
|
TranscodingJobType,
|
||||||
cancellationTokenSource.Token)
|
cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
|
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
|
||||||
|
@ -1227,7 +1226,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath);
|
var transcodingLock = _transcodingJobHelper.GetTranscodingLock(playlistPath);
|
||||||
await transcodingLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
await transcodingLock.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||||
var released = false;
|
var released = false;
|
||||||
var startTranscoding = false;
|
var startTranscoding = false;
|
||||||
|
|
||||||
|
@ -1323,24 +1322,28 @@ namespace Jellyfin.Api.Controllers
|
||||||
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, segmentId, job, cancellationToken).ConfigureAwait(false);
|
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<double>();
|
var segmentLengthTicks = TimeSpan.FromSeconds(segmentlength).Ticks;
|
||||||
|
var wholeSegments = runtimeTicks / segmentLengthTicks;
|
||||||
|
var remainingTicks = runtimeTicks % segmentLengthTicks;
|
||||||
|
|
||||||
var ticks = state.RunTimeTicks ?? 0;
|
var segmentsLen = wholeSegments + (remainingTicks == 0 ? 0 : 1);
|
||||||
|
var segments = new double[segmentsLen];
|
||||||
var segmentLengthTicks = TimeSpan.FromSeconds(state.SegmentLength).Ticks;
|
for (int i = 0; i < wholeSegments; i++)
|
||||||
|
|
||||||
while (ticks > 0)
|
|
||||||
{
|
{
|
||||||
var length = ticks >= segmentLengthTicks ? segmentLengthTicks : ticks;
|
segments[i] = segmentlength;
|
||||||
|
|
||||||
result.Add(TimeSpan.FromTicks(length).TotalSeconds);
|
|
||||||
|
|
||||||
ticks -= length;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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))
|
else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var outputFmp4HeaderArg = string.Empty;
|
var outputFmp4HeaderArg = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) switch
|
||||||
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
|
|
||||||
if (isWindows)
|
|
||||||
{
|
{
|
||||||
// on Windows, the path of fmp4 header file needs to be configured
|
// on Windows, the path of fmp4 header file needs to be configured
|
||||||
outputFmp4HeaderArg = " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"";
|
true => " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"",
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// on Linux/Unix, ffmpeg generate fmp4 header file to m3u8 output folder
|
// 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;
|
segmentFormat = "fmp4" + outputFmp4HeaderArg;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Jellyfin.Api.Helpers
|
namespace Jellyfin.Api.Helpers
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.5" />
|
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.6" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.4" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.1.4" />
|
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.1.4" />
|
||||||
|
|
|
@ -27,13 +27,13 @@
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
|
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.5" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.5" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.6" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.5">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.6">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.5">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.6">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
@ -207,7 +208,7 @@ namespace Jellyfin.Server.Implementations.Security
|
||||||
auth = httpReq.Request.Headers[HeaderNames.Authorization];
|
auth = httpReq.Request.Headers[HeaderNames.Authorization];
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetAuthorization(auth);
|
return auth.Count > 0 ? GetAuthorization(auth[0]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -224,7 +225,7 @@ namespace Jellyfin.Server.Implementations.Security
|
||||||
auth = httpReq.Headers[HeaderNames.Authorization];
|
auth = httpReq.Headers[HeaderNames.Authorization];
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetAuthorization(auth);
|
return auth.Count > 0 ? GetAuthorization(auth[0]) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -232,43 +233,38 @@ namespace Jellyfin.Server.Implementations.Security
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="authorizationHeader">The authorization header.</param>
|
/// <param name="authorizationHeader">The authorization header.</param>
|
||||||
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
/// <returns>Dictionary{System.StringSystem.String}.</returns>
|
||||||
private Dictionary<string, string>? GetAuthorization(string? authorizationHeader)
|
private Dictionary<string, string>? GetAuthorization(ReadOnlySpan<char> authorizationHeader)
|
||||||
{
|
{
|
||||||
if (authorizationHeader == null)
|
var firstSpace = authorizationHeader.IndexOf(' ');
|
||||||
|
|
||||||
|
// There should be at least two parts
|
||||||
|
if (firstSpace == -1)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var parts = authorizationHeader.Split(' ', 2);
|
var name = authorizationHeader[..firstSpace];
|
||||||
|
|
||||||
// There should be at least to parts
|
if (!name.Equals("MediaBrowser", StringComparison.OrdinalIgnoreCase)
|
||||||
if (parts.Length != 2)
|
&& !name.Equals("Emby", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var acceptedNames = new[] { "MediaBrowser", "Emby" };
|
authorizationHeader = authorizationHeader[(firstSpace + 1)..];
|
||||||
|
|
||||||
// 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(',');
|
|
||||||
|
|
||||||
var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
var result = new Dictionary<string, string>(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('"'));
|
var key = trimmedItem[..firstEqualsSign].ToString();
|
||||||
result[param[0]] = value;
|
var value = NormalizeValue(trimmedItem[(firstEqualsSign + 1)..].Trim('"').ToString());
|
||||||
|
result[key] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,8 +38,8 @@
|
||||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.5" />
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.6" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.5" />
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.6" />
|
||||||
<PackageReference Include="prometheus-net" Version="4.1.1" />
|
<PackageReference Include="prometheus-net" Version="4.1.1" />
|
||||||
<PackageReference Include="prometheus-net.AspNetCore" Version="4.1.1" />
|
<PackageReference Include="prometheus-net.AspNetCore" Version="4.1.1" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
||||||
|
|
|
@ -81,6 +81,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Jellyfin.Server.Tests", "te
|
||||||
EndProject
|
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}"
|
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
|
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
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
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}.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.ActiveCfg = Release|Any CPU
|
||||||
{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E}.Release|Any CPU.Build.0 = 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
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -240,6 +246,7 @@ Global
|
||||||
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||||
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9} = {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}
|
{68B0B823-A5AC-4E8B-82EA-965AAC7BF76E} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||||
|
{A964008C-2136-4716-B6CB-B3426C22320A} = {FBBB5129-006E-4AD7-BAD5-8B7CA1D10ED6}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE}
|
SolutionGuid = {3448830C-EBDC-426C-85CD-7BBB9651A7FE}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace MediaBrowser.Common.Extensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Static extensions for the <see cref="IEnumerable{T}"/> interface.
|
||||||
|
/// </summary>
|
||||||
|
public static class EnumerableExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the value is contained in the source collection.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="source">An instance of the <see cref="IEnumerable{String}"/> interface.</param>
|
||||||
|
/// <param name="value">The value to look for in the collection.</param>
|
||||||
|
/// <param name="stringComparison">The string comparison.</param>
|
||||||
|
/// <returns>A value indicating whether the value is contained in the collection.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException">The source is null.</exception>
|
||||||
|
public static bool Contains(this IEnumerable<string> source, ReadOnlySpan<char> value, StringComparison stringComparison)
|
||||||
|
{
|
||||||
|
if (source == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(source));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source is IList<string> 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -84,7 +84,7 @@ namespace MediaBrowser.Controller.Channels
|
||||||
|
|
||||||
internal static bool IsChannelVisible(BaseItem channelItem, User user)
|
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);
|
return channel.IsVisible(user);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,32 +51,47 @@ namespace MediaBrowser.Controller.Channels
|
||||||
/// Gets the channels internal.
|
/// Gets the channels internal.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
|
/// <returns>The channels.</returns>
|
||||||
QueryResult<Channel> GetChannelsInternal(ChannelQuery query);
|
QueryResult<Channel> GetChannelsInternal(ChannelQuery query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the channels.
|
/// Gets the channels.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="query">The query.</param>
|
/// <param name="query">The query.</param>
|
||||||
|
/// <returns>The channels.</returns>
|
||||||
QueryResult<BaseItemDto> GetChannels(ChannelQuery query);
|
QueryResult<BaseItemDto> GetChannels(ChannelQuery query);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the latest media.
|
/// Gets the latest channel items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="query">The item query.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>The latest channels.</returns>
|
||||||
Task<QueryResult<BaseItemDto>> GetLatestChannelItems(InternalItemsQuery query, CancellationToken cancellationToken);
|
Task<QueryResult<BaseItemDto>> GetLatestChannelItems(InternalItemsQuery query, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the latest media.
|
/// Gets the latest channel items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="query">The item query.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>The latest channels.</returns>
|
||||||
Task<QueryResult<BaseItem>> GetLatestChannelItemsInternal(InternalItemsQuery query, CancellationToken cancellationToken);
|
Task<QueryResult<BaseItem>> GetLatestChannelItemsInternal(InternalItemsQuery query, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the channel items.
|
/// Gets the channel items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="query">The query.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>The channel items.</returns>
|
||||||
Task<QueryResult<BaseItemDto>> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken);
|
Task<QueryResult<BaseItemDto>> GetChannelItems(InternalItemsQuery query, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the channel items internal.
|
/// Gets the channel items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="query">The query.</param>
|
||||||
|
/// <param name="progress">The progress to report to.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>The channel items.</returns>
|
||||||
Task<QueryResult<BaseItem>> GetChannelItemsInternal(InternalItemsQuery query, IProgress<double> progress, CancellationToken cancellationToken);
|
Task<QueryResult<BaseItem>> GetChannelItemsInternal(InternalItemsQuery query, IProgress<double> progress, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -84,9 +99,14 @@ namespace MediaBrowser.Controller.Channels
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="item">The item.</param>
|
/// <param name="item">The item.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task{IEnumerable{MediaSourceInfo}}.</returns>
|
/// <returns>The item media sources.</returns>
|
||||||
IEnumerable<MediaSourceInfo> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken);
|
IEnumerable<MediaSourceInfo> GetStaticMediaSources(BaseItem item, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the item supports media probe.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="item">The item.</param>
|
||||||
|
/// <returns>Whether media probe should be enabled.</returns>
|
||||||
bool EnableMediaProbe(BaseItem item);
|
bool EnableMediaProbe(BaseItem item);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.Channels
|
||||||
|
{
|
||||||
|
public interface IDisableMediaSourceDisplay
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.Channels
|
||||||
|
{
|
||||||
|
public interface IHasFolderAttributes
|
||||||
|
{
|
||||||
|
string[] Attributes { get; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -7,11 +5,17 @@ using MediaBrowser.Model.Dto;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Channels
|
namespace MediaBrowser.Controller.Channels
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The channel requires a media info callback.
|
||||||
|
/// </summary>
|
||||||
public interface IRequiresMediaInfoCallback
|
public interface IRequiresMediaInfoCallback
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the channel item media information.
|
/// Gets the channel item media information.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="id">The channel item id.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>The enumerable of media source info.</returns>
|
||||||
Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken);
|
Task<IEnumerable<MediaSourceInfo>> GetChannelItemMediaInfo(string id, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.Entities;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Channels
|
namespace MediaBrowser.Controller.Channels
|
||||||
{
|
{
|
||||||
|
@ -19,35 +18,4 @@ namespace MediaBrowser.Controller.Channels
|
||||||
/// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
|
/// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
|
||||||
Task<IEnumerable<ChannelItemInfo>> Search(ChannelSearchInfo searchInfo, CancellationToken cancellationToken);
|
Task<IEnumerable<ChannelItemInfo>> Search(ChannelSearchInfo searchInfo, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface ISupportsLatestMedia
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the latest media.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request">The request.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
|
|
||||||
Task<IEnumerable<ChannelItemInfo>> 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; }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the latest media.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request">The request.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>The latest media.</returns>
|
||||||
|
Task<IEnumerable<ChannelItemInfo>> GetLatestMedia(ChannelLatestMediaSearch request, CancellationToken cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.Channels
|
||||||
|
{
|
||||||
|
public interface ISupportsMediaProbe
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,7 +30,7 @@ namespace MediaBrowser.Controller.Channels
|
||||||
public List<ChannelMediaContentType> ContentTypes { get; set; }
|
public List<ChannelMediaContentType> ContentTypes { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? MaxPageSize { get; set; }
|
public int? MaxPageSize { get; set; }
|
||||||
|
|
||||||
|
@ -41,7 +41,7 @@ namespace MediaBrowser.Controller.Channels
|
||||||
public List<ChannelItemSortField> DefaultSortFields { get; set; }
|
public List<ChannelItemSortField> DefaultSortFields { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SupportsSortOrderToggle { get; set; }
|
public bool SupportsSortOrderToggle { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -36,11 +36,17 @@ namespace MediaBrowser.Controller.Dto
|
||||||
/// <param name="options">The options.</param>
|
/// <param name="options">The options.</param>
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <param name="owner">The owner.</param>
|
/// <param name="owner">The owner.</param>
|
||||||
|
/// <returns>The <see cref="IReadOnlyList{T}"/> of <see cref="BaseItemDto"/>.</returns>
|
||||||
IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null);
|
IReadOnlyList<BaseItemDto> GetBaseItemDtos(IReadOnlyList<BaseItem> items, DtoOptions options, User user = null, BaseItem owner = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the item by name dto.
|
/// Gets the item by name dto.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="item">The item.</param>
|
||||||
|
/// <param name="options">The dto options.</param>
|
||||||
|
/// <param name="taggedItems">The list of tagged items.</param>
|
||||||
|
/// <param name="user">The user.</param>
|
||||||
|
/// <returns>The item dto.</returns>
|
||||||
BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, User user = null);
|
BaseItemDto GetItemByNameDto(BaseItem item, DtoOptions options, List<BaseItem> taggedItems, User user = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AggregateFolder : Folder
|
public class AggregateFolder : Folder
|
||||||
{
|
{
|
||||||
|
private bool _requiresRefresh;
|
||||||
|
|
||||||
public AggregateFolder()
|
public AggregateFolder()
|
||||||
{
|
{
|
||||||
PhysicalLocationsList = Array.Empty<string>();
|
PhysicalLocationsList = Array.Empty<string>();
|
||||||
|
@ -85,8 +87,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _requiresRefresh;
|
|
||||||
|
|
||||||
public override bool RequiresRefresh()
|
public override bool RequiresRefresh()
|
||||||
{
|
{
|
||||||
var changed = base.RequiresRefresh() || _requiresRefresh;
|
var changed = base.RequiresRefresh() || _requiresRefresh;
|
||||||
|
@ -106,11 +106,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
ClearCache();
|
ClearCache();
|
||||||
|
|
||||||
var changed = base.BeforeMetadataRefresh(replaceAllMetdata) || _requiresRefresh;
|
var changed = base.BeforeMetadataRefresh(replaceAllMetadata) || _requiresRefresh;
|
||||||
_requiresRefresh = false;
|
_requiresRefresh = false;
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -208,9 +208,9 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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 or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
|
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||||
|
|
||||||
if (IsAccessedByName)
|
if (IsAccessedByName)
|
||||||
{
|
{
|
||||||
|
|
|
@ -38,7 +38,7 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
public override bool IsDisplayedAsFolder => true;
|
public override bool IsDisplayedAsFolder => true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Gets the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself.
|
/// If the item is a folder, it returns the folder itself.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The containing folder path.</value>
|
/// <value>The containing folder path.</value>
|
||||||
|
@ -106,9 +106,9 @@ namespace MediaBrowser.Controller.Entities.Audio
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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 or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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();
|
var newPath = GetRebasedPath();
|
||||||
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
||||||
|
|
|
@ -92,7 +92,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public const string ShortsFolderName = "shorts";
|
public const string ShortsFolderName = "shorts";
|
||||||
public const string FeaturettesFolderName = "featurettes";
|
public const string FeaturettesFolderName = "featurettes";
|
||||||
|
|
||||||
public static readonly string[] AllExtrasTypesFolderNames = {
|
public static readonly string[] AllExtrasTypesFolderNames =
|
||||||
|
{
|
||||||
ExtrasFolderName,
|
ExtrasFolderName,
|
||||||
BehindTheScenesFolderName,
|
BehindTheScenesFolderName,
|
||||||
DeletedScenesFolderName,
|
DeletedScenesFolderName,
|
||||||
|
@ -177,7 +178,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public virtual bool AlwaysScanInternalMetadataPath => false;
|
public virtual bool AlwaysScanInternalMetadataPath => false;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether this instance is in mixed folder.
|
/// Gets or sets a value indicating whether this instance is in mixed folder.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if this instance is in mixed folder; otherwise, <c>false</c>.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
@ -244,7 +245,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public ProgramAudio? Audio { get; set; }
|
public ProgramAudio? Audio { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
/// Default is based on the type for everything except actual generic folders.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The display prefs id.</value>
|
/// <value>The display prefs id.</value>
|
||||||
|
@ -280,7 +281,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Gets the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself.
|
/// If the item is a folder, it returns the folder itself.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
@ -305,8 +306,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public string ServiceName { get; set; }
|
public string ServiceName { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If this content came from an external service, the id of the content on that service.
|
/// Gets or sets the external id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// If this content came from an external service, the id of the content on that service.
|
||||||
|
/// </remarks>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string ExternalId { get; set; }
|
public string ExternalId { get; set; }
|
||||||
|
|
||||||
|
@ -330,7 +334,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the type of the location.
|
/// Gets the type of the location.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The type of the location.</value>
|
/// <value>The type of the location.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
@ -449,8 +453,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is just a helper for convenience.
|
/// Gets the primary image path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This is just a helper for convenience.
|
||||||
|
/// </remarks>
|
||||||
/// <value>The primary image path.</value>
|
/// <value>The primary image path.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string PrimaryImagePath => this.GetImagePath(ImageType.Primary);
|
public string PrimaryImagePath => this.GetImagePath(ImageType.Primary);
|
||||||
|
@ -541,7 +548,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public DateTime DateLastRefreshed { get; set; }
|
public DateTime DateLastRefreshed { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The logger.
|
/// Gets or sets the logger.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ILogger<BaseItem> Logger { get; set; }
|
public static ILogger<BaseItem> Logger { get; set; }
|
||||||
|
|
||||||
|
@ -621,7 +628,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
private Guid[] _themeVideoIds;
|
private Guid[] _themeVideoIds;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the sort.
|
/// Gets or sets the name of the sort.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name of the sort.</value>
|
/// <value>The name of the sort.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
@ -848,7 +855,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The premiere date.</value>
|
/// <value>The premiere date.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
@ -945,7 +952,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public int? ProductionYear { get; set; }
|
public int? ProductionYear { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
/// This could be episode number, album track number, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The index number.</value>
|
/// <value>The index number.</value>
|
||||||
|
@ -953,7 +960,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public int? IndexNumber { get; set; }
|
public int? IndexNumber { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The parent index number.</value>
|
/// <value>The parent index number.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
@ -1017,9 +1024,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
// if (!user.IsParentalScheduleAllowed())
|
// if (!user.IsParentalScheduleAllowed())
|
||||||
//{
|
// {
|
||||||
// return PlayAccess.None;
|
// return PlayAccess.None;
|
||||||
//}
|
// }
|
||||||
|
|
||||||
return PlayAccess.Full;
|
return PlayAccess.Full;
|
||||||
}
|
}
|
||||||
|
@ -2645,7 +2652,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is called before any metadata refresh and returns true if changes were made.
|
/// This is called before any metadata refresh and returns true if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public virtual bool BeforeMetadataRefresh(bool replaceAllMetdata)
|
/// <param name="replaceAllMetadata">Whether to replace all metadata.</param>
|
||||||
|
/// <returns>true if the item has change, else false.</returns>
|
||||||
|
public virtual bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
_sortName = null;
|
_sortName = null;
|
||||||
|
|
||||||
|
|
|
@ -29,30 +29,45 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public class CollectionFolder : Folder, ICollectionFolder
|
public class CollectionFolder : Folder, ICollectionFolder
|
||||||
{
|
{
|
||||||
private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
private static readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||||
public static IXmlSerializer XmlSerializer { get; set; }
|
private static readonly Dictionary<string, LibraryOptions> _libraryOptions = new Dictionary<string, LibraryOptions>();
|
||||||
|
private bool _requiresRefresh;
|
||||||
public static IServerApplicationHost ApplicationHost { get; set; }
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="CollectionFolder"/> class.
|
||||||
|
/// </summary>
|
||||||
public CollectionFolder()
|
public CollectionFolder()
|
||||||
{
|
{
|
||||||
PhysicalLocationsList = Array.Empty<string>();
|
PhysicalLocationsList = Array.Empty<string>();
|
||||||
PhysicalFolderIds = Array.Empty<Guid>();
|
PhysicalFolderIds = Array.Empty<Guid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static IXmlSerializer XmlSerializer { get; set; }
|
||||||
|
|
||||||
|
public static IServerApplicationHost ApplicationHost { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsPlayedStatus => false;
|
public override bool SupportsPlayedStatus => false;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsInheritedParentImages => false;
|
public override bool SupportsInheritedParentImages => false;
|
||||||
|
|
||||||
|
public string CollectionType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item's children.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Our children are actually just references to the ones in the physical root...
|
||||||
|
/// </remarks>
|
||||||
|
/// <value>The actual children.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public override IEnumerable<BaseItem> Children => GetActualChildren();
|
||||||
|
|
||||||
public override bool CanDelete()
|
public override bool CanDelete()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CollectionType { get; set; }
|
|
||||||
|
|
||||||
private static readonly Dictionary<string, LibraryOptions> LibraryOptions = new Dictionary<string, LibraryOptions>();
|
|
||||||
public LibraryOptions GetLibraryOptions()
|
public LibraryOptions GetLibraryOptions()
|
||||||
{
|
{
|
||||||
return GetLibraryOptions(Path);
|
return GetLibraryOptions(Path);
|
||||||
|
@ -106,12 +121,12 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public static LibraryOptions GetLibraryOptions(string path)
|
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);
|
options = LoadLibraryOptions(path);
|
||||||
LibraryOptions[path] = options;
|
_libraryOptions[path] = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
|
@ -120,9 +135,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public static void SaveLibraryOptions(string path, LibraryOptions options)
|
public static void SaveLibraryOptions(string path, LibraryOptions options)
|
||||||
{
|
{
|
||||||
lock (LibraryOptions)
|
lock (_libraryOptions)
|
||||||
{
|
{
|
||||||
LibraryOptions[path] = options;
|
_libraryOptions[path] = options;
|
||||||
|
|
||||||
var clone = JsonSerializer.Deserialize<LibraryOptions>(JsonSerializer.SerializeToUtf8Bytes(options, _jsonOptions), _jsonOptions);
|
var clone = JsonSerializer.Deserialize<LibraryOptions>(JsonSerializer.SerializeToUtf8Bytes(options, _jsonOptions), _jsonOptions);
|
||||||
foreach (var mediaPath in clone.PathInfos)
|
foreach (var mediaPath in clone.PathInfos)
|
||||||
|
@ -139,15 +154,18 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public static void OnCollectionFolderChange()
|
public static void OnCollectionFolderChange()
|
||||||
{
|
{
|
||||||
lock (LibraryOptions)
|
lock (_libraryOptions)
|
||||||
{
|
{
|
||||||
LibraryOptions.Clear();
|
_libraryOptions.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Allow different display preferences for each collection folder.
|
/// Gets the display preferences id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Allow different display preferences for each collection folder.
|
||||||
|
/// </remarks>
|
||||||
/// <value>The display prefs id.</value>
|
/// <value>The display prefs id.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override Guid DisplayPreferencesId => Id;
|
public override Guid DisplayPreferencesId => Id;
|
||||||
|
@ -155,21 +173,20 @@ namespace MediaBrowser.Controller.Entities
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override string[] PhysicalLocations => PhysicalLocationsList;
|
public override string[] PhysicalLocations => PhysicalLocationsList;
|
||||||
|
|
||||||
|
public string[] PhysicalLocationsList { get; set; }
|
||||||
|
|
||||||
|
public Guid[] PhysicalFolderIds { get; set; }
|
||||||
|
|
||||||
public override bool IsSaveLocalMetadataEnabled()
|
public override bool IsSaveLocalMetadataEnabled()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] PhysicalLocationsList { get; set; }
|
|
||||||
|
|
||||||
public Guid[] PhysicalFolderIds { get; set; }
|
|
||||||
|
|
||||||
protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
|
protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
return CreateResolveArgs(directoryService, true).FileSystemChildren;
|
return CreateResolveArgs(directoryService, true).FileSystemChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _requiresRefresh;
|
|
||||||
public override bool RequiresRefresh()
|
public override bool RequiresRefresh()
|
||||||
{
|
{
|
||||||
var changed = base.RequiresRefresh() || _requiresRefresh;
|
var changed = base.RequiresRefresh() || _requiresRefresh;
|
||||||
|
@ -201,9 +218,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return changed;
|
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;
|
_requiresRefresh = false;
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
@ -312,13 +329,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Our children are actually just references to the ones in the physical root...
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The actual children.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public override IEnumerable<BaseItem> Children => GetActualChildren();
|
|
||||||
|
|
||||||
public IEnumerable<BaseItem> GetActualChildren()
|
public IEnumerable<BaseItem> GetActualChildren()
|
||||||
{
|
{
|
||||||
return GetPhysicalFolders(true).SelectMany(c => c.Children);
|
return GetPhysicalFolders(true).SelectMany(c => c.Children);
|
||||||
|
|
|
@ -37,6 +37,11 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Folder : BaseItem
|
public class Folder : BaseItem
|
||||||
{
|
{
|
||||||
|
public Folder()
|
||||||
|
{
|
||||||
|
LinkedChildren = Array.Empty<LinkedChild>();
|
||||||
|
}
|
||||||
|
|
||||||
public static IUserViewManager UserViewManager { get; set; }
|
public static IUserViewManager UserViewManager { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -50,11 +55,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public DateTime? DateLastMediaAdded { get; set; }
|
public DateTime? DateLastMediaAdded { get; set; }
|
||||||
|
|
||||||
public Folder()
|
|
||||||
{
|
|
||||||
LinkedChildren = Array.Empty<LinkedChild>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsThemeMedia => true;
|
public override bool SupportsThemeMedia => true;
|
||||||
|
|
||||||
|
@ -86,6 +86,85 @@ namespace MediaBrowser.Controller.Entities
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public virtual bool SupportsDateLastMediaAdded => false;
|
public virtual bool SupportsDateLastMediaAdded => false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string FileNameWithoutExtension
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsFileProtocol)
|
||||||
|
{
|
||||||
|
return System.IO.Path.GetFileName(Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the actual children.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The actual children.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public virtual IEnumerable<BaseItem> Children => LoadChildren();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets thread-safe access to all recursive children of this folder - without regard to user.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The recursive children.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public IEnumerable<BaseItem> 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()
|
public override bool CanDelete()
|
||||||
{
|
{
|
||||||
if (IsRoot)
|
if (IsRoot)
|
||||||
|
@ -108,20 +187,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return baseResult;
|
return baseResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override string FileNameWithoutExtension
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsFileProtocol)
|
|
||||||
{
|
|
||||||
return System.IO.Path.GetFileName(Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override bool IsAllowTagFilterEnforced()
|
protected override bool IsAllowTagFilterEnforced()
|
||||||
{
|
{
|
||||||
if (this is ICollectionFolder)
|
if (this is ICollectionFolder)
|
||||||
|
@ -137,9 +202,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
protected virtual bool SupportsShortcutChildren => false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the child.
|
/// Adds the child.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -169,20 +231,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
LibraryManager.CreateItem(item, this);
|
LibraryManager.CreateItem(item, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the actual children.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The actual children.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public virtual IEnumerable<BaseItem> Children => LoadChildren();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// thread-safe access to all recursive children of this folder - without regard to user.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The recursive children.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public IEnumerable<BaseItem> RecursiveChildren => GetRecursiveChildren();
|
|
||||||
|
|
||||||
public override bool IsVisible(User user)
|
public override bool IsVisible(User user)
|
||||||
{
|
{
|
||||||
if (this is ICollectionFolder && !(this is BasePluginFolder))
|
if (this is ICollectionFolder && !(this is BasePluginFolder))
|
||||||
|
@ -1428,8 +1476,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual bool FilterLinkedChildrenPerUser => false;
|
|
||||||
|
|
||||||
public bool ContainsLinkedChildByItemId(Guid itemId)
|
public bool ContainsLinkedChildByItemId(Guid itemId)
|
||||||
{
|
{
|
||||||
var linkedChildren = LinkedChildren;
|
var linkedChildren = LinkedChildren;
|
||||||
|
@ -1530,9 +1576,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
.Where(i => i.Item2 != null);
|
.Where(i => i.Item2 != null);
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
protected override bool SupportsOwnedItems => base.SupportsOwnedItems || SupportsShortcutChildren;
|
|
||||||
|
|
||||||
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var changesFound = false;
|
var changesFound = false;
|
||||||
|
@ -1696,51 +1739,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return !IsPlayed(user);
|
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)
|
public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user, DtoOptions fields)
|
||||||
{
|
{
|
||||||
if (!SupportsUserDataFromChildren)
|
if (!SupportsUserDataFromChildren)
|
||||||
|
|
|
@ -16,6 +16,23 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Genre : BaseItem, IItemByName
|
public class Genre : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the folder containing the item.
|
||||||
|
/// If the item is a folder, it returns the folder itself.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The containing folder path.</value>
|
||||||
|
[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<string> GetUserDataKeys()
|
public override List<string> GetUserDataKeys()
|
||||||
{
|
{
|
||||||
var list = base.GetUserDataKeys();
|
var list = base.GetUserDataKeys();
|
||||||
|
@ -34,20 +51,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the folder containing the item.
|
|
||||||
/// If the item is a folder, it returns the folder itself.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The containing folder path.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public override string ContainingFolderPath => Path;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool IsDisplayedAsFolder => true;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsAncestors => false;
|
|
||||||
|
|
||||||
public override bool IsSaveLocalMetadataEnabled()
|
public override bool IsSaveLocalMetadataEnabled()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
@ -72,9 +75,6 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return LibraryManager.GetItemList(query);
|
return LibraryManager.GetItemList(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPeople => false;
|
|
||||||
|
|
||||||
public static string GetPath(string name)
|
public static string GetPath(string name)
|
||||||
{
|
{
|
||||||
return GetPath(name, true);
|
return GetPath(name, true);
|
||||||
|
@ -107,12 +107,10 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return base.RequiresRefresh();
|
return base.RequiresRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheridoc />
|
||||||
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
/// </summary>
|
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
|
|
||||||
{
|
{
|
||||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
|
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||||
|
|
||||||
var newPath = GetRebasedPath();
|
var newPath = GetRebasedPath();
|
||||||
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
||||||
|
|
|
@ -9,7 +9,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public interface IHasSeries
|
public interface IHasSeries
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the series.
|
/// Gets or sets the name of the series.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The name of the series.</value>
|
/// <value>The name of the series.</value>
|
||||||
string SeriesName { get; set; }
|
string SeriesName { get; set; }
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
#nullable disable
|
||||||
|
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
namespace MediaBrowser.Controller.Entities
|
||||||
|
{
|
||||||
|
public interface IHasShares
|
||||||
|
{
|
||||||
|
Share[] Shares { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,3 @@
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
@ -20,9 +18,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public int? Limit { get; set; }
|
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; }
|
public bool? IsFolder { get; set; }
|
||||||
|
|
||||||
|
@ -58,23 +56,23 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public bool? CollapseBoxSetItems { get; set; }
|
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; }
|
public Guid[] PersonIds { get; set; }
|
||||||
|
|
||||||
|
@ -82,7 +80,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public Guid[] ExcludeItemIds { get; set; }
|
public Guid[] ExcludeItemIds { get; set; }
|
||||||
|
|
||||||
public string AdjacentTo { get; set; }
|
public string? AdjacentTo { get; set; }
|
||||||
|
|
||||||
public string[] PersonTypes { get; set; }
|
public string[] PersonTypes { get; set; }
|
||||||
|
|
||||||
|
@ -182,13 +180,13 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public Guid ParentId { get; set; }
|
public Guid ParentId { get; set; }
|
||||||
|
|
||||||
public string ParentType { get; set; }
|
public string? ParentType { get; set; }
|
||||||
|
|
||||||
public Guid[] AncestorIds { get; set; }
|
public Guid[] AncestorIds { get; set; }
|
||||||
|
|
||||||
public Guid[] TopParentIds { get; set; }
|
public Guid[] TopParentIds { get; set; }
|
||||||
|
|
||||||
public BaseItem Parent
|
public BaseItem? Parent
|
||||||
{
|
{
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
|
@ -213,9 +211,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public SeriesStatus[] SeriesStatuses { get; set; }
|
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; }
|
public Guid[] AlbumIds { get; set; }
|
||||||
|
|
||||||
|
@ -223,9 +221,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public Guid[] ExcludeArtistIds { get; set; }
|
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; }
|
public bool GroupByPresentationUniqueKey { get; set; }
|
||||||
|
|
||||||
|
@ -235,7 +233,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public bool ForceDirect { get; set; }
|
public bool ForceDirect { get; set; }
|
||||||
|
|
||||||
public Dictionary<string, string> ExcludeProviderIds { get; set; }
|
public Dictionary<string, string>? ExcludeProviderIds { get; set; }
|
||||||
|
|
||||||
public bool EnableGroupByMetadataKey { get; set; }
|
public bool EnableGroupByMetadataKey { get; set; }
|
||||||
|
|
||||||
|
@ -253,13 +251,13 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public int MinSimilarityScore { get; set; }
|
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; }
|
public bool? IsDeadArtist { get; set; }
|
||||||
|
|
||||||
|
@ -283,12 +281,10 @@ namespace MediaBrowser.Controller.Entities
|
||||||
ExcludeInheritedTags = Array.Empty<string>();
|
ExcludeInheritedTags = Array.Empty<string>();
|
||||||
ExcludeItemIds = Array.Empty<Guid>();
|
ExcludeItemIds = Array.Empty<Guid>();
|
||||||
ExcludeItemTypes = Array.Empty<string>();
|
ExcludeItemTypes = Array.Empty<string>();
|
||||||
ExcludeProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
ExcludeTags = Array.Empty<string>();
|
ExcludeTags = Array.Empty<string>();
|
||||||
GenreIds = Array.Empty<Guid>();
|
GenreIds = Array.Empty<Guid>();
|
||||||
Genres = Array.Empty<string>();
|
Genres = Array.Empty<string>();
|
||||||
GroupByPresentationUniqueKey = true;
|
GroupByPresentationUniqueKey = true;
|
||||||
HasAnyProviderId = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
ImageTypes = Array.Empty<ImageType>();
|
ImageTypes = Array.Empty<ImageType>();
|
||||||
IncludeItemTypes = Array.Empty<string>();
|
IncludeItemTypes = Array.Empty<string>();
|
||||||
ItemIds = Array.Empty<Guid>();
|
ItemIds = Array.Empty<Guid>();
|
||||||
|
@ -309,32 +305,33 @@ namespace MediaBrowser.Controller.Entities
|
||||||
Years = Array.Empty<int>();
|
Years = Array.Empty<int>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InternalItemsQuery(User user)
|
public InternalItemsQuery(User? user)
|
||||||
: this()
|
: this()
|
||||||
{
|
{
|
||||||
SetUser(user);
|
if (user != null)
|
||||||
|
{
|
||||||
|
SetUser(user);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUser(User user)
|
public void SetUser(User user)
|
||||||
{
|
{
|
||||||
if (user != null)
|
MaxParentalRating = user.MaxParentalAgeRating;
|
||||||
|
|
||||||
|
if (MaxParentalRating.HasValue)
|
||||||
{
|
{
|
||||||
MaxParentalRating = user.MaxParentalAgeRating;
|
string other = UnratedItem.Other.ToString();
|
||||||
|
BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems)
|
||||||
if (MaxParentalRating.HasValue)
|
.Where(i => i != other)
|
||||||
{
|
.Select(e => Enum.Parse<UnratedItem>(e, true)).ToArray();
|
||||||
BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems)
|
|
||||||
.Where(i => i != UnratedItem.Other.ToString())
|
|
||||||
.Select(e => Enum.Parse<UnratedItem>(e, true)).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags);
|
|
||||||
|
|
||||||
User = user;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags);
|
||||||
|
|
||||||
|
User = user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Dictionary<string, string> HasAnyProviderId { get; set; }
|
public Dictionary<string, string>? HasAnyProviderId { get; set; }
|
||||||
|
|
||||||
public Guid[] AlbumArtistIds { get; set; }
|
public Guid[] AlbumArtistIds { get; set; }
|
||||||
|
|
||||||
|
@ -356,8 +353,8 @@ namespace MediaBrowser.Controller.Entities
|
||||||
|
|
||||||
public int? MinWidth { get; set; }
|
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; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,9 +144,9 @@ namespace MediaBrowser.Controller.Entities.Movies
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
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)
|
if (!ProductionYear.HasValue)
|
||||||
{
|
{
|
||||||
|
|
|
@ -36,9 +36,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return info;
|
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)
|
if (!ProductionYear.HasValue)
|
||||||
{
|
{
|
||||||
|
|
|
@ -50,7 +50,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Gets the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself.
|
/// If the item is a folder, it returns the folder itself.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The containing folder path.</value>
|
/// <value>The containing folder path.</value>
|
||||||
|
@ -67,6 +67,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether to enable alpha numeric sorting.
|
||||||
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool EnableAlphaNumericSorting => false;
|
public override bool EnableAlphaNumericSorting => false;
|
||||||
|
|
||||||
|
@ -126,9 +129,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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 or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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();
|
var newPath = GetRebasedPath();
|
||||||
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
||||||
|
|
|
@ -4,11 +4,6 @@
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
public interface IHasShares
|
|
||||||
{
|
|
||||||
Share[] Shares { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Share
|
public class Share
|
||||||
{
|
{
|
||||||
public string UserId { get; set; }
|
public string UserId { get; set; }
|
||||||
|
|
|
@ -29,7 +29,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Gets the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself.
|
/// If the item is a folder, it returns the folder itself.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The containing folder path.</value>
|
/// <value>The containing folder path.</value>
|
||||||
|
@ -105,9 +105,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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 or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
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();
|
var newPath = GetRebasedPath();
|
||||||
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
||||||
|
|
|
@ -34,7 +34,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
|
public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the season in which it aired.
|
/// Gets or sets the season in which it aired.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The aired season.</value>
|
/// <value>The aired season.</value>
|
||||||
public int? AirsBeforeSeasonNumber { get; set; }
|
public int? AirsBeforeSeasonNumber { get; set; }
|
||||||
|
@ -44,7 +44,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
public int? AirsBeforeEpisodeNumber { get; set; }
|
public int? AirsBeforeEpisodeNumber { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is the ending episode number for double episodes.
|
/// Gets or sets the ending episode number for double episodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The index number.</value>
|
/// <value>The index number.</value>
|
||||||
public int? IndexNumberEnd { get; set; }
|
public int? IndexNumberEnd { get; set; }
|
||||||
|
@ -116,7 +116,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This Episode's Series Instance.
|
/// Gets the Episode's Series Instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The series.</value>
|
/// <value>The series.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
@ -261,6 +261,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Guid SeasonId { get; set; }
|
public Guid SeasonId { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public Guid SeriesId { get; set; }
|
public Guid SeriesId { get; set; }
|
||||||
|
|
||||||
|
@ -318,9 +319,9 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
return id;
|
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)
|
if (!IsLocked)
|
||||||
{
|
{
|
||||||
|
@ -328,7 +329,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (LibraryManager.FillMissingEpisodeNumbersFromPath(this, replaceAllMetdata))
|
if (LibraryManager.FillMissingEpisodeNumbersFromPath(this, replaceAllMetadata))
|
||||||
{
|
{
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This Episode's Series Instance.
|
/// Gets this Episode's Series Instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The series.</value>
|
/// <value>The series.</value>
|
||||||
[JsonIgnore]
|
[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.
|
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||||
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))
|
if (!IndexNumber.HasValue && !string.IsNullOrEmpty(Path))
|
||||||
{
|
{
|
||||||
|
|
|
@ -59,8 +59,11 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||||
public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
|
public IReadOnlyList<Guid> RemoteTrailerIds { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// airdate, dvd or absolute.
|
/// Gets or sets the display order.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Valid options are airdate, dvd or absolute.
|
||||||
|
/// </remarks>
|
||||||
public string DisplayOrder { get; set; }
|
public string DisplayOrder { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -45,9 +45,9 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return info;
|
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)
|
if (!ProductionYear.HasValue)
|
||||||
{
|
{
|
||||||
|
|
|
@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
public const double MinLikeValue = 6.5;
|
public const double MinLikeValue = 6.5;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
/// This should never be serialized.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>null</c> if [likes] contains no value, <c>true</c> if [likes]; otherwise, <c>false</c>.</value>
|
/// <value><c>null</c> if [likes] contains no value, <c>true</c> if [likes]; otherwise, <c>false</c>.</value>
|
||||||
|
|
|
@ -23,6 +23,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
private List<Guid> _childrenIds = null;
|
private List<Guid> _childrenIds = null;
|
||||||
private readonly object _childIdsLock = new object();
|
private readonly object _childIdsLock = new object();
|
||||||
|
|
||||||
protected override List<BaseItem> LoadChildren()
|
protected override List<BaseItem> LoadChildren()
|
||||||
{
|
{
|
||||||
lock (_childIdsLock)
|
lock (_childIdsLock)
|
||||||
|
@ -87,10 +88,10 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
ClearCache();
|
ClearCache();
|
||||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
|
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||||
|
|
||||||
if (string.Equals("default", Name, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals("default", Name, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,13 +15,19 @@ namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
public class UserView : Folder, IHasCollectionType
|
public class UserView : Folder, IHasCollectionType
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
|
/// Gets or sets the view type.
|
||||||
|
/// </summary>
|
||||||
public string ViewType { get; set; }
|
public string ViewType { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
|
/// Gets or sets the display parent id.
|
||||||
|
/// </summary>
|
||||||
public new Guid DisplayParentId { get; set; }
|
public new Guid DisplayParentId { get; set; }
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <summary>
|
||||||
|
/// Gets or sets the user id.
|
||||||
|
/// </summary>
|
||||||
public Guid? UserId { get; set; }
|
public Guid? UserId { get; set; }
|
||||||
|
|
||||||
public static ITVSeriesManager TVSeriesManager;
|
public static ITVSeriesManager TVSeriesManager;
|
||||||
|
@ -110,10 +116,10 @@ namespace MediaBrowser.Controller.Entities
|
||||||
return GetChildren(user, false);
|
return GetChildren(user, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string[] UserSpecificViewTypes = new string[]
|
private static readonly string[] UserSpecificViewTypes = new string[]
|
||||||
{
|
{
|
||||||
Model.Entities.CollectionType.Playlists
|
Model.Entities.CollectionType.Playlists
|
||||||
};
|
};
|
||||||
|
|
||||||
public static bool IsUserSpecific(Folder folder)
|
public static bool IsUserSpecific(Folder folder)
|
||||||
{
|
{
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the folder containing the item.
|
/// Gets the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself.
|
/// If the item is a folder, it returns the folder itself.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The containing folder path.</value>
|
/// <value>The containing folder path.</value>
|
||||||
|
@ -112,11 +112,13 @@ namespace MediaBrowser.Controller.Entities
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetdata)
|
/// <param name="replaceAllMetadata">Whether to replace all metadata.</param>
|
||||||
|
/// <returns>true if the item has change, else false.</returns>
|
||||||
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetdata);
|
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||||
|
|
||||||
var newPath = GetRebasedPath();
|
var newPath = GetRebasedPath();
|
||||||
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
if (!string.Equals(Path, newPath, StringComparison.Ordinal))
|
||||||
|
|
|
@ -33,7 +33,7 @@ namespace MediaBrowser.Controller.Extensions
|
||||||
{
|
{
|
||||||
// will throw if input contains invalid unicode chars
|
// will throw if input contains invalid unicode chars
|
||||||
// https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
|
// https://mnaoumov.wordpress.com/2014/06/14/stripping-invalid-characters-from-utf-16-strings/
|
||||||
text = Regex.Replace(text, "([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])", "");
|
text = Regex.Replace(text, "([\ud800-\udbff](?![\udc00-\udfff]))|((?<![\ud800-\udbff])[\udc00-\udfff])", string.Empty);
|
||||||
return Normalize(text, form, false);
|
return Normalize(text, form, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,12 @@ namespace MediaBrowser.Controller.Library
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a set of files into a list of BaseItem.
|
/// Resolves a set of files into a list of BaseItem.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="files">The list of tiles.</param>
|
||||||
|
/// <param name="directoryService">Instance of the <see cref="IDirectoryService"/> interface.</param>
|
||||||
|
/// <param name="parent">The parent folder.</param>
|
||||||
|
/// <param name="libraryOptions">The library options.</param>
|
||||||
|
/// <param name="collectionType">The collection type.</param>
|
||||||
|
/// <returns>The items resolved from the paths.</returns>
|
||||||
IEnumerable<BaseItem> ResolvePaths(
|
IEnumerable<BaseItem> ResolvePaths(
|
||||||
IEnumerable<FileSystemMetadata> files,
|
IEnumerable<FileSystemMetadata> files,
|
||||||
IDirectoryService directoryService,
|
IDirectoryService directoryService,
|
||||||
|
|
|
@ -148,7 +148,7 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
public bool IsNews { get; set; }
|
public bool IsNews { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this instance is kids.
|
/// Gets a value indicating whether this instance is kids.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
public override SourceType SourceType => SourceType.LiveTV;
|
public override SourceType SourceType => SourceType.LiveTV;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The start date of the program, in UTC.
|
/// Gets or sets start date of the program, in UTC.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public DateTime StartDate { get; set; }
|
public DateTime StartDate { get; set; }
|
||||||
|
|
|
@ -28,18 +28,17 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
public string[] Tags { get; set; }
|
public string[] Tags { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Id of the recording.
|
/// Gets or sets the id of the recording.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the series timer identifier.
|
/// Gets or sets the series timer identifier.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The series timer identifier.</value>
|
|
||||||
public string SeriesTimerId { get; set; }
|
public string SeriesTimerId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ChannelId of the recording.
|
/// Gets or sets the channelId of the recording.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ChannelId { get; set; }
|
public string ChannelId { get; set; }
|
||||||
|
|
||||||
|
@ -52,24 +51,24 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
public string ShowId { get; set; }
|
public string ShowId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Name of the recording.
|
/// Gets or sets the name of the recording.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Name { get; set; }
|
public string Name { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Description of the recording.
|
/// Gets or sets the description of the recording.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
|
|
||||||
public string SeriesId { get; set; }
|
public string SeriesId { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The start date of the recording, in UTC.
|
/// Gets or sets the start date of the recording, in UTC.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime StartDate { get; set; }
|
public DateTime StartDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The end date of the recording, in UTC.
|
/// Gets or sets the end date of the recording, in UTC.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DateTime EndDate { get; set; }
|
public DateTime EndDate { get; set; }
|
||||||
|
|
||||||
|
@ -133,7 +132,7 @@ namespace MediaBrowser.Controller.LiveTv
|
||||||
public bool IsSeries { get; set; }
|
public bool IsSeries { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether this instance is live.
|
/// Gets a value indicating whether this instance is live.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if this instance is live; otherwise, <c>false</c>.</value>
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
|
|
|
@ -596,7 +596,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
&& string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
|
&& string.Equals(encodingOptions.HardwareAccelerationType, "nvenc", StringComparison.OrdinalIgnoreCase)
|
||||||
&& isNvdecDecoder)
|
&& 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
|
if (state.IsVideoRequest
|
||||||
|
@ -1070,7 +1071,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc)
|
else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc)
|
||||||
|| string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_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)
|
switch (encodingOptions.EncoderPreset)
|
||||||
{
|
{
|
||||||
case "veryslow":
|
case "veryslow":
|
||||||
|
@ -1251,7 +1251,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase)
|
if (string.Equals(videoEncoder, "h264_amf", StringComparison.OrdinalIgnoreCase)
|
||||||
&& profile.Contains("constrainedbaseline", StringComparison.OrdinalIgnoreCase))
|
&& profile.Contains("baseline", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
profile = "constrained_baseline";
|
profile = "constrained_baseline";
|
||||||
}
|
}
|
||||||
|
@ -2933,6 +2933,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
|
||||||
return threads;
|
return threads;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nullable disable
|
#nullable disable
|
||||||
public void TryStreamCopy(EncodingJobInfo state)
|
public void TryStreamCopy(EncodingJobInfo state)
|
||||||
{
|
{
|
||||||
|
|
|
@ -430,7 +430,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Predicts the audio sample rate that will be in the output stream.
|
/// Gets the target video level.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public double? TargetVideoLevel
|
public double? TargetVideoLevel
|
||||||
{
|
{
|
||||||
|
@ -453,7 +453,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Predicts the audio sample rate that will be in the output stream.
|
/// Gets the target video bit depth.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? TargetVideoBitDepth
|
public int? TargetVideoBitDepth
|
||||||
{
|
{
|
||||||
|
@ -488,7 +488,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Predicts the audio sample rate that will be in the output stream.
|
/// Gets the target framerate.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public float? TargetFramerate
|
public float? TargetFramerate
|
||||||
{
|
{
|
||||||
|
@ -520,7 +520,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Predicts the audio sample rate that will be in the output stream.
|
/// Gets the target packet length.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int? TargetPacketLength
|
public int? TargetPacketLength
|
||||||
{
|
{
|
||||||
|
@ -536,7 +536,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Predicts the audio sample rate that will be in the output stream.
|
/// Gets the target video profile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string TargetVideoProfile
|
public string TargetVideoProfile
|
||||||
{
|
{
|
||||||
|
@ -700,25 +700,4 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
Progress.Report(percentComplete.Value);
|
Progress.Report(percentComplete.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Enum TranscodingJobType.
|
|
||||||
/// </summary>
|
|
||||||
public enum TranscodingJobType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The progressive.
|
|
||||||
/// </summary>
|
|
||||||
Progressive,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The HLS.
|
|
||||||
/// </summary>
|
|
||||||
Hls,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The dash.
|
|
||||||
/// </summary>
|
|
||||||
Dash
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,59 +4,10 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
|
||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.MediaEncoding
|
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; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether this instance has fixed resolution.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if this instance has fixed resolution; otherwise, <c>false</c>.</value>
|
|
||||||
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
|
// For now until api and media encoding layers are unified
|
||||||
public class BaseEncodingJobOptions
|
public class BaseEncodingJobOptions
|
||||||
{
|
{
|
||||||
|
|
|
@ -20,7 +20,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
public interface IMediaEncoder : ITranscoderSupport
|
public interface IMediaEncoder : ITranscoderSupport
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The location of the discovered FFmpeg tool.
|
/// Gets location of the discovered FFmpeg tool.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
FFmpegLocation EncoderLocation { get; }
|
FFmpegLocation EncoderLocation { get; }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
namespace MediaBrowser.Controller.MediaEncoding
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enum TranscodingJobType.
|
||||||
|
/// </summary>
|
||||||
|
public enum TranscodingJobType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The progressive.
|
||||||
|
/// </summary>
|
||||||
|
Progressive,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The HLS.
|
||||||
|
/// </summary>
|
||||||
|
Hls,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The dash.
|
||||||
|
/// </summary>
|
||||||
|
Dash
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,14 +22,14 @@ namespace MediaBrowser.Controller.Playlists
|
||||||
{
|
{
|
||||||
public class Playlist : Folder, IHasShares
|
public class Playlist : Folder, IHasShares
|
||||||
{
|
{
|
||||||
public static string[] SupportedExtensions =
|
public static readonly IReadOnlyList<string> SupportedExtensions = new[]
|
||||||
{
|
{
|
||||||
".m3u",
|
".m3u",
|
||||||
".m3u8",
|
".m3u8",
|
||||||
".pls",
|
".pls",
|
||||||
".wpl",
|
".wpl",
|
||||||
".zpl"
|
".zpl"
|
||||||
};
|
};
|
||||||
|
|
||||||
public Guid OwnerUserId { get; set; }
|
public Guid OwnerUserId { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace MediaBrowser.Controller.Plugins
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that a <see cref="IServerEntryPoint"/> should be invoked as a pre-startup task.
|
||||||
|
/// </summary>
|
||||||
|
public interface IRunBeforeStartup
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,13 +14,7 @@ namespace MediaBrowser.Controller.Plugins
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Run the initialization for this module. This method is invoked at application start.
|
/// Run the initialization for this module. This method is invoked at application start.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
||||||
Task RunAsync();
|
Task RunAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Indicates that a <see cref="IServerEntryPoint"/> should be invoked as a pre-startup task.
|
|
||||||
/// </summary>
|
|
||||||
public interface IRunBeforeStartup
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ namespace MediaBrowser.Controller.Providers
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether all existing data should be overwritten with new data from 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ReplaceAllMetadata { get; set; }
|
public bool ReplaceAllMetadata { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the synced file information.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="id">The identifier.</param>
|
|
||||||
/// <param name="target">The target.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task<SyncedFileInfo>.</returns>
|
|
||||||
Task<SyncedFileInfo> GetSyncedFileInfo(string id, SyncTarget target, CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
namespace MediaBrowser.Controller.Sync
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A marker interface.
|
|
||||||
/// </summary>
|
|
||||||
public interface IRemoteSyncProvider
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Transfers the file.
|
|
||||||
/// </summary>
|
|
||||||
Task<SyncedFileInfo> SendFile(SyncJob syncJob, string originalMediaPath, Stream inputStream, bool isMedia, string[] outputPathParts, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
Task<QueryResult<FileSystemMetadata>> GetFiles(string[] directoryPathParts, SyncTarget target, CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ISupportsDirectCopy
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Sends the file.
|
|
||||||
/// </summary>
|
|
||||||
Task<SyncedFileInfo> SendFile(SyncJob syncJob, string originalMediaPath, string inputPath, bool isMedia, string[] outputPathParts, SyncTarget target, IProgress<double> progress, CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the name.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The name.</value>
|
|
||||||
string Name { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the synchronize targets.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="userId">The user identifier.</param>
|
|
||||||
/// <returns>IEnumerable<SyncTarget>.</returns>
|
|
||||||
List<SyncTarget> GetSyncTargets(string userId);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets all synchronize targets.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>IEnumerable<SyncTarget>.</returns>
|
|
||||||
List<SyncTarget> GetAllSyncTargets();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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<string, string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the path.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The path.</value>
|
|
||||||
public string Path { get; set; }
|
|
||||||
|
|
||||||
public string[] PathParts { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the protocol.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The protocol.</value>
|
|
||||||
public MediaProtocol Protocol { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the required HTTP headers.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The required HTTP headers.</value>
|
|
||||||
public Dictionary<string, string> RequiredHttpHeaders { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the identifier.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The identifier.</value>
|
|
||||||
public string Id { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,16 +6,26 @@ using MediaBrowser.Model.Querying;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.TV
|
namespace MediaBrowser.Controller.TV
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The TV Series manager.
|
||||||
|
/// </summary>
|
||||||
public interface ITVSeriesManager
|
public interface ITVSeriesManager
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the next up.
|
/// Gets the next up.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="query">The next up query.</param>
|
||||||
|
/// <param name="options">The dto options.</param>
|
||||||
|
/// <returns>The next up items.</returns>
|
||||||
QueryResult<BaseItem> GetNextUp(NextUpQuery query, DtoOptions options);
|
QueryResult<BaseItem> GetNextUp(NextUpQuery query, DtoOptions options);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the next up.
|
/// Gets the next up.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="request">The next up request.</param>
|
||||||
|
/// <param name="parentsFolders">The list of parent folders.</param>
|
||||||
|
/// <param name="options">The dto options.</param>
|
||||||
|
/// <returns>The next up items.</returns>
|
||||||
QueryResult<BaseItem> GetNextUp(NextUpQuery request, BaseItem[] parentsFolders, DtoOptions options);
|
QueryResult<BaseItem> GetNextUp(NextUpQuery request, BaseItem[] parentsFolders, DtoOptions options);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -466,7 +466,7 @@ namespace MediaBrowser.LocalMetadata.Images
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool AddImage(IEnumerable<FileSystemMetadata> files, List<LocalImageInfo> images, string name, ImageType type)
|
private bool AddImage(List<FileSystemMetadata> files, List<LocalImageInfo> images, string name, ImageType type)
|
||||||
{
|
{
|
||||||
var image = GetImage(files, name);
|
var image = GetImage(files, name);
|
||||||
|
|
||||||
|
@ -484,9 +484,20 @@ namespace MediaBrowser.LocalMetadata.Images
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FileSystemMetadata? GetImage(IEnumerable<FileSystemMetadata> files, string name)
|
private static FileSystemMetadata? GetImage(IReadOnlyList<FileSystemMetadata> 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,17 +15,6 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
{
|
{
|
||||||
private readonly ILocalizationManager _localization;
|
private readonly ILocalizationManager _localization;
|
||||||
|
|
||||||
private static readonly HashSet<string> SubtitleExtensions = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
|
||||||
{
|
|
||||||
".srt",
|
|
||||||
".ssa",
|
|
||||||
".ass",
|
|
||||||
".sub",
|
|
||||||
".smi",
|
|
||||||
".sami",
|
|
||||||
".vtt"
|
|
||||||
};
|
|
||||||
|
|
||||||
public SubtitleResolver(ILocalizationManager localization)
|
public SubtitleResolver(ILocalizationManager localization)
|
||||||
{
|
{
|
||||||
_localization = localization;
|
_localization = localization;
|
||||||
|
@ -88,6 +77,115 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void AddExternalSubtitleStreams(
|
||||||
|
List<MediaStream> 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<char> 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<char> 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(
|
private void AddExternalSubtitleStreams(
|
||||||
List<MediaStream> streams,
|
List<MediaStream> streams,
|
||||||
string folder,
|
string folder,
|
||||||
|
@ -100,104 +198,5 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
|
|
||||||
AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
|
AddExternalSubtitleStreams(streams, videoPath, startIndex, files);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void AddExternalSubtitleStreams(
|
|
||||||
List<MediaStream> 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,58 +69,52 @@ namespace MediaBrowser.Providers.Music
|
||||||
|
|
||||||
private IEnumerable<RemoteSearchResult> GetResultsFromResponse(Stream stream)
|
private IEnumerable<RemoteSearchResult> 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,
|
||||||
ValidationType = ValidationType.None,
|
IgnoreProcessingInstructions = true,
|
||||||
CheckCharacters = false,
|
IgnoreComments = true
|
||||||
IgnoreProcessingInstructions = true,
|
};
|
||||||
IgnoreComments = true
|
|
||||||
};
|
|
||||||
|
|
||||||
using (var reader = XmlReader.Create(oReader, settings))
|
using var reader = XmlReader.Create(oReader, settings);
|
||||||
{
|
reader.MoveToContent();
|
||||||
reader.MoveToContent();
|
reader.Read();
|
||||||
reader.Read();
|
|
||||||
|
|
||||||
// Loop through each element
|
// Loop through each element
|
||||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
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":
|
reader.Read();
|
||||||
{
|
continue;
|
||||||
if (reader.IsEmptyElement)
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var subReader = reader.ReadSubtree())
|
|
||||||
{
|
|
||||||
return ParseArtistList(subReader).ToList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
reader.Skip();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using var subReader = reader.ReadSubtree();
|
||||||
|
return ParseArtistList(subReader).ToList();
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
default:
|
||||||
{
|
{
|
||||||
reader.Read();
|
reader.Skip();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return Enumerable.Empty<RemoteSearchResult>();
|
else
|
||||||
|
{
|
||||||
|
reader.Read();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Enumerable.Empty<RemoteSearchResult>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<RemoteSearchResult> ParseArtistList(XmlReader reader)
|
private IEnumerable<RemoteSearchResult> ParseArtistList(XmlReader reader)
|
||||||
|
@ -145,13 +139,11 @@ namespace MediaBrowser.Providers.Music
|
||||||
|
|
||||||
var mbzId = reader.GetAttribute("id");
|
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);
|
yield return artist;
|
||||||
if (artist != null)
|
|
||||||
{
|
|
||||||
yield return artist;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -128,53 +128,49 @@ namespace MediaBrowser.Providers.Music
|
||||||
|
|
||||||
private IEnumerable<RemoteSearchResult> GetResultsFromResponse(Stream stream)
|
private IEnumerable<RemoteSearchResult> 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,
|
Name = i.Title,
|
||||||
CheckCharacters = false,
|
ProductionYear = i.Year
|
||||||
IgnoreProcessingInstructions = true,
|
|
||||||
IgnoreComments = true
|
|
||||||
};
|
};
|
||||||
|
|
||||||
using (var reader = XmlReader.Create(oReader, settings))
|
if (i.Artists.Count > 0)
|
||||||
{
|
{
|
||||||
var results = ReleaseResult.Parse(reader);
|
result.AlbumArtist = new RemoteSearchResult
|
||||||
|
|
||||||
return results.Select(i =>
|
|
||||||
{
|
{
|
||||||
var result = new RemoteSearchResult
|
SearchProviderName = Name,
|
||||||
{
|
Name = i.Artists[0].Item1
|
||||||
Name = i.Title,
|
};
|
||||||
ProductionYear = i.Year
|
|
||||||
};
|
|
||||||
|
|
||||||
if (i.Artists.Count > 0)
|
result.AlbumArtist.SetProviderId(MetadataProvider.MusicBrainzArtist, i.Artists[0].Item2);
|
||||||
{
|
|
||||||
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;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(i.ReleaseId))
|
||||||
|
{
|
||||||
|
result.SetProviderId(MetadataProvider.MusicBrainzAlbum, i.ReleaseId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId))
|
||||||
|
{
|
||||||
|
result.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, i.ReleaseGroupId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
@ -339,10 +335,8 @@ namespace MediaBrowser.Providers.Music
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
using (var subReader = reader.ReadSubtree())
|
using var subReader = reader.ReadSubtree();
|
||||||
{
|
return ParseReleaseList(subReader).ToList();
|
||||||
return ParseReleaseList(subReader).ToList();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -383,13 +377,11 @@ namespace MediaBrowser.Providers.Music
|
||||||
|
|
||||||
var releaseId = reader.GetAttribute("id");
|
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);
|
yield return release;
|
||||||
if (release != null)
|
|
||||||
{
|
|
||||||
yield return release;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -460,14 +452,12 @@ namespace MediaBrowser.Providers.Music
|
||||||
|
|
||||||
case "artist-credit":
|
case "artist-credit":
|
||||||
{
|
{
|
||||||
using (var subReader = reader.ReadSubtree())
|
using var subReader = reader.ReadSubtree();
|
||||||
{
|
var artist = ParseArtistCredit(subReader);
|
||||||
var artist = ParseArtistCredit(subReader);
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(artist.Item1))
|
if (!string.IsNullOrEmpty(artist.Item1))
|
||||||
{
|
{
|
||||||
result.Artists.Add(artist);
|
result.Artists.Add(artist);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
@ -505,12 +495,10 @@ namespace MediaBrowser.Providers.Music
|
||||||
switch (reader.Name)
|
switch (reader.Name)
|
||||||
{
|
{
|
||||||
case "name-credit":
|
case "name-credit":
|
||||||
{
|
{
|
||||||
using (var subReader = reader.ReadSubtree())
|
using var subReader = reader.ReadSubtree();
|
||||||
{
|
return ParseArtistNameCredit(subReader);
|
||||||
return ParseArtistNameCredit(subReader);
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
|
@ -545,10 +533,8 @@ namespace MediaBrowser.Providers.Music
|
||||||
case "artist":
|
case "artist":
|
||||||
{
|
{
|
||||||
var id = reader.GetAttribute("id");
|
var id = reader.GetAttribute("id");
|
||||||
using (var subReader = reader.ReadSubtree())
|
using var subReader = reader.ReadSubtree();
|
||||||
{
|
return ParseArtistArtistCredit(subReader, id);
|
||||||
return ParseArtistArtistCredit(subReader, id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -647,47 +633,43 @@ namespace MediaBrowser.Providers.Music
|
||||||
IgnoreComments = true
|
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();
|
if (reader.NodeType == XmlNodeType.Element)
|
||||||
reader.Read();
|
|
||||||
|
|
||||||
// Loop through each element
|
|
||||||
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
|
|
||||||
{
|
{
|
||||||
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;
|
||||||
reader.Read();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var subReader = reader.ReadSubtree())
|
|
||||||
{
|
|
||||||
return GetFirstReleaseGroupId(subReader);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
using var subReader = reader.ReadSubtree();
|
||||||
{
|
return GetFirstReleaseGroupId(subReader);
|
||||||
reader.Skip();
|
}
|
||||||
break;
|
|
||||||
}
|
default:
|
||||||
|
{
|
||||||
|
reader.Skip();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
reader.Read();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
return null;
|
{
|
||||||
|
reader.Read();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetFirstReleaseGroupId(XmlReader reader)
|
private string GetFirstReleaseGroupId(XmlReader reader)
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -55,14 +54,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
|
||||||
return Enumerable.Empty<RemoteImageInfo>();
|
return Enumerable.Empty<RemoteImageInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (personResult?.Images?.Profiles == null)
|
||||||
{
|
{
|
||||||
return Enumerable.Empty<RemoteImageInfo>();
|
return Enumerable.Empty<RemoteImageInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var remoteImages = new RemoteImageInfo[personResult.Images.Profiles.Count];
|
var remoteImages = new RemoteImageInfo[personResult.Images.Profiles.Count];
|
||||||
var language = item.GetPreferredMetadataLanguage();
|
|
||||||
|
|
||||||
for (var i = 0; i < personResult.Images.Profiles.Count; i++)
|
for (var i = 0; i < personResult.Images.Profiles.Count; i++)
|
||||||
{
|
{
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -32,7 +31,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
|
||||||
{
|
{
|
||||||
if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId))
|
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)
|
if (personResult != null)
|
||||||
{
|
{
|
||||||
|
@ -96,7 +95,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
|
||||||
|
|
||||||
if (personTmdbId > 0)
|
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;
|
result.HasMetadata = true;
|
||||||
|
|
||||||
|
|
|
@ -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.
|
/// Gets a person eg. cast or crew member from the TMDb API based on its TMDb id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="personTmdbId">The person's TMDb id.</param>
|
/// <param name="personTmdbId">The person's TMDb id.</param>
|
||||||
|
/// <param name="language">The episode's language.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>The TMDb person information or null if not found.</returns>
|
/// <returns>The TMDb person information or null if not found.</returns>
|
||||||
public async Task<Person> GetPersonAsync(int personTmdbId, CancellationToken cancellationToken)
|
public async Task<Person> 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))
|
if (_memoryCache.TryGetValue(key, out Person person))
|
||||||
{
|
{
|
||||||
return person;
|
return person;
|
||||||
|
@ -290,6 +291,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
|
||||||
|
|
||||||
person = await _tmDbClient.GetPersonAsync(
|
person = await _tmDbClient.GetPersonAsync(
|
||||||
personTmdbId,
|
personTmdbId,
|
||||||
|
TmdbUtils.NormalizeLanguage(language),
|
||||||
PersonMethods.TvCredits | PersonMethods.MovieCredits | PersonMethods.Images | PersonMethods.ExternalIds,
|
PersonMethods.TvCredits | PersonMethods.MovieCredits | PersonMethods.Images | PersonMethods.ExternalIds,
|
||||||
cancellationToken).ConfigureAwait(false);
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
|
||||||
|
|
||||||
if (parts.Length == 2)
|
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();
|
language = parts[0] + "-" + parts[1].ToUpperInvariant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -172,23 +172,19 @@ namespace MediaBrowser.Providers.Studios
|
||||||
|
|
||||||
public IEnumerable<string> GetAvailableImages(string file)
|
public IEnumerable<string> 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<string>();
|
||||||
|
|
||||||
|
foreach (var line in reader.ReadAllLines())
|
||||||
{
|
{
|
||||||
using (var reader = new StreamReader(fileStream))
|
if (!string.IsNullOrWhiteSpace(line))
|
||||||
{
|
{
|
||||||
var lines = new List<string>();
|
lines.Add(line);
|
||||||
|
|
||||||
foreach (var line in reader.ReadAllLines())
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrWhiteSpace(line))
|
|
||||||
{
|
|
||||||
lines.Add(line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return lines;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,48 +187,46 @@ namespace MediaBrowser.Providers.Subtitles
|
||||||
{
|
{
|
||||||
var saveInMediaFolder = libraryOptions.SaveSubtitlesWithMedia;
|
var saveInMediaFolder = libraryOptions.SaveSubtitlesWithMedia;
|
||||||
|
|
||||||
using (var stream = response.Stream)
|
using var stream = response.Stream;
|
||||||
using (var memoryStream = new MemoryStream())
|
using var memoryStream = new MemoryStream();
|
||||||
|
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||||
|
memoryStream.Position = 0;
|
||||||
|
|
||||||
|
var savePaths = new List<string>();
|
||||||
|
var saveFileName = Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.ToLowerInvariant();
|
||||||
|
|
||||||
|
if (response.IsForced)
|
||||||
{
|
{
|
||||||
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
saveFileName += ".forced";
|
||||||
memoryStream.Position = 0;
|
}
|
||||||
|
|
||||||
var savePaths = new List<string>();
|
saveFileName += "." + response.Format.ToLowerInvariant();
|
||||||
var saveFileName = Path.GetFileNameWithoutExtension(video.Path) + "." + response.Language.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)
|
// TODO: Add some error to the user: return BadRequest("Could not save subtitle, bad path.");
|
||||||
{
|
if (internalPath.StartsWith(video.GetInternalMetadataPath(), StringComparison.Ordinal))
|
||||||
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.");
|
savePaths.Add(internalPath);
|
||||||
if (mediaFolderPath.StartsWith(video.ContainingFolderPath, StringComparison.Ordinal))
|
}
|
||||||
{
|
|
||||||
savePaths.Add(mediaFolderPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var internalPath = Path.GetFullPath(Path.Combine(video.GetInternalMetadataPath(), saveFileName));
|
if (savePaths.Count > 0)
|
||||||
|
{
|
||||||
// TODO: Add some error to the user: return BadRequest("Could not save subtitle, bad path.");
|
await TrySaveToFiles(memoryStream, savePaths).ConfigureAwait(false);
|
||||||
if (internalPath.StartsWith(video.GetInternalMetadataPath(), StringComparison.Ordinal))
|
}
|
||||||
{
|
else
|
||||||
savePaths.Add(internalPath);
|
{
|
||||||
}
|
_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));
|
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
|
||||||
|
|
||||||
// use FileShare.None as this bypasses dotnet bug dotnet/runtime#42790 .
|
// 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))
|
using var fs = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, FileStreamBufferSize, true);
|
||||||
{
|
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
||||||
await stream.CopyToAsync(fs).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -15,7 +15,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
|
@ -16,7 +16,7 @@ RUN apt-get update \
|
||||||
|
|
||||||
# Install dotnet repository
|
# Install dotnet repository
|
||||||
# https://dotnet.microsoft.com/download/linux-package-manager/debian9/sdk-current
|
# 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 \
|
&& mkdir -p dotnet-sdk \
|
||||||
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
&& tar -xzf dotnet-sdk.tar.gz -C dotnet-sdk \
|
||||||
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
&& ln -s $( pwd )/dotnet-sdk/dotnet /usr/bin/dotnet
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue