mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-07-24 06:30:56 +02:00
Merge branch 'master' into bindfix
This commit is contained in:
commit
5756c6dbad
|
@ -7,7 +7,7 @@ parameters:
|
||||||
default: "ubuntu-latest"
|
default: "ubuntu-latest"
|
||||||
- name: DotNetSdkVersion
|
- name: DotNetSdkVersion
|
||||||
type: string
|
type: string
|
||||||
default: 5.0.100
|
default: 5.0.103
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: CompatibilityCheck
|
- job: CompatibilityCheck
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
default: "ubuntu-latest"
|
default: "ubuntu-latest"
|
||||||
- name: GeneratorVersion
|
- name: GeneratorVersion
|
||||||
type: string
|
type: string
|
||||||
default: "5.0.0-beta2"
|
default: "5.0.1"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: GenerateApiClients
|
- job: GenerateApiClients
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
parameters:
|
parameters:
|
||||||
LinuxImage: 'ubuntu-latest'
|
LinuxImage: 'ubuntu-latest'
|
||||||
RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
||||||
DotNetSdkVersion: 5.0.100
|
DotNetSdkVersion: 5.0.103
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Build
|
- job: Build
|
||||||
|
|
|
@ -193,6 +193,10 @@ jobs:
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-latest'
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
|
variables:
|
||||||
|
- name: JellyfinVersion
|
||||||
|
value: $[replace(variables['Build.SourceBranch'],'refs/tags/v','')]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: UseDotNet@2
|
- task: UseDotNet@2
|
||||||
displayName: 'Use .NET 5.0 sdk'
|
displayName: 'Use .NET 5.0 sdk'
|
||||||
|
@ -204,9 +208,15 @@ jobs:
|
||||||
displayName: 'Build Stable Nuget packages'
|
displayName: 'Build Stable Nuget packages'
|
||||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||||
inputs:
|
inputs:
|
||||||
command: 'pack'
|
command: 'custom'
|
||||||
packagesToPack: 'Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj'
|
projects: |
|
||||||
versioningScheme: 'off'
|
Jellyfin.Data/Jellyfin.Data.csproj
|
||||||
|
MediaBrowser.Common/MediaBrowser.Common.csproj
|
||||||
|
MediaBrowser.Controller/MediaBrowser.Controller.csproj
|
||||||
|
MediaBrowser.Model/MediaBrowser.Model.csproj
|
||||||
|
Emby.Naming/Emby.Naming.csproj
|
||||||
|
custom: 'pack'
|
||||||
|
arguments: -o $(Build.ArtifactStagingDirectory) -p:Version=$(JellyfinVersion)
|
||||||
|
|
||||||
- task: DotNetCoreCLI@2
|
- task: DotNetCoreCLI@2
|
||||||
displayName: 'Build Unstable Nuget packages'
|
displayName: 'Build Unstable Nuget packages'
|
||||||
|
@ -233,7 +243,7 @@ jobs:
|
||||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||||
inputs:
|
inputs:
|
||||||
command: 'push'
|
command: 'push'
|
||||||
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg;$(Build.ArtifactStagingDirectory)/**/*.snupkg'
|
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'
|
||||||
nuGetFeedType: 'external'
|
nuGetFeedType: 'external'
|
||||||
publishFeedCredentials: 'NugetOrg'
|
publishFeedCredentials: 'NugetOrg'
|
||||||
allowPackageConflicts: true # This ignores an error if the version already exists
|
allowPackageConflicts: true # This ignores an error if the version already exists
|
||||||
|
|
|
@ -10,7 +10,7 @@ parameters:
|
||||||
default: "tests/**/*Tests.csproj"
|
default: "tests/**/*Tests.csproj"
|
||||||
- name: DotNetSdkVersion
|
- name: DotNetSdkVersion
|
||||||
type: string
|
type: string
|
||||||
default: 5.0.100
|
default: 5.0.103
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: Test
|
- job: Test
|
||||||
|
|
|
@ -6,7 +6,7 @@ variables:
|
||||||
- name: RestoreBuildProjects
|
- name: RestoreBuildProjects
|
||||||
value: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
value: 'Jellyfin.Server/Jellyfin.Server.csproj'
|
||||||
- name: DotNetSdkVersion
|
- name: DotNetSdkVersion
|
||||||
value: 5.0.100
|
value: 5.0.103
|
||||||
|
|
||||||
pr:
|
pr:
|
||||||
autoCancel: true
|
autoCancel: true
|
||||||
|
|
30
.drone.yml
30
.drone.yml
|
@ -1,30 +0,0 @@
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: build-debug
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: submodules
|
|
||||||
image: docker:git
|
|
||||||
commands:
|
|
||||||
- git submodule update --init --recursive
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
image: microsoft/dotnet:2-sdk
|
|
||||||
commands:
|
|
||||||
- dotnet publish "Jellyfin.Server" --configuration Debug --output "../ci/ci-debug"
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
name: build-release
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: submodules
|
|
||||||
image: docker:git
|
|
||||||
commands:
|
|
||||||
- git submodule update --init --recursive
|
|
||||||
|
|
||||||
- name: build
|
|
||||||
image: microsoft/dotnet:2-sdk
|
|
||||||
commands:
|
|
||||||
- dotnet publish "Jellyfin.Server" --configuration Release --output "../ci/ci-release"
|
|
||||||
|
|
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
|
@ -24,7 +24,7 @@ jobs:
|
||||||
- name: Setup .NET Core
|
- name: Setup .NET Core
|
||||||
uses: actions/setup-dotnet@v1
|
uses: actions/setup-dotnet@v1
|
||||||
with:
|
with:
|
||||||
dotnet-version: '5.0.100'
|
dotnet-version: '5.0.x'
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v1
|
||||||
with:
|
with:
|
||||||
|
|
|
@ -80,6 +80,7 @@
|
||||||
- [nvllsvm](https://github.com/nvllsvm)
|
- [nvllsvm](https://github.com/nvllsvm)
|
||||||
- [nyanmisaka](https://github.com/nyanmisaka)
|
- [nyanmisaka](https://github.com/nyanmisaka)
|
||||||
- [OancaAndrei](https://github.com/OancaAndrei)
|
- [OancaAndrei](https://github.com/OancaAndrei)
|
||||||
|
- [obradovichv](https://github.com/obradovichv)
|
||||||
- [oddstr13](https://github.com/oddstr13)
|
- [oddstr13](https://github.com/oddstr13)
|
||||||
- [orryverducci](https://github.com/orryverducci)
|
- [orryverducci](https://github.com/orryverducci)
|
||||||
- [petermcneil](https://github.com/petermcneil)
|
- [petermcneil](https://github.com/petermcneil)
|
||||||
|
|
|
@ -96,6 +96,7 @@ namespace Emby.Dlna.Didl
|
||||||
|
|
||||||
using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
|
using (StringWriter builder = new StringWriterWithEncoding(Encoding.UTF8))
|
||||||
{
|
{
|
||||||
|
// If this using are changed to single lines, then write.Flush needs to be appended before the return.
|
||||||
using (var writer = XmlWriter.Create(builder, settings))
|
using (var writer = XmlWriter.Create(builder, settings))
|
||||||
{
|
{
|
||||||
// writer.WriteStartDocument();
|
// writer.WriteStartDocument();
|
||||||
|
|
|
@ -315,7 +315,7 @@ namespace Emby.Dlna.Main
|
||||||
var uri = new UriBuilder(_appHost.GetSmartApiUrl(address.Address) + descriptorUri);
|
var uri = new UriBuilder(_appHost.GetSmartApiUrl(address.Address) + descriptorUri);
|
||||||
// DLNA will only work over http, so we must reset to http:// : {port}
|
// DLNA will only work over http, so we must reset to http:// : {port}
|
||||||
uri.Scheme = "http://";
|
uri.Scheme = "http://";
|
||||||
uri.Port = _netConfig.PublicPort;
|
uri.Port = _netConfig.HttpServerPortNumber;
|
||||||
|
|
||||||
var device = new SsdpRootDevice
|
var device = new SsdpRootDevice
|
||||||
{
|
{
|
||||||
|
|
|
@ -235,7 +235,13 @@ namespace Emby.Dlna.PlayTo
|
||||||
_logger.LogDebug("Setting mute");
|
_logger.LogDebug("Setting mute");
|
||||||
var value = mute ? 1 : 0;
|
var value = mute ? 1 : 0;
|
||||||
|
|
||||||
await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
|
await new SsdpHttpClient(_httpClientFactory)
|
||||||
|
.SendCommandAsync(
|
||||||
|
Properties.BaseUrl,
|
||||||
|
service,
|
||||||
|
command.Name,
|
||||||
|
rendererCommands.BuildPost(command, service.ServiceType, value),
|
||||||
|
cancellationToken: cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
IsMuted = mute;
|
IsMuted = mute;
|
||||||
|
@ -270,7 +276,13 @@ namespace Emby.Dlna.PlayTo
|
||||||
// Remote control will perform better
|
// Remote control will perform better
|
||||||
Volume = value;
|
Volume = value;
|
||||||
|
|
||||||
await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, rendererCommands.BuildPost(command, service.ServiceType, value))
|
await new SsdpHttpClient(_httpClientFactory)
|
||||||
|
.SendCommandAsync(
|
||||||
|
Properties.BaseUrl,
|
||||||
|
service,
|
||||||
|
command.Name,
|
||||||
|
rendererCommands.BuildPost(command, service.ServiceType, value),
|
||||||
|
cancellationToken: cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,7 +303,13 @@ namespace Emby.Dlna.PlayTo
|
||||||
throw new InvalidOperationException("Unable to find service");
|
throw new InvalidOperationException("Unable to find service");
|
||||||
}
|
}
|
||||||
|
|
||||||
await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"))
|
await new SsdpHttpClient(_httpClientFactory)
|
||||||
|
.SendCommandAsync(
|
||||||
|
Properties.BaseUrl,
|
||||||
|
service,
|
||||||
|
command.Name,
|
||||||
|
avCommands.BuildPost(command, service.ServiceType, string.Format(CultureInfo.InvariantCulture, "{0:hh}:{0:mm}:{0:ss}", value), "REL_TIME"),
|
||||||
|
cancellationToken: cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
RestartTimer(true);
|
RestartTimer(true);
|
||||||
|
@ -325,14 +343,21 @@ namespace Emby.Dlna.PlayTo
|
||||||
}
|
}
|
||||||
|
|
||||||
var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
|
var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
|
||||||
await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header)
|
await new SsdpHttpClient(_httpClientFactory)
|
||||||
|
.SendCommandAsync(
|
||||||
|
Properties.BaseUrl,
|
||||||
|
service,
|
||||||
|
command.Name,
|
||||||
|
post,
|
||||||
|
header: header,
|
||||||
|
cancellationToken: cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
await Task.Delay(50).ConfigureAwait(false);
|
await Task.Delay(50, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await SetPlay(avCommands, CancellationToken.None).ConfigureAwait(false);
|
await SetPlay(avCommands, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -396,7 +421,13 @@ namespace Emby.Dlna.PlayTo
|
||||||
|
|
||||||
var service = GetAvTransportService();
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
|
await new SsdpHttpClient(_httpClientFactory)
|
||||||
|
.SendCommandAsync(
|
||||||
|
Properties.BaseUrl,
|
||||||
|
service,
|
||||||
|
command.Name,
|
||||||
|
avCommands.BuildPost(command, service.ServiceType, 1),
|
||||||
|
cancellationToken: cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
RestartTimer(true);
|
RestartTimer(true);
|
||||||
|
@ -414,7 +445,13 @@ namespace Emby.Dlna.PlayTo
|
||||||
|
|
||||||
var service = GetAvTransportService();
|
var service = GetAvTransportService();
|
||||||
|
|
||||||
await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, avCommands.BuildPost(command, service.ServiceType, 1))
|
await new SsdpHttpClient(_httpClientFactory)
|
||||||
|
.SendCommandAsync(
|
||||||
|
Properties.BaseUrl,
|
||||||
|
service,
|
||||||
|
command.Name,
|
||||||
|
avCommands.BuildPost(command, service.ServiceType, 1),
|
||||||
|
cancellationToken: cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
TransportState = TransportState.Paused;
|
TransportState = TransportState.Paused;
|
||||||
|
@ -990,7 +1027,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
|
|
||||||
var deviceProperties = new DeviceInfo()
|
var deviceProperties = new DeviceInfo()
|
||||||
{
|
{
|
||||||
Name = string.Join(" ", friendlyNames),
|
Name = string.Join(' ', friendlyNames),
|
||||||
BaseUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", url.Host, url.Port)
|
BaseUrl = string.Format(CultureInfo.InvariantCulture, "http://{0}:{1}", url.Host, url.Port)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -777,7 +777,7 @@ namespace Emby.Dlna.PlayTo
|
||||||
var currentWait = 0;
|
var currentWait = 0;
|
||||||
while (_device.TransportState != TransportState.Playing && currentWait < MaxWait)
|
while (_device.TransportState != TransportState.Playing && currentWait < MaxWait)
|
||||||
{
|
{
|
||||||
await Task.Delay(Interval).ConfigureAwait(false);
|
await Task.Delay(Interval, cancellationToken).ConfigureAwait(false);
|
||||||
currentWait += Interval;
|
currentWait += Interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -896,16 +896,16 @@ namespace Emby.Dlna.PlayTo
|
||||||
|
|
||||||
var parts = url.Split('/');
|
var parts = url.Split('/');
|
||||||
|
|
||||||
for (var i = 0; i < parts.Length; i++)
|
for (var i = 0; i < parts.Length - 1; i++)
|
||||||
{
|
{
|
||||||
var part = parts[i];
|
var part = parts[i];
|
||||||
|
|
||||||
if (string.Equals(part, "audio", StringComparison.OrdinalIgnoreCase) ||
|
if (string.Equals(part, "audio", StringComparison.OrdinalIgnoreCase) ||
|
||||||
string.Equals(part, "videos", StringComparison.OrdinalIgnoreCase))
|
string.Equals(part, "videos", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (parts.Length > i + 1)
|
if (Guid.TryParse(parts[i + 1], out var result))
|
||||||
{
|
{
|
||||||
return Guid.Parse(parts[i + 1]);
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Emby.Dlna.Profiles
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
Identification = new DeviceIdentification
|
||||||
{
|
{
|
||||||
FriendlyName = @"KDL-\d{2}[EHLNPB]X\d[01]\d.*",
|
FriendlyName = @"KDL-[0-9]{2}[EHLNPB]X[0-9][01][0-9].*",
|
||||||
Manufacturer = "Sony",
|
Manufacturer = "Sony",
|
||||||
|
|
||||||
Headers = new[]
|
Headers = new[]
|
||||||
|
@ -21,7 +21,7 @@ namespace Emby.Dlna.Profiles
|
||||||
new HttpHeaderInfo
|
new HttpHeaderInfo
|
||||||
{
|
{
|
||||||
Name = "X-AV-Client-Info",
|
Name = "X-AV-Client-Info",
|
||||||
Value = @".*KDL-\d{2}[EHLNPB]X\d[01]\d.*",
|
Value = @".*KDL-[0-9]{2}[EHLNPB]X[0-9][01][0-9].*",
|
||||||
Match = HeaderMatchType.Regex
|
Match = HeaderMatchType.Regex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Emby.Dlna.Profiles
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
Identification = new DeviceIdentification
|
||||||
{
|
{
|
||||||
FriendlyName = @"KDL-\d{2}([A-Z]X\d2\d|CX400).*",
|
FriendlyName = @"KDL-[0-9]{2}([A-Z]X[0-9]2[0-9]|CX400).*",
|
||||||
Manufacturer = "Sony",
|
Manufacturer = "Sony",
|
||||||
|
|
||||||
Headers = new[]
|
Headers = new[]
|
||||||
|
@ -21,7 +21,7 @@ namespace Emby.Dlna.Profiles
|
||||||
new HttpHeaderInfo
|
new HttpHeaderInfo
|
||||||
{
|
{
|
||||||
Name = "X-AV-Client-Info",
|
Name = "X-AV-Client-Info",
|
||||||
Value = @".*KDL-\d{2}([A-Z]X\d2\d|CX400).*",
|
Value = @".*KDL-[0-9]{2}([A-Z]X[0-9]2[0-9]|CX400).*",
|
||||||
Match = HeaderMatchType.Regex
|
Match = HeaderMatchType.Regex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Emby.Dlna.Profiles
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
Identification = new DeviceIdentification
|
||||||
{
|
{
|
||||||
FriendlyName = @"KDL-\d{2}[A-Z]X\d5(\d|G).*",
|
FriendlyName = @"KDL-[0-9]{2}[A-Z]X[0-9]5([0-9]|G).*",
|
||||||
Manufacturer = "Sony",
|
Manufacturer = "Sony",
|
||||||
|
|
||||||
Headers = new[]
|
Headers = new[]
|
||||||
|
@ -21,7 +21,7 @@ namespace Emby.Dlna.Profiles
|
||||||
new HttpHeaderInfo
|
new HttpHeaderInfo
|
||||||
{
|
{
|
||||||
Name = "X-AV-Client-Info",
|
Name = "X-AV-Client-Info",
|
||||||
Value = @".*KDL-\d{2}[A-Z]X\d5(\d|G).*",
|
Value = @".*KDL-[0-9]{2}[A-Z]X[0-9]5([0-9]|G).*",
|
||||||
Match = HeaderMatchType.Regex
|
Match = HeaderMatchType.Regex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Emby.Dlna.Profiles
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
Identification = new DeviceIdentification
|
||||||
{
|
{
|
||||||
FriendlyName = @"KDL-\d{2}[WR][5689]\d{2}A.*",
|
FriendlyName = @"KDL-[0-9]{2}[WR][5689][0-9]{2}A.*",
|
||||||
Manufacturer = "Sony",
|
Manufacturer = "Sony",
|
||||||
|
|
||||||
Headers = new[]
|
Headers = new[]
|
||||||
|
@ -21,7 +21,7 @@ namespace Emby.Dlna.Profiles
|
||||||
new HttpHeaderInfo
|
new HttpHeaderInfo
|
||||||
{
|
{
|
||||||
Name = "X-AV-Client-Info",
|
Name = "X-AV-Client-Info",
|
||||||
Value = @".*KDL-\d{2}[WR][5689]\d{2}A.*",
|
Value = @".*KDL-[0-9]{2}[WR][5689][0-9]{2}A.*",
|
||||||
Match = HeaderMatchType.Regex
|
Match = HeaderMatchType.Regex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace Emby.Dlna.Profiles
|
||||||
|
|
||||||
Identification = new DeviceIdentification
|
Identification = new DeviceIdentification
|
||||||
{
|
{
|
||||||
FriendlyName = @"(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*",
|
FriendlyName = @"(KDL-[0-9]{2}W[5-9][0-9]{2}B|KDL-[0-9]{2}R480|XBR-[0-9]{2}X[89][0-9]{2}B|KD-[0-9]{2}[SX][89][0-9]{3}B).*",
|
||||||
Manufacturer = "Sony",
|
Manufacturer = "Sony",
|
||||||
|
|
||||||
Headers = new[]
|
Headers = new[]
|
||||||
|
@ -21,7 +21,7 @@ namespace Emby.Dlna.Profiles
|
||||||
new HttpHeaderInfo
|
new HttpHeaderInfo
|
||||||
{
|
{
|
||||||
Name = "X-AV-Client-Info",
|
Name = "X-AV-Client-Info",
|
||||||
Value = @".*(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*",
|
Value = @".*(KDL-[0-9]{2}W[5-9][0-9]{2}B|KDL-[0-9]{2}R480|XBR-[0-9]{2}X[89][0-9]{2}B|KD-[0-9]{2}[SX][89][0-9]{3}B).*",
|
||||||
Match = HeaderMatchType.Regex
|
Match = HeaderMatchType.Regex
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
<Name>Sony Bravia (2010)</Name>
|
<Name>Sony Bravia (2010)</Name>
|
||||||
<Identification>
|
<Identification>
|
||||||
<FriendlyName>KDL-\d{2}[EHLNPB]X\d[01]\d.*</FriendlyName>
|
<FriendlyName>KDL-[0-9]{2}[EHLNPB]X[0-9][01][0-9].*</FriendlyName>
|
||||||
<Manufacturer>Sony</Manufacturer>
|
<Manufacturer>Sony</Manufacturer>
|
||||||
<Headers>
|
<Headers>
|
||||||
<HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-\d{2}[EHLNPB]X\d[01]\d.*" match="Regex" />
|
<HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-[0-9]{2}[EHLNPB]X[0-9][01][0-9].*" match="Regex" />
|
||||||
</Headers>
|
</Headers>
|
||||||
</Identification>
|
</Identification>
|
||||||
<Manufacturer>Microsoft Corporation</Manufacturer>
|
<Manufacturer>Microsoft Corporation</Manufacturer>
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
<Name>Sony Bravia (2011)</Name>
|
<Name>Sony Bravia (2011)</Name>
|
||||||
<Identification>
|
<Identification>
|
||||||
<FriendlyName>KDL-\d{2}([A-Z]X\d2\d|CX400).*</FriendlyName>
|
<FriendlyName>KDL-[0-9]{2}([A-Z]X[0-9]2[0-9]|CX400).*</FriendlyName>
|
||||||
<Manufacturer>Sony</Manufacturer>
|
<Manufacturer>Sony</Manufacturer>
|
||||||
<Headers>
|
<Headers>
|
||||||
<HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-\d{2}([A-Z]X\d2\d|CX400).*" match="Regex" />
|
<HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-[0-9]{2}([A-Z]X[0-9]2[0-9]|CX400).*" match="Regex" />
|
||||||
</Headers>
|
</Headers>
|
||||||
</Identification>
|
</Identification>
|
||||||
<Manufacturer>Microsoft Corporation</Manufacturer>
|
<Manufacturer>Microsoft Corporation</Manufacturer>
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
<Name>Sony Bravia (2012)</Name>
|
<Name>Sony Bravia (2012)</Name>
|
||||||
<Identification>
|
<Identification>
|
||||||
<FriendlyName>KDL-\d{2}[A-Z]X\d5(\d|G).*</FriendlyName>
|
<FriendlyName>KDL-[0-9]{2}[A-Z]X[0-9]5([0-9]|G).*</FriendlyName>
|
||||||
<Manufacturer>Sony</Manufacturer>
|
<Manufacturer>Sony</Manufacturer>
|
||||||
<Headers>
|
<Headers>
|
||||||
<HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-\d{2}[A-Z]X\d5(\d|G).*" match="Regex" />
|
<HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-[0-9]{2}[A-Z]X[0-9]5([0-9]|G).*" match="Regex" />
|
||||||
</Headers>
|
</Headers>
|
||||||
</Identification>
|
</Identification>
|
||||||
<Manufacturer>Microsoft Corporation</Manufacturer>
|
<Manufacturer>Microsoft Corporation</Manufacturer>
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
<Name>Sony Bravia (2013)</Name>
|
<Name>Sony Bravia (2013)</Name>
|
||||||
<Identification>
|
<Identification>
|
||||||
<FriendlyName>KDL-\d{2}[WR][5689]\d{2}A.*</FriendlyName>
|
<FriendlyName>KDL-[0-9]{2}[WR][5689][0-9]{2}A.*</FriendlyName>
|
||||||
<Manufacturer>Sony</Manufacturer>
|
<Manufacturer>Sony</Manufacturer>
|
||||||
<Headers>
|
<Headers>
|
||||||
<HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-\d{2}[WR][5689]\d{2}A.*" match="Regex" />
|
<HttpHeaderInfo name="X-AV-Client-Info" value=".*KDL-[0-9]{2}[WR][5689][0-9]{2}A.*" match="Regex" />
|
||||||
</Headers>
|
</Headers>
|
||||||
</Identification>
|
</Identification>
|
||||||
<Manufacturer>Microsoft Corporation</Manufacturer>
|
<Manufacturer>Microsoft Corporation</Manufacturer>
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||||
<Name>Sony Bravia (2014)</Name>
|
<Name>Sony Bravia (2014)</Name>
|
||||||
<Identification>
|
<Identification>
|
||||||
<FriendlyName>(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*</FriendlyName>
|
<FriendlyName>(KDL-[0-9]{2}W[5-9][0-9]{2}B|KDL-[0-9]{2}R480|XBR-[0-9]{2}X[89][0-9]{2}B|KD-[0-9]{2}[SX][89][0-9]{3}B).*</FriendlyName>
|
||||||
<Manufacturer>Sony</Manufacturer>
|
<Manufacturer>Sony</Manufacturer>
|
||||||
<Headers>
|
<Headers>
|
||||||
<HttpHeaderInfo name="X-AV-Client-Info" value=".*(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*" match="Regex" />
|
<HttpHeaderInfo name="X-AV-Client-Info" value=".*(KDL-[0-9]{2}W[5-9][0-9]{2}B|KDL-[0-9]{2}R480|XBR-[0-9]{2}X[89][0-9]{2}B|KD-[0-9]{2}[SX][89][0-9]{3}B).*" match="Regex" />
|
||||||
</Headers>
|
</Headers>
|
||||||
</Identification>
|
</Identification>
|
||||||
<Manufacturer>Microsoft Corporation</Manufacturer>
|
<Manufacturer>Microsoft Corporation</Manufacturer>
|
||||||
|
|
|
@ -15,13 +15,13 @@ namespace Emby.Naming.AudioBook
|
||||||
/// <param name="files">List of files composing the actual audiobook.</param>
|
/// <param name="files">List of files composing the actual audiobook.</param>
|
||||||
/// <param name="extras">List of extra files.</param>
|
/// <param name="extras">List of extra files.</param>
|
||||||
/// <param name="alternateVersions">Alternative version of files.</param>
|
/// <param name="alternateVersions">Alternative version of files.</param>
|
||||||
public AudioBookInfo(string name, int? year, List<AudioBookFileInfo>? files, List<AudioBookFileInfo>? extras, List<AudioBookFileInfo>? alternateVersions)
|
public AudioBookInfo(string name, int? year, List<AudioBookFileInfo> files, List<AudioBookFileInfo> extras, List<AudioBookFileInfo> alternateVersions)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Year = year;
|
Year = year;
|
||||||
Files = files ?? new List<AudioBookFileInfo>();
|
Files = files;
|
||||||
Extras = extras ?? new List<AudioBookFileInfo>();
|
Extras = extras;
|
||||||
AlternateVersions = alternateVersions ?? new List<AudioBookFileInfo>();
|
AlternateVersions = alternateVersions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -73,7 +73,7 @@ namespace Emby.Naming.AudioBook
|
||||||
|
|
||||||
var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber != null || x.PartNumber != null);
|
var haveChaptersOrPages = stackFiles.Any(x => x.ChapterNumber != null || x.PartNumber != null);
|
||||||
var groupedBy = stackFiles.GroupBy(file => new { file.ChapterNumber, file.PartNumber });
|
var groupedBy = stackFiles.GroupBy(file => new { file.ChapterNumber, file.PartNumber });
|
||||||
var nameWithReplacedDots = nameParserResult.Name.Replace(" ", ".");
|
var nameWithReplacedDots = nameParserResult.Name.Replace(' ', '.');
|
||||||
|
|
||||||
foreach (var group in groupedBy)
|
foreach (var group in groupedBy)
|
||||||
{
|
{
|
||||||
|
|
|
@ -282,7 +282,13 @@ namespace Emby.Naming.Common
|
||||||
SupportsAbsoluteEpisodeNumbers = true
|
SupportsAbsoluteEpisodeNumbers = true
|
||||||
},
|
},
|
||||||
|
|
||||||
// Case Closed (1996-2007)/Case Closed - 317.mkv
|
// Not a Kodi rule as well, but below rule also causes false positives for triple-digit episode names
|
||||||
|
// [bar] Foo - 1 [baz] special case of below expression to prevent false positives with digits in the series name
|
||||||
|
new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>[\w\s]+?)[\s_]*-[\s_]*(?<epnumber>[0-9]+).*$")
|
||||||
|
{
|
||||||
|
IsNamed = true
|
||||||
|
},
|
||||||
|
|
||||||
// /server/anything_102.mp4
|
// /server/anything_102.mp4
|
||||||
// /server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv
|
// /server/james.corden.2017.04.20.anne.hathaway.720p.hdtv.x264-crooks.mkv
|
||||||
// /server/anything_1996.11.14.mp4
|
// /server/anything_1996.11.14.mp4
|
||||||
|
@ -299,11 +305,6 @@ namespace Emby.Naming.Common
|
||||||
|
|
||||||
// *** End Kodi Standard Naming
|
// *** End Kodi Standard Naming
|
||||||
|
|
||||||
// [bar] Foo - 1 [baz]
|
|
||||||
new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>[\w\s]+?)[-\s_]+(?<epnumber>[0-9]+).*$")
|
|
||||||
{
|
|
||||||
IsNamed = true
|
|
||||||
},
|
|
||||||
new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
|
new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
|
||||||
{
|
{
|
||||||
IsNamed = true
|
IsNamed = true
|
||||||
|
@ -587,7 +588,7 @@ namespace Emby.Naming.Common
|
||||||
AudioBookNamesExpressions = new[]
|
AudioBookNamesExpressions = new[]
|
||||||
{
|
{
|
||||||
// Detect year usually in brackets after name Batman (2020)
|
// Detect year usually in brackets after name Batman (2020)
|
||||||
@"^(?<name>.+?)\s*\(\s*(?<year>\d{4})\s*\)\s*$",
|
@"^(?<name>.+?)\s*\(\s*(?<year>[0-9]{4})\s*\)\s*$",
|
||||||
@"^\s*(?<name>[^ ].*?)\s*$"
|
@"^\s*(?<name>[^ ].*?)\s*$"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Authors>Jellyfin Contributors</Authors>
|
<Authors>Jellyfin Contributors</Authors>
|
||||||
<PackageId>Jellyfin.Naming</PackageId>
|
<PackageId>Jellyfin.Naming</PackageId>
|
||||||
<VersionPrefix>10.7.0</VersionPrefix>
|
<VersionPrefix>10.8.0</VersionPrefix>
|
||||||
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
|
||||||
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
|
@ -60,7 +60,7 @@ namespace Emby.Naming.TV
|
||||||
bool supportSpecialAliases,
|
bool supportSpecialAliases,
|
||||||
bool supportNumericSeasonFolders)
|
bool supportNumericSeasonFolders)
|
||||||
{
|
{
|
||||||
var filename = Path.GetFileName(path) ?? string.Empty;
|
string filename = Path.GetFileName(path);
|
||||||
|
|
||||||
if (supportSpecialAliases)
|
if (supportSpecialAliases)
|
||||||
{
|
{
|
||||||
|
|
|
@ -185,8 +185,8 @@ namespace Emby.Naming.Video
|
||||||
if (!string.IsNullOrEmpty(folderName)
|
if (!string.IsNullOrEmpty(folderName)
|
||||||
&& folderName.Length > 1
|
&& folderName.Length > 1
|
||||||
&& videos.All(i => i.Files.Count == 1
|
&& videos.All(i => i.Files.Count == 1
|
||||||
&& IsEligibleForMultiVersion(folderName, i.Files[0].Path))
|
&& IsEligibleForMultiVersion(folderName, i.Files[0].Path))
|
||||||
&& HaveSameYear(videos))
|
&& HaveSameYear(videos))
|
||||||
{
|
{
|
||||||
var ordered = videos.OrderBy(i => i.Name).ToList();
|
var ordered = videos.OrderBy(i => i.Name).ToList();
|
||||||
|
|
||||||
|
@ -216,10 +216,9 @@ namespace Emby.Naming.Video
|
||||||
return videos.Select(i => i.Year ?? -1).Distinct().Count() < 2;
|
return videos.Select(i => i.Year ?? -1).Distinct().Count() < 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsEligibleForMultiVersion(string folderName, string? testFilename)
|
private bool IsEligibleForMultiVersion(string folderName, string testFilePath)
|
||||||
{
|
{
|
||||||
testFilename = Path.GetFileNameWithoutExtension(testFilename) ?? string.Empty;
|
string testFilename = Path.GetFileNameWithoutExtension(testFilePath);
|
||||||
|
|
||||||
if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase))
|
if (testFilename.StartsWith(folderName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (CleanStringParser.TryClean(testFilename, _options.CleanStringRegexes, out var cleanName))
|
if (CleanStringParser.TryClean(testFilename, _options.CleanStringRegexes, out var cleanName))
|
||||||
|
@ -233,8 +232,8 @@ namespace Emby.Naming.Video
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.IsNullOrEmpty(testFilename)
|
return string.IsNullOrEmpty(testFilename)
|
||||||
|| testFilename[0].Equals('-')
|
|| testFilename[0] == '-'
|
||||||
|| testFilename[0].Equals('_')
|
|| testFilename[0] == '_'
|
||||||
|| string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty));
|
|| string.IsNullOrWhiteSpace(Regex.Replace(testFilename, @"\[([^]]*)\]", string.Empty));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ 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) ?? string.Empty;
|
var extension = Path.GetExtension(path);
|
||||||
return _options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
return _options.VideoFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ 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) ?? string.Empty;
|
var extension = Path.GetExtension(path);
|
||||||
return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
return _options.StubFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -75,10 +75,6 @@ namespace Emby.Notifications
|
||||||
Type = NotificationType.VideoPlaybackStopped.ToString()
|
Type = NotificationType.VideoPlaybackStopped.ToString()
|
||||||
},
|
},
|
||||||
new NotificationTypeInfo
|
new NotificationTypeInfo
|
||||||
{
|
|
||||||
Type = NotificationType.CameraImageUploaded.ToString()
|
|
||||||
},
|
|
||||||
new NotificationTypeInfo
|
|
||||||
{
|
{
|
||||||
Type = NotificationType.UserLockedOut.ToString()
|
Type = NotificationType.UserLockedOut.ToString()
|
||||||
},
|
},
|
||||||
|
@ -114,10 +110,6 @@ namespace Emby.Notifications
|
||||||
{
|
{
|
||||||
note.Category = _localization.GetLocalizedString("Plugin");
|
note.Category = _localization.GetLocalizedString("Plugin");
|
||||||
}
|
}
|
||||||
else if (note.Type.IndexOf("CameraImageUploaded", StringComparison.OrdinalIgnoreCase) != -1)
|
|
||||||
{
|
|
||||||
note.Category = _localization.GetLocalizedString("Sync");
|
|
||||||
}
|
|
||||||
else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1)
|
else if (note.Type.IndexOf("UserLockedOut", StringComparison.OrdinalIgnoreCase) != -1)
|
||||||
{
|
{
|
||||||
note.Category = _localization.GetLocalizedString("User");
|
note.Category = _localization.GetLocalizedString("User");
|
||||||
|
|
|
@ -336,19 +336,19 @@ namespace Emby.Server.Implementations.Channels
|
||||||
return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).Result;
|
return GetChannel(GetInternalChannelId(channel.Name)) ?? GetChannel(channel, CancellationToken.None).Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<MediaSourceInfo> GetSavedMediaSources(BaseItem item)
|
private MediaSourceInfo[] GetSavedMediaSources(BaseItem item)
|
||||||
{
|
{
|
||||||
var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json");
|
var path = Path.Combine(item.GetInternalMetadataPath(), "channelmediasourceinfos.json");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var jsonString = File.ReadAllText(path, Encoding.UTF8);
|
var bytes = File.ReadAllBytes(path);
|
||||||
return JsonSerializer.Deserialize<List<MediaSourceInfo>>(jsonString, _jsonOptions)
|
return JsonSerializer.Deserialize<MediaSourceInfo[]>(bytes, _jsonOptions)
|
||||||
?? new List<MediaSourceInfo>();
|
?? Array.Empty<MediaSourceInfo>();
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
return new List<MediaSourceInfo>();
|
return Array.Empty<MediaSourceInfo>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using SQLitePCL.pretty;
|
using SQLitePCL.pretty;
|
||||||
|
|
||||||
|
@ -59,7 +60,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
connection.RunInTransaction(conn =>
|
connection.RunInTransaction(conn =>
|
||||||
{
|
{
|
||||||
conn.ExecuteAll(string.Join(";", queries));
|
conn.ExecuteAll(string.Join(';', queries));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,11 +143,10 @@ namespace Emby.Server.Implementations.Data
|
||||||
return result[index].ReadGuidFromBlob();
|
return result[index].ReadGuidFromBlob();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Conditional("DEBUG")]
|
||||||
private static void CheckName(string name)
|
private static void CheckName(string name)
|
||||||
{
|
{
|
||||||
#if DEBUG
|
|
||||||
throw new ArgumentException("Invalid param name: " + name, nameof(name));
|
throw new ArgumentException("Invalid param name: " + name, nameof(name));
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void TryBind(this IStatement statement, string name, double value)
|
public static void TryBind(this IStatement statement, string name, double value)
|
||||||
|
|
|
@ -687,7 +687,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (item.Genres.Length > 0)
|
if (item.Genres.Length > 0)
|
||||||
{
|
{
|
||||||
saveItemStatement.TryBind("@Genres", string.Join("|", item.Genres));
|
saveItemStatement.TryBind("@Genres", string.Join('|', item.Genres));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -749,7 +749,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (item.LockedFields.Length > 0)
|
if (item.LockedFields.Length > 0)
|
||||||
{
|
{
|
||||||
saveItemStatement.TryBind("@LockedFields", string.Join("|", item.LockedFields));
|
saveItemStatement.TryBind("@LockedFields", string.Join('|', item.LockedFields));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -758,7 +758,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (item.Studios.Length > 0)
|
if (item.Studios.Length > 0)
|
||||||
{
|
{
|
||||||
saveItemStatement.TryBind("@Studios", string.Join("|", item.Studios));
|
saveItemStatement.TryBind("@Studios", string.Join('|', item.Studios));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -785,7 +785,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (item.Tags.Length > 0)
|
if (item.Tags.Length > 0)
|
||||||
{
|
{
|
||||||
saveItemStatement.TryBind("@Tags", string.Join("|", item.Tags));
|
saveItemStatement.TryBind("@Tags", string.Join('|', item.Tags));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -807,7 +807,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (item is Trailer trailer && trailer.TrailerTypes.Length > 0)
|
if (item is Trailer trailer && trailer.TrailerTypes.Length > 0)
|
||||||
{
|
{
|
||||||
saveItemStatement.TryBind("@TrailerTypes", string.Join("|", trailer.TrailerTypes));
|
saveItemStatement.TryBind("@TrailerTypes", string.Join('|', trailer.TrailerTypes));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -902,7 +902,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (item.ProductionLocations.Length > 0)
|
if (item.ProductionLocations.Length > 0)
|
||||||
{
|
{
|
||||||
saveItemStatement.TryBind("@ProductionLocations", string.Join("|", item.ProductionLocations));
|
saveItemStatement.TryBind("@ProductionLocations", string.Join('|', item.ProductionLocations));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -911,7 +911,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (item.ExtraIds.Length > 0)
|
if (item.ExtraIds.Length > 0)
|
||||||
{
|
{
|
||||||
saveItemStatement.TryBind("@ExtraIds", string.Join("|", item.ExtraIds));
|
saveItemStatement.TryBind("@ExtraIds", string.Join('|', item.ExtraIds));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -931,7 +931,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
string artists = null;
|
string artists = null;
|
||||||
if (item is IHasArtist hasArtists && hasArtists.Artists.Count > 0)
|
if (item is IHasArtist hasArtists && hasArtists.Artists.Count > 0)
|
||||||
{
|
{
|
||||||
artists = string.Join("|", hasArtists.Artists);
|
artists = string.Join('|', hasArtists.Artists);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveItemStatement.TryBind("@Artists", artists);
|
saveItemStatement.TryBind("@Artists", artists);
|
||||||
|
@ -940,7 +940,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
if (item is IHasAlbumArtist hasAlbumArtists
|
if (item is IHasAlbumArtist hasAlbumArtists
|
||||||
&& hasAlbumArtists.AlbumArtists.Count > 0)
|
&& hasAlbumArtists.AlbumArtists.Count > 0)
|
||||||
{
|
{
|
||||||
albumArtists = string.Join("|", hasAlbumArtists.AlbumArtists);
|
albumArtists = string.Join('|', hasAlbumArtists.AlbumArtists);
|
||||||
}
|
}
|
||||||
|
|
||||||
saveItemStatement.TryBind("@AlbumArtists", albumArtists);
|
saveItemStatement.TryBind("@AlbumArtists", albumArtists);
|
||||||
|
@ -2549,7 +2549,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (groups.Count > 0)
|
if (groups.Count > 0)
|
||||||
{
|
{
|
||||||
return " Group by " + string.Join(",", groups);
|
return " Group by " + string.Join(',', groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
|
@ -2578,7 +2578,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandText = "select "
|
var commandText = "select "
|
||||||
+ string.Join(",", GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" }))
|
+ string.Join(',', GetFinalColumnsToSelect(query, new[] { "count(distinct PresentationUniqueKey)" }))
|
||||||
+ GetFromText()
|
+ GetFromText()
|
||||||
+ GetJoinUserDataText(query);
|
+ GetJoinUserDataText(query);
|
||||||
|
|
||||||
|
@ -2630,7 +2630,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandText = "select "
|
var commandText = "select "
|
||||||
+ string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns))
|
+ string.Join(',', GetFinalColumnsToSelect(query, _retriveItemColumns))
|
||||||
+ GetFromText()
|
+ GetFromText()
|
||||||
+ GetJoinUserDataText(query);
|
+ GetJoinUserDataText(query);
|
||||||
|
|
||||||
|
@ -2880,7 +2880,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
var commandText = "select "
|
var commandText = "select "
|
||||||
+ string.Join(",", GetFinalColumnsToSelect(query, _retriveItemColumns))
|
+ string.Join(',', GetFinalColumnsToSelect(query, _retriveItemColumns))
|
||||||
+ GetFromText()
|
+ GetFromText()
|
||||||
+ GetJoinUserDataText(query);
|
+ GetJoinUserDataText(query);
|
||||||
|
|
||||||
|
@ -2923,15 +2923,15 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (EnableGroupByPresentationUniqueKey(query))
|
if (EnableGroupByPresentationUniqueKey(query))
|
||||||
{
|
{
|
||||||
commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText();
|
commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText();
|
||||||
}
|
}
|
||||||
else if (query.GroupBySeriesPresentationUniqueKey)
|
else if (query.GroupBySeriesPresentationUniqueKey)
|
||||||
{
|
{
|
||||||
commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct SeriesPresentationUniqueKey)" })) + GetFromText();
|
commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct SeriesPresentationUniqueKey)" })) + GetFromText();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (guid)" })) + GetFromText();
|
commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (guid)" })) + GetFromText();
|
||||||
}
|
}
|
||||||
|
|
||||||
commandText += GetJoinUserDataText(query)
|
commandText += GetJoinUserDataText(query)
|
||||||
|
@ -3039,7 +3039,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
return string.Empty;
|
return string.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
return " ORDER BY " + string.Join(",", orderBy.Select(i =>
|
return " ORDER BY " + string.Join(',', orderBy.Select(i =>
|
||||||
{
|
{
|
||||||
var columnMap = MapOrderByField(i.Item1, query);
|
var columnMap = MapOrderByField(i.Item1, query);
|
||||||
|
|
||||||
|
@ -3137,7 +3137,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
var commandText = "select "
|
var commandText = "select "
|
||||||
+ string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }))
|
+ string.Join(',', GetFinalColumnsToSelect(query, new[] { "guid" }))
|
||||||
+ GetFromText()
|
+ GetFromText()
|
||||||
+ GetJoinUserDataText(query);
|
+ GetJoinUserDataText(query);
|
||||||
|
|
||||||
|
@ -3203,7 +3203,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
var commandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid", "path" })) + GetFromText();
|
var commandText = "select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "guid", "path" })) + GetFromText();
|
||||||
|
|
||||||
var whereClauses = GetWhereClauses(query, null);
|
var whereClauses = GetWhereClauses(query, null);
|
||||||
if (whereClauses.Count != 0)
|
if (whereClauses.Count != 0)
|
||||||
|
@ -3284,7 +3284,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
var now = DateTime.UtcNow;
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
var commandText = "select "
|
var commandText = "select "
|
||||||
+ string.Join(",", GetFinalColumnsToSelect(query, new[] { "guid" }))
|
+ string.Join(',', GetFinalColumnsToSelect(query, new[] { "guid" }))
|
||||||
+ GetFromText()
|
+ GetFromText()
|
||||||
+ GetJoinUserDataText(query);
|
+ GetJoinUserDataText(query);
|
||||||
|
|
||||||
|
@ -3327,15 +3327,15 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (EnableGroupByPresentationUniqueKey(query))
|
if (EnableGroupByPresentationUniqueKey(query))
|
||||||
{
|
{
|
||||||
commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText();
|
commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" })) + GetFromText();
|
||||||
}
|
}
|
||||||
else if (query.GroupBySeriesPresentationUniqueKey)
|
else if (query.GroupBySeriesPresentationUniqueKey)
|
||||||
{
|
{
|
||||||
commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct SeriesPresentationUniqueKey)" })) + GetFromText();
|
commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct SeriesPresentationUniqueKey)" })) + GetFromText();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
commandText += " select " + string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (guid)" })) + GetFromText();
|
commandText += " select " + string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (guid)" })) + GetFromText();
|
||||||
}
|
}
|
||||||
|
|
||||||
commandText += GetJoinUserDataText(query)
|
commandText += GetJoinUserDataText(query)
|
||||||
|
@ -3596,7 +3596,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
else if (excludeTypes.Length > 1)
|
else if (excludeTypes.Length > 1)
|
||||||
{
|
{
|
||||||
var inClause = string.Join(",", excludeTypes.Select(i => "'" + i + "'"));
|
var inClause = string.Join(',', excludeTypes.Select(i => "'" + i + "'"));
|
||||||
whereClauses.Add($"type not in ({inClause})");
|
whereClauses.Add($"type not in ({inClause})");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3607,7 +3607,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
else if (includeTypes.Length > 1)
|
else if (includeTypes.Length > 1)
|
||||||
{
|
{
|
||||||
var inClause = string.Join(",", includeTypes.Select(i => "'" + i + "'"));
|
var inClause = string.Join(',', includeTypes.Select(i => "'" + i + "'"));
|
||||||
whereClauses.Add($"type in ({inClause})");
|
whereClauses.Add($"type in ({inClause})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3618,7 +3618,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
else if (query.ChannelIds.Count > 1)
|
else if (query.ChannelIds.Count > 1)
|
||||||
{
|
{
|
||||||
var inClause = string.Join(",", query.ChannelIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
|
var inClause = string.Join(',', query.ChannelIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
|
||||||
whereClauses.Add($"ChannelId in ({inClause})");
|
whereClauses.Add($"ChannelId in ({inClause})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4351,7 +4351,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
else if (query.Years.Length > 1)
|
else if (query.Years.Length > 1)
|
||||||
{
|
{
|
||||||
var val = string.Join(",", query.Years);
|
var val = string.Join(',', query.Years);
|
||||||
|
|
||||||
whereClauses.Add("ProductionYear in (" + val + ")");
|
whereClauses.Add("ProductionYear in (" + val + ")");
|
||||||
}
|
}
|
||||||
|
@ -4401,7 +4401,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
else if (queryMediaTypes.Length > 1)
|
else if (queryMediaTypes.Length > 1)
|
||||||
{
|
{
|
||||||
var val = string.Join(",", queryMediaTypes.Select(i => "'" + i + "'"));
|
var val = string.Join(',', queryMediaTypes.Select(i => "'" + i + "'"));
|
||||||
|
|
||||||
whereClauses.Add("MediaType in (" + val + ")");
|
whereClauses.Add("MediaType in (" + val + ")");
|
||||||
}
|
}
|
||||||
|
@ -4498,7 +4498,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
var paramName = "@HasAnyProviderId" + index;
|
var paramName = "@HasAnyProviderId" + index;
|
||||||
|
|
||||||
// this is a search for the placeholder
|
// this is a search for the placeholder
|
||||||
hasProviderIds.Add("ProviderIds like " + paramName + "");
|
hasProviderIds.Add("ProviderIds like " + paramName);
|
||||||
|
|
||||||
// this replaces the placeholder with a value, here: %key=val%
|
// this replaces the placeholder with a value, here: %key=val%
|
||||||
if (statement != null)
|
if (statement != null)
|
||||||
|
@ -4549,7 +4549,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
else if (enableItemsByName && includedItemByNameTypes.Count > 1)
|
else if (enableItemsByName && includedItemByNameTypes.Count > 1)
|
||||||
{
|
{
|
||||||
var itemByNameTypeVal = string.Join(",", includedItemByNameTypes.Select(i => "'" + i + "'"));
|
var itemByNameTypeVal = string.Join(',', includedItemByNameTypes.Select(i => "'" + i + "'"));
|
||||||
whereClauses.Add("(TopParentId=@TopParentId or Type in (" + itemByNameTypeVal + "))");
|
whereClauses.Add("(TopParentId=@TopParentId or Type in (" + itemByNameTypeVal + "))");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -4564,7 +4564,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
else if (queryTopParentIds.Length > 1)
|
else if (queryTopParentIds.Length > 1)
|
||||||
{
|
{
|
||||||
var val = string.Join(",", queryTopParentIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
|
var val = string.Join(',', queryTopParentIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
|
||||||
|
|
||||||
if (enableItemsByName && includedItemByNameTypes.Count == 1)
|
if (enableItemsByName && includedItemByNameTypes.Count == 1)
|
||||||
{
|
{
|
||||||
|
@ -4576,7 +4576,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
}
|
}
|
||||||
else if (enableItemsByName && includedItemByNameTypes.Count > 1)
|
else if (enableItemsByName && includedItemByNameTypes.Count > 1)
|
||||||
{
|
{
|
||||||
var itemByNameTypeVal = string.Join(",", includedItemByNameTypes.Select(i => "'" + i + "'"));
|
var itemByNameTypeVal = string.Join(',', includedItemByNameTypes.Select(i => "'" + i + "'"));
|
||||||
whereClauses.Add("(Type in (" + itemByNameTypeVal + ") or TopParentId in (" + val + "))");
|
whereClauses.Add("(Type in (" + itemByNameTypeVal + ") or TopParentId in (" + val + "))");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -4597,7 +4597,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
|
|
||||||
if (query.AncestorIds.Length > 1)
|
if (query.AncestorIds.Length > 1)
|
||||||
{
|
{
|
||||||
var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
|
var inClause = string.Join(',', query.AncestorIds.Select(i => "'" + i.ToString("N", CultureInfo.InvariantCulture) + "'"));
|
||||||
whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
|
whereClauses.Add(string.Format(CultureInfo.InvariantCulture, "Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5148,7 +5148,7 @@ AND Type = @InternalPersonType)");
|
||||||
}
|
}
|
||||||
else if (queryPersonTypes.Count > 1)
|
else if (queryPersonTypes.Count > 1)
|
||||||
{
|
{
|
||||||
var val = string.Join(",", queryPersonTypes.Select(i => "'" + i + "'"));
|
var val = string.Join(',', queryPersonTypes.Select(i => "'" + i + "'"));
|
||||||
|
|
||||||
whereClauses.Add("PersonType in (" + val + ")");
|
whereClauses.Add("PersonType in (" + val + ")");
|
||||||
}
|
}
|
||||||
|
@ -5162,7 +5162,7 @@ AND Type = @InternalPersonType)");
|
||||||
}
|
}
|
||||||
else if (queryExcludePersonTypes.Count > 1)
|
else if (queryExcludePersonTypes.Count > 1)
|
||||||
{
|
{
|
||||||
var val = string.Join(",", queryExcludePersonTypes.Select(i => "'" + i + "'"));
|
var val = string.Join(',', queryExcludePersonTypes.Select(i => "'" + i + "'"));
|
||||||
|
|
||||||
whereClauses.Add("PersonType not in (" + val + ")");
|
whereClauses.Add("PersonType not in (" + val + ")");
|
||||||
}
|
}
|
||||||
|
@ -5308,19 +5308,19 @@ AND Type = @InternalPersonType)");
|
||||||
|
|
||||||
var typeClause = itemValueTypes.Length == 1 ?
|
var typeClause = itemValueTypes.Length == 1 ?
|
||||||
("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
|
("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
|
||||||
("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + ")");
|
("Type in (" + string.Join(',', itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + ")");
|
||||||
|
|
||||||
var commandText = "Select Value From ItemValues where " + typeClause;
|
var commandText = "Select Value From ItemValues where " + typeClause;
|
||||||
|
|
||||||
if (withItemTypes.Count > 0)
|
if (withItemTypes.Count > 0)
|
||||||
{
|
{
|
||||||
var typeString = string.Join(",", withItemTypes.Select(i => "'" + i + "'"));
|
var typeString = string.Join(',', withItemTypes.Select(i => "'" + i + "'"));
|
||||||
commandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))";
|
commandText += " AND ItemId In (select guid from typedbaseitems where type in (" + typeString + "))";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (excludeItemTypes.Count > 0)
|
if (excludeItemTypes.Count > 0)
|
||||||
{
|
{
|
||||||
var typeString = string.Join(",", excludeItemTypes.Select(i => "'" + i + "'"));
|
var typeString = string.Join(',', excludeItemTypes.Select(i => "'" + i + "'"));
|
||||||
commandText += " AND ItemId not In (select guid from typedbaseitems where type in (" + typeString + "))";
|
commandText += " AND ItemId not In (select guid from typedbaseitems where type in (" + typeString + "))";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5363,7 +5363,7 @@ AND Type = @InternalPersonType)");
|
||||||
|
|
||||||
var typeClause = itemValueTypes.Length == 1 ?
|
var typeClause = itemValueTypes.Length == 1 ?
|
||||||
("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
|
("Type=" + itemValueTypes[0].ToString(CultureInfo.InvariantCulture)) :
|
||||||
("Type in (" + string.Join(",", itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + ")");
|
("Type in (" + string.Join(',', itemValueTypes.Select(i => i.ToString(CultureInfo.InvariantCulture))) + ")");
|
||||||
|
|
||||||
InternalItemsQuery typeSubQuery = null;
|
InternalItemsQuery typeSubQuery = null;
|
||||||
|
|
||||||
|
@ -5427,7 +5427,7 @@ AND Type = @InternalPersonType)");
|
||||||
columns = GetFinalColumnsToSelect(query, columns);
|
columns = GetFinalColumnsToSelect(query, columns);
|
||||||
|
|
||||||
var commandText = "select "
|
var commandText = "select "
|
||||||
+ string.Join(",", columns)
|
+ string.Join(',', columns)
|
||||||
+ GetFromText()
|
+ GetFromText()
|
||||||
+ GetJoinUserDataText(query);
|
+ GetJoinUserDataText(query);
|
||||||
|
|
||||||
|
@ -5504,7 +5504,7 @@ AND Type = @InternalPersonType)");
|
||||||
if (query.EnableTotalRecordCount)
|
if (query.EnableTotalRecordCount)
|
||||||
{
|
{
|
||||||
var countText = "select "
|
var countText = "select "
|
||||||
+ string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" }))
|
+ string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" }))
|
||||||
+ GetFromText()
|
+ GetFromText()
|
||||||
+ GetJoinUserDataText(query)
|
+ GetJoinUserDataText(query)
|
||||||
+ whereText;
|
+ whereText;
|
||||||
|
@ -5565,7 +5565,7 @@ AND Type = @InternalPersonType)");
|
||||||
if (query.EnableTotalRecordCount)
|
if (query.EnableTotalRecordCount)
|
||||||
{
|
{
|
||||||
commandText = "select "
|
commandText = "select "
|
||||||
+ string.Join(",", GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" }))
|
+ string.Join(',', GetFinalColumnsToSelect(query, new[] { "count (distinct PresentationUniqueKey)" }))
|
||||||
+ GetFromText()
|
+ GetFromText()
|
||||||
+ GetJoinUserDataText(query)
|
+ GetJoinUserDataText(query)
|
||||||
+ whereText;
|
+ whereText;
|
||||||
|
|
|
@ -47,7 +47,7 @@ namespace Emby.Server.Implementations.Data
|
||||||
connection.RunInTransaction(
|
connection.RunInTransaction(
|
||||||
db =>
|
db =>
|
||||||
{
|
{
|
||||||
db.ExecuteAll(string.Join(";", new[] {
|
db.ExecuteAll(string.Join(';', new[] {
|
||||||
|
|
||||||
"create table if not exists UserDatas (key nvarchar not null, userId INT not null, rating float null, played bit not null, playCount int not null, isFavorite bit not null, playbackPositionTicks bigint not null, lastPlayedDate datetime null, AudioStreamIndex INT, SubtitleStreamIndex INT)",
|
"create table if not exists UserDatas (key nvarchar not null, userId INT not null, rating float null, played bit not null, playCount int not null, isFavorite bit not null, playbackPositionTicks bigint not null, lastPlayedDate datetime null, AudioStreamIndex INT, SubtitleStreamIndex INT)",
|
||||||
|
|
||||||
|
|
|
@ -249,7 +249,7 @@ namespace Emby.Server.Implementations.Dto
|
||||||
var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
|
var activeRecording = liveTvManager.GetActiveRecordingInfo(item.Path);
|
||||||
if (activeRecording != null)
|
if (activeRecording != null)
|
||||||
{
|
{
|
||||||
dto.Type = "Recording";
|
dto.Type = BaseItemKind.Recording;
|
||||||
dto.CanDownload = false;
|
dto.CanDownload = false;
|
||||||
dto.RunTimeTicks = null;
|
dto.RunTimeTicks = null;
|
||||||
|
|
||||||
|
@ -582,16 +582,22 @@ namespace Emby.Server.Implementations.Dto
|
||||||
{
|
{
|
||||||
baseItemPerson.PrimaryImageTag = GetTagAndFillBlurhash(dto, entity, ImageType.Primary);
|
baseItemPerson.PrimaryImageTag = GetTagAndFillBlurhash(dto, entity, ImageType.Primary);
|
||||||
baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
|
baseItemPerson.Id = entity.Id.ToString("N", CultureInfo.InvariantCulture);
|
||||||
// Only add BlurHash for the person's image.
|
if (dto.ImageBlurHashes != null)
|
||||||
baseItemPerson.ImageBlurHashes = new Dictionary<ImageType, Dictionary<string, string>>();
|
|
||||||
foreach (var (imageType, blurHash) in dto.ImageBlurHashes)
|
|
||||||
{
|
{
|
||||||
baseItemPerson.ImageBlurHashes[imageType] = new Dictionary<string, string>();
|
// Only add BlurHash for the person's image.
|
||||||
foreach (var (imageId, blurHashValue) in blurHash)
|
baseItemPerson.ImageBlurHashes = new Dictionary<ImageType, Dictionary<string, string>>();
|
||||||
|
foreach (var (imageType, blurHash) in dto.ImageBlurHashes)
|
||||||
{
|
{
|
||||||
if (string.Equals(baseItemPerson.PrimaryImageTag, imageId, StringComparison.OrdinalIgnoreCase))
|
if (blurHash != null)
|
||||||
{
|
{
|
||||||
baseItemPerson.ImageBlurHashes[imageType][imageId] = blurHashValue;
|
baseItemPerson.ImageBlurHashes[imageType] = new Dictionary<string, string>();
|
||||||
|
foreach (var (imageId, blurHashValue) in blurHash)
|
||||||
|
{
|
||||||
|
if (string.Equals(baseItemPerson.PrimaryImageTag, imageId, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
baseItemPerson.ImageBlurHashes[imageType][imageId] = blurHashValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -898,7 +904,7 @@ namespace Emby.Server.Implementations.Dto
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dto.Type = item.GetClientTypeName();
|
dto.Type = item.GetBaseItemKind();
|
||||||
if ((item.CommunityRating ?? 0) > 0)
|
if ((item.CommunityRating ?? 0) > 0)
|
||||||
{
|
{
|
||||||
dto.CommunityRating = item.CommunityRating;
|
dto.CommunityRating = item.CommunityRating;
|
||||||
|
@ -1151,7 +1157,7 @@ namespace Emby.Server.Implementations.Dto
|
||||||
if (episodeSeries != null)
|
if (episodeSeries != null)
|
||||||
{
|
{
|
||||||
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
|
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
|
||||||
if (!dto.ImageTags.ContainsKey(ImageType.Primary))
|
if (dto.ImageTags == null || !dto.ImageTags.ContainsKey(ImageType.Primary))
|
||||||
{
|
{
|
||||||
AttachPrimaryImageAspectRatio(dto, episodeSeries);
|
AttachPrimaryImageAspectRatio(dto, episodeSeries);
|
||||||
}
|
}
|
||||||
|
@ -1201,7 +1207,7 @@ namespace Emby.Server.Implementations.Dto
|
||||||
if (series != null)
|
if (series != null)
|
||||||
{
|
{
|
||||||
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
|
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
|
||||||
if (!dto.ImageTags.ContainsKey(ImageType.Primary))
|
if (dto.ImageTags == null || !dto.ImageTags.ContainsKey(ImageType.Primary))
|
||||||
{
|
{
|
||||||
AttachPrimaryImageAspectRatio(dto, series);
|
AttachPrimaryImageAspectRatio(dto, series);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="Mono.Nat" Version="3.0.1" />
|
<PackageReference Include="Mono.Nat" Version="3.0.1" />
|
||||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.1" />
|
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.1" />
|
||||||
<PackageReference Include="sharpcompress" Version="0.26.0" />
|
<PackageReference Include="sharpcompress" Version="0.28.0" />
|
||||||
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
|
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
|
||||||
<PackageReference Include="DotNet.Glob" Version="3.1.2" />
|
<PackageReference Include="DotNet.Glob" Version="3.1.2" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.Buffers;
|
||||||
using System.IO.Pipelines;
|
using System.IO.Pipelines;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -138,7 +139,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
writer.Advance(bytesRead);
|
writer.Advance(bytesRead);
|
||||||
|
|
||||||
// Make the data available to the PipeReader
|
// Make the data available to the PipeReader
|
||||||
FlushResult flushResult = await writer.FlushAsync().ConfigureAwait(false);
|
FlushResult flushResult = await writer.FlushAsync(cancellationToken).ConfigureAwait(false);
|
||||||
if (flushResult.IsCompleted)
|
if (flushResult.IsCompleted)
|
||||||
{
|
{
|
||||||
// The PipeReader stopped reading
|
// The PipeReader stopped reading
|
||||||
|
@ -181,32 +182,16 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
}
|
}
|
||||||
|
|
||||||
WebSocketMessage<object>? stub;
|
WebSocketMessage<object>? stub;
|
||||||
|
long bytesConsumed = 0;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
stub = DeserializeWebSocketMessage(buffer, out bytesConsumed);
|
||||||
if (buffer.IsSingleSegment)
|
|
||||||
{
|
|
||||||
stub = JsonSerializer.Deserialize<WebSocketMessage<object>>(buffer.FirstSpan, _jsonOptions);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var buf = ArrayPool<byte>.Shared.Rent(Convert.ToInt32(buffer.Length));
|
|
||||||
try
|
|
||||||
{
|
|
||||||
buffer.CopyTo(buf);
|
|
||||||
stub = JsonSerializer.Deserialize<WebSocketMessage<object>>(buf, _jsonOptions);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
ArrayPool<byte>.Shared.Return(buf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (JsonException ex)
|
catch (JsonException ex)
|
||||||
{
|
{
|
||||||
// Tell the PipeReader how much of the buffer we have consumed
|
// Tell the PipeReader how much of the buffer we have consumed
|
||||||
reader.AdvanceTo(buffer.End);
|
reader.AdvanceTo(buffer.End);
|
||||||
_logger.LogError(ex, "Error processing web socket message");
|
_logger.LogError(ex, "Error processing web socket message: {Data}", Encoding.UTF8.GetString(buffer));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -217,27 +202,34 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell the PipeReader how much of the buffer we have consumed
|
// Tell the PipeReader how much of the buffer we have consumed
|
||||||
reader.AdvanceTo(buffer.End);
|
reader.AdvanceTo(buffer.GetPosition(bytesConsumed));
|
||||||
|
|
||||||
_logger.LogDebug("WS {IP} received message: {@Message}", RemoteEndPoint, stub);
|
_logger.LogDebug("WS {IP} received message: {@Message}", RemoteEndPoint, stub);
|
||||||
|
|
||||||
var info = new WebSocketMessageInfo
|
if (stub.MessageType == SessionMessageType.KeepAlive)
|
||||||
{
|
|
||||||
MessageType = stub.MessageType,
|
|
||||||
Data = stub.Data?.ToString(), // Data can be null
|
|
||||||
Connection = this
|
|
||||||
};
|
|
||||||
|
|
||||||
if (info.MessageType == SessionMessageType.KeepAlive)
|
|
||||||
{
|
{
|
||||||
await SendKeepAliveResponse().ConfigureAwait(false);
|
await SendKeepAliveResponse().ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await OnReceive(info).ConfigureAwait(false);
|
await OnReceive(
|
||||||
|
new WebSocketMessageInfo
|
||||||
|
{
|
||||||
|
MessageType = stub.MessageType,
|
||||||
|
Data = stub.Data?.ToString(), // Data can be null
|
||||||
|
Connection = this
|
||||||
|
}).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal WebSocketMessage<object>? DeserializeWebSocketMessage(ReadOnlySequence<byte> bytes, out long bytesConsumed)
|
||||||
|
{
|
||||||
|
var jsonReader = new Utf8JsonReader(bytes);
|
||||||
|
var ret = JsonSerializer.Deserialize<WebSocketMessage<object>>(ref jsonReader, _jsonOptions);
|
||||||
|
bytesConsumed = jsonReader.BytesConsumed;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
private Task SendKeepAliveResponse()
|
private Task SendKeepAliveResponse()
|
||||||
{
|
{
|
||||||
LastKeepAliveDate = DateTime.UtcNow;
|
LastKeepAliveDate = DateTime.UtcNow;
|
||||||
|
|
|
@ -582,9 +582,7 @@ namespace Emby.Server.Implementations.IO
|
||||||
|
|
||||||
public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
|
public virtual IEnumerable<FileSystemMetadata> GetDirectories(string path, bool recursive = false)
|
||||||
{
|
{
|
||||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
return ToMetadata(new DirectoryInfo(path).EnumerateDirectories("*", GetEnumerationOptions(recursive)));
|
||||||
|
|
||||||
return ToMetadata(new DirectoryInfo(path).EnumerateDirectories("*", searchOption));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
|
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, bool recursive = false)
|
||||||
|
@ -594,16 +592,16 @@ namespace Emby.Server.Implementations.IO
|
||||||
|
|
||||||
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string> extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
|
public virtual IEnumerable<FileSystemMetadata> GetFiles(string path, IReadOnlyList<string> extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
|
||||||
{
|
{
|
||||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
var enumerationOptions = GetEnumerationOptions(recursive);
|
||||||
|
|
||||||
// On linux and osx the search pattern is case sensitive
|
// On linux and osx the search pattern is case sensitive
|
||||||
// If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
|
// If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
|
||||||
if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Count == 1)
|
if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Count == 1)
|
||||||
{
|
{
|
||||||
return ToMetadata(new DirectoryInfo(path).EnumerateFiles("*" + extensions[0], searchOption));
|
return ToMetadata(new DirectoryInfo(path).EnumerateFiles("*" + extensions[0], enumerationOptions));
|
||||||
}
|
}
|
||||||
|
|
||||||
var files = new DirectoryInfo(path).EnumerateFiles("*", searchOption);
|
var files = new DirectoryInfo(path).EnumerateFiles("*", enumerationOptions);
|
||||||
|
|
||||||
if (extensions != null && extensions.Count > 0)
|
if (extensions != null && extensions.Count > 0)
|
||||||
{
|
{
|
||||||
|
@ -625,10 +623,10 @@ namespace Emby.Server.Implementations.IO
|
||||||
public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
|
public virtual IEnumerable<FileSystemMetadata> GetFileSystemEntries(string path, bool recursive = false)
|
||||||
{
|
{
|
||||||
var directoryInfo = new DirectoryInfo(path);
|
var directoryInfo = new DirectoryInfo(path);
|
||||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
var enumerationOptions = GetEnumerationOptions(recursive);
|
||||||
|
|
||||||
return ToMetadata(directoryInfo.EnumerateDirectories("*", searchOption))
|
return ToMetadata(directoryInfo.EnumerateDirectories("*", enumerationOptions))
|
||||||
.Concat(ToMetadata(directoryInfo.EnumerateFiles("*", searchOption)));
|
.Concat(ToMetadata(directoryInfo.EnumerateFiles("*", enumerationOptions)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private IEnumerable<FileSystemMetadata> ToMetadata(IEnumerable<FileSystemInfo> infos)
|
private IEnumerable<FileSystemMetadata> ToMetadata(IEnumerable<FileSystemInfo> infos)
|
||||||
|
@ -638,8 +636,7 @@ namespace Emby.Server.Implementations.IO
|
||||||
|
|
||||||
public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
|
public virtual IEnumerable<string> GetDirectoryPaths(string path, bool recursive = false)
|
||||||
{
|
{
|
||||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
return Directory.EnumerateDirectories(path, "*", GetEnumerationOptions(recursive));
|
||||||
return Directory.EnumerateDirectories(path, "*", searchOption);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual IEnumerable<string> GetFilePaths(string path, bool recursive = false)
|
public virtual IEnumerable<string> GetFilePaths(string path, bool recursive = false)
|
||||||
|
@ -649,16 +646,16 @@ namespace Emby.Server.Implementations.IO
|
||||||
|
|
||||||
public virtual IEnumerable<string> GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
|
public virtual IEnumerable<string> GetFilePaths(string path, string[] extensions, bool enableCaseSensitiveExtensions, bool recursive = false)
|
||||||
{
|
{
|
||||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
var enumerationOptions = GetEnumerationOptions(recursive);
|
||||||
|
|
||||||
// On linux and osx the search pattern is case sensitive
|
// On linux and osx the search pattern is case sensitive
|
||||||
// If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
|
// If we're OK with case-sensitivity, and we're only filtering for one extension, then use the native method
|
||||||
if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Length == 1)
|
if ((enableCaseSensitiveExtensions || _isEnvironmentCaseInsensitive) && extensions != null && extensions.Length == 1)
|
||||||
{
|
{
|
||||||
return Directory.EnumerateFiles(path, "*" + extensions[0], searchOption);
|
return Directory.EnumerateFiles(path, "*" + extensions[0], enumerationOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
var files = Directory.EnumerateFiles(path, "*", searchOption);
|
var files = Directory.EnumerateFiles(path, "*", enumerationOptions);
|
||||||
|
|
||||||
if (extensions != null && extensions.Length > 0)
|
if (extensions != null && extensions.Length > 0)
|
||||||
{
|
{
|
||||||
|
@ -679,8 +676,18 @@ namespace Emby.Server.Implementations.IO
|
||||||
|
|
||||||
public virtual IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
|
public virtual IEnumerable<string> GetFileSystemEntryPaths(string path, bool recursive = false)
|
||||||
{
|
{
|
||||||
var searchOption = recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
|
return Directory.EnumerateFileSystemEntries(path, "*", GetEnumerationOptions(recursive));
|
||||||
return Directory.EnumerateFileSystemEntries(path, "*", searchOption);
|
}
|
||||||
|
|
||||||
|
private EnumerationOptions GetEnumerationOptions(bool recursive)
|
||||||
|
{
|
||||||
|
return new EnumerationOptions
|
||||||
|
{
|
||||||
|
RecurseSubdirectories = recursive,
|
||||||
|
IgnoreInaccessible = true,
|
||||||
|
// Don't skip any files.
|
||||||
|
AttributesToSkip = 0
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void RunProcess(string path, string args, string workingDirectory)
|
private static void RunProcess(string path, string args, string workingDirectory)
|
||||||
|
|
|
@ -515,7 +515,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: @bond Fix
|
// TODO: @bond Fix
|
||||||
var json = JsonSerializer.Serialize(mediaSource, _jsonOptions);
|
var json = JsonSerializer.SerializeToUtf8Bytes(mediaSource, _jsonOptions);
|
||||||
_logger.LogInformation("Live stream opened: " + json);
|
_logger.LogInformation("Live stream opened: " + json);
|
||||||
var clone = JsonSerializer.Deserialize<MediaSourceInfo>(json, _jsonOptions);
|
var clone = JsonSerializer.Deserialize<MediaSourceInfo>(json, _jsonOptions);
|
||||||
|
|
||||||
|
|
|
@ -79,11 +79,6 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||||
return new MusicArtist();
|
return new MusicArtist();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_config.Configuration.EnableSimpleArtistDetection)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Avoid mis-identifying top folders
|
// Avoid mis-identifying top folders
|
||||||
if (args.Parent.IsRoot)
|
if (args.Parent.IsRoot)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,7 +41,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
|
||||||
}
|
}
|
||||||
|
|
||||||
// It's a directory-based playlist if the directory contains a playlist file
|
// It's a directory-based playlist if the directory contains a playlist file
|
||||||
var filePaths = Directory.EnumerateFiles(args.Path);
|
var filePaths = Directory.EnumerateFiles(args.Path, "*", new EnumerationOptions { IgnoreInaccessible = true });
|
||||||
if (filePaths.Any(f => f.EndsWith(PlaylistXmlSaver.DefaultPlaylistFilename, StringComparison.OrdinalIgnoreCase)))
|
if (filePaths.Any(f => f.EndsWith(PlaylistXmlSaver.DefaultPlaylistFilename, StringComparison.OrdinalIgnoreCase)))
|
||||||
{
|
{
|
||||||
return new Playlist
|
return new Playlist
|
||||||
|
|
|
@ -47,11 +47,11 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var jsonString = File.ReadAllText(_dataPath, Encoding.UTF8);
|
var bytes = File.ReadAllBytes(_dataPath);
|
||||||
_items = JsonSerializer.Deserialize<T[]>(jsonString, _jsonOptions);
|
_items = JsonSerializer.Deserialize<T[]>(bytes, _jsonOptions);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (JsonException ex)
|
||||||
{
|
{
|
||||||
Logger.LogError(ex, "Error deserializing {Path}", _dataPath);
|
Logger.LogError(ex, "Error deserializing {Path}", _dataPath);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ using System.Net.Http;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common;
|
using MediaBrowser.Common;
|
||||||
|
@ -35,8 +36,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
private readonly ICryptoProvider _cryptoProvider;
|
private readonly ICryptoProvider _cryptoProvider;
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
|
private readonly ConcurrentDictionary<string, NameValuePair> _tokens = new ConcurrentDictionary<string, NameValuePair>();
|
||||||
private DateTime _lastErrorResponse;
|
|
||||||
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
|
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.GetOptions();
|
||||||
|
private DateTime _lastErrorResponse;
|
||||||
|
|
||||||
public SchedulesDirect(
|
public SchedulesDirect(
|
||||||
ILogger<SchedulesDirect> logger,
|
ILogger<SchedulesDirect> logger,
|
||||||
|
@ -111,7 +112,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
options.Headers.TryAddWithoutValidation("token", token);
|
options.Headers.TryAddWithoutValidation("token", token);
|
||||||
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
using var response = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
||||||
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
var dailySchedules = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Day>>(responseStream, _jsonOptions).ConfigureAwait(false);
|
var dailySchedules = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.Day>>(responseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
|
_logger.LogDebug("Found {ScheduleCount} programs on {ChannelID} ScheduleDirect", dailySchedules.Count, channelId);
|
||||||
|
|
||||||
using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
|
using var programRequestOptions = new HttpRequestMessage(HttpMethod.Post, ApiUrl + "/programs");
|
||||||
|
@ -122,12 +123,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
|
|
||||||
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
|
using var innerResponse = await Send(programRequestOptions, true, info, cancellationToken).ConfigureAwait(false);
|
||||||
await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var innerResponseStream = await innerResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
var programDetails = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream, _jsonOptions).ConfigureAwait(false);
|
var programDetails = await JsonSerializer.DeserializeAsync<List<ScheduleDirect.ProgramDetails>>(innerResponseStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
var programDict = programDetails.ToDictionary(p => p.programID, y => y);
|
var programDict = programDetails.ToDictionary(p => p.programID, y => y);
|
||||||
|
|
||||||
var programIdsWithImages =
|
var programIdsWithImages = programDetails
|
||||||
programDetails.Where(p => p.hasImageArtwork).Select(p => p.programID)
|
.Where(p => p.hasImageArtwork).Select(p => p.programID)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
|
var images = await GetImageForPrograms(info, programIdsWithImages, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
|
@ -182,8 +183,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
|
|
||||||
private static int GetSizeOrder(ScheduleDirect.ImageData image)
|
private static int GetSizeOrder(ScheduleDirect.ImageData image)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(image.height)
|
if (int.TryParse(image.height, out int value))
|
||||||
&& int.TryParse(image.height, out int value))
|
|
||||||
{
|
{
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -704,7 +704,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
httpResponse.EnsureSuccessStatusCode();
|
httpResponse.EnsureSuccessStatusCode();
|
||||||
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
using var response = httpResponse.Content;
|
using var response = httpResponse.Content;
|
||||||
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Lineups>(stream, _jsonOptions).ConfigureAwait(false);
|
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Lineups>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
|
return root.lineups.Any(i => string.Equals(info.ListingsId, i.lineup, StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
@ -776,7 +776,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
||||||
|
|
||||||
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
using var httpResponse = await Send(options, true, info, cancellationToken).ConfigureAwait(false);
|
||||||
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
await using var stream = await httpResponse.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
||||||
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Channel>(stream, _jsonOptions).ConfigureAwait(false);
|
var root = await JsonSerializer.DeserializeAsync<ScheduleDirect.Channel>(stream, _jsonOptions, cancellationToken).ConfigureAwait(false);
|
||||||
_logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
|
_logger.LogInformation("Found {ChannelCount} channels on the lineup on ScheduleDirect", root.map.Count);
|
||||||
_logger.LogInformation("Mapping Stations to Channel");
|
_logger.LogInformation("Mapping Stations to Channel");
|
||||||
|
|
||||||
|
|
|
@ -2239,7 +2239,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||||
|
|
||||||
public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
|
public async Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true)
|
||||||
{
|
{
|
||||||
info = JsonSerializer.Deserialize<TunerHostInfo>(JsonSerializer.Serialize(info));
|
info = JsonSerializer.Deserialize<TunerHostInfo>(JsonSerializer.SerializeToUtf8Bytes(info));
|
||||||
|
|
||||||
var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
|
var provider = _tunerHosts.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
@ -2283,7 +2283,7 @@ namespace Emby.Server.Implementations.LiveTv
|
||||||
{
|
{
|
||||||
// Hack to make the object a pure ListingsProviderInfo instead of an AddListingProvider
|
// Hack to make the object a pure ListingsProviderInfo instead of an AddListingProvider
|
||||||
// ServerConfiguration.SaveConfiguration crashes during xml serialization for AddListingProvider
|
// ServerConfiguration.SaveConfiguration crashes during xml serialization for AddListingProvider
|
||||||
info = JsonSerializer.Deserialize<ListingsProviderInfo>(JsonSerializer.Serialize(info));
|
info = JsonSerializer.Deserialize<ListingsProviderInfo>(JsonSerializer.SerializeToUtf8Bytes(info));
|
||||||
|
|
||||||
var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
|
var provider = _listingProviders.FirstOrDefault(i => string.Equals(info.Type, i.Type, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
public LegacyHdHomerunChannelCommands(string url)
|
public LegacyHdHomerunChannelCommands(string url)
|
||||||
{
|
{
|
||||||
// parse url for channel and program
|
// parse url for channel and program
|
||||||
var regExp = new Regex(@"\/ch(\d+)-?(\d*)");
|
var regExp = new Regex(@"\/ch([0-9]+)-?([0-9]*)");
|
||||||
var match = regExp.Match(url);
|
var match = regExp.Match(url);
|
||||||
if (match.Success)
|
if (match.Success)
|
||||||
{
|
{
|
||||||
|
|
|
@ -92,7 +92,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await tcpClient.ConnectAsync(remoteAddress, HdHomerunManager.HdHomeRunPort).ConfigureAwait(false);
|
await tcpClient.ConnectAsync(remoteAddress, HdHomerunManager.HdHomeRunPort, openCancellationToken).ConfigureAwait(false);
|
||||||
localAddress = ((IPEndPoint)tcpClient.Client.LocalEndPoint).Address;
|
localAddress = ((IPEndPoint)tcpClient.Client.LocalEndPoint).Address;
|
||||||
tcpClient.Close();
|
tcpClient.Close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -155,7 +155,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
|
|
||||||
if (channelIdValues.Count > 0)
|
if (channelIdValues.Count > 0)
|
||||||
{
|
{
|
||||||
channel.Id = string.Join("_", channelIdValues);
|
channel.Id = string.Join('_', channelIdValues);
|
||||||
}
|
}
|
||||||
|
|
||||||
return channel;
|
return channel;
|
||||||
|
|
|
@ -159,7 +159,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
||||||
|
|
||||||
EnableStreamSharing = false;
|
EnableStreamSharing = false;
|
||||||
await DeleteTempFiles(new List<string> { TempFilePath }).ConfigureAwait(false);
|
await DeleteTempFiles(new List<string> { TempFilePath }).ConfigureAwait(false);
|
||||||
});
|
}, CancellationToken.None);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
|
private void Resolve(TaskCompletionSource<bool> openTaskCompletionSource)
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}",
|
"CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}",
|
||||||
"Channels": "Канали",
|
"Channels": "Канали",
|
||||||
"ChapterNameValue": "Глава {0}",
|
"ChapterNameValue": "Глава {0}",
|
||||||
"Collections": "Колекции",
|
"Collections": "Поредици",
|
||||||
"DeviceOfflineWithName": "{0} се разкачи",
|
"DeviceOfflineWithName": "{0} се разкачи",
|
||||||
"DeviceOnlineWithName": "{0} е свързан",
|
"DeviceOnlineWithName": "{0} е свързан",
|
||||||
"FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}",
|
"FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}",
|
||||||
|
@ -55,26 +55,26 @@
|
||||||
"NotificationOptionPluginInstalled": "Приставката е инсталирана",
|
"NotificationOptionPluginInstalled": "Приставката е инсталирана",
|
||||||
"NotificationOptionPluginUninstalled": "Приставката е деинсталирана",
|
"NotificationOptionPluginUninstalled": "Приставката е деинсталирана",
|
||||||
"NotificationOptionPluginUpdateInstalled": "Обновлението на приставката е инсталирано",
|
"NotificationOptionPluginUpdateInstalled": "Обновлението на приставката е инсталирано",
|
||||||
"NotificationOptionServerRestartRequired": "Нужно е повторно пускане на сървъра",
|
"NotificationOptionServerRestartRequired": "Сървърът трябва да се рестартира",
|
||||||
"NotificationOptionTaskFailed": "Грешка в планирана задача",
|
"NotificationOptionTaskFailed": "Грешка в планирана задача",
|
||||||
"NotificationOptionUserLockedOut": "Потребителя е заключен",
|
"NotificationOptionUserLockedOut": "Потребителят е заключен",
|
||||||
"NotificationOptionVideoPlayback": "Възпроизвеждането на видео започна",
|
"NotificationOptionVideoPlayback": "Възпроизвеждането на видео започна",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
|
"NotificationOptionVideoPlaybackStopped": "Възпроизвеждането на видео е спряно",
|
||||||
"Photos": "Снимки",
|
"Photos": "Снимки",
|
||||||
"Playlists": "Списъци",
|
"Playlists": "Списъци",
|
||||||
"Plugin": "Приставка",
|
"Plugin": "Приставка",
|
||||||
"PluginInstalledWithName": "{0} е инсталирано",
|
"PluginInstalledWithName": "{0} е инсталиранa",
|
||||||
"PluginUninstalledWithName": "{0} е деинсталирано",
|
"PluginUninstalledWithName": "{0} е деинсталиранa",
|
||||||
"PluginUpdatedWithName": "{0} е обновено",
|
"PluginUpdatedWithName": "{0} е обновенa",
|
||||||
"ProviderValue": "Доставчик: {0}",
|
"ProviderValue": "Доставчик: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} се провали",
|
"ScheduledTaskFailedWithName": "{0} се провали",
|
||||||
"ScheduledTaskStartedWithName": "{0} започна",
|
"ScheduledTaskStartedWithName": "{0} започна",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} е нужно да се рестартира",
|
"ServerNameNeedsToBeRestarted": "{0} трябва да се рестартира",
|
||||||
"Shows": "Сериали",
|
"Shows": "Сериали",
|
||||||
"Songs": "Песни",
|
"Songs": "Песни",
|
||||||
"StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.",
|
"StartupEmbyServerIsLoading": "Сървърът зарежда. Моля, опитайте отново след малко.",
|
||||||
"SubtitleDownloadFailureForItem": "Неуспешно изтегляне на субтитри за {0}",
|
"SubtitleDownloadFailureForItem": "Неуспешно изтегляне на субтитри за {0}",
|
||||||
"SubtitleDownloadFailureFromForItem": "Поднадписите за {1} от {0} не можаха да се изтеглят",
|
"SubtitleDownloadFailureFromForItem": "Субтитрите за {1} от {0} не можаха да бъдат изтеглени",
|
||||||
"Sync": "Синхронизиране",
|
"Sync": "Синхронизиране",
|
||||||
"System": "Система",
|
"System": "Система",
|
||||||
"TvShows": "Телевизионни сериали",
|
"TvShows": "Телевизионни сериали",
|
||||||
|
@ -92,12 +92,12 @@
|
||||||
"ValueHasBeenAddedToLibrary": "{0} беше добавен във Вашата библиотека",
|
"ValueHasBeenAddedToLibrary": "{0} беше добавен във Вашата библиотека",
|
||||||
"ValueSpecialEpisodeName": "Специални - {0}",
|
"ValueSpecialEpisodeName": "Специални - {0}",
|
||||||
"VersionNumber": "Версия {0}",
|
"VersionNumber": "Версия {0}",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи поднадписи, на база конфигурацията за мета-данни.",
|
"TaskDownloadMissingSubtitlesDescription": "Търси Интернет за липсващи субтитри, на база конфигурацията за мета-данни.",
|
||||||
"TaskDownloadMissingSubtitles": "Изтегляне на липсващи поднадписи",
|
"TaskDownloadMissingSubtitles": "Изтегляне на липсващи субтитри",
|
||||||
"TaskRefreshChannelsDescription": "Обновява информацията за интернет канала.",
|
"TaskRefreshChannelsDescription": "Обновява информацията за интернет канала.",
|
||||||
"TaskRefreshChannels": "Обновяване на Канали",
|
"TaskRefreshChannels": "Обновяване на Канали",
|
||||||
"TaskCleanTranscodeDescription": "Изтрива прекодирани файлове по-стари от един ден.",
|
"TaskCleanTranscodeDescription": "Изтрива транскодирани файлове по-стари от един ден.",
|
||||||
"TaskCleanTranscode": "Изчиства директорията за прекодиране",
|
"TaskCleanTranscode": "Изчиства директорията за транскодиране",
|
||||||
"TaskUpdatePluginsDescription": "Изтегля и инсталира актуализации за добавките, които са настроени за автоматична актуализация.",
|
"TaskUpdatePluginsDescription": "Изтегля и инсталира актуализации за добавките, които са настроени за автоматична актуализация.",
|
||||||
"TaskUpdatePlugins": "Актуализира добавките",
|
"TaskUpdatePlugins": "Актуализира добавките",
|
||||||
"TaskRefreshPeopleDescription": "Актуализира мета-данните за артистите и режисьорите за Вашата медийна библиотека.",
|
"TaskRefreshPeopleDescription": "Актуализира мета-данните за артистите и режисьорите за Вашата медийна библиотека.",
|
||||||
|
@ -113,5 +113,8 @@
|
||||||
"TasksChannelsCategory": "Интернет Канали",
|
"TasksChannelsCategory": "Интернет Канали",
|
||||||
"TasksApplicationCategory": "Приложение",
|
"TasksApplicationCategory": "Приложение",
|
||||||
"TasksLibraryCategory": "Библиотека",
|
"TasksLibraryCategory": "Библиотека",
|
||||||
"TasksMaintenanceCategory": "Поддръжка"
|
"TasksMaintenanceCategory": "Поддръжка",
|
||||||
|
"Undefined": "Неопределено",
|
||||||
|
"Forced": "Принудително",
|
||||||
|
"Default": "По подразбиране"
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,10 +18,10 @@
|
||||||
"HeaderAlbumArtists": "Artistes del Àlbum",
|
"HeaderAlbumArtists": "Artistes del Àlbum",
|
||||||
"HeaderContinueWatching": "Continua Veient",
|
"HeaderContinueWatching": "Continua Veient",
|
||||||
"HeaderFavoriteAlbums": "Àlbums Preferits",
|
"HeaderFavoriteAlbums": "Àlbums Preferits",
|
||||||
"HeaderFavoriteArtists": "Artistes Preferits",
|
"HeaderFavoriteArtists": "Artistes Predilectes",
|
||||||
"HeaderFavoriteEpisodes": "Episodis Preferits",
|
"HeaderFavoriteEpisodes": "Episodis Predilectes",
|
||||||
"HeaderFavoriteShows": "Programes Preferits",
|
"HeaderFavoriteShows": "Programes Predilectes",
|
||||||
"HeaderFavoriteSongs": "Cançons Preferides",
|
"HeaderFavoriteSongs": "Cançons Predilectes",
|
||||||
"HeaderLiveTV": "TV en Directe",
|
"HeaderLiveTV": "TV en Directe",
|
||||||
"HeaderNextUp": "A continuació",
|
"HeaderNextUp": "A continuació",
|
||||||
"HeaderRecordingGroups": "Grups d'Enregistrament",
|
"HeaderRecordingGroups": "Grups d'Enregistrament",
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
"MessageApplicationUpdatedTo": "El Servidor de Jellyfin ha estat actualitzat a {0}",
|
"MessageApplicationUpdatedTo": "El Servidor de Jellyfin ha estat actualitzat a {0}",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "La secció {0} de la configuració del servidor ha estat actualitzada",
|
"MessageNamedServerConfigurationUpdatedWithValue": "La secció {0} de la configuració del servidor ha estat actualitzada",
|
||||||
"MessageServerConfigurationUpdated": "S'ha actualitzat la configuració del servidor",
|
"MessageServerConfigurationUpdated": "S'ha actualitzat la configuració del servidor",
|
||||||
"MixedContent": "Contingut mesclat",
|
"MixedContent": "Contingut barrejat",
|
||||||
"Movies": "Pel·lícules",
|
"Movies": "Pel·lícules",
|
||||||
"Music": "Música",
|
"Music": "Música",
|
||||||
"MusicVideos": "Vídeos musicals",
|
"MusicVideos": "Vídeos musicals",
|
||||||
|
@ -76,7 +76,7 @@
|
||||||
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
|
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
|
||||||
"SubtitleDownloadFailureFromForItem": "Els subtítols no s'han pogut baixar de {0} per {1}",
|
"SubtitleDownloadFailureFromForItem": "Els subtítols no s'han pogut baixar de {0} per {1}",
|
||||||
"Sync": "Sincronitzar",
|
"Sync": "Sincronitzar",
|
||||||
"System": "System",
|
"System": "Sistema",
|
||||||
"TvShows": "Espectacles de TV",
|
"TvShows": "Espectacles de TV",
|
||||||
"User": "User",
|
"User": "User",
|
||||||
"UserCreatedWithName": "S'ha creat l'usuari {0}",
|
"UserCreatedWithName": "S'ha creat l'usuari {0}",
|
||||||
|
@ -113,5 +113,10 @@
|
||||||
"TasksChannelsCategory": "Canals d'internet",
|
"TasksChannelsCategory": "Canals d'internet",
|
||||||
"TasksApplicationCategory": "Aplicació",
|
"TasksApplicationCategory": "Aplicació",
|
||||||
"TasksLibraryCategory": "Biblioteca",
|
"TasksLibraryCategory": "Biblioteca",
|
||||||
"TasksMaintenanceCategory": "Manteniment"
|
"TasksMaintenanceCategory": "Manteniment",
|
||||||
|
"TaskCleanActivityLogDescription": "Eliminat entrades del registre d'activitats mes antigues que l'antiguitat configurada.",
|
||||||
|
"TaskCleanActivityLog": "Buidar Registre d'Activitat",
|
||||||
|
"Undefined": "Indefinit",
|
||||||
|
"Forced": "Forçat",
|
||||||
|
"Default": "Defecto"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"AppDeviceValues": "App: {0}, Gerät: {1}",
|
"AppDeviceValues": "App: {0}, Gerät: {1}",
|
||||||
"Application": "Anwendung",
|
"Application": "Anwendung",
|
||||||
"Artists": "Interpreten",
|
"Artists": "Interpreten",
|
||||||
"AuthenticationSucceededWithUserName": "{0} hat sich erfolgreich angemeldet",
|
"AuthenticationSucceededWithUserName": "{0} wurde angemeldet",
|
||||||
"Books": "Bücher",
|
"Books": "Bücher",
|
||||||
"CameraImageUploadedFrom": "Ein neues Kamerafoto wurde von {0} hochgeladen",
|
"CameraImageUploadedFrom": "Ein neues Kamerafoto wurde von {0} hochgeladen",
|
||||||
"Channels": "Kanäle",
|
"Channels": "Kanäle",
|
||||||
|
@ -94,22 +94,22 @@
|
||||||
"VersionNumber": "Version {0}",
|
"VersionNumber": "Version {0}",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Durchsucht das Internet nach fehlenden Untertiteln, basierend auf den Meta Einstellungen.",
|
"TaskDownloadMissingSubtitlesDescription": "Durchsucht das Internet nach fehlenden Untertiteln, basierend auf den Meta Einstellungen.",
|
||||||
"TaskDownloadMissingSubtitles": "Lade fehlende Untertitel herunter",
|
"TaskDownloadMissingSubtitles": "Lade fehlende Untertitel herunter",
|
||||||
"TaskRefreshChannelsDescription": "Erneuere Internet Kanal Informationen.",
|
"TaskRefreshChannelsDescription": "Aktualisiere Internet Kanal Informationen.",
|
||||||
"TaskRefreshChannels": "Erneuere Kanäle",
|
"TaskRefreshChannels": "Aktualisiere Kanäle",
|
||||||
"TaskCleanTranscodeDescription": "Löscht Transkodierdateien welche älter als ein Tag sind.",
|
"TaskCleanTranscodeDescription": "Löscht Transkodierdateien, welche älter als einen Tag sind.",
|
||||||
"TaskCleanTranscode": "Lösche Transkodier Pfad",
|
"TaskCleanTranscode": "Lösche Transkodier-Pfad",
|
||||||
"TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche dazu eingestellt sind automatisch zu updaten und installiert sie.",
|
"TaskUpdatePluginsDescription": "Lädt Updates für Plugins herunter, welche für automatische Updates konfiguriert sind und installiert diese.",
|
||||||
"TaskUpdatePlugins": "Update Plugins",
|
"TaskUpdatePlugins": "Aktualisiere Plugins",
|
||||||
"TaskRefreshPeopleDescription": "Erneuert Metadaten für Schauspieler und Regisseure in deinen Bibliotheken.",
|
"TaskRefreshPeopleDescription": "Aktualisiert Metadaten für Schauspieler und Regisseure in deinen Bibliotheken.",
|
||||||
"TaskRefreshPeople": "Erneuere Schauspieler",
|
"TaskRefreshPeople": "Aktualisiere Schauspieler",
|
||||||
"TaskCleanLogsDescription": "Lösche Log Dateien die älter als {0} Tage sind.",
|
"TaskCleanLogsDescription": "Lösche Log Dateien, die älter als {0} Tage sind.",
|
||||||
"TaskCleanLogs": "Lösche Log Pfad",
|
"TaskCleanLogs": "Lösche Log-Verzeichnis",
|
||||||
"TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.",
|
"TaskRefreshLibraryDescription": "Scanne alle Bibliotheken nach neu hinzugefügten Dateien und aktualisiere Metadaten.",
|
||||||
"TaskRefreshLibrary": "Scanne Medien-Bibliothek",
|
"TaskRefreshLibrary": "Scanne Medien-Bibliothek",
|
||||||
"TaskRefreshChapterImagesDescription": "Kreiert Vorschaubilder für Videos welche Kapitel haben.",
|
"TaskRefreshChapterImagesDescription": "Erstellt Vorschaubilder für Videos, welche Kapitel besitzen.",
|
||||||
"TaskRefreshChapterImages": "Extrahiert Kapitel-Bilder",
|
"TaskRefreshChapterImages": "Extrahiert Kapitel-Bilder",
|
||||||
"TaskCleanCacheDescription": "Löscht Zwischenspeicherdatein die nicht länger von System gebraucht werden.",
|
"TaskCleanCacheDescription": "Löscht nicht mehr benötigte Zwischenspeicherdateien.",
|
||||||
"TaskCleanCache": "Leere Cache Pfad",
|
"TaskCleanCache": "Leere Zwischenspeicher",
|
||||||
"TasksChannelsCategory": "Internet Kanäle",
|
"TasksChannelsCategory": "Internet Kanäle",
|
||||||
"TasksApplicationCategory": "Anwendung",
|
"TasksApplicationCategory": "Anwendung",
|
||||||
"TasksLibraryCategory": "Bibliothek",
|
"TasksLibraryCategory": "Bibliothek",
|
||||||
|
|
26
Emby.Server.Implementations/Localization/Core/eo.json
Normal file
26
Emby.Server.Implementations/Localization/Core/eo.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"NotificationOptionInstallationFailed": "Instalada fiasko",
|
||||||
|
"NotificationOptionAudioPlaybackStopped": "Sono de ludado haltis",
|
||||||
|
"NotificationOptionAudioPlayback": "Ludado de sono startis",
|
||||||
|
"NameSeasonUnknown": "Sezono Nekonata",
|
||||||
|
"NameSeasonNumber": "Sezono {0}",
|
||||||
|
"NameInstallFailed": "{0} instalado fiaskis",
|
||||||
|
"Music": "Muziko",
|
||||||
|
"Movies": "Filmoj",
|
||||||
|
"ItemRemovedWithName": "{0} forigis el la biblioteko",
|
||||||
|
"ItemAddedWithName": "{0} aldonis al la biblioteko",
|
||||||
|
"HeaderLiveTV": "Viva Televido",
|
||||||
|
"HeaderContinueWatching": "Daŭrigi Spektado",
|
||||||
|
"HeaderAlbumArtists": "Artistoj de Albumo",
|
||||||
|
"Folders": "Dosierujoj",
|
||||||
|
"DeviceOnlineWithName": "{0} estas konektita",
|
||||||
|
"Default": "Defaŭlte",
|
||||||
|
"Collections": "Kolektoj",
|
||||||
|
"ChapterNameValue": "Ĉapitro {0}",
|
||||||
|
"Channels": "Kanaloj",
|
||||||
|
"Books": "Libroj",
|
||||||
|
"Artists": "Artistoj",
|
||||||
|
"Application": "Aplikaĵo",
|
||||||
|
"AppDeviceValues": "Aplikaĵo: {0}, Aparato: {1}",
|
||||||
|
"Albums": "Albumoj"
|
||||||
|
}
|
|
@ -116,5 +116,6 @@
|
||||||
"TaskCleanActivityLogDescription": "Elimina las entradas del registro de actividad anteriores al periodo configurado.",
|
"TaskCleanActivityLogDescription": "Elimina las entradas del registro de actividad anteriores al periodo configurado.",
|
||||||
"TaskCleanActivityLog": "Limpiar Registro de Actividades",
|
"TaskCleanActivityLog": "Limpiar Registro de Actividades",
|
||||||
"Undefined": "Sin definir",
|
"Undefined": "Sin definir",
|
||||||
"Forced": "Forzado"
|
"Forced": "Forzado",
|
||||||
|
"Default": "Por Defecto"
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@
|
||||||
"NotificationOptionAudioPlayback": "پخش صدا آغاز شد",
|
"NotificationOptionAudioPlayback": "پخش صدا آغاز شد",
|
||||||
"NotificationOptionAudioPlaybackStopped": "پخش صدا متوقف شد",
|
"NotificationOptionAudioPlaybackStopped": "پخش صدا متوقف شد",
|
||||||
"NotificationOptionCameraImageUploaded": "تصاویر دوربین آپلود شد",
|
"NotificationOptionCameraImageUploaded": "تصاویر دوربین آپلود شد",
|
||||||
"NotificationOptionInstallationFailed": "نصب شکست خورد",
|
"NotificationOptionInstallationFailed": "نصب ناموفق",
|
||||||
"NotificationOptionNewLibraryContent": "محتوای جدید افزوده شد",
|
"NotificationOptionNewLibraryContent": "محتوای جدید افزوده شد",
|
||||||
"NotificationOptionPluginError": "خرابی افزونه",
|
"NotificationOptionPluginError": "خرابی افزونه",
|
||||||
"NotificationOptionPluginInstalled": "افزونه نصب شد",
|
"NotificationOptionPluginInstalled": "افزونه نصب شد",
|
||||||
|
@ -115,5 +115,8 @@
|
||||||
"TasksLibraryCategory": "کتابخانه",
|
"TasksLibraryCategory": "کتابخانه",
|
||||||
"TasksMaintenanceCategory": "تعمیر",
|
"TasksMaintenanceCategory": "تعمیر",
|
||||||
"Forced": "اجباری",
|
"Forced": "اجباری",
|
||||||
"Default": "پیشفرض"
|
"Default": "پیشفرض",
|
||||||
|
"TaskCleanActivityLogDescription": "ورودیهای قدیمیتر از سن تنظیم شده در سیاهه فعالیت را حذف میکند.",
|
||||||
|
"TaskCleanActivityLog": "پاکسازی سیاهه فعالیت",
|
||||||
|
"Undefined": "تعریف نشده"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,121 +1,121 @@
|
||||||
{
|
{
|
||||||
"HeaderLiveTV": "Live-TV",
|
"HeaderLiveTV": "Live TV",
|
||||||
"NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa.",
|
"NewVersionIsAvailable": "Uusi versio Jellyfin-palvelimesta on ladattavissa.",
|
||||||
"NameSeasonUnknown": "Tuntematon kausi",
|
"NameSeasonUnknown": "Tuntematon kausi",
|
||||||
"NameSeasonNumber": "Kausi {0}",
|
"NameSeasonNumber": "Kausi {0}",
|
||||||
"NameInstallFailed": "{0} asennus epäonnistui",
|
"NameInstallFailed": "{0} asennus epäonnistui",
|
||||||
"MusicVideos": "Musiikkivideot",
|
"MusicVideos": "Musiikkivideot",
|
||||||
"Music": "Musiikki",
|
"Music": "Musiikki",
|
||||||
"Movies": "Elokuvat",
|
"Movies": "Elokuvat",
|
||||||
"MixedContent": "Sekoitettu sisältö",
|
"MixedContent": "Sekalainen sisältö",
|
||||||
"MessageServerConfigurationUpdated": "Palvelimen asetukset on päivitetty",
|
"MessageServerConfigurationUpdated": "Palvelimen asetukset on päivitetty",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusryhmä {0} on päivitetty",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusten osio {0} on päivitetty",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty versioon {0}",
|
"MessageApplicationUpdatedTo": "Jellyfin-palvelin on päivitetty versioon {0}",
|
||||||
"MessageApplicationUpdated": "Jellyfin palvelin on päivitetty",
|
"MessageApplicationUpdated": "Jellyfin-palvelin on päivitetty",
|
||||||
"Latest": "Uusimmat",
|
"Latest": "Viimeisimmät",
|
||||||
"LabelRunningTimeValue": "Toiston kesto: {0}",
|
"LabelRunningTimeValue": "Kesto: {0}",
|
||||||
"LabelIpAddressValue": "IP-osoite: {0}",
|
"LabelIpAddressValue": "IP-osoite: {0}",
|
||||||
"ItemRemovedWithName": "{0} poistettiin kirjastosta",
|
"ItemRemovedWithName": "{0} poistettiin kirjastosta",
|
||||||
"ItemAddedWithName": "{0} lisättiin kirjastoon",
|
"ItemAddedWithName": "{0} lisättiin kirjastoon",
|
||||||
"Inherit": "Periytyä",
|
"Inherit": "Peri",
|
||||||
"HomeVideos": "Kotivideot",
|
"HomeVideos": "Kotivideot",
|
||||||
"HeaderRecordingGroups": "Tallennusryhmät",
|
"HeaderRecordingGroups": "Tallennusryhmät",
|
||||||
"HeaderNextUp": "Seuraavaksi",
|
"HeaderNextUp": "Seuraavaksi",
|
||||||
"HeaderFavoriteSongs": "Suosikkikappaleet",
|
"HeaderFavoriteSongs": "Suosikkikappaleet",
|
||||||
"HeaderFavoriteShows": "Suosikkisarjat",
|
"HeaderFavoriteShows": "Suosikkisarjat",
|
||||||
"HeaderFavoriteEpisodes": "Suosikkijaksot",
|
"HeaderFavoriteEpisodes": "Suosikkijaksot",
|
||||||
"HeaderFavoriteArtists": "Suosikkiartistit",
|
"HeaderFavoriteArtists": "Suosikkiesittäjät",
|
||||||
"HeaderFavoriteAlbums": "Suosikkialbumit",
|
"HeaderFavoriteAlbums": "Suosikkialbumit",
|
||||||
"HeaderContinueWatching": "Jatka katsomista",
|
"HeaderContinueWatching": "Jatka katselua",
|
||||||
"HeaderAlbumArtists": "Albumin artistit",
|
"HeaderAlbumArtists": "Albumin esittäjät",
|
||||||
"Genres": "Tyylilajit",
|
"Genres": "Tyylilajit",
|
||||||
"Folders": "Kansiot",
|
"Folders": "Kansiot",
|
||||||
"Favorites": "Suosikit",
|
"Favorites": "Suosikit",
|
||||||
"FailedLoginAttemptWithUserName": "Kirjautuminen epäonnistui kohteesta {0}",
|
"FailedLoginAttemptWithUserName": "Epäonnistunut kirjautumisyritys lähteestä \"{0}\"",
|
||||||
"DeviceOnlineWithName": "{0} on yhdistetty",
|
"DeviceOnlineWithName": "{0} on yhdistetty",
|
||||||
"DeviceOfflineWithName": "{0} yhteys on katkaistu",
|
"DeviceOfflineWithName": "{0} on katkaissut yhteyden",
|
||||||
"Collections": "Kokoelmat",
|
"Collections": "Kokoelmat",
|
||||||
"ChapterNameValue": "Jakso: {0}",
|
"ChapterNameValue": "Kappale {0}",
|
||||||
"Channels": "Kanavat",
|
"Channels": "Kanavat",
|
||||||
"CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}",
|
"CameraImageUploadedFrom": "Uusi kameran kuva on sirretty lähteestä {0}",
|
||||||
"Books": "Kirjat",
|
"Books": "Kirjat",
|
||||||
"AuthenticationSucceededWithUserName": "{0} todennus onnistui",
|
"AuthenticationSucceededWithUserName": "{0} on todennettu",
|
||||||
"Artists": "Artistit",
|
"Artists": "Esittäjät",
|
||||||
"Application": "Sovellus",
|
"Application": "Sovellus",
|
||||||
"AppDeviceValues": "Sovellus: {0}, Laite: {1}",
|
"AppDeviceValues": "Sovellus: {0}, Laite: {1}",
|
||||||
"Albums": "Albumit",
|
"Albums": "Albumit",
|
||||||
"User": "Käyttäjä",
|
"User": "Käyttäjä",
|
||||||
"System": "Järjestelmä",
|
"System": "Järjestelmä",
|
||||||
"ScheduledTaskFailedWithName": "{0} epäonnistui",
|
"ScheduledTaskFailedWithName": "{0} epäonnistui",
|
||||||
"PluginUpdatedWithName": "{0} päivitetty",
|
"PluginUpdatedWithName": "{0} päivitettiin",
|
||||||
"PluginInstalledWithName": "{0} asennettu",
|
"PluginInstalledWithName": "{0} asennettiin",
|
||||||
"Photos": "Kuvat",
|
"Photos": "Valokuvat",
|
||||||
"ScheduledTaskStartedWithName": "{0} aloitettu",
|
"ScheduledTaskStartedWithName": "\"{0}\" käynnistetty",
|
||||||
"PluginUninstalledWithName": "{0} poistettu",
|
"PluginUninstalledWithName": "{0} poistettiin",
|
||||||
"Playlists": "Soittolistat",
|
"Playlists": "Soittolistat",
|
||||||
"VersionNumber": "Versio {0}",
|
"VersionNumber": "Versio {0}",
|
||||||
"ValueSpecialEpisodeName": "Erikois - {0}",
|
"ValueSpecialEpisodeName": "Erikoisjakso - {0}",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} lisättiin mediakirjastoon",
|
"ValueHasBeenAddedToLibrary": "\"{0}\" on lisätty mediakirjastoon",
|
||||||
"UserStoppedPlayingItemWithValues": "{0} toistaminen valmistui {1} laitteella {2}",
|
"UserStoppedPlayingItemWithValues": "{0} lopetti kohteen \"{1}\" toiston sijainnissa \"{2}\"",
|
||||||
"UserStartedPlayingItemWithValues": "{0} toistaa {1} laitteella {2}",
|
"UserStartedPlayingItemWithValues": "{0} toistaa kohdetta \"{1}\" sijainnissa \"{2}\"",
|
||||||
"UserPolicyUpdatedWithName": "Käyttöoikeudet päivitetty käyttäjälle {0}",
|
"UserPolicyUpdatedWithName": "Käyttäjän {0} käyttöoikeudet on päivitetty",
|
||||||
"UserPasswordChangedWithName": "Salasana vaihdettu käyttäjälle {0}",
|
"UserPasswordChangedWithName": "Käyttäjän {0} salasana on vaihdettu",
|
||||||
"UserOnlineFromDevice": "{0} on paikalla osoitteesta {1}",
|
"UserOnlineFromDevice": "{0} on yhdistänyt sijainnista \"{1}\"",
|
||||||
"UserOfflineFromDevice": "{0} yhteys katkaistu kohteesta {1}",
|
"UserOfflineFromDevice": "{0} on katkaissut yhteyden sijainnista \"{1}\"",
|
||||||
"UserLockedOutWithName": "Käyttäjä {0} lukittu",
|
"UserLockedOutWithName": "Käyttäjä {0} on lukittu",
|
||||||
"UserDownloadingItemWithValues": "{0} lataa {1}",
|
"UserDownloadingItemWithValues": "{0} lataa kohdetta \"{1}\"",
|
||||||
"UserDeletedWithName": "Käyttäjä {0} poistettu",
|
"UserDeletedWithName": "Käyttäjä {0} on poistettu",
|
||||||
"UserCreatedWithName": "Käyttäjä {0} luotu",
|
"UserCreatedWithName": "Käyttäjä {0} on luotu",
|
||||||
"TvShows": "TV-ohjelmat",
|
"TvShows": "Sarjat",
|
||||||
"Sync": "Synkronoi",
|
"Sync": "Synkronointi",
|
||||||
"SubtitleDownloadFailureFromForItem": "Tekstitystä ei voitu ladata osoitteesta {0} kohteelle {1}",
|
"SubtitleDownloadFailureFromForItem": "Tekstityksen lataus lähteestä \"{0}\" kohteelle \"{1}\" epäonnistui",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Yritä hetken kuluttua uudelleen.",
|
"StartupEmbyServerIsLoading": "Jellyfin-palvelin latautuu. Yritä hetken kuluttua uudelleen.",
|
||||||
"Songs": "Kappaleet",
|
"Songs": "Kappaleet",
|
||||||
"Shows": "Ohjelmat",
|
"Shows": "Sarjat",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} on käynnistettävä uudelleen",
|
"ServerNameNeedsToBeRestarted": "\"{0}\" on käynnistettävä uudelleen",
|
||||||
"ProviderValue": "Tarjoaja: {0}",
|
"ProviderValue": "Lähde: {0}",
|
||||||
"Plugin": "Liitännäinen",
|
"Plugin": "Laajennus",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Videon toisto pysäytetty",
|
"NotificationOptionVideoPlaybackStopped": "Videon toisto lopetettu",
|
||||||
"NotificationOptionVideoPlayback": "Videota toistetaan",
|
"NotificationOptionVideoPlayback": "Videon toisto aloitettu",
|
||||||
"NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos",
|
"NotificationOptionUserLockedOut": "Käyttäjä on lukittu",
|
||||||
"NotificationOptionTaskFailed": "Ajastettu tehtävä epäonnistui",
|
"NotificationOptionTaskFailed": "Ajoitettu tehtävä epäonnistui",
|
||||||
"NotificationOptionServerRestartRequired": "Palvelin on käynnistettävä uudelleen",
|
"NotificationOptionServerRestartRequired": "Tarvitaan palvelimen uudelleenkäynnistys",
|
||||||
"NotificationOptionPluginUpdateInstalled": "Liitännäinen päivitetty",
|
"NotificationOptionPluginUpdateInstalled": "Laajennus on päivitetty",
|
||||||
"NotificationOptionPluginUninstalled": "Liitännäinen poistettu",
|
"NotificationOptionPluginUninstalled": "Laajennus on poistettu",
|
||||||
"NotificationOptionPluginInstalled": "Liitännäinen asennettu",
|
"NotificationOptionPluginInstalled": "Laajennus on asennettu",
|
||||||
"NotificationOptionPluginError": "Ongelma liitännäisessä",
|
"NotificationOptionPluginError": "Laajennuksen virhe",
|
||||||
"NotificationOptionNewLibraryContent": "Uutta sisältöä lisätty",
|
"NotificationOptionNewLibraryContent": "Sisältöä on lisätty",
|
||||||
"NotificationOptionInstallationFailed": "Asennus epäonnistui",
|
"NotificationOptionInstallationFailed": "Asennus epäonnistui",
|
||||||
"NotificationOptionCameraImageUploaded": "Kameran kuva ladattu",
|
"NotificationOptionCameraImageUploaded": "Kameran kuva on tallennettu",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Äänen toisto lopetettu",
|
"NotificationOptionAudioPlaybackStopped": "Äänen toisto lopetettu",
|
||||||
"NotificationOptionAudioPlayback": "Toistetaan ääntä",
|
"NotificationOptionAudioPlayback": "Äänen toisto aloitettu",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "Sovelluspäivitys asennettu",
|
"NotificationOptionApplicationUpdateInstalled": "Sovelluspäivitys asennettiin",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla",
|
"NotificationOptionApplicationUpdateAvailable": "Sovelluspäivitys on saatavilla",
|
||||||
"TasksMaintenanceCategory": "Ylläpito",
|
"TasksMaintenanceCategory": "Ylläpito",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Etsii puuttuvia tekstityksiä videon metadatatietojen pohjalta.",
|
"TaskDownloadMissingSubtitlesDescription": "Etsii puuttuvia tekstityksiä määritettyjen metatietoasetusten mukaisesti.",
|
||||||
"TaskDownloadMissingSubtitles": "Lataa puuttuvat tekstitykset",
|
"TaskDownloadMissingSubtitles": "Lataa puuttuvat tekstitykset",
|
||||||
"TaskRefreshChannelsDescription": "Päivittää internet-kanavien tiedot.",
|
"TaskRefreshChannelsDescription": "Päivittää internet-kanavien tiedot.",
|
||||||
"TaskRefreshChannels": "Päivitä kanavat",
|
"TaskRefreshChannels": "Päivitä kanavat",
|
||||||
"TaskCleanTranscodeDescription": "Poistaa transkoodatut tiedostot jotka ovat yli päivän vanhoja.",
|
"TaskCleanTranscodeDescription": "Poistaa päivää vanhemmat transkoodaustiedostot.",
|
||||||
"TaskCleanTranscode": "Puhdista transkoodaushakemisto",
|
"TaskCleanTranscode": "Puhdista transkoodauskansio",
|
||||||
"TaskUpdatePluginsDescription": "Lataa ja asentaa päivitykset liitännäisille jotka on asetettu päivittymään automaattisesti.",
|
"TaskUpdatePluginsDescription": "Lataa ja asentaa päivitykset laajennuksille, jotka on määritetty päivittymään automaattisesti.",
|
||||||
"TaskUpdatePlugins": "Päivitä liitännäiset",
|
"TaskUpdatePlugins": "Päivitä laajennukset",
|
||||||
"TaskRefreshPeopleDescription": "Päivittää näyttelijöiden ja ohjaajien mediatiedot kirjastossasi.",
|
"TaskRefreshPeopleDescription": "Päivittää mediakirjaston näyttelijöiden ja ohjaajien metatiedot.",
|
||||||
"TaskRefreshPeople": "Päivitä henkilöt",
|
"TaskRefreshPeople": "Päivitä henkilöt",
|
||||||
"TaskCleanLogsDescription": "Poistaa lokitiedostot jotka ovat yli {0} päivää vanhoja.",
|
"TaskCleanLogsDescription": "Poistaa {0} päivää vanhemmat lokitiedostot.",
|
||||||
"TaskCleanLogs": "Puhdista lokihakemisto",
|
"TaskCleanLogs": "Siivoa lokikansio",
|
||||||
"TaskRefreshLibraryDescription": "Skannaa mediakirjastosi uudet tiedostot ja päivittää metatiedot.",
|
"TaskRefreshLibraryDescription": "Tarkastaa mediakirjastosi sisällön uusien tiedostojen varalta ja päivittää metatiedot.",
|
||||||
"TaskRefreshLibrary": "Skannaa mediakirjasto",
|
"TaskRefreshLibrary": "Päivitä mediakirjasto",
|
||||||
"TaskRefreshChapterImagesDescription": "Luo pienoiskuvat videoille joissa on jaksoja.",
|
"TaskRefreshChapterImagesDescription": "Luo esikatselukuvat videoille, jotka sisältävät kappalejaon.",
|
||||||
"TaskRefreshChapterImages": "Pura jakson kuvat",
|
"TaskRefreshChapterImages": "Pura kappalejaon kuvat",
|
||||||
"TaskCleanCacheDescription": "Poistaa järjestelmälle tarpeettomat väliaikaistiedostot.",
|
"TaskCleanCacheDescription": "Poistaa tarpeettomiksi jääneet väliaikaistiedostot.",
|
||||||
"TaskCleanCache": "Tyhjennä välimuisti-hakemisto",
|
"TaskCleanCache": "Tyhjennä välimuistikansio",
|
||||||
"TasksChannelsCategory": "Internet kanavat",
|
"TasksChannelsCategory": "Internet-kanavat",
|
||||||
"TasksApplicationCategory": "Sovellus",
|
"TasksApplicationCategory": "Sovellus",
|
||||||
"TasksLibraryCategory": "Kirjasto",
|
"TasksLibraryCategory": "Kirjasto",
|
||||||
"Forced": "Pakotettu",
|
"Forced": "Pakotettu",
|
||||||
"Default": "Oletus",
|
"Default": "Oletus",
|
||||||
"TaskCleanActivityLogDescription": "Poistaa määritettyä vanhemmat tapahtumat aktiviteettilokista.",
|
"TaskCleanActivityLogDescription": "Poistaa määritettyä ikää vanhemmat tapahtumat toimintahistoriasta.",
|
||||||
"TaskCleanActivityLog": "Tyhjennä aktiviteettiloki",
|
"TaskCleanActivityLog": "Tyhjennä toimintahistoria",
|
||||||
"Undefined": "Määrittelemätön"
|
"Undefined": "Määrittelemätön"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,101 +3,101 @@
|
||||||
"ValueSpecialEpisodeName": "Espesyal - {0}",
|
"ValueSpecialEpisodeName": "Espesyal - {0}",
|
||||||
"ValueHasBeenAddedToLibrary": "Naidagdag na ang {0} sa iyong librerya ng medya",
|
"ValueHasBeenAddedToLibrary": "Naidagdag na ang {0} sa iyong librerya ng medya",
|
||||||
"UserStoppedPlayingItemWithValues": "Natapos ni {0} ang {1} sa {2}",
|
"UserStoppedPlayingItemWithValues": "Natapos ni {0} ang {1} sa {2}",
|
||||||
"UserStartedPlayingItemWithValues": "Si {0} ay nagplaplay ng {1} sa {2}",
|
"UserStartedPlayingItemWithValues": "Si {0} ay nagpla-play ng {1} sa {2}",
|
||||||
"UserPolicyUpdatedWithName": "Ang user policy ay naiupdate para kay {0}",
|
"UserPolicyUpdatedWithName": "Ang user policy ay nai-update para kay {0}",
|
||||||
"UserPasswordChangedWithName": "Napalitan na ang password ni {0}",
|
"UserPasswordChangedWithName": "Napalitan na ang password ni {0}",
|
||||||
"UserOnlineFromDevice": "Si {0} ay nakakonekta galing sa {1}",
|
"UserOnlineFromDevice": "Si {0} ay naka-konekta galing sa {1}",
|
||||||
"UserOfflineFromDevice": "Si {0} ay nadiskonekta galing sa {1}",
|
"UserOfflineFromDevice": "Si {0} ay na-diskonekta galing sa {1}",
|
||||||
"UserLockedOutWithName": "Si {0} ay nalock out",
|
"UserLockedOutWithName": "Si {0} ay nalock out",
|
||||||
"UserDownloadingItemWithValues": "Nagdadownload si {0} ng {1}",
|
"UserDownloadingItemWithValues": "Nagdadownload si {0} ng {1}",
|
||||||
"UserDeletedWithName": "Natanggal na is user {0}",
|
"UserDeletedWithName": "Natanggal na is user {0}",
|
||||||
"UserCreatedWithName": "Nagawa na si user {0}",
|
"UserCreatedWithName": "Nagawa na si user {0}",
|
||||||
"User": "User",
|
"User": "User",
|
||||||
"TvShows": "Pelikula",
|
"TvShows": "Mga Palabas sa Telebisyon",
|
||||||
"System": "Sistema",
|
"System": "Sistema",
|
||||||
"Sync": "Pag-sync",
|
"Sync": "Pag-sync",
|
||||||
"SubtitleDownloadFailureFromForItem": "Hindi naidownload ang subtitles {0} para sa {1}",
|
"SubtitleDownloadFailureFromForItem": "Hindi nai-download ang subtitles {0} para sa {1}",
|
||||||
"StartupEmbyServerIsLoading": "Nagloload ang Jellyfin Server. Sandaling maghintay.",
|
"StartupEmbyServerIsLoading": "Naglo-load ang Jellyfin Server. Mangyaring subukan ulit sandali.",
|
||||||
"Songs": "Kanta",
|
"Songs": "Mga Kanta",
|
||||||
"Shows": "Pelikula",
|
"Shows": "Mga Pelikula",
|
||||||
"ServerNameNeedsToBeRestarted": "Kailangan irestart ang {0}",
|
"ServerNameNeedsToBeRestarted": "Kailangan irestart ang {0}",
|
||||||
"ScheduledTaskStartedWithName": "Nagsimula na ang {0}",
|
"ScheduledTaskStartedWithName": "Nagsimula na ang {0}",
|
||||||
"ScheduledTaskFailedWithName": "Hindi gumana and {0}",
|
"ScheduledTaskFailedWithName": "Hindi gumana ang {0}",
|
||||||
"ProviderValue": "Ang provider ay {0}",
|
"ProviderValue": "Tagapagtustos: {0}",
|
||||||
"PluginUpdatedWithName": "Naiupdate na ang {0}",
|
"PluginUpdatedWithName": "Naiupdate na ang {0}",
|
||||||
"PluginUninstalledWithName": "Naiuninstall na ang {0}",
|
"PluginUninstalledWithName": "Naiuninstall na ang {0}",
|
||||||
"PluginInstalledWithName": "Nainstall na ang {0}",
|
"PluginInstalledWithName": "Nainstall na ang {0}",
|
||||||
"Plugin": "Plugin",
|
"Plugin": "Plugin",
|
||||||
"Playlists": "Playlists",
|
"Playlists": "Mga Playlist",
|
||||||
"Photos": "Larawan",
|
"Photos": "Mga Larawan",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Huminto na ang pelikula",
|
"NotificationOptionVideoPlaybackStopped": "Huminto na ang pelikula",
|
||||||
"NotificationOptionVideoPlayback": "Nagsimula na ang pelikula",
|
"NotificationOptionVideoPlayback": "Nagsimula na ang pelikula",
|
||||||
"NotificationOptionUserLockedOut": "Nakalock out ang user",
|
"NotificationOptionUserLockedOut": "Naka-lock out ang user",
|
||||||
"NotificationOptionTaskFailed": "Hindi gumana ang scheduled task",
|
"NotificationOptionTaskFailed": "Hindi gumana ang scheduled task",
|
||||||
"NotificationOptionServerRestartRequired": "Kailangan irestart ang server",
|
"NotificationOptionServerRestartRequired": "Kailangan i-restart ang server",
|
||||||
"NotificationOptionPluginUpdateInstalled": "Naiupdate na ang plugin",
|
"NotificationOptionPluginUpdateInstalled": "Nai-update na ang plugin",
|
||||||
"NotificationOptionPluginUninstalled": "Naiuninstall na ang plugin",
|
"NotificationOptionPluginUninstalled": "Nai-uninstall na ang plugin",
|
||||||
"NotificationOptionPluginInstalled": "Nainstall na ang plugin",
|
"NotificationOptionPluginInstalled": "Nainstall na ang plugin",
|
||||||
"NotificationOptionPluginError": "Hindi gumagana ang plugin",
|
"NotificationOptionPluginError": "Hindi gumagana ang plugin",
|
||||||
"NotificationOptionNewLibraryContent": "May bagong content na naidagdag",
|
"NotificationOptionNewLibraryContent": "May bagong content na naidagdag",
|
||||||
"NotificationOptionInstallationFailed": "Hindi nainstall ng mabuti",
|
"NotificationOptionInstallationFailed": "Hindi nainstall ng mabuti",
|
||||||
"NotificationOptionCameraImageUploaded": "Naiupload na ang picture",
|
"NotificationOptionCameraImageUploaded": "Naiupload na ang litrato",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Huminto na ang patugtog",
|
"NotificationOptionAudioPlaybackStopped": "Huminto na ang patugtog",
|
||||||
"NotificationOptionAudioPlayback": "Nagsimula na ang patugtog",
|
"NotificationOptionAudioPlayback": "Nagsimula na ang patugtog",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "Naiupdate na ang aplikasyon",
|
"NotificationOptionApplicationUpdateInstalled": "Naiupdate na ang aplikasyon",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "May bagong update ang aplikasyon",
|
"NotificationOptionApplicationUpdateAvailable": "May bagong update ang aplikasyon",
|
||||||
"NewVersionIsAvailable": "May bagong version ng Jellyfin Server na pwede idownload.",
|
"NewVersionIsAvailable": "May bagong version ng Jellyfin Server na pwede i-download.",
|
||||||
"NameSeasonUnknown": "Hindi alam ang season",
|
"NameSeasonUnknown": "Hindi matukoy ang season",
|
||||||
"NameSeasonNumber": "Season {0}",
|
"NameSeasonNumber": "Season {0}",
|
||||||
"NameInstallFailed": "Hindi nainstall ang {0}",
|
"NameInstallFailed": "Hindi nainstall ang {0}",
|
||||||
"MusicVideos": "Music video",
|
"MusicVideos": "Mga Music video",
|
||||||
"Music": "Kanta",
|
"Music": "Mga Kanta",
|
||||||
"Movies": "Pelikula",
|
"Movies": "Mga Pelikula",
|
||||||
"MixedContent": "Halo-halong content",
|
"MixedContent": "Halo-halong content",
|
||||||
"MessageServerConfigurationUpdated": "Naiupdate na ang server configuration",
|
"MessageServerConfigurationUpdated": "Naiupdate na ang server configuration",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Naiupdate na ang server configuration section {0}",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Naiupdate na ang server configuration section {0}",
|
||||||
"MessageApplicationUpdatedTo": "Ang Jellyfin Server ay naiupdate to {0}",
|
"MessageApplicationUpdatedTo": "Ang bersyon ng Jellyfin Server ay naiupdate sa {0}",
|
||||||
"MessageApplicationUpdated": "Naiupdate na ang Jellyfin Server",
|
"MessageApplicationUpdated": "Naiupdate na ang Jellyfin Server",
|
||||||
"Latest": "Pinakabago",
|
"Latest": "Pinakabago",
|
||||||
"LabelRunningTimeValue": "Oras: {0}",
|
"LabelRunningTimeValue": "Oras: {0}",
|
||||||
"LabelIpAddressValue": "Ang IP Address ay {0}",
|
"LabelIpAddressValue": "IP address: {0}",
|
||||||
"ItemRemovedWithName": "Naitanggal ang {0} sa librerya",
|
"ItemRemovedWithName": "Naitanggal ang {0} sa librerya",
|
||||||
"ItemAddedWithName": "Naidagdag ang {0} sa librerya",
|
"ItemAddedWithName": "Naidagdag ang {0} sa librerya",
|
||||||
"Inherit": "Manahin",
|
"Inherit": "Manahin",
|
||||||
"HeaderRecordingGroups": "Pagtatalang Grupo",
|
"HeaderRecordingGroups": "Pagtatalang Grupo",
|
||||||
"HeaderNextUp": "Susunod",
|
"HeaderNextUp": "Susunod",
|
||||||
"HeaderLiveTV": "Live TV",
|
"HeaderLiveTV": "Live TV",
|
||||||
"HeaderFavoriteSongs": "Paboritong Kanta",
|
"HeaderFavoriteSongs": "Mga Paboritong Kanta",
|
||||||
"HeaderFavoriteShows": "Paboritong Pelikula",
|
"HeaderFavoriteShows": "Mga Paboritong Pelikula",
|
||||||
"HeaderFavoriteEpisodes": "Paboritong Episodes",
|
"HeaderFavoriteEpisodes": "Mga Paboritong Episode",
|
||||||
"HeaderFavoriteArtists": "Paboritong Artista",
|
"HeaderFavoriteArtists": "Mga Paboritong Artista",
|
||||||
"HeaderFavoriteAlbums": "Paboritong Albums",
|
"HeaderFavoriteAlbums": "Mga Paboritong Album",
|
||||||
"HeaderContinueWatching": "Ituloy Manood",
|
"HeaderContinueWatching": "Magpatuloy sa Panonood",
|
||||||
"HeaderAlbumArtists": "Artista ng Album",
|
"HeaderAlbumArtists": "Mga Artista ng Album",
|
||||||
"Genres": "Kategorya",
|
"Genres": "Mga Kategorya",
|
||||||
"Folders": "Folders",
|
"Folders": "Mga Folder",
|
||||||
"Favorites": "Paborito",
|
"Favorites": "Mga Paborito",
|
||||||
"FailedLoginAttemptWithUserName": "maling login galing {0}",
|
"FailedLoginAttemptWithUserName": "Maling login galing kay/sa {0}",
|
||||||
"DeviceOnlineWithName": "nakakonekta si {0}",
|
"DeviceOnlineWithName": "Nakakonekta si/ang {0}",
|
||||||
"DeviceOfflineWithName": "nadiskonekta si {0}",
|
"DeviceOfflineWithName": "Nadiskonekta si/ang {0}",
|
||||||
"Collections": "Koleksyon",
|
"Collections": "Mga Koleksyon",
|
||||||
"ChapterNameValue": "Kabanata {0}",
|
"ChapterNameValue": "Kabanata {0}",
|
||||||
"Channels": "Channel",
|
"Channels": "Mga Channel",
|
||||||
"CameraImageUploadedFrom": "May bagong larawan na naupload galing {0}",
|
"CameraImageUploadedFrom": "May bagong larawan na naupload galing sa/kay {0}",
|
||||||
"Books": "Libro",
|
"Books": "Mga Libro",
|
||||||
"AuthenticationSucceededWithUserName": "{0} na patunayan",
|
"AuthenticationSucceededWithUserName": "Napatunayan si/ang {0}",
|
||||||
"Artists": "Artista",
|
"Artists": "Mga Artista",
|
||||||
"Application": "Aplikasyon",
|
"Application": "Aplikasyon",
|
||||||
"AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}",
|
"AppDeviceValues": "Aplikasyon: {0}, Aparato: {1}",
|
||||||
"Albums": "Albums",
|
"Albums": "Mga Album",
|
||||||
"TaskRefreshLibrary": "Suriin and Librerya ng Medya",
|
"TaskRefreshLibrary": "Suriin and Librerya ng Medya",
|
||||||
"TaskRefreshChapterImagesDescription": "Gumawa ng larawan para sa mga pelikula na may kabanata.",
|
"TaskRefreshChapterImagesDescription": "Gumawa ng larawan para sa mga pelikula na may kabanata.",
|
||||||
"TaskRefreshChapterImages": "Kunin ang mga larawan ng kabanata",
|
"TaskRefreshChapterImages": "Kunin ang mga larawan ng kabanata",
|
||||||
"TaskCleanCacheDescription": "Tanggalin ang mga cache file na hindi na kailangan ng systema.",
|
"TaskCleanCacheDescription": "Tanggalin ang mga cache file na hindi na kailangan ng sistema.",
|
||||||
"TasksChannelsCategory": "Palabas sa internet",
|
"TasksChannelsCategory": "Palabas sa internet",
|
||||||
"TasksLibraryCategory": "Librerya",
|
"TasksLibraryCategory": "Librerya",
|
||||||
"TasksMaintenanceCategory": "Pagpapanatili",
|
"TasksMaintenanceCategory": "Pagpapanatili",
|
||||||
"HomeVideos": "Sariling pelikula",
|
"HomeVideos": "Sariling video/pelikula",
|
||||||
"TaskRefreshPeopleDescription": "Ini-update ang metadata para sa mga aktor at direktor sa iyong librerya ng medya.",
|
"TaskRefreshPeopleDescription": "Ini-update ang metadata para sa mga aktor at direktor sa iyong librerya ng medya.",
|
||||||
"TaskRefreshPeople": "I-refresh ang Tauhan",
|
"TaskRefreshPeople": "I-refresh ang Tauhan",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Hinahanap sa internet ang mga nawawalang subtiles base sa metadata configuration.",
|
"TaskDownloadMissingSubtitlesDescription": "Hinahanap sa internet ang mga nawawalang subtiles base sa metadata configuration.",
|
||||||
|
@ -105,14 +105,17 @@
|
||||||
"TaskRefreshChannelsDescription": "Ni-rerefresh ang impormasyon sa internet channels.",
|
"TaskRefreshChannelsDescription": "Ni-rerefresh ang impormasyon sa internet channels.",
|
||||||
"TaskRefreshChannels": "I-refresh ang Channels",
|
"TaskRefreshChannels": "I-refresh ang Channels",
|
||||||
"TaskCleanTranscodeDescription": "Binubura ang transcode files na mas matanda ng isang araw.",
|
"TaskCleanTranscodeDescription": "Binubura ang transcode files na mas matanda ng isang araw.",
|
||||||
"TaskUpdatePluginsDescription": "Nag download at install ng updates sa plugins na naka configure para sa automatikong pag update.",
|
"TaskUpdatePluginsDescription": "Nag download at install ng updates sa plugins na naka configure para sa awtomatikong pag-update.",
|
||||||
"TaskUpdatePlugins": "I-update ang Plugins",
|
"TaskUpdatePlugins": "I-update ang Plugins",
|
||||||
"TaskCleanLogsDescription": "Binubura and files ng talaan na mas mantanda ng {0} araw.",
|
"TaskCleanLogsDescription": "Binubura and files ng talaan na mas mantanda ng {0} araw.",
|
||||||
"TaskCleanTranscode": "Linisin and Direktoryo ng Transcode",
|
"TaskCleanTranscode": "Linisin and Direktoryo ng Transcode",
|
||||||
"TaskCleanLogs": "Linisin and Direktoryo ng Talaan",
|
"TaskCleanLogs": "Linisin and Direktoryo ng Talaan",
|
||||||
"TaskRefreshLibraryDescription": "Sinusuri ang iyong librerya ng medya para sa bagong files at irefresh ang metadata.",
|
"TaskRefreshLibraryDescription": "Sinusuri ang iyong librerya ng medya para sa bagong files at irefresh ang metadata.",
|
||||||
"TaskCleanCache": "Linisin and Direktoryo ng Cache",
|
"TaskCleanCache": "Linisin and Direktoryo ng Cache",
|
||||||
"TasksApplicationCategory": "Application",
|
"TasksApplicationCategory": "Aplikasyon",
|
||||||
"TaskCleanActivityLog": "Linisin ang Tala ng Aktibidad",
|
"TaskCleanActivityLog": "Linisin ang Tala ng Aktibidad",
|
||||||
"TaskCleanActivityLogDescription": "Tanggalin ang mga tala ng aktibidad na mas matanda sa naka configure na edad."
|
"TaskCleanActivityLogDescription": "Tanggalin ang mga tala ng aktibidad na mas luma sa nakatakda na edad.",
|
||||||
|
"Default": "Default",
|
||||||
|
"Undefined": "Hindi tiyak",
|
||||||
|
"Forced": "Sapilitan"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"Albums": "संग्रह",
|
"Albums": "एल्बम",
|
||||||
"HeaderRecordingGroups": "रिकॉर्डिंग समूह",
|
"HeaderRecordingGroups": "रिकॉर्डिंग समूह",
|
||||||
"HeaderNextUp": "इसके बाद",
|
"HeaderNextUp": "इसके बाद",
|
||||||
"HeaderLiveTV": "लाइव टीवी",
|
"HeaderLiveTV": "लाइव टीवी",
|
||||||
|
@ -26,5 +26,30 @@
|
||||||
"AuthenticationSucceededWithUserName": "सफलता से प्रमाणीकृत",
|
"AuthenticationSucceededWithUserName": "सफलता से प्रमाणीकृत",
|
||||||
"Artists": "कलाकारों",
|
"Artists": "कलाकारों",
|
||||||
"Application": "एप्लिकेशन",
|
"Application": "एप्लिकेशन",
|
||||||
"AppDeviceValues": "एप: {0}, मशीन: {1}"
|
"AppDeviceValues": "एप: {0}, उपकरण: {1}",
|
||||||
|
"NotificationOptionPluginUninstalled": "प्लगइन अनइंस्टाल हो गया",
|
||||||
|
"NotificationOptionPluginInstalled": "प्लगइन इनस्टॉल हो गया",
|
||||||
|
"NotificationOptionPluginError": "प्लगइन फ़ैल हो गया",
|
||||||
|
"NotificationOptionInstallationFailed": "इंस्टालेशन फ़ैल हो गया",
|
||||||
|
"NotificationOptionAudioPlaybackStopped": "संगीत बंद कर दिया गया",
|
||||||
|
"NotificationOptionAudioPlayback": "संगीत शुरू कर दिया गया",
|
||||||
|
"NotificationOptionCameraImageUploaded": "कैमरा फोटो अपलोड किया गया",
|
||||||
|
"NotificationOptionApplicationUpdateInstalled": "एप्लीकेशन अपडेट इनस्टॉल कर दिया है",
|
||||||
|
"NotificationOptionApplicationUpdateAvailable": "एप्लीकेशन अपडेट उपलभ्द है",
|
||||||
|
"NewVersionIsAvailable": "जेलीफिन सर्वर का एक नया वर्जन डाउनलोड के लिए उपलब्ध है।",
|
||||||
|
"NameSeasonUnknown": "अनजान भाग",
|
||||||
|
"NameSeasonNumber": "भाग {0}",
|
||||||
|
"NameInstallFailed": "{0} इनस्टॉल करते समय फेल हो गया है",
|
||||||
|
"MusicVideos": "संगीत वीडियो",
|
||||||
|
"Music": "संगीत",
|
||||||
|
"Movies": "फ़िल्म",
|
||||||
|
"MixedContent": "मिला-जुला कंटेंट",
|
||||||
|
"MessageServerConfigurationUpdated": "सर्वर कॉन्फ़िगरेशन अपडेट हो गया है",
|
||||||
|
"MessageNamedServerConfigurationUpdatedWithValue": "सर्वर कॉन्फ़िगरेशन भाग {0} अपडेट हो गया है",
|
||||||
|
"MessageApplicationUpdatedTo": "जैलीफिन सर्वर {0} में अपडेट हो गया है",
|
||||||
|
"MessageApplicationUpdated": "जैलीफिन सर्वर अपडेट हो गया है",
|
||||||
|
"Latest": "सबसे नया",
|
||||||
|
"LabelIpAddressValue": "आई पी एड्रेस: {0}",
|
||||||
|
"ItemRemovedWithName": "{0} लाइब्रेरी में से निकाल दिया है",
|
||||||
|
"HomeVideos": "होम वीडियोस"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,122 +1,122 @@
|
||||||
{
|
{
|
||||||
"Albums": "Álbomdar",
|
"Albums": "Älbomdar",
|
||||||
"AppDeviceValues": "Qoldanba: {0}, Qurylǵy: {1}",
|
"AppDeviceValues": "Qoldanba: {0}, Qūrylğy: {1}",
|
||||||
"Application": "Qoldanba",
|
"Application": "Qoldanba",
|
||||||
"Artists": "Oryndaýshylar",
|
"Artists": "Oryndauşylar",
|
||||||
"AuthenticationSucceededWithUserName": "{0} túpnusqalyq rastalýy sátti aıaqtaldy",
|
"AuthenticationSucceededWithUserName": "{0} tüpnūsqalyq rastaluy sättı aiaqtaldy",
|
||||||
"Books": "Kitaptar",
|
"Books": "Kıtaptar",
|
||||||
"CameraImageUploadedFrom": "{0} kamerasynan jańa sýret júktep salyndy",
|
"CameraImageUploadedFrom": "{0} kamerasynan jaŋa suret jüktep salyndy",
|
||||||
"Channels": "Arnalar",
|
"Channels": "Arnalar",
|
||||||
"ChapterNameValue": "{0}-sahna",
|
"ChapterNameValue": "{0}-sahna",
|
||||||
"Collections": "Jıyntyqtar",
|
"Collections": "Jiyntyqtar",
|
||||||
"DeviceOfflineWithName": "{0} ajyratylǵan",
|
"DeviceOfflineWithName": "{0} ajyratylğan",
|
||||||
"DeviceOnlineWithName": "{0} qosylǵan",
|
"DeviceOnlineWithName": "{0} qosylğan",
|
||||||
"FailedLoginAttemptWithUserName": "{0} tarapynan kirý áreketi sátsiz aıaqtaldy",
|
"FailedLoginAttemptWithUserName": "{0} tarapynan kıru äreketı sätsız aiaqtaldy",
|
||||||
"Favorites": "Tańdaýlylar",
|
"Favorites": "Taŋdaulylar",
|
||||||
"Folders": "Qaltalar",
|
"Folders": "Qaltalar",
|
||||||
"Genres": "Janrlar",
|
"Genres": "Janrlar",
|
||||||
"HeaderAlbumArtists": "Álbom oryndaýshylary",
|
"HeaderAlbumArtists": "Älbom oryndauşylary",
|
||||||
"HeaderContinueWatching": "Qaraýdy jalǵastyrý",
|
"HeaderContinueWatching": "Qaraudy jalğastyru",
|
||||||
"HeaderFavoriteAlbums": "Tańdaýly álbomdar",
|
"HeaderFavoriteAlbums": "Taŋdauly älbomdar",
|
||||||
"HeaderFavoriteArtists": "Tańdaýly oryndaýshylar",
|
"HeaderFavoriteArtists": "Taŋdauly oryndauşylar",
|
||||||
"HeaderFavoriteEpisodes": "Tańdaýly bólimder",
|
"HeaderFavoriteEpisodes": "Taŋdauly telebölımder",
|
||||||
"HeaderFavoriteShows": "Tańdaýly kórsetimder",
|
"HeaderFavoriteShows": "Taŋdauly körsetımder",
|
||||||
"HeaderFavoriteSongs": "Tańdaýly áýender",
|
"HeaderFavoriteSongs": "Taŋdauly äuender",
|
||||||
"HeaderLiveTV": "Efır",
|
"HeaderLiveTV": "Efir",
|
||||||
"HeaderNextUp": "Kezekti",
|
"HeaderNextUp": "Kezektı",
|
||||||
"HeaderRecordingGroups": "Jazba toptary",
|
"HeaderRecordingGroups": "Jazba toptary",
|
||||||
"HomeVideos": "Úılik beıneler",
|
"HomeVideos": "Üilık beineler",
|
||||||
"Inherit": "Muraǵa ıelený",
|
"Inherit": "İelenu",
|
||||||
"ItemAddedWithName": "{0} tasyǵyshhanaǵa ústeldi",
|
"ItemAddedWithName": "{0} tasyğyşhanağa üstelindı",
|
||||||
"ItemRemovedWithName": "{0} tasyǵyshhanadan alastaldy",
|
"ItemRemovedWithName": "{0} tasyğyşhanadan alastaldy",
|
||||||
"LabelIpAddressValue": "IP-mekenjaıy: {0}",
|
"LabelIpAddressValue": "IP-mekenjaiy: {0}",
|
||||||
"LabelRunningTimeValue": "Oınatý ýaqyty: {0}",
|
"LabelRunningTimeValue": "Oinatu uaqyty: {0}",
|
||||||
"Latest": "Eń keıingi",
|
"Latest": "Eŋ keiıngı",
|
||||||
"MessageApplicationUpdated": "Jellyfin Serveri jańartyldy",
|
"MessageApplicationUpdated": "Jellyfin Serverı jaŋartyldy",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin Serveri {0} nusqasyna jańartyldy",
|
"MessageApplicationUpdatedTo": "Jellyfin Serverı {0} nūsqasyna jaŋartyldy",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Server konfıgýrasýasynyń {0} bólimi jańartyldy",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Server teŋşelımderınıŋ {0} bölımı jaŋartyldy",
|
||||||
"MessageServerConfigurationUpdated": "Server konfıgýrasıasy jańartyldy",
|
"MessageServerConfigurationUpdated": "Server teŋşelımderı jaŋartyldy",
|
||||||
"MixedContent": "Aralas mazmun",
|
"MixedContent": "Aralas mazmūn",
|
||||||
"Movies": "Fılmder",
|
"Movies": "Filmder",
|
||||||
"Music": "Mýzyka",
|
"Music": "Muzyka",
|
||||||
"MusicVideos": "Mýzykalyq beıneler",
|
"MusicVideos": "Muzykalyq beineler",
|
||||||
"NameInstallFailed": "{0} ornatylýy sátsiz",
|
"NameInstallFailed": "{0} ornatyluy sätsız",
|
||||||
"NameSeasonNumber": "{0}-maýsym",
|
"NameSeasonNumber": "{0}-mausym",
|
||||||
"NameSeasonUnknown": "Belgisiz maýsym",
|
"NameSeasonUnknown": "Belgısız mausym",
|
||||||
"NewVersionIsAvailable": "Jańa Jellyfin Server nusqasy júktep alýǵa qoljetimdi.",
|
"NewVersionIsAvailable": "Jaŋa Jellyfin Server nūsqasy jüktep aluğa qoljetımdı.",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "Qoldanba jańartýy qoljetimdi",
|
"NotificationOptionApplicationUpdateAvailable": "Qoldanba jaŋartuy qoljetımdı",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "Qoldanba jańartýy ornatyldy",
|
"NotificationOptionApplicationUpdateInstalled": "Qoldanba jaŋartuy ornatyldy",
|
||||||
"NotificationOptionAudioPlayback": "Dybys oınatýy bastaldy",
|
"NotificationOptionAudioPlayback": "Dybys oinatuy bastaldy",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Dybys oınatýy toqtatyldy",
|
"NotificationOptionAudioPlaybackStopped": "Dybys oinatuy toqtatyldy",
|
||||||
"NotificationOptionCameraImageUploaded": "Kameradan fotosýret júktep salynǵan",
|
"NotificationOptionCameraImageUploaded": "Kameradan fotosuret jüktep salynğan",
|
||||||
"NotificationOptionInstallationFailed": "Ornatý sátsizdigi",
|
"NotificationOptionInstallationFailed": "Ornatu sätsızdıgı",
|
||||||
"NotificationOptionNewLibraryContent": "Jańa mazmun ústelgen",
|
"NotificationOptionNewLibraryContent": "Jaŋa mazmūn üstelıngen",
|
||||||
"NotificationOptionPluginError": "Plagın sátsizdigi",
|
"NotificationOptionPluginError": "Plagin sätsızdıgı",
|
||||||
"NotificationOptionPluginInstalled": "Plagın ornatyldy",
|
"NotificationOptionPluginInstalled": "Plagin ornatyldy",
|
||||||
"NotificationOptionPluginUninstalled": "Plagın ornatýy boldyrylmady",
|
"NotificationOptionPluginUninstalled": "Plagin ornatuy boldyrylmady",
|
||||||
"NotificationOptionPluginUpdateInstalled": "Plagın jańartýy ornatyldy",
|
"NotificationOptionPluginUpdateInstalled": "Plagin jaŋartuy ornatyldy",
|
||||||
"NotificationOptionServerRestartRequired": "Serverdi qaıta iske qosý qajet",
|
"NotificationOptionServerRestartRequired": "Serverdı qaita ıske qosu qajet",
|
||||||
"NotificationOptionTaskFailed": "Josparlaǵan tapsyrma sátsizdigi",
|
"NotificationOptionTaskFailed": "Josparlağan tapsyrma sätsızdıgı",
|
||||||
"NotificationOptionUserLockedOut": "Paıdalanýshy qursaýly",
|
"NotificationOptionUserLockedOut": "Paidalanuşy qūrsauly",
|
||||||
"NotificationOptionVideoPlayback": "Beıne oınatýy bastaldy",
|
"NotificationOptionVideoPlayback": "Beine oinatuy bastaldy",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Beıne oınatýy toqtatyldy",
|
"NotificationOptionVideoPlaybackStopped": "Beine oinatuy toqtatyldy",
|
||||||
"Photos": "Fotosýretter",
|
"Photos": "Fotosuretter",
|
||||||
"Playlists": "Oınatý tizimderi",
|
"Playlists": "Oinatu tızımderı",
|
||||||
"Plugin": "Plagın",
|
"Plugin": "Plagin",
|
||||||
"PluginInstalledWithName": "{0} ornatyldy",
|
"PluginInstalledWithName": "{0} ornatyldy",
|
||||||
"PluginUninstalledWithName": "{0} joıyldy",
|
"PluginUninstalledWithName": "{0} joiyldy",
|
||||||
"PluginUpdatedWithName": "{0} jańartyldy",
|
"PluginUpdatedWithName": "{0} jaŋartyldy",
|
||||||
"ProviderValue": "Jetkizýshi: {0}",
|
"ProviderValue": "Jetkızuşı: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} sátsiz",
|
"ScheduledTaskFailedWithName": "{0} sätsız",
|
||||||
"ScheduledTaskStartedWithName": "{0} iske qosyldy",
|
"ScheduledTaskStartedWithName": "{0} ıske qosyldy",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} qaıta iske qosý qajet",
|
"ServerNameNeedsToBeRestarted": "{0} qaita ıske qosu qajet",
|
||||||
"Shows": "Kórsetimder",
|
"Shows": "Körsetımder",
|
||||||
"Songs": "Áýender",
|
"Songs": "Äuender",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin Server júktelýde. Áreketti kóp uzamaı qaıtalańyz.",
|
"StartupEmbyServerIsLoading": "Jellyfin Server jüktelude. Ärekettı köp ūzamai qaitalaŋyz.",
|
||||||
"SubtitleDownloadFailureForItem": "Субтитрлер {0} үшін жүктеліп алынуы сәтсіз",
|
"SubtitleDownloadFailureForItem": "Субтитрлер {0} үшін жүктеліп алынуы сәтсіз",
|
||||||
"SubtitleDownloadFailureFromForItem": "{1} úshin sýbtıtrlerdi {0} kózinen júktep alý sátsiz",
|
"SubtitleDownloadFailureFromForItem": "{1} üşın subtitrlerdı {0} közınen jüktep alu sätsız",
|
||||||
"Sync": "Úndestirý",
|
"Sync": "Ündestıru",
|
||||||
"System": "Júıe",
|
"System": "Jüie",
|
||||||
"TvShows": "TD-kórsetimder",
|
"TvShows": "TD-körsetımder",
|
||||||
"User": "Paıdalanýshy",
|
"User": "Paidalanuşy",
|
||||||
"UserCreatedWithName": "Paıdalanýshy {0} jasalǵan",
|
"UserCreatedWithName": "Paidalanuşy {0} jasalğan",
|
||||||
"UserDeletedWithName": "Paıdalanýshy {0} joıylǵan",
|
"UserDeletedWithName": "Paidalanuşy {0} joiylğan",
|
||||||
"UserDownloadingItemWithValues": "{0} mynany júktep alýda: {1}",
|
"UserDownloadingItemWithValues": "{0} — {1} jüktep aluda",
|
||||||
"UserLockedOutWithName": "Paıdalanýshy {0} qursaýly",
|
"UserLockedOutWithName": "Paidalanuşy {0} qūrsaulanğan",
|
||||||
"UserOfflineFromDevice": "{0} - {1} tarapynan ajyratylǵan",
|
"UserOfflineFromDevice": "{0} — {1} tarapynan ajyratyldy",
|
||||||
"UserOnlineFromDevice": "{0} - {1} arqyly qosylǵan",
|
"UserOnlineFromDevice": "{0} — {1} tarapynan qosyldy",
|
||||||
"UserPasswordChangedWithName": "Paıdalanýshy {0} úshin paról ózgertildi",
|
"UserPasswordChangedWithName": "Paidalanuşy {0} üşın paröl özgertıldı",
|
||||||
"UserPolicyUpdatedWithName": "Paıdalanýshy {0} úshin saıasattary jańartyldy",
|
"UserPolicyUpdatedWithName": "Paidalanuşy {0} üşın saiasattary jaŋartyldy",
|
||||||
"UserStartedPlayingItemWithValues": "{0} - {1} oınatýyn {2} bastady",
|
"UserStartedPlayingItemWithValues": "{0} — {2} tarapynan {1} oinatuda",
|
||||||
"UserStoppedPlayingItemWithValues": "{0} - {1} oınatýyn {2} toqtatty",
|
"UserStoppedPlayingItemWithValues": "{0} — {2} tarapynan {1} oinatuyn toqtatty",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} (tasyǵyshhanaǵa ústelindi)",
|
"ValueHasBeenAddedToLibrary": "{0} tasyğyşhanağa üstelındı",
|
||||||
"ValueSpecialEpisodeName": "Arnaıy - {0}",
|
"ValueSpecialEpisodeName": "Arnaiy - {0}",
|
||||||
"VersionNumber": "Nusqasy {0}",
|
"VersionNumber": "Nūsqasy {0}",
|
||||||
"Default": "Ádepki",
|
"Default": "Ädepkı",
|
||||||
"TaskDownloadMissingSubtitles": "Joq sýbtıtrlerdi júktep alý",
|
"TaskDownloadMissingSubtitles": "Joq subtitrlerdı jüktep alu",
|
||||||
"TaskRefreshChannels": "Arnalardy jańartý",
|
"TaskRefreshChannels": "Arnalardy jaŋğyrtu",
|
||||||
"TaskCleanTranscode": "Qaıta kodtaý katalogyn tazalaý",
|
"TaskCleanTranscode": "Qaita kodtau katalogyn tazalau",
|
||||||
"TaskUpdatePlugins": "Plagınderdi jańartý",
|
"TaskUpdatePlugins": "Plaginderdı jaŋartu",
|
||||||
"TaskRefreshPeople": "Adamdardy jańartý",
|
"TaskRefreshPeople": "Adamdardy jaŋğyrtu",
|
||||||
"TaskCleanLogs": "Jurnal katalogyn tazalaý",
|
"TaskCleanLogs": "Jūrnal katalogyn tazalau",
|
||||||
"TaskRefreshLibrary": "Tasyǵyshhanany skanerleý",
|
"TaskRefreshLibrary": "Tasyğyşhanany skanerleu",
|
||||||
"TaskRefreshChapterImages": "Sahna keskinderin shyǵaryp alý",
|
"TaskRefreshChapterImages": "Sahna suretterın şyğaryp alu",
|
||||||
"TaskCleanCache": "Kesh katalogyn tazalaý",
|
"TaskCleanCache": "Keş katalogyn tazalau",
|
||||||
"TaskCleanActivityLog": "Áreket jurnalyn tazalaý",
|
"TaskCleanActivityLog": "Äreket jūrnalyn tazalau",
|
||||||
"TasksChannelsCategory": "Internet-arnalar",
|
"TasksChannelsCategory": "Internet-arnalar",
|
||||||
"TasksApplicationCategory": "Qoldanba",
|
"TasksApplicationCategory": "Qoldanba",
|
||||||
"TasksLibraryCategory": "Tasyǵyshhana",
|
"TasksLibraryCategory": "Tasyğyşhana",
|
||||||
"TasksMaintenanceCategory": "Qyzmet kórsetý",
|
"TasksMaintenanceCategory": "Qyzmet körsetu",
|
||||||
"Undefined": "Anyqtalmady",
|
"Undefined": "Anyqtalmağan",
|
||||||
"Forced": "Májbúrli",
|
"Forced": "Mäjbürlı",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Metaderekter teńshelimi negіzіnde joq sýbtıtrlerdі Internetten іzdeıdі.",
|
"TaskDownloadMissingSubtitlesDescription": "Metaderekter teŋşelımderı negızınde joq subtitrlerdı Internetten ızdeidı.",
|
||||||
"TaskRefreshChannelsDescription": "Internet-arnalar málimetterin jańartady.",
|
"TaskRefreshChannelsDescription": "Internet-arnalar mälımetterın jaŋğyrtady.",
|
||||||
"TaskCleanTranscodeDescription": "Bіr kúnnen asqan qaıta kodtaý faıldaryn joıady.",
|
"TaskCleanTranscodeDescription": "Bіr künnen asqan qaita kodtau faildaryn joiady.",
|
||||||
"TaskUpdatePluginsDescription": "Avtomatty túrde jańartýǵa teńshelgen plagınder úshin jańartýlardy júktep alady jáne ornatady.",
|
"TaskUpdatePluginsDescription": "Avtomatty türde jaŋartuğa teŋşelgen plaginder üşın jaŋartulardy jüktep alady jäne ornatady.",
|
||||||
"TaskRefreshPeopleDescription": "Tasyǵyshhanadaǵy aktórler men rejısórler metaderekterіn jańartady.",
|
"TaskRefreshPeopleDescription": "Tasyğyşhanadağy aktörler men rejisörler metaderekterın jaŋartady.",
|
||||||
"TaskCleanLogsDescription": "{0} kúnnen asqan jurnal faıldaryn joıady.",
|
"TaskCleanLogsDescription": "{0} künnen asqan jūrnal faildaryn joiady.",
|
||||||
"TaskRefreshLibraryDescription": "Tasyǵyshhanadaǵy jańa faıldardy skanerleıdі jáne metaderekterdі jańartady.",
|
"TaskRefreshLibraryDescription": "Tasyğyşhanadağy jaŋa faildardy skanerleidі jäne metaderekterdı jaŋğyrtady.",
|
||||||
"TaskRefreshChapterImagesDescription": "Sahnalarǵa bólіngen beıneler úshіn nobaılar jasaıdy.",
|
"TaskRefreshChapterImagesDescription": "Sahnalary bar beineler üşіn nobailar jasaidy.",
|
||||||
"TaskCleanCacheDescription": "Júıede qajet emes keshtelgen faıldardy joıady.",
|
"TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.",
|
||||||
"TaskCleanActivityLogDescription": "Áreketter jurnalyndaǵy teńshelgen jasynan asqan jazbalaly joıady."
|
"TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teŋşelgen jasynan asqan jazbalary joiady."
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,5 +117,6 @@
|
||||||
"TaskCleanActivityLog": "Tøm aktivitetslogg",
|
"TaskCleanActivityLog": "Tøm aktivitetslogg",
|
||||||
"Undefined": "Udefinert",
|
"Undefined": "Udefinert",
|
||||||
"Forced": "Tvungen",
|
"Forced": "Tvungen",
|
||||||
"Default": "Standard"
|
"Default": "Standard",
|
||||||
|
"TaskCleanActivityLogDescription": "Sletter oppføringer i aktivitetsloggen som er eldre enn den konfigurerte alderen."
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,8 +89,8 @@
|
||||||
"UserPolicyUpdatedWithName": "Политики пользователя {0} были обновлены",
|
"UserPolicyUpdatedWithName": "Политики пользователя {0} были обновлены",
|
||||||
"UserStartedPlayingItemWithValues": "{0} - воспроизведение «{1}» на {2}",
|
"UserStartedPlayingItemWithValues": "{0} - воспроизведение «{1}» на {2}",
|
||||||
"UserStoppedPlayingItemWithValues": "{0} - воспроизведение остановлено «{1}» на {2}",
|
"UserStoppedPlayingItemWithValues": "{0} - воспроизведение остановлено «{1}» на {2}",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} (добавлено в медиатеку)",
|
"ValueHasBeenAddedToLibrary": "{0} добавлено в медиатеку",
|
||||||
"ValueSpecialEpisodeName": "Специальный эпизод - {0}",
|
"ValueSpecialEpisodeName": "Спецэпизод - {0}",
|
||||||
"VersionNumber": "Версия {0}",
|
"VersionNumber": "Версия {0}",
|
||||||
"TaskDownloadMissingSubtitles": "Загрузка отсутствующих субтитров",
|
"TaskDownloadMissingSubtitles": "Загрузка отсутствующих субтитров",
|
||||||
"TaskRefreshChannels": "Обновление каналов",
|
"TaskRefreshChannels": "Обновление каналов",
|
||||||
|
|
|
@ -117,5 +117,5 @@
|
||||||
"TaskCleanActivityLogDescription": "Radera aktivitets logg inlägg som är äldre än definerad ålder.",
|
"TaskCleanActivityLogDescription": "Radera aktivitets logg inlägg som är äldre än definerad ålder.",
|
||||||
"TaskCleanActivityLog": "Rensa Aktivitets Logg",
|
"TaskCleanActivityLog": "Rensa Aktivitets Logg",
|
||||||
"Undefined": "odefinierad",
|
"Undefined": "odefinierad",
|
||||||
"Forced": "Tvinga"
|
"Forced": "Tvingad"
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
"DeviceOfflineWithName": "{0} bağlantısı kesildi",
|
"DeviceOfflineWithName": "{0} bağlantısı kesildi",
|
||||||
"DeviceOnlineWithName": "{0} bağlı",
|
"DeviceOnlineWithName": "{0} bağlı",
|
||||||
"FailedLoginAttemptWithUserName": "{0} adresinden giriş başarısız oldu",
|
"FailedLoginAttemptWithUserName": "{0} adresinden giriş başarısız oldu",
|
||||||
"Favorites": "Favorilerim",
|
"Favorites": "Favoriler",
|
||||||
"Folders": "Klasörler",
|
"Folders": "Klasörler",
|
||||||
"Genres": "Türler",
|
"Genres": "Türler",
|
||||||
"HeaderAlbumArtists": "Albüm Sanatçıları",
|
"HeaderAlbumArtists": "Albüm Sanatçıları",
|
||||||
|
@ -117,5 +117,6 @@
|
||||||
"TaskCleanActivityLog": "İşlem Günlüğünü Temizle",
|
"TaskCleanActivityLog": "İşlem Günlüğünü Temizle",
|
||||||
"TaskCleanActivityLogDescription": "Belirtilen sureden daha eski etkinlik log kayıtları silindi.",
|
"TaskCleanActivityLogDescription": "Belirtilen sureden daha eski etkinlik log kayıtları silindi.",
|
||||||
"Undefined": "Bilinmeyen",
|
"Undefined": "Bilinmeyen",
|
||||||
"Default": "Varsayılan"
|
"Default": "Varsayılan",
|
||||||
|
"Forced": "Zorla"
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
"Favorites": "Yêu Thích",
|
"Favorites": "Yêu Thích",
|
||||||
"Folders": "Thư Mục",
|
"Folders": "Thư Mục",
|
||||||
"Genres": "Thể Loại",
|
"Genres": "Thể Loại",
|
||||||
"HeaderAlbumArtists": "Bộ Sưu Tập Nghệ sĩ",
|
"HeaderAlbumArtists": "Tuyển Tập Nghệ sĩ",
|
||||||
"HeaderContinueWatching": "Xem Tiếp",
|
"HeaderContinueWatching": "Xem Tiếp",
|
||||||
"HeaderLiveTV": "TV Trực Tiếp",
|
"HeaderLiveTV": "TV Trực Tiếp",
|
||||||
"Movies": "Phim",
|
"Movies": "Phim",
|
||||||
|
@ -13,7 +13,7 @@
|
||||||
"Songs": "Các Bài Hát",
|
"Songs": "Các Bài Hát",
|
||||||
"Sync": "Đồng Bộ",
|
"Sync": "Đồng Bộ",
|
||||||
"ValueSpecialEpisodeName": "Đặc Biệt - {0}",
|
"ValueSpecialEpisodeName": "Đặc Biệt - {0}",
|
||||||
"Albums": "Albums",
|
"Albums": "Tuyển Tập",
|
||||||
"Artists": "Các Nghệ Sĩ",
|
"Artists": "Các Nghệ Sĩ",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Tìm kiếm phụ đề bị thiếu trên Internet dựa trên cấu hình dữ liệu mô tả.",
|
"TaskDownloadMissingSubtitlesDescription": "Tìm kiếm phụ đề bị thiếu trên Internet dựa trên cấu hình dữ liệu mô tả.",
|
||||||
"TaskDownloadMissingSubtitles": "Tải Xuống Phụ Đề Bị Thiếu",
|
"TaskDownloadMissingSubtitles": "Tải Xuống Phụ Đề Bị Thiếu",
|
||||||
|
|
|
@ -113,5 +113,9 @@
|
||||||
"TaskCleanCache": "清理緩存目錄",
|
"TaskCleanCache": "清理緩存目錄",
|
||||||
"TasksChannelsCategory": "互聯網頻道",
|
"TasksChannelsCategory": "互聯網頻道",
|
||||||
"TasksLibraryCategory": "庫",
|
"TasksLibraryCategory": "庫",
|
||||||
"TaskRefreshPeople": "刷新人物"
|
"TaskRefreshPeople": "刷新人物",
|
||||||
|
"TaskCleanActivityLog": "清理活動記錄",
|
||||||
|
"Undefined": "未定義",
|
||||||
|
"Forced": "強制",
|
||||||
|
"Default": "預設"
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"Albums": "專輯",
|
"Albums": "專輯",
|
||||||
"AppDeviceValues": "軟體:{0},裝置:{1}",
|
"AppDeviceValues": "App:{0},裝置:{1}",
|
||||||
"Application": "應用程式",
|
"Application": "應用程式",
|
||||||
"Artists": "演出者",
|
"Artists": "演出者",
|
||||||
"AuthenticationSucceededWithUserName": "{0} 成功授權",
|
"AuthenticationSucceededWithUserName": "{0} 成功授權",
|
||||||
|
|
|
@ -77,6 +77,8 @@ chb|||Chibcha|chibcha
|
||||||
che||ce|Chechen|tchétchène
|
che||ce|Chechen|tchétchène
|
||||||
chg|||Chagatai|djaghataï
|
chg|||Chagatai|djaghataï
|
||||||
chi|zho|zh|Chinese|chinois
|
chi|zho|zh|Chinese|chinois
|
||||||
|
chi|zho|zh-tw|Chinese; Traditional|chinois
|
||||||
|
chi|zho|zh-hk|Chinese; Hong Kong|chinois
|
||||||
chk|||Chuukese|chuuk
|
chk|||Chuukese|chuuk
|
||||||
chm|||Mari|mari
|
chm|||Mari|mari
|
||||||
chn|||Chinook jargon|chinook, jargon
|
chn|||Chinook jargon|chinook, jargon
|
||||||
|
|
|
@ -166,7 +166,7 @@ namespace Emby.Server.Implementations.MediaEncoder
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error extracting chapter images for {0}", string.Join(",", video.Path));
|
_logger.LogError(ex, "Error extracting chapter images for {0}", string.Join(',', video.Path));
|
||||||
success = false;
|
success = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,6 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
{
|
{
|
||||||
assembly = Assembly.LoadFrom(file);
|
assembly = Assembly.LoadFrom(file);
|
||||||
|
|
||||||
// This force loads all reference dll's that the plugin uses in the try..catch block.
|
|
||||||
// Removing this will cause JF to bomb out if referenced dll's cause issues.
|
|
||||||
assembly.GetExportedTypes();
|
assembly.GetExportedTypes();
|
||||||
}
|
}
|
||||||
catch (FileLoadException ex)
|
catch (FileLoadException ex)
|
||||||
|
@ -122,6 +120,20 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
ChangePluginState(plugin, PluginStatus.Malfunctioned);
|
ChangePluginState(plugin, PluginStatus.Malfunctioned);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
catch (TypeLoadException ex) // Undocumented exception
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to load assembly {Path}. This error occurs when a plugin references an incompatible version of one of the shared libraries. Disabling plugin.", file);
|
||||||
|
ChangePluginState(plugin, PluginStatus.NotSupported);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#pragma warning disable CA1031 // Do not catch general exception types
|
||||||
|
catch (Exception ex)
|
||||||
|
#pragma warning restore CA1031 // Do not catch general exception types
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to load assembly {Path}. Unknown exception was thrown. Disabling plugin.", file);
|
||||||
|
ChangePluginState(plugin, PluginStatus.Malfunctioned);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Loaded assembly {Assembly} from {Path}", assembly.FullName, file);
|
_logger.LogInformation("Loaded assembly {Assembly} from {Path}", assembly.FullName, file);
|
||||||
yield return assembly;
|
yield return assembly;
|
||||||
|
@ -336,7 +348,7 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var data = JsonSerializer.Serialize(manifest, _jsonOptions);
|
var data = JsonSerializer.Serialize(manifest, _jsonOptions);
|
||||||
File.WriteAllText(Path.Combine(path, "meta.json"), data, Encoding.UTF8);
|
File.WriteAllText(Path.Combine(path, "meta.json"), data);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#pragma warning disable CA1031 // Do not catch general exception types
|
#pragma warning disable CA1031 // Do not catch general exception types
|
||||||
|
@ -374,7 +386,7 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
private LocalPlugin? GetPluginByAssembly(Assembly assembly)
|
private LocalPlugin? GetPluginByAssembly(Assembly assembly)
|
||||||
{
|
{
|
||||||
// Find which plugin it is by the path.
|
// Find which plugin it is by the path.
|
||||||
return _plugins.FirstOrDefault(p => string.Equals(p.Path, Path.GetDirectoryName(assembly.Location), StringComparison.Ordinal));
|
return _plugins.FirstOrDefault(p => p.DllFiles.Contains(assembly.Location, StringComparer.Ordinal));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -421,15 +433,17 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
{
|
{
|
||||||
plugin.Instance = instance;
|
plugin.Instance = instance;
|
||||||
var manifest = plugin.Manifest;
|
var manifest = plugin.Manifest;
|
||||||
var pluginStr = plugin.Instance.Version.ToString();
|
var pluginStr = instance.Version.ToString();
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
if (string.Equals(manifest.Version, pluginStr, StringComparison.Ordinal))
|
if (string.Equals(manifest.Version, pluginStr, StringComparison.Ordinal)
|
||||||
|
|| manifest.Id != instance.Id)
|
||||||
{
|
{
|
||||||
// If a plugin without a manifest failed to load due to an external issue (eg config),
|
// If a plugin without a manifest failed to load due to an external issue (eg config),
|
||||||
// this updates the manifest to the actual plugin values.
|
// this updates the manifest to the actual plugin values.
|
||||||
manifest.Version = pluginStr;
|
manifest.Version = pluginStr;
|
||||||
manifest.Name = plugin.Instance.Name;
|
manifest.Name = plugin.Instance.Name;
|
||||||
manifest.Description = plugin.Instance.Description;
|
manifest.Description = plugin.Instance.Description;
|
||||||
|
manifest.Id = plugin.Instance.Id;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -505,39 +519,43 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
return _plugins.Remove(plugin);
|
return _plugins.Remove(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
private LocalPlugin LoadManifest(string dir)
|
internal LocalPlugin LoadManifest(string dir)
|
||||||
{
|
{
|
||||||
Version? version;
|
Version? version;
|
||||||
PluginManifest? manifest = null;
|
PluginManifest? manifest = null;
|
||||||
var metafile = Path.Combine(dir, "meta.json");
|
var metafile = Path.Combine(dir, "meta.json");
|
||||||
if (File.Exists(metafile))
|
if (File.Exists(metafile))
|
||||||
{
|
{
|
||||||
|
// Only path where this stays null is when File.ReadAllBytes throws an IOException
|
||||||
|
byte[] data = null!;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var data = File.ReadAllText(metafile, Encoding.UTF8);
|
data = File.ReadAllBytes(metafile);
|
||||||
manifest = JsonSerializer.Deserialize<PluginManifest>(data, _jsonOptions);
|
manifest = JsonSerializer.Deserialize<PluginManifest>(data, _jsonOptions);
|
||||||
}
|
}
|
||||||
#pragma warning disable CA1031 // Do not catch general exception types
|
catch (IOException ex)
|
||||||
catch (Exception ex)
|
|
||||||
#pragma warning restore CA1031 // Do not catch general exception types
|
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error deserializing {Path}.", dir);
|
_logger.LogError(ex, "Error reading file {Path}.", dir);
|
||||||
}
|
}
|
||||||
}
|
catch (JsonException ex)
|
||||||
|
|
||||||
if (manifest != null)
|
|
||||||
{
|
|
||||||
if (!Version.TryParse(manifest.TargetAbi, out var targetAbi))
|
|
||||||
{
|
{
|
||||||
targetAbi = _minimumVersion;
|
_logger.LogError(ex, "Error deserializing {Json}.", Encoding.UTF8.GetString(data!));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Version.TryParse(manifest.Version, out version))
|
if (manifest != null)
|
||||||
{
|
{
|
||||||
manifest.Version = _minimumVersion.ToString();
|
if (!Version.TryParse(manifest.TargetAbi, out var targetAbi))
|
||||||
}
|
{
|
||||||
|
targetAbi = _minimumVersion;
|
||||||
|
}
|
||||||
|
|
||||||
return new LocalPlugin(dir, _appVersion >= targetAbi, manifest);
|
if (!Version.TryParse(manifest.Version, out version))
|
||||||
|
{
|
||||||
|
manifest.Version = _minimumVersion.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LocalPlugin(dir, _appVersion >= targetAbi, manifest);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No metafile, so lets see if the folder is versioned.
|
// No metafile, so lets see if the folder is versioned.
|
||||||
|
@ -559,7 +577,7 @@ namespace Emby.Server.Implementations.Plugins
|
||||||
// Auto-create a plugin manifest, so we can disable it, if it fails to load.
|
// Auto-create a plugin manifest, so we can disable it, if it fails to load.
|
||||||
manifest = new PluginManifest
|
manifest = new PluginManifest
|
||||||
{
|
{
|
||||||
Status = PluginStatus.Restart,
|
Status = PluginStatus.Active,
|
||||||
Name = metafile,
|
Name = metafile,
|
||||||
AutoUpdate = false,
|
AutoUpdate = false,
|
||||||
Id = metafile.GetMD5(),
|
Id = metafile.GetMD5(),
|
||||||
|
|
|
@ -143,21 +143,21 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
||||||
{
|
{
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
try
|
var bytes = File.ReadAllBytes(path);
|
||||||
|
if (bytes.Length > 0)
|
||||||
{
|
{
|
||||||
var jsonString = File.ReadAllText(path, Encoding.UTF8);
|
try
|
||||||
if (!string.IsNullOrWhiteSpace(jsonString))
|
|
||||||
{
|
{
|
||||||
_lastExecutionResult = JsonSerializer.Deserialize<TaskResult>(jsonString, _jsonOptions);
|
_lastExecutionResult = JsonSerializer.Deserialize<TaskResult>(bytes, _jsonOptions);
|
||||||
}
|
}
|
||||||
else
|
catch (JsonException ex)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Scheduled Task history file {Path} is empty. Skipping deserialization.", path);
|
_logger.LogError(ex, "Error deserializing {File}", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
else
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error deserializing {File}", path);
|
_logger.LogDebug("Scheduled Task history file {Path} is empty. Skipping deserialization.", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,7 +177,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
||||||
|
|
||||||
lock (_lastExecutionResultSyncLock)
|
lock (_lastExecutionResultSyncLock)
|
||||||
{
|
{
|
||||||
using FileStream createStream = File.OpenWrite(path);
|
using FileStream createStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
JsonSerializer.SerializeAsync(createStream, value, _jsonOptions);
|
JsonSerializer.SerializeAsync(createStream, value, _jsonOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -541,8 +541,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
||||||
TaskTriggerInfo[] list = null;
|
TaskTriggerInfo[] list = null;
|
||||||
if (File.Exists(path))
|
if (File.Exists(path))
|
||||||
{
|
{
|
||||||
var jsonString = File.ReadAllText(path, Encoding.UTF8);
|
var bytes = File.ReadAllBytes(path);
|
||||||
list = JsonSerializer.Deserialize<TaskTriggerInfo[]>(jsonString, _jsonOptions);
|
list = JsonSerializer.Deserialize<TaskTriggerInfo[]>(bytes, _jsonOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return defaults if file doesn't exist.
|
// Return defaults if file doesn't exist.
|
||||||
|
@ -577,9 +577,8 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
||||||
var path = GetConfigurationFilePath();
|
var path = GetConfigurationFilePath();
|
||||||
|
|
||||||
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
||||||
|
using FileStream createStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None);
|
||||||
var json = JsonSerializer.Serialize(triggers, _jsonOptions);
|
JsonSerializer.SerializeAsync(createStream, triggers, _jsonOptions);
|
||||||
File.WriteAllText(path, json, Encoding.UTF8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -143,7 +143,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
||||||
|
|
||||||
Directory.CreateDirectory(parentPath);
|
Directory.CreateDirectory(parentPath);
|
||||||
|
|
||||||
string text = string.Join("|", previouslyFailedImages);
|
string text = string.Join('|', previouslyFailedImages);
|
||||||
File.WriteAllText(failHistoryPath, text);
|
File.WriteAllText(failHistoryPath, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -80,10 +80,11 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
||||||
// Delete log files more than n days old
|
// Delete log files more than n days old
|
||||||
var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays);
|
var minDateModified = DateTime.UtcNow.AddDays(-_configurationManager.CommonConfiguration.LogFileRetentionDays);
|
||||||
|
|
||||||
// Only delete the .txt log files, the *.log files created by serilog get managed by itself
|
// Only delete files that serilog doesn't manage (anything that doesn't start with 'log_'
|
||||||
var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, new[] { ".txt" }, true, true)
|
var filesToDelete = _fileSystem.GetFiles(_configurationManager.CommonApplicationPaths.LogDirectoryPath, true)
|
||||||
.Where(f => _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
|
.Where(f => !f.Name.StartsWith("log_", StringComparison.Ordinal)
|
||||||
.ToList();
|
&& _fileSystem.GetLastWriteTimeUtc(f) < minDateModified)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
var index = 0;
|
var index = 0;
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
||||||
|
|
||||||
var dueTime = triggerDate - now;
|
var dueTime = triggerDate - now;
|
||||||
|
|
||||||
logger.LogInformation("Daily trigger for {Task} set to fire at {TriggerDate:g}, which is {DueTime:g} from now.", taskName, triggerDate, dueTime);
|
logger.LogInformation("Daily trigger for {Task} set to fire at {TriggerDate:yyyy-MM-dd HH:mm:ss.fff zzz}, which is {DueTime:c} from now.", taskName, triggerDate, dueTime);
|
||||||
|
|
||||||
Timer = new Timer(state => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
|
Timer = new Timer(state => OnTriggered(), null, dueTime, TimeSpan.FromMilliseconds(-1));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1456,7 +1456,12 @@ namespace Emby.Server.Implementations.Session
|
||||||
throw new SecurityException("Unknown quick connect token");
|
throw new SecurityException("Unknown quick connect token");
|
||||||
}
|
}
|
||||||
|
|
||||||
request.UserId = result.Items[0].UserId;
|
var info = result.Items[0];
|
||||||
|
request.UserId = info.UserId;
|
||||||
|
|
||||||
|
// There's no need to keep the quick connect token in the database, as AuthenticateNewSessionInternal() issues a long lived token.
|
||||||
|
_authRepo.Delete(info);
|
||||||
|
|
||||||
return AuthenticateNewSessionInternal(request, false);
|
return AuthenticateNewSessionInternal(request, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -143,10 +143,31 @@ namespace Emby.Server.Implementations.TV
|
||||||
var allNextUp = seriesKeys
|
var allNextUp = seriesKeys
|
||||||
.Select(i => GetNextUp(i, currentUser, dtoOptions));
|
.Select(i => GetNextUp(i, currentUser, dtoOptions));
|
||||||
|
|
||||||
|
// If viewing all next up for all series, remove first episodes
|
||||||
|
// But if that returns empty, keep those first episodes (avoid completely empty view)
|
||||||
|
var alwaysEnableFirstEpisode = !string.IsNullOrEmpty(request.SeriesId);
|
||||||
|
var anyFound = false;
|
||||||
|
|
||||||
return allNextUp
|
return allNextUp
|
||||||
.Where(i =>
|
.Where(i =>
|
||||||
{
|
{
|
||||||
return i.Item1 != DateTime.MinValue;
|
if (request.DisableFirstEpisode)
|
||||||
|
{
|
||||||
|
return i.Item1 != DateTime.MinValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alwaysEnableFirstEpisode || i.Item1 != DateTime.MinValue)
|
||||||
|
{
|
||||||
|
anyFound = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anyFound && i.Item1 == DateTime.MinValue)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
})
|
})
|
||||||
.Select(i => i.Item2())
|
.Select(i => i.Item2())
|
||||||
.Where(i => i != null);
|
.Where(i => i != null);
|
||||||
|
|
28
Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
Normal file
28
Jellyfin.Api/Attributes/AcceptsFileAttribute.cs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Jellyfin.Api.Attributes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Internal produces image attribute.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Method)]
|
||||||
|
public class AcceptsFileAttribute : Attribute
|
||||||
|
{
|
||||||
|
private readonly string[] _contentTypes;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AcceptsFileAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="contentTypes">Content types this endpoint produces.</param>
|
||||||
|
public AcceptsFileAttribute(params string[] contentTypes)
|
||||||
|
{
|
||||||
|
_contentTypes = contentTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the configured content types.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>the configured content types.</returns>
|
||||||
|
public string[] GetContentTypes() => _contentTypes;
|
||||||
|
}
|
||||||
|
}
|
18
Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
Normal file
18
Jellyfin.Api/Attributes/AcceptsImageFileAttribute.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
namespace Jellyfin.Api.Attributes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Produces file attribute of "image/*".
|
||||||
|
/// </summary>
|
||||||
|
public class AcceptsImageFileAttribute : AcceptsFileAttribute
|
||||||
|
{
|
||||||
|
private const string ContentType = "image/*";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="AcceptsImageFileAttribute"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public AcceptsImageFileAttribute()
|
||||||
|
: base(ContentType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
Normal file
12
Jellyfin.Api/Attributes/ParameterObsoleteAttribute.cs
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Jellyfin.Api.Attributes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Attribute to mark a parameter as obsolete.
|
||||||
|
/// </summary>
|
||||||
|
[AttributeUsage(AttributeTargets.Parameter)]
|
||||||
|
public class ParameterObsoleteAttribute : Attribute
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,8 +3,10 @@ using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Extensions;
|
using Jellyfin.Api.Extensions;
|
||||||
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
@ -88,8 +90,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] string? searchTerm,
|
[FromQuery] string? searchTerm,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
||||||
[FromQuery] bool? isFavorite,
|
[FromQuery] bool? isFavorite,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
||||||
|
@ -127,8 +129,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
var query = new InternalItemsQuery(user)
|
var query = new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
MediaTypes = mediaTypes,
|
MediaTypes = mediaTypes,
|
||||||
StartIndex = startIndex,
|
StartIndex = startIndex,
|
||||||
Limit = limit,
|
Limit = limit,
|
||||||
|
@ -287,8 +289,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] string? searchTerm,
|
[FromQuery] string? searchTerm,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
||||||
[FromQuery] bool? isFavorite,
|
[FromQuery] bool? isFavorite,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
||||||
|
@ -326,8 +328,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
var query = new InternalItemsQuery(user)
|
var query = new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
MediaTypes = mediaTypes,
|
MediaTypes = mediaTypes,
|
||||||
StartIndex = startIndex,
|
StartIndex = startIndex,
|
||||||
Limit = limit,
|
Limit = limit,
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Extensions;
|
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Channels;
|
using MediaBrowser.Controller.Channels;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
@ -121,9 +120,9 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] Guid? userId,
|
[FromQuery] Guid? userId,
|
||||||
[FromQuery] int? startIndex,
|
[FromQuery] int? startIndex,
|
||||||
[FromQuery] int? limit,
|
[FromQuery] int? limit,
|
||||||
[FromQuery] string? sortOrder,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
||||||
[FromQuery] string? sortBy,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields)
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields)
|
||||||
{
|
{
|
||||||
var user = userId.HasValue && !userId.Equals(Guid.Empty)
|
var user = userId.HasValue && !userId.Equals(Guid.Empty)
|
||||||
|
|
|
@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Extensions;
|
using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.Helpers;
|
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
using MediaBrowser.Controller.Collections;
|
using MediaBrowser.Controller.Collections;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
|
|
|
@ -7,17 +7,11 @@ using Jellyfin.Api.Attributes;
|
||||||
using Jellyfin.Api.Models;
|
using Jellyfin.Api.Models;
|
||||||
using MediaBrowser.Common.Plugins;
|
using MediaBrowser.Common.Plugins;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
using MediaBrowser.Controller.Configuration;
|
|
||||||
using MediaBrowser.Controller.Extensions;
|
|
||||||
using MediaBrowser.Controller.Plugins;
|
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Plugins;
|
using MediaBrowser.Model.Plugins;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Extensions;
|
|
||||||
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;
|
|
||||||
|
|
||||||
namespace Jellyfin.Api.Controllers
|
namespace Jellyfin.Api.Controllers
|
||||||
{
|
{
|
||||||
|
@ -51,7 +45,6 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// Gets the configuration pages.
|
/// Gets the configuration pages.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="enableInMainMenu">Whether to enable in the main menu.</param>
|
/// <param name="enableInMainMenu">Whether to enable in the main menu.</param>
|
||||||
/// <param name="pageType">The <see cref="ConfigurationPageInfo"/>.</param>
|
|
||||||
/// <response code="200">ConfigurationPages returned.</response>
|
/// <response code="200">ConfigurationPages returned.</response>
|
||||||
/// <response code="404">Server still loading.</response>
|
/// <response code="404">Server still loading.</response>
|
||||||
/// <returns>An <see cref="IEnumerable{ConfigurationPageInfo}"/> with infos about the plugins.</returns>
|
/// <returns>An <see cref="IEnumerable{ConfigurationPageInfo}"/> with infos about the plugins.</returns>
|
||||||
|
@ -59,40 +52,9 @@ namespace Jellyfin.Api.Controllers
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
public ActionResult<IEnumerable<ConfigurationPageInfo?>> GetConfigurationPages(
|
public ActionResult<IEnumerable<ConfigurationPageInfo?>> GetConfigurationPages(
|
||||||
[FromQuery] bool? enableInMainMenu,
|
[FromQuery] bool? enableInMainMenu)
|
||||||
[FromQuery] ConfigurationPageType? pageType)
|
|
||||||
{
|
{
|
||||||
const string unavailableMessage = "The server is still loading. Please try again momentarily.";
|
var configPages = _pluginManager.Plugins.SelectMany(GetConfigPages).ToList();
|
||||||
|
|
||||||
var pages = _appHost.GetExports<IPluginConfigurationPage>().ToList();
|
|
||||||
|
|
||||||
if (pages == null)
|
|
||||||
{
|
|
||||||
return NotFound(unavailableMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't allow a failing plugin to fail them all
|
|
||||||
var configPages = pages.Select(p =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return new ConfigurationPageInfo(p);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error getting plugin information from {Plugin}", p.GetType().Name);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.Where(i => i != null)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
configPages.AddRange(_pluginManager.Plugins.SelectMany(GetConfigPages));
|
|
||||||
|
|
||||||
if (pageType.HasValue)
|
|
||||||
{
|
|
||||||
configPages = configPages.Where(p => p!.ConfigurationPageType == pageType).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enableInMainMenu.HasValue)
|
if (enableInMainMenu.HasValue)
|
||||||
{
|
{
|
||||||
|
@ -121,24 +83,14 @@ namespace Jellyfin.Api.Controllers
|
||||||
var isJs = false;
|
var isJs = false;
|
||||||
var isTemplate = false;
|
var isTemplate = false;
|
||||||
|
|
||||||
var page = _appHost.GetExports<IPluginConfigurationPage>().FirstOrDefault(p => string.Equals(p.Name, name, StringComparison.OrdinalIgnoreCase));
|
var altPage = GetPluginPages().FirstOrDefault(p => string.Equals(p.Item1.Name, name, StringComparison.OrdinalIgnoreCase));
|
||||||
if (page != null)
|
if (altPage != null)
|
||||||
{
|
{
|
||||||
plugin = page.Plugin;
|
plugin = altPage.Item2;
|
||||||
stream = page.GetHtmlStream();
|
stream = plugin.GetType().Assembly.GetManifestResourceStream(altPage.Item1.EmbeddedResourcePath);
|
||||||
}
|
|
||||||
|
|
||||||
if (plugin == null)
|
isJs = string.Equals(Path.GetExtension(altPage.Item1.EmbeddedResourcePath), ".js", StringComparison.OrdinalIgnoreCase);
|
||||||
{
|
isTemplate = altPage.Item1.EmbeddedResourcePath.EndsWith(".template.html", StringComparison.Ordinal);
|
||||||
var altPage = GetPluginPages().FirstOrDefault(p => string.Equals(p.Item1.Name, name, StringComparison.OrdinalIgnoreCase));
|
|
||||||
if (altPage != null)
|
|
||||||
{
|
|
||||||
plugin = altPage.Item2;
|
|
||||||
stream = plugin.GetType().Assembly.GetManifestResourceStream(altPage.Item1.EmbeddedResourcePath);
|
|
||||||
|
|
||||||
isJs = string.Equals(Path.GetExtension(altPage.Item1.EmbeddedResourcePath), ".js", StringComparison.OrdinalIgnoreCase);
|
|
||||||
isTemplate = altPage.Item1.EmbeddedResourcePath.EndsWith(".template.html", StringComparison.Ordinal);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin != null && stream != null)
|
if (plugin != null && stream != null)
|
||||||
|
|
|
@ -15,7 +15,6 @@ using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.Models.PlaybackDtos;
|
using Jellyfin.Api.Models.PlaybackDtos;
|
||||||
using Jellyfin.Api.Models.StreamingDtos;
|
using Jellyfin.Api.Models.StreamingDtos;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Devices;
|
using MediaBrowser.Controller.Devices;
|
||||||
using MediaBrowser.Controller.Dlna;
|
using MediaBrowser.Controller.Dlna;
|
||||||
|
|
|
@ -1,13 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
|
||||||
using MediaBrowser.Controller.Entities.Movies;
|
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Playlists;
|
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Querying;
|
using MediaBrowser.Model.Querying;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
@ -51,7 +50,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
public ActionResult<QueryFiltersLegacy> GetQueryFiltersLegacy(
|
public ActionResult<QueryFiltersLegacy> GetQueryFiltersLegacy(
|
||||||
[FromQuery] Guid? userId,
|
[FromQuery] Guid? userId,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes)
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes)
|
||||||
{
|
{
|
||||||
var user = userId.HasValue && !userId.Equals(Guid.Empty)
|
var user = userId.HasValue && !userId.Equals(Guid.Empty)
|
||||||
|
@ -60,10 +59,10 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
BaseItem? item = null;
|
BaseItem? item = null;
|
||||||
if (includeItemTypes.Length != 1
|
if (includeItemTypes.Length != 1
|
||||||
|| !(string.Equals(includeItemTypes[0], nameof(BoxSet), StringComparison.OrdinalIgnoreCase)
|
|| !(includeItemTypes[0] == BaseItemKind.BoxSet
|
||||||
|| string.Equals(includeItemTypes[0], nameof(Playlist), StringComparison.OrdinalIgnoreCase)
|
|| includeItemTypes[0] == BaseItemKind.Playlist
|
||||||
|| string.Equals(includeItemTypes[0], nameof(Trailer), StringComparison.OrdinalIgnoreCase)
|
|| includeItemTypes[0] == BaseItemKind.Trailer
|
||||||
|| string.Equals(includeItemTypes[0], "Program", StringComparison.OrdinalIgnoreCase)))
|
|| includeItemTypes[0] == BaseItemKind.Program))
|
||||||
{
|
{
|
||||||
item = _libraryManager.GetParentItem(parentId, user?.Id);
|
item = _libraryManager.GetParentItem(parentId, user?.Id);
|
||||||
}
|
}
|
||||||
|
@ -72,7 +71,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
{
|
{
|
||||||
User = user,
|
User = user,
|
||||||
MediaTypes = mediaTypes,
|
MediaTypes = mediaTypes,
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
Recursive = true,
|
Recursive = true,
|
||||||
EnableTotalRecordCount = false,
|
EnableTotalRecordCount = false,
|
||||||
DtoOptions = new DtoOptions
|
DtoOptions = new DtoOptions
|
||||||
|
@ -137,7 +136,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
public ActionResult<QueryFilters> GetQueryFilters(
|
public ActionResult<QueryFilters> GetQueryFilters(
|
||||||
[FromQuery] Guid? userId,
|
[FromQuery] Guid? userId,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery] bool? isAiring,
|
[FromQuery] bool? isAiring,
|
||||||
[FromQuery] bool? isMovie,
|
[FromQuery] bool? isMovie,
|
||||||
[FromQuery] bool? isSports,
|
[FromQuery] bool? isSports,
|
||||||
|
@ -152,10 +151,10 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
BaseItem? parentItem = null;
|
BaseItem? parentItem = null;
|
||||||
if (includeItemTypes.Length == 1
|
if (includeItemTypes.Length == 1
|
||||||
&& (string.Equals(includeItemTypes[0], nameof(BoxSet), StringComparison.OrdinalIgnoreCase)
|
&& (includeItemTypes[0] == BaseItemKind.BoxSet
|
||||||
|| string.Equals(includeItemTypes[0], nameof(Playlist), StringComparison.OrdinalIgnoreCase)
|
|| includeItemTypes[0] == BaseItemKind.Playlist
|
||||||
|| string.Equals(includeItemTypes[0], nameof(Trailer), StringComparison.OrdinalIgnoreCase)
|
|| includeItemTypes[0] == BaseItemKind.Trailer
|
||||||
|| string.Equals(includeItemTypes[0], "Program", StringComparison.OrdinalIgnoreCase)))
|
|| includeItemTypes[0] == BaseItemKind.Program))
|
||||||
{
|
{
|
||||||
parentItem = null;
|
parentItem = null;
|
||||||
}
|
}
|
||||||
|
@ -167,7 +166,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
var filters = new QueryFilters();
|
var filters = new QueryFilters();
|
||||||
var genreQuery = new InternalItemsQuery(user)
|
var genreQuery = new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
DtoOptions = new DtoOptions
|
DtoOptions = new DtoOptions
|
||||||
{
|
{
|
||||||
Fields = Array.Empty<ItemFields>(),
|
Fields = Array.Empty<ItemFields>(),
|
||||||
|
@ -192,10 +191,10 @@ namespace Jellyfin.Api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
if (includeItemTypes.Length == 1
|
if (includeItemTypes.Length == 1
|
||||||
&& (string.Equals(includeItemTypes[0], nameof(MusicAlbum), StringComparison.OrdinalIgnoreCase)
|
&& (includeItemTypes[0] == BaseItemKind.MusicAlbum
|
||||||
|| string.Equals(includeItemTypes[0], nameof(MusicVideo), StringComparison.OrdinalIgnoreCase)
|
|| includeItemTypes[0] == BaseItemKind.MusicVideo
|
||||||
|| string.Equals(includeItemTypes[0], nameof(MusicArtist), StringComparison.OrdinalIgnoreCase)
|
|| includeItemTypes[0] == BaseItemKind.MusicArtist
|
||||||
|| string.Equals(includeItemTypes[0], nameof(Audio), StringComparison.OrdinalIgnoreCase)))
|
|| includeItemTypes[0] == BaseItemKind.Audio))
|
||||||
{
|
{
|
||||||
filters.Genres = _libraryManager.GetMusicGenres(genreQuery).Items.Select(i => new NameGuidPair
|
filters.Genres = _libraryManager.GetMusicGenres(genreQuery).Items.Select(i => new NameGuidPair
|
||||||
{
|
{
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
@ -74,8 +75,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] string? searchTerm,
|
[FromQuery] string? searchTerm,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery] bool? isFavorite,
|
[FromQuery] bool? isFavorite,
|
||||||
[FromQuery] int? imageTypeLimit,
|
[FromQuery] int? imageTypeLimit,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
|
||||||
|
@ -96,8 +97,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
var query = new InternalItemsQuery(user)
|
var query = new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
StartIndex = startIndex,
|
StartIndex = startIndex,
|
||||||
Limit = limit,
|
Limit = limit,
|
||||||
IsFavorite = isFavorite,
|
IsFavorite = isFavorite,
|
||||||
|
|
|
@ -2,13 +2,11 @@ using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Attributes;
|
using Jellyfin.Api.Attributes;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
|
|
@ -87,6 +87,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
||||||
[HttpPost("Users/{userId}/Images/{imageType}")]
|
[HttpPost("Users/{userId}/Images/{imageType}")]
|
||||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||||
|
[AcceptsImageFile]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
|
||||||
|
@ -133,6 +134,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
/// <returns>A <see cref="NoContentResult"/>.</returns>
|
||||||
[HttpPost("Users/{userId}/Images/{imageType}/{index}")]
|
[HttpPost("Users/{userId}/Images/{imageType}/{index}")]
|
||||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||||
|
[AcceptsImageFile]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
[ProducesResponseType(StatusCodes.Status403Forbidden)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageType", Justification = "Imported from ServiceStack")]
|
||||||
|
@ -312,6 +314,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
||||||
[HttpPost("Items/{itemId}/Images/{imageType}")]
|
[HttpPost("Items/{itemId}/Images/{imageType}")]
|
||||||
[Authorize(Policy = Policies.RequiresElevation)]
|
[Authorize(Policy = Policies.RequiresElevation)]
|
||||||
|
[AcceptsImageFile]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
||||||
|
@ -346,6 +349,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
|
||||||
[HttpPost("Items/{itemId}/Images/{imageType}/{imageIndex}")]
|
[HttpPost("Items/{itemId}/Images/{imageType}/{imageIndex}")]
|
||||||
[Authorize(Policy = Policies.RequiresElevation)]
|
[Authorize(Policy = Policies.RequiresElevation)]
|
||||||
|
[AcceptsImageFile]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "index", Justification = "Imported from ServiceStack")]
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Extensions;
|
using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
|
|
|
@ -2,8 +2,6 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Net.Mime;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
using System;
|
using System;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Extensions;
|
using Jellyfin.Api.Extensions;
|
||||||
|
@ -175,16 +174,16 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] int? limit,
|
[FromQuery] int? limit,
|
||||||
[FromQuery] bool? recursive,
|
[FromQuery] bool? recursive,
|
||||||
[FromQuery] string? searchTerm,
|
[FromQuery] string? searchTerm,
|
||||||
[FromQuery] string? sortOrder,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
||||||
[FromQuery] bool? isFavorite,
|
[FromQuery] bool? isFavorite,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
|
||||||
[FromQuery] string? sortBy,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
|
||||||
[FromQuery] bool? isPlayed,
|
[FromQuery] bool? isPlayed,
|
||||||
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
|
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
|
||||||
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
|
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
|
||||||
|
@ -233,8 +232,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
|
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
|
||||||
|
|
||||||
if (includeItemTypes.Length == 1
|
if (includeItemTypes.Length == 1
|
||||||
&& (includeItemTypes[0].Equals("Playlist", StringComparison.OrdinalIgnoreCase)
|
&& (includeItemTypes[0] == BaseItemKind.Playlist
|
||||||
|| includeItemTypes[0].Equals("BoxSet", StringComparison.OrdinalIgnoreCase)))
|
|| includeItemTypes[0] == BaseItemKind.BoxSet))
|
||||||
{
|
{
|
||||||
parentId = null;
|
parentId = null;
|
||||||
}
|
}
|
||||||
|
@ -251,7 +250,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
&& string.Equals(hasCollectionType.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
|
&& string.Equals(hasCollectionType.CollectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
recursive = true;
|
recursive = true;
|
||||||
includeItemTypes = new[] { "Playlist" };
|
includeItemTypes = new[] { BaseItemKind.Playlist };
|
||||||
}
|
}
|
||||||
|
|
||||||
var enabledChannels = user!.GetPreferenceValues<Guid>(PreferenceKind.EnabledChannels);
|
var enabledChannels = user!.GetPreferenceValues<Guid>(PreferenceKind.EnabledChannels);
|
||||||
|
@ -286,8 +285,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
{
|
{
|
||||||
IsPlayed = isPlayed,
|
IsPlayed = isPlayed,
|
||||||
MediaTypes = mediaTypes,
|
MediaTypes = mediaTypes,
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
|
||||||
Recursive = recursive ?? false,
|
Recursive = recursive ?? false,
|
||||||
OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
|
OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
|
||||||
IsFavorite = isFavorite,
|
IsFavorite = isFavorite,
|
||||||
|
@ -608,16 +607,16 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] int? limit,
|
[FromQuery] int? limit,
|
||||||
[FromQuery] bool? recursive,
|
[FromQuery] bool? recursive,
|
||||||
[FromQuery] string? searchTerm,
|
[FromQuery] string? searchTerm,
|
||||||
[FromQuery] string? sortOrder,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
|
||||||
[FromQuery] bool? isFavorite,
|
[FromQuery] bool? isFavorite,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
|
||||||
[FromQuery] string? sortBy,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
|
||||||
[FromQuery] bool? isPlayed,
|
[FromQuery] bool? isPlayed,
|
||||||
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
|
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
|
||||||
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
|
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
|
||||||
|
@ -773,8 +772,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] bool? enableUserData,
|
[FromQuery] bool? enableUserData,
|
||||||
[FromQuery] int? imageTypeLimit,
|
[FromQuery] int? imageTypeLimit,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery] bool enableTotalRecordCount = true,
|
[FromQuery] bool enableTotalRecordCount = true,
|
||||||
[FromQuery] bool? enableImages = true)
|
[FromQuery] bool? enableImages = true)
|
||||||
{
|
{
|
||||||
|
@ -810,8 +809,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
CollapseBoxSetItems = false,
|
CollapseBoxSetItems = false,
|
||||||
EnableTotalRecordCount = enableTotalRecordCount,
|
EnableTotalRecordCount = enableTotalRecordCount,
|
||||||
AncestorIds = ancestorIds,
|
AncestorIds = ancestorIds,
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
|
||||||
SearchTerm = searchTerm
|
SearchTerm = searchTerm
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,6 @@ using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Attributes;
|
using Jellyfin.Api.Attributes;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Extensions;
|
using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.Helpers;
|
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
using Jellyfin.Api.Models.LibraryDtos;
|
using Jellyfin.Api.Models.LibraryDtos;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
|
|
|
@ -553,8 +553,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] bool? isSports,
|
[FromQuery] bool? isSports,
|
||||||
[FromQuery] int? startIndex,
|
[FromQuery] int? startIndex,
|
||||||
[FromQuery] int? limit,
|
[FromQuery] int? limit,
|
||||||
[FromQuery] string? sortBy,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
|
||||||
[FromQuery] string? sortOrder,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
|
||||||
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
|
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
|
||||||
[FromQuery] bool? enableImages,
|
[FromQuery] bool? enableImages,
|
||||||
|
|
|
@ -83,6 +83,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// For backwards compatibility parameters can be sent via Query or Body, with Query having higher precedence.
|
/// For backwards compatibility parameters can be sent via Query or Body, with Query having higher precedence.
|
||||||
|
/// Query parameters are obsolete.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="itemId">The item id.</param>
|
/// <param name="itemId">The item id.</param>
|
||||||
/// <param name="userId">The user id.</param>
|
/// <param name="userId">The user id.</param>
|
||||||
|
@ -106,20 +107,20 @@ namespace Jellyfin.Api.Controllers
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult<PlaybackInfoResponse>> GetPostedPlaybackInfo(
|
public async Task<ActionResult<PlaybackInfoResponse>> GetPostedPlaybackInfo(
|
||||||
[FromRoute, Required] Guid itemId,
|
[FromRoute, Required] Guid itemId,
|
||||||
[FromQuery] Guid? userId,
|
[FromQuery, ParameterObsolete] Guid? userId,
|
||||||
[FromQuery] int? maxStreamingBitrate,
|
[FromQuery, ParameterObsolete] int? maxStreamingBitrate,
|
||||||
[FromQuery] long? startTimeTicks,
|
[FromQuery, ParameterObsolete] long? startTimeTicks,
|
||||||
[FromQuery] int? audioStreamIndex,
|
[FromQuery, ParameterObsolete] int? audioStreamIndex,
|
||||||
[FromQuery] int? subtitleStreamIndex,
|
[FromQuery, ParameterObsolete] int? subtitleStreamIndex,
|
||||||
[FromQuery] int? maxAudioChannels,
|
[FromQuery, ParameterObsolete] int? maxAudioChannels,
|
||||||
[FromQuery] string? mediaSourceId,
|
[FromQuery, ParameterObsolete] string? mediaSourceId,
|
||||||
[FromQuery] string? liveStreamId,
|
[FromQuery, ParameterObsolete] string? liveStreamId,
|
||||||
[FromQuery] bool? autoOpenLiveStream,
|
[FromQuery, ParameterObsolete] bool? autoOpenLiveStream,
|
||||||
[FromQuery] bool? enableDirectPlay,
|
[FromQuery, ParameterObsolete] bool? enableDirectPlay,
|
||||||
[FromQuery] bool? enableDirectStream,
|
[FromQuery, ParameterObsolete] bool? enableDirectStream,
|
||||||
[FromQuery] bool? enableTranscoding,
|
[FromQuery, ParameterObsolete] bool? enableTranscoding,
|
||||||
[FromQuery] bool? allowVideoStreamCopy,
|
[FromQuery, ParameterObsolete] bool? allowVideoStreamCopy,
|
||||||
[FromQuery] bool? allowAudioStreamCopy,
|
[FromQuery, ParameterObsolete] bool? allowAudioStreamCopy,
|
||||||
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] PlaybackInfoDto? playbackInfoDto)
|
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] PlaybackInfoDto? playbackInfoDto)
|
||||||
{
|
{
|
||||||
var authInfo = _authContext.GetAuthorizationInfo(Request);
|
var authInfo = _authContext.GetAuthorizationInfo(Request);
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Entities.Audio;
|
using MediaBrowser.Controller.Entities.Audio;
|
||||||
|
@ -74,8 +75,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] string? searchTerm,
|
[FromQuery] string? searchTerm,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery] bool? isFavorite,
|
[FromQuery] bool? isFavorite,
|
||||||
[FromQuery] int? imageTypeLimit,
|
[FromQuery] int? imageTypeLimit,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
|
||||||
|
@ -96,8 +97,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
var query = new InternalItemsQuery(user)
|
var query = new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
StartIndex = startIndex,
|
StartIndex = startIndex,
|
||||||
Limit = limit,
|
Limit = limit,
|
||||||
IsFavorite = isFavorite,
|
IsFavorite = isFavorite,
|
||||||
|
|
|
@ -4,7 +4,6 @@ using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using MediaBrowser.Common.Json;
|
|
||||||
using MediaBrowser.Common.Updates;
|
using MediaBrowser.Common.Updates;
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Model.Updates;
|
using MediaBrowser.Model.Updates;
|
||||||
|
@ -158,7 +157,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
[HttpPost("Repositories")]
|
[HttpPost("Repositories")]
|
||||||
[Authorize(Policy = Policies.DefaultAuthorization)]
|
[Authorize(Policy = Policies.DefaultAuthorization)]
|
||||||
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
[ProducesResponseType(StatusCodes.Status204NoContent)]
|
||||||
public ActionResult SetRepositories([FromBody] List<RepositoryInfo> repositoryInfos)
|
public ActionResult SetRepositories([FromBody, Required] List<RepositoryInfo> repositoryInfos)
|
||||||
{
|
{
|
||||||
_serverConfigurationManager.Configuration.PluginRepositories = repositoryInfos;
|
_serverConfigurationManager.Configuration.PluginRepositories = repositoryInfos;
|
||||||
_serverConfigurationManager.SaveConfiguration();
|
_serverConfigurationManager.SaveConfiguration();
|
||||||
|
|
|
@ -3,7 +3,6 @@ using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Extensions;
|
using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.Helpers;
|
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
|
|
|
@ -3,9 +3,9 @@ using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Jellyfin.Api.Attributes;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Extensions;
|
using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.Helpers;
|
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
using Jellyfin.Api.Models.PlaylistDtos;
|
using Jellyfin.Api.Models.PlaylistDtos;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
|
@ -57,6 +57,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
/// For backwards compatibility parameters can be sent via Query or Body, with Query having higher precedence.
|
/// For backwards compatibility parameters can be sent via Query or Body, with Query having higher precedence.
|
||||||
|
/// Query parameters are obsolete.
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
/// <param name="name">The playlist name.</param>
|
/// <param name="name">The playlist name.</param>
|
||||||
/// <param name="ids">The item ids.</param>
|
/// <param name="ids">The item ids.</param>
|
||||||
|
@ -70,10 +71,10 @@ namespace Jellyfin.Api.Controllers
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||||
public async Task<ActionResult<PlaylistCreationResult>> CreatePlaylist(
|
public async Task<ActionResult<PlaylistCreationResult>> CreatePlaylist(
|
||||||
[FromQuery] string? name,
|
[FromQuery, ParameterObsolete] string? name,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] IReadOnlyList<Guid> ids,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder)), ParameterObsolete] IReadOnlyList<Guid> ids,
|
||||||
[FromQuery] Guid? userId,
|
[FromQuery, ParameterObsolete] Guid? userId,
|
||||||
[FromQuery] string? mediaType,
|
[FromQuery, ParameterObsolete] string? mediaType,
|
||||||
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] CreatePlaylistDto? createPlaylistRequest)
|
[FromBody(EmptyBodyBehavior = EmptyBodyBehavior.Allow)] CreatePlaylistDto? createPlaylistRequest)
|
||||||
{
|
{
|
||||||
if (ids.Count == 0)
|
if (ids.Count == 0)
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel.DataAnnotations;
|
using System.ComponentModel.DataAnnotations;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Mime;
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Jellyfin.Api.Attributes;
|
using Jellyfin.Api.Attributes;
|
||||||
|
@ -300,9 +298,7 @@ namespace Jellyfin.Api.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
var imagePath = Path.Combine(plugin.Path, plugin.Manifest.ImagePath ?? string.Empty);
|
var imagePath = Path.Combine(plugin.Path, plugin.Manifest.ImagePath ?? string.Empty);
|
||||||
if (((ServerConfiguration)_config.CommonConfiguration).DisablePluginImages
|
if (plugin.Manifest.ImagePath == null || !System.IO.File.Exists(imagePath))
|
||||||
|| plugin.Manifest.ImagePath == null
|
|
||||||
|| !System.IO.File.Exists(imagePath))
|
|
||||||
{
|
{
|
||||||
return NotFound();
|
return NotFound();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||||
using Jellyfin.Api.Constants;
|
using Jellyfin.Api.Constants;
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Drawing;
|
using MediaBrowser.Controller.Drawing;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
|
@ -83,8 +84,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] int? limit,
|
[FromQuery] int? limit,
|
||||||
[FromQuery] Guid? userId,
|
[FromQuery] Guid? userId,
|
||||||
[FromQuery, Required] string searchTerm,
|
[FromQuery, Required] string searchTerm,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery] bool? isMovie,
|
[FromQuery] bool? isMovie,
|
||||||
|
@ -109,8 +110,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
IncludeStudios = includeStudios,
|
IncludeStudios = includeStudios,
|
||||||
StartIndex = startIndex,
|
StartIndex = startIndex,
|
||||||
UserId = userId ?? Guid.Empty,
|
UserId = userId ?? Guid.Empty,
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
|
||||||
MediaTypes = mediaTypes,
|
MediaTypes = mediaTypes,
|
||||||
ParentId = parentId,
|
ParentId = parentId,
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ using Jellyfin.Api.Extensions;
|
||||||
using Jellyfin.Api.Helpers;
|
using Jellyfin.Api.Helpers;
|
||||||
using Jellyfin.Api.ModelBinders;
|
using Jellyfin.Api.ModelBinders;
|
||||||
using Jellyfin.Data.Entities;
|
using Jellyfin.Data.Entities;
|
||||||
|
using Jellyfin.Data.Enums;
|
||||||
using MediaBrowser.Controller.Dto;
|
using MediaBrowser.Controller.Dto;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
|
@ -73,8 +74,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
[FromQuery] string? searchTerm,
|
[FromQuery] string? searchTerm,
|
||||||
[FromQuery] Guid? parentId,
|
[FromQuery] Guid? parentId,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] excludeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
|
||||||
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] includeItemTypes,
|
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
|
||||||
[FromQuery] bool? isFavorite,
|
[FromQuery] bool? isFavorite,
|
||||||
[FromQuery] bool? enableUserData,
|
[FromQuery] bool? enableUserData,
|
||||||
[FromQuery] int? imageTypeLimit,
|
[FromQuery] int? imageTypeLimit,
|
||||||
|
@ -96,8 +97,8 @@ namespace Jellyfin.Api.Controllers
|
||||||
|
|
||||||
var query = new InternalItemsQuery(user)
|
var query = new InternalItemsQuery(user)
|
||||||
{
|
{
|
||||||
ExcludeItemTypes = excludeItemTypes,
|
ExcludeItemTypes = RequestHelpers.GetItemTypeStrings(excludeItemTypes),
|
||||||
IncludeItemTypes = includeItemTypes,
|
IncludeItemTypes = RequestHelpers.GetItemTypeStrings(includeItemTypes),
|
||||||
StartIndex = startIndex,
|
StartIndex = startIndex,
|
||||||
Limit = limit,
|
Limit = limit,
|
||||||
IsFavorite = isFavorite,
|
IsFavorite = isFavorite,
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue