Merge branch 'master' into Comment4
This commit is contained in:
commit
0f47b3ec2d
72
.ci/azure-pipelines-api-client.yml
Normal file
72
.ci/azure-pipelines-api-client.yml
Normal file
|
@ -0,0 +1,72 @@
|
|||
parameters:
|
||||
- name: LinuxImage
|
||||
type: string
|
||||
default: "ubuntu-latest"
|
||||
- name: GeneratorVersion
|
||||
type: string
|
||||
default: "5.0.0-beta2"
|
||||
|
||||
jobs:
|
||||
- job: GenerateApiClients
|
||||
displayName: 'Generate Api Clients'
|
||||
dependsOn: Test
|
||||
|
||||
pool:
|
||||
vmImage: "${{ parameters.LinuxImage }}"
|
||||
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download OpenAPI Spec Artifact'
|
||||
inputs:
|
||||
source: 'current'
|
||||
artifact: "OpenAPI Spec"
|
||||
path: "$(System.ArtifactsDirectory)/openapispec"
|
||||
runVersion: "latest"
|
||||
|
||||
- task: CmdLine@2
|
||||
displayName: 'Download OpenApi Generator'
|
||||
inputs:
|
||||
script: "wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/${{ parameters.GeneratorVersion }}/openapi-generator-cli-${{ parameters.GeneratorVersion }}.jar -O openapi-generator-cli.jar"
|
||||
|
||||
## Generate npm api client
|
||||
# Unstable
|
||||
- task: CmdLine@2
|
||||
displayName: 'Build unstable typescript axios client'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
script: "bash ./apiclient/templates/typescript/axios/generate.sh $(System.ArtifactsDirectory) $(Build.BuildNumber)"
|
||||
|
||||
# Stable
|
||||
- task: CmdLine@2
|
||||
displayName: 'Build stable typescript axios client'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||
inputs:
|
||||
script: "bash ./apiclient/templates/typescript/axios/generate.sh $(System.ArtifactsDirectory)"
|
||||
|
||||
## Run npm install
|
||||
- task: Npm@1
|
||||
displayName: 'Install npm dependencies'
|
||||
inputs:
|
||||
command: install
|
||||
workingDir: ./apiclient/generated/typescript/axios
|
||||
|
||||
## Publish npm packages
|
||||
# Unstable
|
||||
- task: Npm@1
|
||||
displayName: 'Publish unstable typescript axios client'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
command: publish
|
||||
publishRegistry: useFeed
|
||||
publishFeed: 'jellyfin/unstable'
|
||||
workingDir: ./apiclient/generated/typescript/axios
|
||||
|
||||
# Stable
|
||||
- task: Npm@1
|
||||
displayName: 'Publish stable typescript axios client'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
|
||||
inputs:
|
||||
command: publish
|
||||
publishRegistry: useExternalRegistry
|
||||
publishEndpoint: 'jellyfin-bot for NPM'
|
||||
workingDir: ./apiclient/generated/typescript/axios
|
|
@ -63,7 +63,38 @@ jobs:
|
|||
sshEndpoint: repository
|
||||
sourceFolder: '$(Build.SourcesDirectory)/deployment/dist'
|
||||
contents: '**'
|
||||
targetFolder: '/srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
|
||||
|
||||
- job: OpenAPISpec
|
||||
dependsOn: Test
|
||||
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/heads/master'),startsWith(variables['Build.SourceBranch'], 'refs/tags/v'))
|
||||
displayName: 'Push OpenAPI Spec to repository'
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: DownloadPipelineArtifact@2
|
||||
displayName: 'Download OpenAPI Spec'
|
||||
inputs:
|
||||
source: 'current'
|
||||
artifact: "OpenAPI Spec"
|
||||
path: "$(System.ArtifactsDirectory)/openapispec"
|
||||
runVersion: "latest"
|
||||
|
||||
- task: SSH@0
|
||||
displayName: 'Create target directory on repository server'
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: 'mkdir -p /srv/repository/incoming/azure/$(Build.BuildNumber)'
|
||||
|
||||
- task: CopyFilesOverSSH@0
|
||||
displayName: 'Upload artifacts to repository server'
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
sourceFolder: '$(System.ArtifactsDirectory)/openapispec'
|
||||
contents: 'openapi.json'
|
||||
targetFolder: '/srv/repository/incoming/azure/$(Build.BuildNumber)'
|
||||
|
||||
- job: BuildDocker
|
||||
displayName: 'Build Docker'
|
||||
|
|
|
@ -56,7 +56,7 @@ jobs:
|
|||
inputs:
|
||||
command: "test"
|
||||
projects: ${{ parameters.TestProjects }}
|
||||
arguments: '--configuration Release --collect:"XPlat Code Coverage" --settings tests/coverletArgs.runsettings --verbosity minimal "-p:GenerateDocumentationFile=False"'
|
||||
arguments: '--configuration Release --collect:"XPlat Code Coverage" --settings tests/coverletArgs.runsettings --verbosity minimal'
|
||||
publishTestResults: true
|
||||
testRunTitle: $(Agent.JobName)
|
||||
workingDirectory: "$(Build.SourcesDirectory)"
|
||||
|
|
|
@ -34,6 +34,12 @@ jobs:
|
|||
Linux: 'ubuntu-latest'
|
||||
Windows: 'windows-latest'
|
||||
macOS: 'macos-latest'
|
||||
|
||||
- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}:
|
||||
- template: azure-pipelines-test.yml
|
||||
parameters:
|
||||
ImageNames:
|
||||
Linux: 'ubuntu-latest'
|
||||
|
||||
- ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
|
||||
- template: azure-pipelines-abi.yml
|
||||
|
@ -55,3 +61,6 @@ jobs:
|
|||
|
||||
- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}:
|
||||
- template: azure-pipelines-package.yml
|
||||
|
||||
- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}:
|
||||
- template: azure-pipelines-api-client.yml
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -276,3 +276,4 @@ BenchmarkDotNet.Artifacts
|
|||
web/
|
||||
web-src.*
|
||||
MediaBrowser.WebDashboard/jellyfin-web
|
||||
apiclient/generated
|
||||
|
|
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
|
@ -11,7 +11,11 @@
|
|||
"cwd": "${workspaceFolder}/Jellyfin.Server",
|
||||
"console": "internalConsole",
|
||||
"stopAtEntry": false,
|
||||
"internalConsoleOptions": "openOnSessionStart"
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "Overriding address\\(es\\) \\'(https?:\\S+)\\'",
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Launch (nowebclient)",
|
||||
|
|
|
@ -103,6 +103,7 @@
|
|||
- [sl1288](https://github.com/sl1288)
|
||||
- [sorinyo2004](https://github.com/sorinyo2004)
|
||||
- [sparky8251](https://github.com/sparky8251)
|
||||
- [spookbits](https://github.com/spookbits)
|
||||
- [stanionascu](https://github.com/stanionascu)
|
||||
- [stevehayles](https://github.com/stevehayles)
|
||||
- [SuperSandro2000](https://github.com/SuperSandro2000)
|
||||
|
@ -135,6 +136,8 @@
|
|||
- [YouKnowBlom](https://github.com/YouKnowBlom)
|
||||
- [KristupasSavickas](https://github.com/KristupasSavickas)
|
||||
- [Pusta](https://github.com/pusta)
|
||||
- [nielsvanvelzen](https://github.com/nielsvanvelzen)
|
||||
- [skyfrk](https://github.com/skyfrk)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
|
@ -198,3 +201,4 @@
|
|||
- [tikuf](https://github.com/tikuf/)
|
||||
- [Tim Hobbs](https://github.com/timhobbs)
|
||||
- [SvenVandenbrande](https://github.com/SvenVandenbrande)
|
||||
- [olsh](https://github.com/olsh)
|
||||
|
|
|
@ -126,14 +126,14 @@ namespace Emby.Dlna
|
|||
var builder = new StringBuilder();
|
||||
|
||||
builder.AppendLine("No matching device profile found. The default will need to be used.");
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, "FriendlyName:{0}", profile.FriendlyName ?? string.Empty).AppendLine();
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, "Manufacturer:{0}", profile.Manufacturer ?? string.Empty).AppendLine();
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, "ManufacturerUrl:{0}", profile.ManufacturerUrl ?? string.Empty).AppendLine();
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, "ModelDescription:{0}", profile.ModelDescription ?? string.Empty).AppendLine();
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, "ModelName:{0}", profile.ModelName ?? string.Empty).AppendLine();
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, "ModelNumber:{0}", profile.ModelNumber ?? string.Empty).AppendLine();
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, "ModelUrl:{0}", profile.ModelUrl ?? string.Empty).AppendLine();
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, "SerialNumber:{0}", profile.SerialNumber ?? string.Empty).AppendLine();
|
||||
builder.Append("FriendlyName:").AppendLine(profile.FriendlyName);
|
||||
builder.Append("Manufacturer:").AppendLine(profile.Manufacturer);
|
||||
builder.Append("ManufacturerUrl:").AppendLine(profile.ManufacturerUrl);
|
||||
builder.Append("ModelDescription:").AppendLine(profile.ModelDescription);
|
||||
builder.Append("ModelName:").AppendLine(profile.ModelName);
|
||||
builder.Append("ModelNumber:").AppendLine(profile.ModelNumber);
|
||||
builder.Append("ModelUrl:").AppendLine(profile.ModelUrl);
|
||||
builder.Append("SerialNumber:").AppendLine(profile.SerialNumber);
|
||||
|
||||
_logger.LogInformation(builder.ToString());
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 278 B |
|
@ -669,62 +669,57 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
|
||||
{
|
||||
if (Enum.TryParse(command.Name, true, out GeneralCommandType commandType))
|
||||
switch (command.Name)
|
||||
{
|
||||
switch (commandType)
|
||||
{
|
||||
case GeneralCommandType.VolumeDown:
|
||||
return _device.VolumeDown(cancellationToken);
|
||||
case GeneralCommandType.VolumeUp:
|
||||
return _device.VolumeUp(cancellationToken);
|
||||
case GeneralCommandType.Mute:
|
||||
return _device.Mute(cancellationToken);
|
||||
case GeneralCommandType.Unmute:
|
||||
return _device.Unmute(cancellationToken);
|
||||
case GeneralCommandType.ToggleMute:
|
||||
return _device.ToggleMute(cancellationToken);
|
||||
case GeneralCommandType.SetAudioStreamIndex:
|
||||
if (command.Arguments.TryGetValue("Index", out string index))
|
||||
case GeneralCommandType.VolumeDown:
|
||||
return _device.VolumeDown(cancellationToken);
|
||||
case GeneralCommandType.VolumeUp:
|
||||
return _device.VolumeUp(cancellationToken);
|
||||
case GeneralCommandType.Mute:
|
||||
return _device.Mute(cancellationToken);
|
||||
case GeneralCommandType.Unmute:
|
||||
return _device.Unmute(cancellationToken);
|
||||
case GeneralCommandType.ToggleMute:
|
||||
return _device.ToggleMute(cancellationToken);
|
||||
case GeneralCommandType.SetAudioStreamIndex:
|
||||
if (command.Arguments.TryGetValue("Index", out string index))
|
||||
{
|
||||
if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val))
|
||||
{
|
||||
if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val))
|
||||
{
|
||||
return SetAudioStreamIndex(val);
|
||||
}
|
||||
|
||||
throw new ArgumentException("Unsupported SetAudioStreamIndex value supplied.");
|
||||
return SetAudioStreamIndex(val);
|
||||
}
|
||||
|
||||
throw new ArgumentException("SetAudioStreamIndex argument cannot be null");
|
||||
case GeneralCommandType.SetSubtitleStreamIndex:
|
||||
if (command.Arguments.TryGetValue("Index", out index))
|
||||
{
|
||||
if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val))
|
||||
{
|
||||
return SetSubtitleStreamIndex(val);
|
||||
}
|
||||
throw new ArgumentException("Unsupported SetAudioStreamIndex value supplied.");
|
||||
}
|
||||
|
||||
throw new ArgumentException("Unsupported SetSubtitleStreamIndex value supplied.");
|
||||
throw new ArgumentException("SetAudioStreamIndex argument cannot be null");
|
||||
case GeneralCommandType.SetSubtitleStreamIndex:
|
||||
if (command.Arguments.TryGetValue("Index", out index))
|
||||
{
|
||||
if (int.TryParse(index, NumberStyles.Integer, _usCulture, out var val))
|
||||
{
|
||||
return SetSubtitleStreamIndex(val);
|
||||
}
|
||||
|
||||
throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null");
|
||||
case GeneralCommandType.SetVolume:
|
||||
if (command.Arguments.TryGetValue("Volume", out string vol))
|
||||
{
|
||||
if (int.TryParse(vol, NumberStyles.Integer, _usCulture, out var volume))
|
||||
{
|
||||
return _device.SetVolume(volume, cancellationToken);
|
||||
}
|
||||
throw new ArgumentException("Unsupported SetSubtitleStreamIndex value supplied.");
|
||||
}
|
||||
|
||||
throw new ArgumentException("Unsupported volume value supplied.");
|
||||
throw new ArgumentException("SetSubtitleStreamIndex argument cannot be null");
|
||||
case GeneralCommandType.SetVolume:
|
||||
if (command.Arguments.TryGetValue("Volume", out string vol))
|
||||
{
|
||||
if (int.TryParse(vol, NumberStyles.Integer, _usCulture, out var volume))
|
||||
{
|
||||
return _device.SetVolume(volume, cancellationToken);
|
||||
}
|
||||
|
||||
throw new ArgumentException("Volume argument cannot be null");
|
||||
default:
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
throw new ArgumentException("Unsupported volume value supplied.");
|
||||
}
|
||||
|
||||
throw new ArgumentException("Volume argument cannot be null");
|
||||
default:
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private async Task SetAudioStreamIndex(int? newIndex)
|
||||
|
@ -816,7 +811,7 @@ namespace Emby.Dlna.PlayTo
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendMessage<T>(string name, Guid messageId, T data, CancellationToken cancellationToken)
|
||||
public Task SendMessage<T>(SessionMessageType name, Guid messageId, T data, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_disposed)
|
||||
{
|
||||
|
@ -828,17 +823,17 @@ namespace Emby.Dlna.PlayTo
|
|||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase))
|
||||
if (name == SessionMessageType.Play)
|
||||
{
|
||||
return SendPlayCommand(data as PlayRequest, cancellationToken);
|
||||
}
|
||||
|
||||
if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
|
||||
if (name == SessionMessageType.PlayState)
|
||||
{
|
||||
return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
|
||||
}
|
||||
|
||||
if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
|
||||
if (name == SessionMessageType.GeneralCommand)
|
||||
{
|
||||
return SendGeneralCommand(data as GeneralCommand, cancellationToken);
|
||||
}
|
||||
|
@ -886,7 +881,10 @@ namespace Emby.Dlna.PlayTo
|
|||
return null;
|
||||
}
|
||||
|
||||
mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
|
||||
if (_mediaSourceManager != null)
|
||||
{
|
||||
mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return mediaSource;
|
||||
}
|
||||
|
|
|
@ -217,15 +217,15 @@ namespace Emby.Dlna.PlayTo
|
|||
|
||||
SupportedCommands = new[]
|
||||
{
|
||||
GeneralCommandType.VolumeDown.ToString(),
|
||||
GeneralCommandType.VolumeUp.ToString(),
|
||||
GeneralCommandType.Mute.ToString(),
|
||||
GeneralCommandType.Unmute.ToString(),
|
||||
GeneralCommandType.ToggleMute.ToString(),
|
||||
GeneralCommandType.SetVolume.ToString(),
|
||||
GeneralCommandType.SetAudioStreamIndex.ToString(),
|
||||
GeneralCommandType.SetSubtitleStreamIndex.ToString(),
|
||||
GeneralCommandType.PlayMediaSource.ToString()
|
||||
GeneralCommandType.VolumeDown,
|
||||
GeneralCommandType.VolumeUp,
|
||||
GeneralCommandType.Mute,
|
||||
GeneralCommandType.Unmute,
|
||||
GeneralCommandType.ToggleMute,
|
||||
GeneralCommandType.SetVolume,
|
||||
GeneralCommandType.SetAudioStreamIndex,
|
||||
GeneralCommandType.SetSubtitleStreamIndex,
|
||||
GeneralCommandType.PlayMediaSource
|
||||
},
|
||||
|
||||
SupportsMediaControl = true
|
||||
|
|
|
@ -146,12 +146,12 @@ namespace Emby.Dlna.Service
|
|||
}
|
||||
}
|
||||
|
||||
return new ControlRequestInfo();
|
||||
throw new EndOfStreamException("Stream ended but no body tag found.");
|
||||
}
|
||||
|
||||
private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
|
||||
{
|
||||
var result = new ControlRequestInfo();
|
||||
string namespaceURI = null, localName = null;
|
||||
|
||||
await reader.MoveToContentAsync().ConfigureAwait(false);
|
||||
await reader.ReadAsync().ConfigureAwait(false);
|
||||
|
@ -161,14 +161,14 @@ namespace Emby.Dlna.Service
|
|||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
result.LocalName = reader.LocalName;
|
||||
result.NamespaceURI = reader.NamespaceURI;
|
||||
localName = reader.LocalName;
|
||||
namespaceURI = reader.NamespaceURI;
|
||||
|
||||
if (!reader.IsEmptyElement)
|
||||
{
|
||||
var result = new ControlRequestInfo(localName, namespaceURI);
|
||||
using var subReader = reader.ReadSubtree();
|
||||
await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -181,7 +181,12 @@ namespace Emby.Dlna.Service
|
|||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
if (localName != null && namespaceURI != null)
|
||||
{
|
||||
return new ControlRequestInfo(localName, namespaceURI);
|
||||
}
|
||||
|
||||
throw new EndOfStreamException("Stream ended but no control found.");
|
||||
}
|
||||
|
||||
private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
|
||||
|
@ -228,11 +233,18 @@ namespace Emby.Dlna.Service
|
|||
|
||||
private class ControlRequestInfo
|
||||
{
|
||||
public ControlRequestInfo(string localName, string namespaceUri)
|
||||
{
|
||||
LocalName = localName;
|
||||
NamespaceURI = namespaceUri;
|
||||
Headers = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public string LocalName { get; set; }
|
||||
|
||||
public string NamespaceURI { get; set; }
|
||||
|
||||
public Dictionary<string, string> Headers { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||
public Dictionary<string, string> Headers { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -455,7 +455,7 @@ namespace Emby.Drawing
|
|||
throw new ArgumentException("Path can't be empty.", nameof(path));
|
||||
}
|
||||
|
||||
if (path.IsEmpty)
|
||||
if (filename.IsEmpty)
|
||||
{
|
||||
throw new ArgumentException("Filename can't be empty.", nameof(filename));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#nullable enable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
@ -19,12 +19,7 @@ namespace Emby.Naming.AudioBook
|
|||
|
||||
public AudioBookFilePathParserResult Parse(string path)
|
||||
{
|
||||
if (path == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(path));
|
||||
}
|
||||
|
||||
var result = new AudioBookFilePathParserResult();
|
||||
AudioBookFilePathParserResult result = default;
|
||||
var fileName = Path.GetFileNameWithoutExtension(path);
|
||||
foreach (var expression in _options.AudioBookPartsExpressions)
|
||||
{
|
||||
|
@ -50,27 +45,14 @@ namespace Emby.Naming.AudioBook
|
|||
{
|
||||
if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
|
||||
{
|
||||
result.ChapterNumber = intValue;
|
||||
result.PartNumber = intValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*var matches = _iRegexProvider.GetRegex("\\d+", RegexOptions.IgnoreCase).Matches(fileName);
|
||||
if (matches.Count > 0)
|
||||
{
|
||||
if (!result.ChapterNumber.HasValue)
|
||||
{
|
||||
result.ChapterNumber = int.Parse(matches[0].Groups[0].Value);
|
||||
}
|
||||
|
||||
if (matches.Count > 1)
|
||||
{
|
||||
result.PartNumber = int.Parse(matches[matches.Count - 1].Groups[0].Value);
|
||||
}
|
||||
}*/
|
||||
result.Success = result.PartNumber.HasValue || result.ChapterNumber.HasValue;
|
||||
result.Success = result.ChapterNumber.HasValue || result.PartNumber.HasValue;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#nullable enable
|
||||
#pragma warning disable CS1591
|
||||
|
||||
namespace Emby.Naming.AudioBook
|
||||
{
|
||||
public class AudioBookFilePathParserResult
|
||||
public struct AudioBookFilePathParserResult
|
||||
{
|
||||
public int? PartNumber { get; set; }
|
||||
|
||||
|
|
|
@ -55,8 +55,8 @@ namespace Emby.Naming.AudioBook
|
|||
{
|
||||
Path = path,
|
||||
Container = container,
|
||||
PartNumber = parsingResult.PartNumber,
|
||||
ChapterNumber = parsingResult.ChapterNumber,
|
||||
PartNumber = parsingResult.PartNumber,
|
||||
IsDirectory = isDirectory
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ using System;
|
|||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
@ -37,6 +38,7 @@ using Emby.Server.Implementations.LiveTv;
|
|||
using Emby.Server.Implementations.Localization;
|
||||
using Emby.Server.Implementations.Net;
|
||||
using Emby.Server.Implementations.Playlists;
|
||||
using Emby.Server.Implementations.Plugins;
|
||||
using Emby.Server.Implementations.QuickConnect;
|
||||
using Emby.Server.Implementations.ScheduledTasks;
|
||||
using Emby.Server.Implementations.Security;
|
||||
|
@ -95,6 +97,7 @@ using MediaBrowser.Model.Tasks;
|
|||
using MediaBrowser.Providers.Chapters;
|
||||
using MediaBrowser.Providers.Manager;
|
||||
using MediaBrowser.Providers.Plugins.TheTvdb;
|
||||
using MediaBrowser.Providers.Plugins.Tmdb;
|
||||
using MediaBrowser.Providers.Subtitles;
|
||||
using MediaBrowser.XbmcMetadata.Providers;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
@ -119,6 +122,7 @@ namespace Emby.Server.Implementations
|
|||
private readonly IFileSystem _fileSystemManager;
|
||||
private readonly INetworkManager _networkManager;
|
||||
private readonly IXmlSerializer _xmlSerializer;
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IStartupOptions _startupOptions;
|
||||
|
||||
private IMediaEncoder _mediaEncoder;
|
||||
|
@ -255,6 +259,8 @@ namespace Emby.Server.Implementations
|
|||
IServiceCollection serviceCollection)
|
||||
{
|
||||
_xmlSerializer = new MyXmlSerializer();
|
||||
_jsonSerializer = new JsonSerializer();
|
||||
|
||||
ServiceCollection = serviceCollection;
|
||||
|
||||
_networkManager = networkManager;
|
||||
|
@ -532,6 +538,7 @@ namespace Emby.Server.Implementations
|
|||
|
||||
ServiceCollection.AddSingleton(_fileSystemManager);
|
||||
ServiceCollection.AddSingleton<TvdbClientManager>();
|
||||
ServiceCollection.AddSingleton<TmdbClientManager>();
|
||||
|
||||
ServiceCollection.AddSingleton(_networkManager);
|
||||
|
||||
|
@ -1021,6 +1028,119 @@ namespace Emby.Server.Implementations
|
|||
|
||||
protected abstract void RestartInternal();
|
||||
|
||||
/// <summary>
|
||||
/// Comparison function used in <see cref="GetPlugins" />.
|
||||
/// </summary>
|
||||
/// <param name="a">Item to compare.</param>
|
||||
/// <param name="b">Item to compare with.</param>
|
||||
/// <returns>Boolean result of the operation.</returns>
|
||||
private static int VersionCompare(
|
||||
(Version PluginVersion, string Name, string Path) a,
|
||||
(Version PluginVersion, string Name, string Path) b)
|
||||
{
|
||||
int compare = string.Compare(a.Name, b.Name, true, CultureInfo.InvariantCulture);
|
||||
|
||||
if (compare == 0)
|
||||
{
|
||||
return a.PluginVersion.CompareTo(b.PluginVersion);
|
||||
}
|
||||
|
||||
return compare;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of plugins to install.
|
||||
/// </summary>
|
||||
/// <param name="path">Path to check.</param>
|
||||
/// <param name="cleanup">True if an attempt should be made to delete old plugs.</param>
|
||||
/// <returns>Enumerable list of dlls to load.</returns>
|
||||
private IEnumerable<string> GetPlugins(string path, bool cleanup = true)
|
||||
{
|
||||
var dllList = new List<string>();
|
||||
var versions = new List<(Version PluginVersion, string Name, string Path)>();
|
||||
var directories = Directory.EnumerateDirectories(path, "*.*", SearchOption.TopDirectoryOnly);
|
||||
string metafile;
|
||||
|
||||
foreach (var dir in directories)
|
||||
{
|
||||
try
|
||||
{
|
||||
metafile = Path.Combine(dir, "meta.json");
|
||||
if (File.Exists(metafile))
|
||||
{
|
||||
var manifest = _jsonSerializer.DeserializeFromFile<PluginManifest>(metafile);
|
||||
|
||||
if (!Version.TryParse(manifest.TargetAbi, out var targetAbi))
|
||||
{
|
||||
targetAbi = new Version(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
if (!Version.TryParse(manifest.Version, out var version))
|
||||
{
|
||||
version = new Version(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
if (ApplicationVersion >= targetAbi)
|
||||
{
|
||||
// Only load Plugins if the plugin is built for this version or below.
|
||||
versions.Add((version, manifest.Name, dir));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No metafile, so lets see if the folder is versioned.
|
||||
metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1];
|
||||
|
||||
int versionIndex = dir.LastIndexOf('_');
|
||||
if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version ver))
|
||||
{
|
||||
// Versioned folder.
|
||||
versions.Add((ver, metafile, dir));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Un-versioned folder - Add it under the path name and version 0.0.0.1.
|
||||
versions.Add((new Version(0, 0, 0, 1), metafile, dir));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
string lastName = string.Empty;
|
||||
versions.Sort(VersionCompare);
|
||||
// Traverse backwards through the list.
|
||||
// The first item will be the latest version.
|
||||
for (int x = versions.Count - 1; x >= 0; x--)
|
||||
{
|
||||
if (!string.Equals(lastName, versions[x].Name, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
dllList.AddRange(Directory.EnumerateFiles(versions[x].Path, "*.dll", SearchOption.AllDirectories));
|
||||
lastName = versions[x].Name;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(lastName) && cleanup)
|
||||
{
|
||||
// Attempt a cleanup of old folders.
|
||||
try
|
||||
{
|
||||
Logger.LogDebug("Deleting {Path}", versions[x].Path);
|
||||
Directory.Delete(versions[x].Path, true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.LogWarning(e, "Unable to delete {Path}", versions[x].Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dllList;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the composable part assemblies.
|
||||
/// </summary>
|
||||
|
@ -1029,7 +1149,7 @@ namespace Emby.Server.Implementations
|
|||
{
|
||||
if (Directory.Exists(ApplicationPaths.PluginsPath))
|
||||
{
|
||||
foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories))
|
||||
foreach (var file in GetPlugins(ApplicationPaths.PluginsPath))
|
||||
{
|
||||
Assembly plugAss;
|
||||
try
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
using System;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.Browser
|
||||
{
|
||||
/// <summary>
|
||||
/// Assists in opening application URLs in an external browser.
|
||||
/// </summary>
|
||||
public static class BrowserLauncher
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens the home page of the web client.
|
||||
/// </summary>
|
||||
/// <param name="appHost">The app host.</param>
|
||||
public static void OpenWebApp(IServerApplicationHost appHost)
|
||||
{
|
||||
TryOpenUrl(appHost, "/web/index.html");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the swagger API page.
|
||||
/// </summary>
|
||||
/// <param name="appHost">The app host.</param>
|
||||
public static void OpenSwaggerPage(IServerApplicationHost appHost)
|
||||
{
|
||||
TryOpenUrl(appHost, "/api-docs/swagger");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored.
|
||||
/// </summary>
|
||||
/// <param name="appHost">The application host.</param>
|
||||
/// <param name="relativeUrl">The URL to open, relative to the server base URL.</param>
|
||||
private static void TryOpenUrl(IServerApplicationHost appHost, string relativeUrl)
|
||||
{
|
||||
try
|
||||
{
|
||||
string baseUrl = appHost.GetLocalApiUrl("localhost");
|
||||
appHost.LaunchUrl(baseUrl + relativeUrl);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
var logger = appHost.Resolve<ILogger<IServerApplicationHost>>();
|
||||
logger?.LogError(ex, "Failed to open browser window with URL {URL}", relativeUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -234,7 +234,9 @@ namespace Emby.Server.Implementations.Data
|
|||
{
|
||||
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
|
||||
{
|
||||
bindParam.Bind(value.ToByteArray());
|
||||
Span<byte> byteValue = stackalloc byte[16];
|
||||
value.TryWriteBytes(byteValue);
|
||||
bindParam.Bind(byteValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -2263,7 +2263,6 @@ namespace Emby.Server.Implementations.Data
|
|||
return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
|
||||
private static readonly HashSet<string> _artistExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"Series",
|
||||
|
@ -3291,7 +3290,6 @@ namespace Emby.Server.Implementations.Data
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
|
||||
|
||||
var statementTexts = new List<string>();
|
||||
|
@ -6006,7 +6004,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Gets the chapter.
|
||||
/// </summary>
|
||||
|
|
|
@ -468,7 +468,6 @@ namespace Emby.Server.Implementations.Dto
|
|||
IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
|
||||
Name = item.Album,
|
||||
Limit = 1
|
||||
|
||||
});
|
||||
|
||||
if (parentAlbumIds.Count > 0)
|
||||
|
@ -1139,6 +1138,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
if (episodeSeries != null)
|
||||
{
|
||||
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
|
||||
AttachPrimaryImageAspectRatio(dto, episodeSeries);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1185,6 +1185,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
if (series != null)
|
||||
{
|
||||
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
|
||||
AttachPrimaryImageAspectRatio(dto, series);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1431,7 +1432,7 @@ namespace Emby.Server.Implementations.Dto
|
|||
return null;
|
||||
}
|
||||
|
||||
return width / height;
|
||||
return (double)width / height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.224" />
|
||||
<PackageReference Include="IPNetwork2" Version="2.5.226" />
|
||||
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
|
||||
|
@ -32,11 +32,11 @@
|
|||
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.7" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.7" />
|
||||
<PackageReference Include="Mono.Nat" Version="2.0.2" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.8" />
|
||||
<PackageReference Include="Mono.Nat" Version="3.0.0" />
|
||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.0" />
|
||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" />
|
||||
<PackageReference Include="sharpcompress" Version="0.26.0" />
|
||||
|
|
|
@ -16,6 +16,7 @@ using MediaBrowser.Controller.Plugins;
|
|||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
|
@ -105,7 +106,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
|
||||
try
|
||||
{
|
||||
_sessionManager.SendMessageToAdminSessions("RefreshProgress", dict, CancellationToken.None);
|
||||
_sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, dict, CancellationToken.None);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -123,7 +124,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
|
||||
try
|
||||
{
|
||||
_sessionManager.SendMessageToAdminSessions("RefreshProgress", collectionFolderDict, CancellationToken.None);
|
||||
_sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, collectionFolderDict, CancellationToken.None);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -345,7 +346,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
|
||||
try
|
||||
{
|
||||
await _sessionManager.SendMessageToUserSessions(new List<Guid> { userId }, "LibraryChanged", info, cancellationToken).ConfigureAwait(false);
|
||||
await _sessionManager.SendMessageToUserSessions(new List<Guid> { userId }, SessionMessageType.LibraryChanged, info, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
@ -10,6 +10,7 @@ using MediaBrowser.Controller.Library;
|
|||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
|
@ -46,25 +47,25 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
|
||||
private async void OnLiveTvManagerSeriesTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
|
||||
{
|
||||
await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false);
|
||||
await SendMessage(SessionMessageType.SeriesTimerCreated, e.Argument).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async void OnLiveTvManagerTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
|
||||
{
|
||||
await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false);
|
||||
await SendMessage(SessionMessageType.TimerCreated, e.Argument).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async void OnLiveTvManagerSeriesTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
|
||||
{
|
||||
await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false);
|
||||
await SendMessage(SessionMessageType.SeriesTimerCancelled, e.Argument).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async void OnLiveTvManagerTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
|
||||
{
|
||||
await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false);
|
||||
await SendMessage(SessionMessageType.TimerCancelled, e.Argument).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private async Task SendMessage(string name, TimerEventInfo info)
|
||||
private async Task SendMessage(SessionMessageType name, TimerEventInfo info)
|
||||
{
|
||||
var users = _userManager.Users.Where(i => i.HasPermission(PermissionKind.EnableLiveTvAccess)).Select(i => i.Id).ToList();
|
||||
|
||||
|
|
|
@ -1,83 +0,0 @@
|
|||
using System.Threading.Tasks;
|
||||
using Emby.Server.Implementations.Browser;
|
||||
using MediaBrowser.Controller;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Extensions;
|
||||
using MediaBrowser.Controller.Plugins;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Emby.Server.Implementations.EntryPoints
|
||||
{
|
||||
/// <summary>
|
||||
/// Class StartupWizard.
|
||||
/// </summary>
|
||||
public sealed class StartupWizard : IServerEntryPoint
|
||||
{
|
||||
private readonly IServerApplicationHost _appHost;
|
||||
private readonly IConfiguration _appConfig;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IStartupOptions _startupOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StartupWizard"/> class.
|
||||
/// </summary>
|
||||
/// <param name="appHost">The application host.</param>
|
||||
/// <param name="appConfig">The application configuration.</param>
|
||||
/// <param name="config">The configuration manager.</param>
|
||||
/// <param name="startupOptions">The application startup options.</param>
|
||||
public StartupWizard(
|
||||
IServerApplicationHost appHost,
|
||||
IConfiguration appConfig,
|
||||
IServerConfigurationManager config,
|
||||
IStartupOptions startupOptions)
|
||||
{
|
||||
_appHost = appHost;
|
||||
_appConfig = appConfig;
|
||||
_config = config;
|
||||
_startupOptions = startupOptions;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task RunAsync()
|
||||
{
|
||||
Run();
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
private void Run()
|
||||
{
|
||||
if (!_appHost.CanLaunchWebBrowser)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Always launch the startup wizard if possible when it has not been completed
|
||||
if (!_config.Configuration.IsStartupWizardCompleted && _appConfig.HostWebClient())
|
||||
{
|
||||
BrowserLauncher.OpenWebApp(_appHost);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do nothing if the web app is configured to not run automatically
|
||||
if (!_config.Configuration.AutoRunWebApp || _startupOptions.NoAutoRunWebApp)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Launch the swagger page if the web client is not hosted, otherwise open the web client
|
||||
if (_appConfig.HostWebClient())
|
||||
{
|
||||
BrowserLauncher.OpenWebApp(_appHost);
|
||||
}
|
||||
else
|
||||
{
|
||||
BrowserLauncher.OpenSwaggerPage(_appHost);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -28,7 +28,6 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
private readonly object _syncLock = new object();
|
||||
private Timer _updateTimer;
|
||||
|
||||
|
||||
public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IUserManager userManager)
|
||||
{
|
||||
_userDataManager = userDataManager;
|
||||
|
@ -116,7 +115,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||
|
||||
private Task SendNotifications(Guid userId, List<BaseItem> changedItems, CancellationToken cancellationToken)
|
||||
{
|
||||
return _sessionManager.SendMessageToUserSessions(new List<Guid> { userId }, "UserDataChanged", () => GetUserDataChangeInfo(userId, changedItems), cancellationToken);
|
||||
return _sessionManager.SendMessageToUserSessions(new List<Guid> { userId }, SessionMessageType.UserDataChanged, () => GetUserDataChangeInfo(userId, changedItems), cancellationToken);
|
||||
}
|
||||
|
||||
private UserDataChangeInfo GetUserDataChangeInfo(Guid userId, List<BaseItem> changedItems)
|
||||
|
|
|
@ -11,6 +11,7 @@ using System.Threading.Tasks;
|
|||
using MediaBrowser.Common.Json;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
@ -227,7 +228,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
Connection = this
|
||||
};
|
||||
|
||||
if (info.MessageType.Equals("KeepAlive", StringComparison.Ordinal))
|
||||
if (info.MessageType == SessionMessageType.KeepAlive)
|
||||
{
|
||||
await SendKeepAliveResponse().ConfigureAwait(false);
|
||||
}
|
||||
|
@ -244,7 +245,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||
new WebSocketMessage<string>
|
||||
{
|
||||
MessageId = Guid.NewGuid(),
|
||||
MessageType = "KeepAlive"
|
||||
MessageType = SessionMessageType.KeepAlive
|
||||
}, CancellationToken.None);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,6 @@ namespace Emby.Server.Implementations
|
|||
/// </summary>
|
||||
bool IsService { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the --noautorunwebapp command line option.
|
||||
/// </summary>
|
||||
bool NoAutoRunWebApp { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the --package-name command line option.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Emby.Naming.Audio;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
@ -113,52 +116,48 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||
IFileSystem fileSystem,
|
||||
ILibraryManager libraryManager)
|
||||
{
|
||||
// check for audio files before digging down into directories
|
||||
var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && libraryManager.IsAudioFile(fileSystemInfo.FullName));
|
||||
if (foundAudioFile)
|
||||
{
|
||||
// at least one audio file exists
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!allowSubfolders)
|
||||
{
|
||||
// not music since no audio file exists and we're not looking into subfolders
|
||||
return false;
|
||||
}
|
||||
|
||||
var discSubfolderCount = 0;
|
||||
var notMultiDisc = false;
|
||||
|
||||
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
|
||||
var parser = new AlbumParser(namingOptions);
|
||||
foreach (var fileSystemInfo in list)
|
||||
|
||||
var directories = list.Where(fileSystemInfo => fileSystemInfo.IsDirectory);
|
||||
|
||||
var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
|
||||
{
|
||||
if (fileSystemInfo.IsDirectory)
|
||||
var path = fileSystemInfo.FullName;
|
||||
var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
|
||||
|
||||
if (hasMusic)
|
||||
{
|
||||
if (allowSubfolders)
|
||||
if (parser.IsMultiPart(path))
|
||||
{
|
||||
if (notMultiDisc)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var path = fileSystemInfo.FullName;
|
||||
var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
|
||||
|
||||
if (hasMusic)
|
||||
{
|
||||
if (parser.IsMultiPart(path))
|
||||
{
|
||||
logger.LogDebug("Found multi-disc folder: " + path);
|
||||
discSubfolderCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album
|
||||
notMultiDisc = true;
|
||||
}
|
||||
}
|
||||
logger.LogDebug("Found multi-disc folder: " + path);
|
||||
Interlocked.Increment(ref discSubfolderCount);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album
|
||||
state.Stop();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var fullName = fileSystemInfo.FullName;
|
||||
});
|
||||
|
||||
if (libraryManager.IsAudioFile(fullName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (notMultiDisc)
|
||||
if (!result.IsCompleted)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities.Audio;
|
||||
using MediaBrowser.Controller.Library;
|
||||
|
@ -94,7 +95,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||
var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
|
||||
|
||||
// If we contain an album assume we are an artist folder
|
||||
return args.FileSystemChildren.Where(i => i.IsDirectory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null;
|
||||
var directories = args.FileSystemChildren.Where(i => i.IsDirectory);
|
||||
|
||||
var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
|
||||
{
|
||||
if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService))
|
||||
{
|
||||
// stop once we see a music album
|
||||
state.Stop();
|
||||
}
|
||||
});
|
||||
|
||||
return !result.IsCompleted ? new MusicArtist() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -874,7 +874,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public List<Lineup> lineups { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public class Headends
|
||||
{
|
||||
public string headend { get; set; }
|
||||
|
@ -886,8 +885,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public List<Lineup> lineups { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class Map
|
||||
{
|
||||
public string stationID { get; set; }
|
||||
|
@ -971,9 +968,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public List<string> date { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public class Rating
|
||||
{
|
||||
public string body { get; set; }
|
||||
|
@ -1017,8 +1011,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
|
|||
public string isPremiereOrFinale { get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class MetadataSchedule
|
||||
{
|
||||
public string modified { get; set; }
|
||||
|
|
|
@ -2135,6 +2135,7 @@ namespace Emby.Server.Implementations.LiveTv
|
|||
}
|
||||
|
||||
private bool _disposed = false;
|
||||
|
||||
/// <summary>
|
||||
/// Releases unmanaged and - optionally - managed resources.
|
||||
/// </summary>
|
||||
|
|
|
@ -563,6 +563,19 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||
|
||||
protected override async Task<ILiveStream> GetChannelStream(TunerHostInfo info, ChannelInfo channelInfo, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
|
||||
{
|
||||
var tunerCount = info.TunerCount;
|
||||
|
||||
if (tunerCount > 0)
|
||||
{
|
||||
var tunerHostId = info.Id;
|
||||
var liveStreams = currentLiveStreams.Where(i => string.Equals(i.TunerHostId, tunerHostId, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (liveStreams.Count() >= tunerCount)
|
||||
{
|
||||
throw new LiveTvConflictException("HDHomeRun simultaneous stream limit has been reached.");
|
||||
}
|
||||
}
|
||||
|
||||
var profile = streamId.Split('_')[0];
|
||||
|
||||
Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelInfo.Id, streamId, profile);
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"Artists": "Kunstenare",
|
||||
"Channels": "Kanale",
|
||||
"Folders": "Fouers",
|
||||
"Favorites": "Gunstelinge",
|
||||
"Folders": "Lêergidse",
|
||||
"Favorites": "Gunstellinge",
|
||||
"HeaderFavoriteShows": "Gunsteling Vertonings",
|
||||
"ValueSpecialEpisodeName": "Spesiale - {0}",
|
||||
"HeaderAlbumArtists": "Album Kunstenaars",
|
||||
"Books": "Boeke",
|
||||
"HeaderNextUp": "Volgende",
|
||||
"Movies": "Rolprente",
|
||||
"Shows": "Program",
|
||||
"HeaderContinueWatching": "Hou Aan Kyk",
|
||||
"Movies": "Flieks",
|
||||
"Shows": "Televisie Reekse",
|
||||
"HeaderContinueWatching": "Kyk Verder",
|
||||
"HeaderFavoriteEpisodes": "Gunsteling Episodes",
|
||||
"Photos": "Fotos",
|
||||
"Playlists": "Speellysse",
|
||||
"Playlists": "Snitlyste",
|
||||
"HeaderFavoriteArtists": "Gunsteling Kunstenaars",
|
||||
"HeaderFavoriteAlbums": "Gunsteling Albums",
|
||||
"Sync": "Sinkroniseer",
|
||||
|
@ -23,7 +23,7 @@
|
|||
"DeviceOfflineWithName": "{0} is ontkoppel",
|
||||
"Collections": "Versamelings",
|
||||
"Inherit": "Ontvang",
|
||||
"HeaderLiveTV": "Live TV",
|
||||
"HeaderLiveTV": "Lewendige TV",
|
||||
"Application": "Program",
|
||||
"AppDeviceValues": "App: {0}, Toestel: {1}",
|
||||
"VersionNumber": "Weergawe {0}",
|
||||
|
@ -85,7 +85,6 @@
|
|||
"ItemAddedWithName": "{0} is in die versameling",
|
||||
"HomeVideos": "Tuis opnames",
|
||||
"HeaderRecordingGroups": "Groep Opnames",
|
||||
"HeaderCameraUploads": "Kamera Oplaai",
|
||||
"Genres": "Genres",
|
||||
"FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}",
|
||||
"ChapterNameValue": "Hoofstuk",
|
||||
|
@ -95,5 +94,23 @@
|
|||
"TasksChannelsCategory": "Internet kanale",
|
||||
"TasksApplicationCategory": "aansoek",
|
||||
"TasksLibraryCategory": "biblioteek",
|
||||
"TasksMaintenanceCategory": "onderhoud"
|
||||
"TasksMaintenanceCategory": "onderhoud",
|
||||
"TaskCleanCacheDescription": "Vee kasregister lêers uit wat nie meer deur die stelsel benodig word nie.",
|
||||
"TaskCleanCache": "Reinig Kasgeheue Lêergids",
|
||||
"TaskDownloadMissingSubtitlesDescription": "Soek aanlyn vir vermiste onderskrifte gebasseer op metadata verstellings.",
|
||||
"TaskDownloadMissingSubtitles": "Laai vermiste onderskrifte af",
|
||||
"TaskRefreshChannelsDescription": "Vervris internet kanaal inligting.",
|
||||
"TaskRefreshChannels": "Vervris Kanale",
|
||||
"TaskCleanTranscodeDescription": "Vee transkodering lêers uit wat ouer is as een dag.",
|
||||
"TaskCleanTranscode": "Reinig Transkoderings Leêrbinder",
|
||||
"TaskUpdatePluginsDescription": "Laai opgedateerde inprop-sagteware af en installeer inprop-sagteware wat verstel is om outomaties op te dateer.",
|
||||
"TaskUpdatePlugins": "Dateer Inprop-Sagteware Op",
|
||||
"TaskRefreshPeopleDescription": "Vervris metadata oor akteurs en regisseurs in u media versameling.",
|
||||
"TaskRefreshPeople": "Vervris Mense",
|
||||
"TaskCleanLogsDescription": "Vee loglêers wat ouer as {0} dae is uit.",
|
||||
"TaskCleanLogs": "Reinig Loglêer Lêervouer",
|
||||
"TaskRefreshLibraryDescription": "Skandeer u media versameling vir nuwe lêers en verfris metadata.",
|
||||
"TaskRefreshLibrary": "Skandeer Media Versameling",
|
||||
"TaskRefreshChapterImagesDescription": "Maak kleinkiekeis (fotos) vir films wat hoofstukke het.",
|
||||
"TaskRefreshChapterImages": "Verkry Hoofstuk Beelde"
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "المجلدات",
|
||||
"Genres": "التضنيفات",
|
||||
"HeaderAlbumArtists": "فناني الألبومات",
|
||||
"HeaderCameraUploads": "تحميلات الكاميرا",
|
||||
"HeaderContinueWatching": "استئناف",
|
||||
"HeaderFavoriteAlbums": "الألبومات المفضلة",
|
||||
"HeaderFavoriteArtists": "الفنانون المفضلون",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Папки",
|
||||
"Genres": "Жанрове",
|
||||
"HeaderAlbumArtists": "Изпълнители на албуми",
|
||||
"HeaderCameraUploads": "Качени от камера",
|
||||
"HeaderContinueWatching": "Продължаване на гледането",
|
||||
"HeaderFavoriteAlbums": "Любими албуми",
|
||||
"HeaderFavoriteArtists": "Любими изпълнители",
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
"HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
|
||||
"HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
|
||||
"HeaderContinueWatching": "দেখতে থাকুন",
|
||||
"HeaderCameraUploads": "ক্যামেরার আপলোড সমূহ",
|
||||
"HeaderAlbumArtists": "এলবাম শিল্পী",
|
||||
"Genres": "জেনার",
|
||||
"Folders": "ফোল্ডারগুলো",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Carpetes",
|
||||
"Genres": "Gèneres",
|
||||
"HeaderAlbumArtists": "Artistes del Àlbum",
|
||||
"HeaderCameraUploads": "Pujades de Càmera",
|
||||
"HeaderContinueWatching": "Continua Veient",
|
||||
"HeaderFavoriteAlbums": "Àlbums Preferits",
|
||||
"HeaderFavoriteArtists": "Artistes Preferits",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Složky",
|
||||
"Genres": "Žánry",
|
||||
"HeaderAlbumArtists": "Umělci alba",
|
||||
"HeaderCameraUploads": "Nahrané fotografie",
|
||||
"HeaderContinueWatching": "Pokračovat ve sledování",
|
||||
"HeaderFavoriteAlbums": "Oblíbená alba",
|
||||
"HeaderFavoriteArtists": "Oblíbení interpreti",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Mapper",
|
||||
"Genres": "Genrer",
|
||||
"HeaderAlbumArtists": "Albumkunstnere",
|
||||
"HeaderCameraUploads": "Kamera Uploads",
|
||||
"HeaderContinueWatching": "Fortsæt Afspilning",
|
||||
"HeaderFavoriteAlbums": "Favoritalbummer",
|
||||
"HeaderFavoriteArtists": "Favoritkunstnere",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Verzeichnisse",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album-Interpreten",
|
||||
"HeaderCameraUploads": "Kamera-Uploads",
|
||||
"HeaderContinueWatching": "Fortsetzen",
|
||||
"HeaderFavoriteAlbums": "Lieblingsalben",
|
||||
"HeaderFavoriteArtists": "Lieblings-Interpreten",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Φάκελοι",
|
||||
"Genres": "Είδη",
|
||||
"HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
|
||||
"HeaderCameraUploads": "Μεταφορτώσεις Κάμερας",
|
||||
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
|
||||
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
|
||||
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Folders",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
"HeaderContinueWatching": "Continue Watching",
|
||||
"HeaderFavoriteAlbums": "Favourite Albums",
|
||||
"HeaderFavoriteArtists": "Favourite Artists",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Folders",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album Artists",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
"HeaderContinueWatching": "Continue Watching",
|
||||
"HeaderFavoriteAlbums": "Favorite Albums",
|
||||
"HeaderFavoriteArtists": "Favorite Artists",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Carpetas",
|
||||
"Genres": "Géneros",
|
||||
"HeaderAlbumArtists": "Artistas de álbum",
|
||||
"HeaderCameraUploads": "Subidas de cámara",
|
||||
"HeaderContinueWatching": "Seguir viendo",
|
||||
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Carpetas",
|
||||
"Genres": "Géneros",
|
||||
"HeaderAlbumArtists": "Artistas del álbum",
|
||||
"HeaderCameraUploads": "Subidas desde la cámara",
|
||||
"HeaderContinueWatching": "Continuar viendo",
|
||||
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Carpetas",
|
||||
"Genres": "Géneros",
|
||||
"HeaderAlbumArtists": "Artistas del álbum",
|
||||
"HeaderCameraUploads": "Subidas desde la cámara",
|
||||
"HeaderContinueWatching": "Continuar viendo",
|
||||
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||
|
|
|
@ -105,7 +105,6 @@
|
|||
"Inherit": "Heredar",
|
||||
"HomeVideos": "Videos caseros",
|
||||
"HeaderRecordingGroups": "Grupos de grabación",
|
||||
"HeaderCameraUploads": "Subidas desde la cámara",
|
||||
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}",
|
||||
"DeviceOnlineWithName": "{0} está conectado",
|
||||
"DeviceOfflineWithName": "{0} se ha desconectado",
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
"Application": "Aplicación",
|
||||
"AppDeviceValues": "App: {0}, Dispositivo: {1}",
|
||||
"HeaderContinueWatching": "Continuar Viendo",
|
||||
"HeaderCameraUploads": "Subidas de Cámara",
|
||||
"HeaderAlbumArtists": "Artistas del Álbum",
|
||||
"Genres": "Géneros",
|
||||
"Folders": "Carpetas",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "پوشهها",
|
||||
"Genres": "ژانرها",
|
||||
"HeaderAlbumArtists": "هنرمندان آلبوم",
|
||||
"HeaderCameraUploads": "آپلودهای دوربین",
|
||||
"HeaderContinueWatching": "ادامه تماشا",
|
||||
"HeaderFavoriteAlbums": "آلبومهای مورد علاقه",
|
||||
"HeaderFavoriteArtists": "هنرمندان مورد علاقه",
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
"HeaderFavoriteSongs": "Lempikappaleet",
|
||||
"HeaderFavoriteShows": "Lempisarjat",
|
||||
"HeaderFavoriteEpisodes": "Lempijaksot",
|
||||
"HeaderCameraUploads": "Kamerasta Lähetetyt",
|
||||
"HeaderFavoriteArtists": "Lempiartistit",
|
||||
"HeaderFavoriteAlbums": "Lempialbumit",
|
||||
"HeaderContinueWatching": "Jatka katsomista",
|
||||
|
|
|
@ -73,7 +73,6 @@
|
|||
"HeaderFavoriteArtists": "Paboritong Artista",
|
||||
"HeaderFavoriteAlbums": "Paboritong Albums",
|
||||
"HeaderContinueWatching": "Ituloy Manood",
|
||||
"HeaderCameraUploads": "Camera Uploads",
|
||||
"HeaderAlbumArtists": "Artista ng Album",
|
||||
"Genres": "Kategorya",
|
||||
"Folders": "Folders",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Dossiers",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Artistes de l'album",
|
||||
"HeaderCameraUploads": "Photos transférées",
|
||||
"HeaderContinueWatching": "Continuer à regarder",
|
||||
"HeaderFavoriteAlbums": "Albums favoris",
|
||||
"HeaderFavoriteArtists": "Artistes favoris",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Dossiers",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Artistes",
|
||||
"HeaderCameraUploads": "Photos transférées",
|
||||
"HeaderContinueWatching": "Continuer à regarder",
|
||||
"HeaderFavoriteAlbums": "Albums favoris",
|
||||
"HeaderFavoriteArtists": "Artistes préférés",
|
||||
|
@ -107,7 +106,7 @@
|
|||
"TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.",
|
||||
"TaskCleanLogs": "Nettoyer le répertoire des journaux",
|
||||
"TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.",
|
||||
"TaskRefreshLibrary": "Scanner toute les Bibliothèques",
|
||||
"TaskRefreshLibrary": "Scanner toutes les Bibliothèques",
|
||||
"TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.",
|
||||
"TaskRefreshChapterImages": "Extraire les images de chapitre",
|
||||
"TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.",
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
{
|
||||
"Albums": "Álbumes"
|
||||
"Albums": "Álbumes",
|
||||
"Collections": "Colecións",
|
||||
"ChapterNameValue": "Capítulos {0}",
|
||||
"Channels": "Canles",
|
||||
"CameraImageUploadedFrom": "Cargouse unha nova imaxe da cámara desde {0}",
|
||||
"Books": "Libros",
|
||||
"AuthenticationSucceededWithUserName": "{0} autenticouse correctamente",
|
||||
"Artists": "Artistas",
|
||||
"Application": "Aplicativo"
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Ordner",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Album-Künstler",
|
||||
"HeaderCameraUploads": "Kamera-Uploads",
|
||||
"HeaderContinueWatching": "weiter schauen",
|
||||
"HeaderFavoriteAlbums": "Lieblingsalben",
|
||||
"HeaderFavoriteArtists": "Lieblings-Künstler",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "תיקיות",
|
||||
"Genres": "ז'אנרים",
|
||||
"HeaderAlbumArtists": "אמני האלבום",
|
||||
"HeaderCameraUploads": "העלאות ממצלמה",
|
||||
"HeaderContinueWatching": "המשך לצפות",
|
||||
"HeaderFavoriteAlbums": "אלבומים מועדפים",
|
||||
"HeaderFavoriteArtists": "אמנים מועדפים",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Mape",
|
||||
"Genres": "Žanrovi",
|
||||
"HeaderAlbumArtists": "Izvođači na albumu",
|
||||
"HeaderCameraUploads": "Uvoz sa kamere",
|
||||
"HeaderContinueWatching": "Nastavi gledati",
|
||||
"HeaderFavoriteAlbums": "Omiljeni albumi",
|
||||
"HeaderFavoriteArtists": "Omiljeni izvođači",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Könyvtárak",
|
||||
"Genres": "Műfajok",
|
||||
"HeaderAlbumArtists": "Album előadók",
|
||||
"HeaderCameraUploads": "Kamera feltöltések",
|
||||
"HeaderContinueWatching": "Megtekintés folytatása",
|
||||
"HeaderFavoriteAlbums": "Kedvenc albumok",
|
||||
"HeaderFavoriteArtists": "Kedvenc előadók",
|
||||
|
|
|
@ -20,7 +20,6 @@
|
|||
"HeaderFavoriteArtists": "Artis Favorit",
|
||||
"HeaderFavoriteAlbums": "Album Favorit",
|
||||
"HeaderContinueWatching": "Lanjut Menonton",
|
||||
"HeaderCameraUploads": "Unggahan Kamera",
|
||||
"HeaderAlbumArtists": "Album Artis",
|
||||
"Genres": "Aliran",
|
||||
"Folders": "Folder",
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
"HeaderFavoriteArtists": "Uppáhalds Listamenn",
|
||||
"HeaderFavoriteAlbums": "Uppáhalds Plötur",
|
||||
"HeaderContinueWatching": "Halda áfram að horfa",
|
||||
"HeaderCameraUploads": "Myndavéla upphal",
|
||||
"HeaderAlbumArtists": "Höfundur plötu",
|
||||
"Genres": "Tegundir",
|
||||
"Folders": "Möppur",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Cartelle",
|
||||
"Genres": "Generi",
|
||||
"HeaderAlbumArtists": "Artisti degli Album",
|
||||
"HeaderCameraUploads": "Caricamenti Fotocamera",
|
||||
"HeaderContinueWatching": "Continua a guardare",
|
||||
"HeaderFavoriteAlbums": "Album Preferiti",
|
||||
"HeaderFavoriteArtists": "Artisti Preferiti",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "フォルダー",
|
||||
"Genres": "ジャンル",
|
||||
"HeaderAlbumArtists": "アルバムアーティスト",
|
||||
"HeaderCameraUploads": "カメラアップロード",
|
||||
"HeaderContinueWatching": "視聴を続ける",
|
||||
"HeaderFavoriteAlbums": "お気に入りのアルバム",
|
||||
"HeaderFavoriteArtists": "お気に入りのアーティスト",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Qaltalar",
|
||||
"Genres": "Janrlar",
|
||||
"HeaderAlbumArtists": "Álbom oryndaýshylary",
|
||||
"HeaderCameraUploads": "Kameradan júktelgender",
|
||||
"HeaderContinueWatching": "Qaraýdy jalǵastyrý",
|
||||
"HeaderFavoriteAlbums": "Tańdaýly álbomdar",
|
||||
"HeaderFavoriteArtists": "Tańdaýly oryndaýshylar",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "폴더",
|
||||
"Genres": "장르",
|
||||
"HeaderAlbumArtists": "앨범 아티스트",
|
||||
"HeaderCameraUploads": "카메라 업로드",
|
||||
"HeaderContinueWatching": "계속 시청하기",
|
||||
"HeaderFavoriteAlbums": "즐겨찾는 앨범",
|
||||
"HeaderFavoriteArtists": "즐겨찾는 아티스트",
|
||||
|
@ -84,8 +83,8 @@
|
|||
"UserDeletedWithName": "사용자 {0} 삭제됨",
|
||||
"UserDownloadingItemWithValues": "{0}이(가) {1}을 다운로드 중입니다",
|
||||
"UserLockedOutWithName": "유저 {0} 은(는) 잠금처리 되었습니다",
|
||||
"UserOfflineFromDevice": "{1}로부터 {0}의 연결이 끊겼습니다",
|
||||
"UserOnlineFromDevice": "{0}은 {1}에서 온라인 상태입니다",
|
||||
"UserOfflineFromDevice": "{1}에서 {0}의 연결이 끊킴",
|
||||
"UserOnlineFromDevice": "{0}이 {1}으로 접속",
|
||||
"UserPasswordChangedWithName": "사용자 {0}의 비밀번호가 변경되었습니다",
|
||||
"UserPolicyUpdatedWithName": "{0}의 사용자 정책이 업데이트되었습니다",
|
||||
"UserStartedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생 중",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Katalogai",
|
||||
"Genres": "Žanrai",
|
||||
"HeaderAlbumArtists": "Albumo atlikėjai",
|
||||
"HeaderCameraUploads": "Kameros",
|
||||
"HeaderContinueWatching": "Žiūrėti toliau",
|
||||
"HeaderFavoriteAlbums": "Mėgstami Albumai",
|
||||
"HeaderFavoriteArtists": "Mėgstami Atlikėjai",
|
||||
|
|
|
@ -72,7 +72,6 @@
|
|||
"ItemAddedWithName": "{0} tika pievienots bibliotēkai",
|
||||
"HeaderLiveTV": "Tiešraides TV",
|
||||
"HeaderContinueWatching": "Turpināt Skatīšanos",
|
||||
"HeaderCameraUploads": "Kameras augšupielādes",
|
||||
"HeaderAlbumArtists": "Albumu Izpildītāji",
|
||||
"Genres": "Žanri",
|
||||
"Folders": "Mapes",
|
||||
|
|
|
@ -51,7 +51,6 @@
|
|||
"HeaderFavoriteArtists": "Омилени Изведувачи",
|
||||
"HeaderFavoriteAlbums": "Омилени Албуми",
|
||||
"HeaderContinueWatching": "Продолжи со гледање",
|
||||
"HeaderCameraUploads": "Поставувања од камера",
|
||||
"HeaderAlbumArtists": "Изведувачи од Албуми",
|
||||
"Genres": "Жанрови",
|
||||
"Folders": "Папки",
|
||||
|
|
|
@ -54,7 +54,6 @@
|
|||
"ItemAddedWithName": "{0} हे संग्रहालयात जोडले गेले",
|
||||
"HomeVideos": "घरचे व्हिडीयो",
|
||||
"HeaderRecordingGroups": "रेकॉर्डिंग गट",
|
||||
"HeaderCameraUploads": "कॅमेरा अपलोड",
|
||||
"CameraImageUploadedFrom": "एक नवीन कॅमेरा चित्र {0} येथून अपलोड केले आहे",
|
||||
"Application": "अॅप्लिकेशन",
|
||||
"AppDeviceValues": "अॅप: {0}, यंत्र: {1}",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Fail-fail",
|
||||
"Genres": "Genre-genre",
|
||||
"HeaderAlbumArtists": "Album Artis-artis",
|
||||
"HeaderCameraUploads": "Muatnaik Kamera",
|
||||
"HeaderContinueWatching": "Terus Menonton",
|
||||
"HeaderFavoriteAlbums": "Album-album Kegemaran",
|
||||
"HeaderFavoriteArtists": "Artis-artis Kegemaran",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Mapper",
|
||||
"Genres": "Sjangre",
|
||||
"HeaderAlbumArtists": "Albumartister",
|
||||
"HeaderCameraUploads": "Kameraopplastinger",
|
||||
"HeaderContinueWatching": "Fortsett å se",
|
||||
"HeaderFavoriteAlbums": "Favorittalbum",
|
||||
"HeaderFavoriteArtists": "Favorittartister",
|
||||
|
@ -50,7 +49,7 @@
|
|||
"NotificationOptionAudioPlayback": "Lydavspilling startet",
|
||||
"NotificationOptionAudioPlaybackStopped": "Lydavspilling stoppet",
|
||||
"NotificationOptionCameraImageUploaded": "Kamerabilde lastet opp",
|
||||
"NotificationOptionInstallationFailed": "Installasjonsfeil",
|
||||
"NotificationOptionInstallationFailed": "Installasjonen feilet",
|
||||
"NotificationOptionNewLibraryContent": "Nytt innhold lagt til",
|
||||
"NotificationOptionPluginError": "Pluginfeil",
|
||||
"NotificationOptionPluginInstalled": "Plugin installert",
|
||||
|
|
|
@ -41,7 +41,6 @@
|
|||
"HeaderFavoriteArtists": "मनपर्ने कलाकारहरू",
|
||||
"HeaderFavoriteAlbums": "मनपर्ने एल्बमहरू",
|
||||
"HeaderContinueWatching": "हेर्न जारी राख्नुहोस्",
|
||||
"HeaderCameraUploads": "क्यामेरा अपलोडहरू",
|
||||
"HeaderAlbumArtists": "एल्बमका कलाकारहरू",
|
||||
"Genres": "विधाहरू",
|
||||
"Folders": "फोल्डरहरू",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Mappen",
|
||||
"Genres": "Genres",
|
||||
"HeaderAlbumArtists": "Albumartiesten",
|
||||
"HeaderCameraUploads": "Camera-uploads",
|
||||
"HeaderContinueWatching": "Kijken hervatten",
|
||||
"HeaderFavoriteAlbums": "Favoriete albums",
|
||||
"HeaderFavoriteArtists": "Favoriete artiesten",
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
"HeaderFavoriteArtists": "Favoritt Artistar",
|
||||
"HeaderFavoriteAlbums": "Favoritt Album",
|
||||
"HeaderContinueWatching": "Fortsett å sjå",
|
||||
"HeaderCameraUploads": "Kamera Opplastingar",
|
||||
"HeaderAlbumArtists": "Album Artist",
|
||||
"Genres": "Sjangrar",
|
||||
"Folders": "Mapper",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Foldery",
|
||||
"Genres": "Gatunki",
|
||||
"HeaderAlbumArtists": "Wykonawcy albumów",
|
||||
"HeaderCameraUploads": "Przekazane obrazy",
|
||||
"HeaderContinueWatching": "Kontynuuj odtwarzanie",
|
||||
"HeaderFavoriteAlbums": "Ulubione albumy",
|
||||
"HeaderFavoriteArtists": "Ulubieni wykonawcy",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Pastas",
|
||||
"Genres": "Gêneros",
|
||||
"HeaderAlbumArtists": "Artistas do Álbum",
|
||||
"HeaderCameraUploads": "Envios da Câmera",
|
||||
"HeaderContinueWatching": "Continuar Assistindo",
|
||||
"HeaderFavoriteAlbums": "Álbuns Favoritos",
|
||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Pastas",
|
||||
"Genres": "Géneros",
|
||||
"HeaderAlbumArtists": "Artistas do Álbum",
|
||||
"HeaderCameraUploads": "Envios a partir da câmara",
|
||||
"HeaderContinueWatching": "Continuar a Ver",
|
||||
"HeaderFavoriteAlbums": "Álbuns Favoritos",
|
||||
"HeaderFavoriteArtists": "Artistas Favoritos",
|
||||
|
@ -26,7 +25,7 @@
|
|||
"HeaderLiveTV": "TV em Direto",
|
||||
"HeaderNextUp": "A Seguir",
|
||||
"HeaderRecordingGroups": "Grupos de Gravação",
|
||||
"HomeVideos": "Videos caseiros",
|
||||
"HomeVideos": "Vídeos Caseiros",
|
||||
"Inherit": "Herdar",
|
||||
"ItemAddedWithName": "{0} foi adicionado à biblioteca",
|
||||
"ItemRemovedWithName": "{0} foi removido da biblioteca",
|
||||
|
|
|
@ -83,7 +83,6 @@
|
|||
"Playlists": "Listas de Reprodução",
|
||||
"Photos": "Fotografias",
|
||||
"Movies": "Filmes",
|
||||
"HeaderCameraUploads": "Carregamentos a partir da câmara",
|
||||
"FailedLoginAttemptWithUserName": "Tentativa de ligação falhada a partir de {0}",
|
||||
"DeviceOnlineWithName": "{0} está connectado",
|
||||
"DeviceOfflineWithName": "{0} desconectou-se",
|
||||
|
|
|
@ -74,7 +74,6 @@
|
|||
"HeaderFavoriteArtists": "Artiști Favoriți",
|
||||
"HeaderFavoriteAlbums": "Albume Favorite",
|
||||
"HeaderContinueWatching": "Vizionează în continuare",
|
||||
"HeaderCameraUploads": "Incărcări Cameră Foto",
|
||||
"HeaderAlbumArtists": "Album Artiști",
|
||||
"Genres": "Genuri",
|
||||
"Folders": "Dosare",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Папки",
|
||||
"Genres": "Жанры",
|
||||
"HeaderAlbumArtists": "Исполнители альбома",
|
||||
"HeaderCameraUploads": "Камеры",
|
||||
"HeaderContinueWatching": "Продолжение просмотра",
|
||||
"HeaderFavoriteAlbums": "Избранные альбомы",
|
||||
"HeaderFavoriteArtists": "Избранные исполнители",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Priečinky",
|
||||
"Genres": "Žánre",
|
||||
"HeaderAlbumArtists": "Umelci albumu",
|
||||
"HeaderCameraUploads": "Nahrané fotografie",
|
||||
"HeaderContinueWatching": "Pokračovať v pozeraní",
|
||||
"HeaderFavoriteAlbums": "Obľúbené albumy",
|
||||
"HeaderFavoriteArtists": "Obľúbení umelci",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Mape",
|
||||
"Genres": "Zvrsti",
|
||||
"HeaderAlbumArtists": "Izvajalci albuma",
|
||||
"HeaderCameraUploads": "Posnetki kamere",
|
||||
"HeaderContinueWatching": "Nadaljuj gledanje",
|
||||
"HeaderFavoriteAlbums": "Priljubljeni albumi",
|
||||
"HeaderFavoriteArtists": "Priljubljeni izvajalci",
|
||||
|
|
116
Emby.Server.Implementations/Localization/Core/sq.json
Normal file
116
Emby.Server.Implementations/Localization/Core/sq.json
Normal file
|
@ -0,0 +1,116 @@
|
|||
{
|
||||
"MessageApplicationUpdatedTo": "Serveri Jellyfin u përditesua në versionin {0}",
|
||||
"Inherit": "Trashgimi",
|
||||
"TaskDownloadMissingSubtitlesDescription": "Kërkon në internet për titra që mungojnë bazuar tek konfigurimi i metadata-ve.",
|
||||
"TaskDownloadMissingSubtitles": "Shkarko titra që mungojnë",
|
||||
"TaskRefreshChannelsDescription": "Rifreskon informacionin e kanaleve të internetit.",
|
||||
"TaskRefreshChannels": "Rifresko Kanalet",
|
||||
"TaskCleanTranscodeDescription": "Fshin skedarët e transkodimit që janë më të vjetër se një ditë.",
|
||||
"TaskCleanTranscode": "Fshi dosjen e transkodimit",
|
||||
"TaskUpdatePluginsDescription": "Shkarkon dhe instalon përditësimi për plugin që janë konfiguruar të përditësohen automatikisht.",
|
||||
"TaskUpdatePlugins": "Përditëso Plugin",
|
||||
"TaskRefreshPeopleDescription": "Përditëson metadata të aktorëve dhe regjizorëve në librarinë tuaj.",
|
||||
"TaskRefreshPeople": "Rifresko aktorët",
|
||||
"TaskCleanLogsDescription": "Fshin skëdarët log që janë më të vjetër se {0} ditë.",
|
||||
"TaskCleanLogs": "Fshi dosjen Log",
|
||||
"TaskRefreshLibraryDescription": "Skanon librarinë media për skedarë të rinj dhe rifreskon metadata.",
|
||||
"TaskRefreshLibrary": "Skano librarinë media",
|
||||
"TaskRefreshChapterImagesDescription": "Krijon imazh për videot që kanë kapituj.",
|
||||
"TaskRefreshChapterImages": "Ekstrakto Imazhet e Kapitullit",
|
||||
"TaskCleanCacheDescription": "Fshi skedarët e cache-s që nuk i duhen më sistemit.",
|
||||
"TaskCleanCache": "Pastro memorjen cache",
|
||||
"TasksChannelsCategory": "Kanalet nga interneti",
|
||||
"TasksApplicationCategory": "Aplikacioni",
|
||||
"TasksLibraryCategory": "Libraria",
|
||||
"TasksMaintenanceCategory": "Mirëmbajtje",
|
||||
"VersionNumber": "Versioni {0}",
|
||||
"ValueSpecialEpisodeName": "Speciale - {0}",
|
||||
"ValueHasBeenAddedToLibrary": "{0} u shtua tek libraria juaj",
|
||||
"UserStoppedPlayingItemWithValues": "{0} mbaroi së shikuari {1} tek {2}",
|
||||
"UserStartedPlayingItemWithValues": "{0} po shikon {1} tek {2}",
|
||||
"UserPolicyUpdatedWithName": "Politika e përdoruesit u përditësua për {0}",
|
||||
"UserPasswordChangedWithName": "Fjalëkalimi u ndryshua për përdoruesin {0}",
|
||||
"UserOnlineFromDevice": "{0} është në linjë nga {1}",
|
||||
"UserOfflineFromDevice": "{0} u shkëput nga {1}",
|
||||
"UserLockedOutWithName": "Përdoruesi {0} u përjashtua",
|
||||
"UserDownloadingItemWithValues": "{0} po shkarkon {1}",
|
||||
"UserDeletedWithName": "Përdoruesi {0} u fshi",
|
||||
"UserCreatedWithName": "Përdoruesi {0} u krijua",
|
||||
"User": "Përdoruesi",
|
||||
"TvShows": "Seriale TV",
|
||||
"System": "Sistemi",
|
||||
"Sync": "Sinkronizo",
|
||||
"SubtitleDownloadFailureFromForItem": "Titrat deshtuan të shkarkohen nga {0} për {1}",
|
||||
"StartupEmbyServerIsLoading": "Serveri Jellyfin po ngarkohet. Ju lutemi provoni përseri pas pak.",
|
||||
"Songs": "Këngë",
|
||||
"Shows": "Seriale",
|
||||
"ServerNameNeedsToBeRestarted": "{0} duhet të ristartoj",
|
||||
"ScheduledTaskStartedWithName": "{0} filloi",
|
||||
"ScheduledTaskFailedWithName": "{0} dështoi",
|
||||
"ProviderValue": "Ofruesi: {0}",
|
||||
"PluginUpdatedWithName": "{0} u përditësua",
|
||||
"PluginUninstalledWithName": "{0} u çinstalua",
|
||||
"PluginInstalledWithName": "{0} u instalua",
|
||||
"Plugin": "Plugin",
|
||||
"Playlists": "Listat për luajtje",
|
||||
"Photos": "Fotografitë",
|
||||
"NotificationOptionVideoPlaybackStopped": "Luajtja e videos ndaloi",
|
||||
"NotificationOptionVideoPlayback": "Luajtja e videos filloi",
|
||||
"NotificationOptionUserLockedOut": "Përdoruesi u përjashtua",
|
||||
"NotificationOptionTaskFailed": "Ushtrimi i planifikuar dështoi",
|
||||
"NotificationOptionServerRestartRequired": "Kërkohet ristartim i serverit",
|
||||
"NotificationOptionPluginUpdateInstalled": "Përditësimi i plugin u instalua",
|
||||
"NotificationOptionPluginUninstalled": "Plugin u çinstalua",
|
||||
"NotificationOptionPluginInstalled": "Plugin u instalua",
|
||||
"NotificationOptionPluginError": "Plugin dështoi",
|
||||
"NotificationOptionNewLibraryContent": "Një përmbajtje e re u shtua",
|
||||
"NotificationOptionInstallationFailed": "Instalimi dështoi",
|
||||
"NotificationOptionCameraImageUploaded": "Fotoja nga kamera u ngarkua",
|
||||
"NotificationOptionAudioPlaybackStopped": "Luajtja e audios ndaloi",
|
||||
"NotificationOptionAudioPlayback": "Luajtja e audios filloi",
|
||||
"NotificationOptionApplicationUpdateInstalled": "Përditësimi i aplikacionit u instalua",
|
||||
"NotificationOptionApplicationUpdateAvailable": "Një perditësim i aplikacionit është gati",
|
||||
"NewVersionIsAvailable": "Një version i ri i Jellyfin është gati për tu shkarkuar.",
|
||||
"NameSeasonUnknown": "Sezon i panjohur",
|
||||
"NameSeasonNumber": "Sezoni {0}",
|
||||
"NameInstallFailed": "Instalimi i {0} dështoi",
|
||||
"MusicVideos": "Video muzikore",
|
||||
"Music": "Muzikë",
|
||||
"Movies": "Filma",
|
||||
"MixedContent": "Përmbajtje e përzier",
|
||||
"MessageServerConfigurationUpdated": "Konfigurimet e serverit u përditësuan",
|
||||
"MessageNamedServerConfigurationUpdatedWithValue": "Seksioni i konfigurimit të serverit {0} u përditësua",
|
||||
"MessageApplicationUpdated": "Serveri Jellyfin u përditësua",
|
||||
"Latest": "Të fundit",
|
||||
"LabelRunningTimeValue": "Kohëzgjatja: {0}",
|
||||
"LabelIpAddressValue": "Adresa IP: {0}",
|
||||
"ItemRemovedWithName": "{0} u fshi nga libraria",
|
||||
"ItemAddedWithName": "{0} u shtua tek libraria",
|
||||
"HomeVideos": "Video personale",
|
||||
"HeaderRecordingGroups": "Grupet e regjistrimit",
|
||||
"HeaderNextUp": "Në vazhdim",
|
||||
"HeaderLiveTV": "TV Live",
|
||||
"HeaderFavoriteSongs": "Kënget e preferuara",
|
||||
"HeaderFavoriteShows": "Serialet e preferuar",
|
||||
"HeaderFavoriteEpisodes": "Episodet e preferuar",
|
||||
"HeaderFavoriteArtists": "Artistët e preferuar",
|
||||
"HeaderFavoriteAlbums": "Albumet e preferuar",
|
||||
"HeaderContinueWatching": "Vazhdo të shikosh",
|
||||
"HeaderAlbumArtists": "Artistët e albumeve",
|
||||
"Genres": "Zhanre",
|
||||
"Folders": "Dosje",
|
||||
"Favorites": "Të preferuara",
|
||||
"FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}",
|
||||
"DeviceOnlineWithName": "{0} u lidh",
|
||||
"DeviceOfflineWithName": "{0} u shkëput",
|
||||
"Collections": "Koleksione",
|
||||
"ChapterNameValue": "Kapituj",
|
||||
"Channels": "Kanale",
|
||||
"CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}",
|
||||
"Books": "Libra",
|
||||
"AuthenticationSucceededWithUserName": "{0} u identifikua me sukses",
|
||||
"Artists": "Artistë",
|
||||
"Application": "Aplikacioni",
|
||||
"AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}",
|
||||
"Albums": "Albumet"
|
||||
}
|
|
@ -74,7 +74,6 @@
|
|||
"HeaderFavoriteArtists": "Омиљени извођачи",
|
||||
"HeaderFavoriteAlbums": "Омиљени албуми",
|
||||
"HeaderContinueWatching": "Настави гледање",
|
||||
"HeaderCameraUploads": "Слања са камере",
|
||||
"HeaderAlbumArtists": "Извођачи албума",
|
||||
"Genres": "Жанрови",
|
||||
"Folders": "Фасцикле",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Mappar",
|
||||
"Genres": "Genrer",
|
||||
"HeaderAlbumArtists": "Albumartister",
|
||||
"HeaderCameraUploads": "Kamerauppladdningar",
|
||||
"HeaderContinueWatching": "Fortsätt kolla",
|
||||
"HeaderFavoriteAlbums": "Favoritalbum",
|
||||
"HeaderFavoriteArtists": "Favoritartister",
|
||||
|
|
|
@ -20,13 +20,12 @@
|
|||
"MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது",
|
||||
"Inherit": "மரபுரிமையாகப் பெறு",
|
||||
"HeaderRecordingGroups": "பதிவு குழுக்கள்",
|
||||
"HeaderCameraUploads": "புகைப்பட பதிவேற்றங்கள்",
|
||||
"Folders": "கோப்புறைகள்",
|
||||
"FailedLoginAttemptWithUserName": "{0} இலிருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது",
|
||||
"DeviceOnlineWithName": "{0} இணைக்கப்பட்டது",
|
||||
"DeviceOfflineWithName": "{0} துண்டிக்கப்பட்டது",
|
||||
"Collections": "தொகுப்புகள்",
|
||||
"CameraImageUploadedFrom": "{0} இலிருந்து புதிய புகைப்படம் பதிவேற்றப்பட்டது",
|
||||
"CameraImageUploadedFrom": "{0} இல் இருந்து புதிய புகைப்படம் பதிவேற்றப்பட்டது",
|
||||
"AppDeviceValues": "செயலி: {0}, சாதனம்: {1}",
|
||||
"TaskDownloadMissingSubtitles": "விடுபட்டுபோன வசன வரிகளைப் பதிவிறக்கு",
|
||||
"TaskRefreshChannels": "சேனல்களை புதுப்பி",
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
"NotificationOptionCameraImageUploaded": "อัปโหลดภาพถ่ายแล้ว",
|
||||
"NotificationOptionAudioPlaybackStopped": "หยุดเล่นเสียง",
|
||||
"NotificationOptionAudioPlayback": "เริ่มเล่นเสียง",
|
||||
"NotificationOptionApplicationUpdateInstalled": "ติดตั้งการอัปเดตแอพพลิเคชันแล้ว",
|
||||
"NotificationOptionApplicationUpdateAvailable": "มีการอัปเดตแอพพลิเคชัน",
|
||||
"NotificationOptionApplicationUpdateInstalled": "ติดตั้งการอัปเดตแอปพลิเคชันแล้ว",
|
||||
"NotificationOptionApplicationUpdateAvailable": "มีการอัปเดตแอปพลิเคชัน",
|
||||
"NewVersionIsAvailable": "เวอร์ชันใหม่ของเซิร์ฟเวอร์ Jellyfin พร้อมให้ดาวน์โหลดแล้ว",
|
||||
"NameSeasonUnknown": "ไม่ทราบซีซัน",
|
||||
"NameSeasonNumber": "ซีซัน {0}",
|
||||
|
@ -50,7 +50,6 @@
|
|||
"HeaderFavoriteArtists": "ศิลปินที่ชื่นชอบ",
|
||||
"HeaderFavoriteAlbums": "อัมบั้มที่ชื่นชอบ",
|
||||
"HeaderContinueWatching": "ดูต่อ",
|
||||
"HeaderCameraUploads": "อัปโหลดรูปถ่าย",
|
||||
"HeaderAlbumArtists": "อัลบั้มศิลปิน",
|
||||
"Genres": "ประเภท",
|
||||
"Folders": "โฟลเดอร์",
|
||||
|
@ -65,8 +64,8 @@
|
|||
"Books": "หนังสือ",
|
||||
"AuthenticationSucceededWithUserName": "{0} ยืนยันตัวสำเร็จแล้ว",
|
||||
"Artists": "ศิลปิน",
|
||||
"Application": "แอพพลิเคชัน",
|
||||
"AppDeviceValues": "แอพ: {0}, อุปกรณ์: {1}",
|
||||
"Application": "แอปพลิเคชัน",
|
||||
"AppDeviceValues": "แอป: {0}, อุปกรณ์: {1}",
|
||||
"Albums": "อัลบั้ม",
|
||||
"ScheduledTaskStartedWithName": "{0} เริ่มต้น",
|
||||
"ScheduledTaskFailedWithName": "{0} ล้มเหลว",
|
||||
|
@ -92,7 +91,7 @@
|
|||
"TaskCleanCacheDescription": "ลบไฟล์แคชที่ระบบไม่ต้องการ",
|
||||
"TaskCleanCache": "ล้างไดเรกทอรีแคช",
|
||||
"TasksChannelsCategory": "ช่องอินเทอร์เน็ต",
|
||||
"TasksApplicationCategory": "แอพพลิเคชัน",
|
||||
"TasksApplicationCategory": "แอปพลิเคชัน",
|
||||
"TasksLibraryCategory": "ไลบรารี",
|
||||
"TasksMaintenanceCategory": "ปิดซ่อมบำรุง",
|
||||
"VersionNumber": "เวอร์ชัน {0}",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "Klasörler",
|
||||
"Genres": "Türler",
|
||||
"HeaderAlbumArtists": "Albüm Sanatçıları",
|
||||
"HeaderCameraUploads": "Kamera Yüklemeleri",
|
||||
"HeaderContinueWatching": "İzlemeye Devam Et",
|
||||
"HeaderFavoriteAlbums": "Favori Albümler",
|
||||
"HeaderFavoriteArtists": "Favori Sanatçılar",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"HeaderFavoriteArtists": "Улюблені виконавці",
|
||||
"HeaderFavoriteAlbums": "Улюблені альбоми",
|
||||
"HeaderContinueWatching": "Продовжити перегляд",
|
||||
"HeaderCameraUploads": "Завантажено з камери",
|
||||
"HeaderAlbumArtists": "Виконавці альбому",
|
||||
"Genres": "Жанри",
|
||||
"Folders": "Каталоги",
|
||||
|
|
|
@ -105,7 +105,6 @@
|
|||
"Inherit": "وراثت میں",
|
||||
"HomeVideos": "ہوم ویڈیو",
|
||||
"HeaderRecordingGroups": "ریکارڈنگ گروپس",
|
||||
"HeaderCameraUploads": "کیمرہ اپلوڈز",
|
||||
"FailedLoginAttemptWithUserName": "لاگن کئ کوشش ناکام {0}",
|
||||
"DeviceOnlineWithName": "{0} متصل ھو چکا ھے",
|
||||
"DeviceOfflineWithName": "{0} منقطع ھو چکا ھے",
|
||||
|
|
116
Emby.Server.Implementations/Localization/Core/vi.json
Normal file
116
Emby.Server.Implementations/Localization/Core/vi.json
Normal file
|
@ -0,0 +1,116 @@
|
|||
{
|
||||
"Collections": "Bộ Sưu Tập",
|
||||
"Favorites": "Yêu Thích",
|
||||
"Folders": "Thư Mục",
|
||||
"Genres": "Thể Loại",
|
||||
"HeaderAlbumArtists": "Bộ Sưu Tập Nghệ sĩ",
|
||||
"HeaderContinueWatching": "Xem Tiếp",
|
||||
"HeaderLiveTV": "TV Trực Tiếp",
|
||||
"Movies": "Phim",
|
||||
"Photos": "Ảnh",
|
||||
"Playlists": "Danh sách phát",
|
||||
"Shows": "Chương Trình TV",
|
||||
"Songs": "Các Bài Hát",
|
||||
"Sync": "Đồng Bộ",
|
||||
"ValueSpecialEpisodeName": "Đặc Biệt - {0}",
|
||||
"Albums": "Albums",
|
||||
"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ả.",
|
||||
"TaskDownloadMissingSubtitles": "Tải xuống phụ đề bị thiếu",
|
||||
"TaskRefreshChannelsDescription": "Làm mới thông tin kênh internet.",
|
||||
"TaskRefreshChannels": "Làm Mới Kênh",
|
||||
"TaskCleanTranscodeDescription": "Xóa các tệp chuyển mã cũ hơn một ngày.",
|
||||
"TaskCleanTranscode": "Làm Sạch Thư Mục Chuyển Mã",
|
||||
"TaskUpdatePluginsDescription": "Tải xuống và cài đặt các bản cập nhật cho các plugin được định cấu hình để cập nhật tự động.",
|
||||
"TaskUpdatePlugins": "Cập Nhật Plugins",
|
||||
"TaskRefreshPeopleDescription": "Cập nhật thông tin chi tiết cho diễn viên và đạo diễn trong thư viện phương tiện của bạn.",
|
||||
"TaskRefreshPeople": "Làm mới Người dùng",
|
||||
"TaskCleanLogsDescription": "Xóa tập tin nhật ký cũ hơn {0} ngày.",
|
||||
"TaskCleanLogs": "Làm sạch nhật ký",
|
||||
"TaskRefreshLibraryDescription": "Quét thư viện phương tiện của bạn để tìm các tệp mới và làm mới thông tin chi tiết.",
|
||||
"TaskRefreshLibrary": "Quét Thư viện Phương tiện",
|
||||
"TaskRefreshChapterImagesDescription": "Tạo hình thu nhỏ cho video có các phân cảnh.",
|
||||
"TaskRefreshChapterImages": "Trích Xuất Ảnh Phân Cảnh",
|
||||
"TaskCleanCacheDescription": "Xóa các tệp cache không còn cần thiết của hệ thống.",
|
||||
"TaskCleanCache": "Làm Sạch Thư Mục Cache",
|
||||
"TasksChannelsCategory": "Kênh Internet",
|
||||
"TasksApplicationCategory": "Ứng Dụng",
|
||||
"TasksLibraryCategory": "Thư Viện",
|
||||
"TasksMaintenanceCategory": "Bảo Trì",
|
||||
"VersionNumber": "Phiên Bản {0}",
|
||||
"ValueHasBeenAddedToLibrary": "{0} đã được thêm vào thư viện của bạn",
|
||||
"UserStoppedPlayingItemWithValues": "{0} đã phát xong {1} trên {2}",
|
||||
"UserStartedPlayingItemWithValues": "{0} đang phát {1} trên {2}",
|
||||
"UserPolicyUpdatedWithName": "Chính sách người dùng đã được cập nhật cho {0}",
|
||||
"UserPasswordChangedWithName": "Mật khẩu đã được thay đổi cho người dùng {0}",
|
||||
"UserOnlineFromDevice": "{0} trực tuyến từ {1}",
|
||||
"UserOfflineFromDevice": "{0} đã ngắt kết nối từ {1}",
|
||||
"UserLockedOutWithName": "User {0} đã bị khóa",
|
||||
"UserDownloadingItemWithValues": "{0} đang tải xuống {1}",
|
||||
"UserDeletedWithName": "Người Dùng {0} đã được xóa",
|
||||
"UserCreatedWithName": "Người Dùng {0} đã được tạo",
|
||||
"User": "Người Dùng",
|
||||
"TvShows": "Chương Trình TV",
|
||||
"System": "Hệ Thống",
|
||||
"SubtitleDownloadFailureFromForItem": "Không thể tải xuống phụ đề từ {0} cho {1}",
|
||||
"StartupEmbyServerIsLoading": "Jellyfin Server đang tải. Vui lòng thử lại trong thời gian ngắn.",
|
||||
"ServerNameNeedsToBeRestarted": "{0} cần được khởi động lại",
|
||||
"ScheduledTaskStartedWithName": "{0} đã bắt đầu",
|
||||
"ScheduledTaskFailedWithName": "{0} đã thất bại",
|
||||
"ProviderValue": "Provider: {0}",
|
||||
"PluginUpdatedWithName": "{0} đã cập nhật",
|
||||
"PluginUninstalledWithName": "{0} đã được gỡ bỏ",
|
||||
"PluginInstalledWithName": "{0} đã được cài đặt",
|
||||
"Plugin": "Plugin",
|
||||
"NotificationOptionVideoPlaybackStopped": "Phát lại video đã dừng",
|
||||
"NotificationOptionVideoPlayback": "Đã bắt đầu phát lại video",
|
||||
"NotificationOptionUserLockedOut": "Người dùng bị khóa",
|
||||
"NotificationOptionTaskFailed": "Lỗi tác vụ đã lên lịch",
|
||||
"NotificationOptionServerRestartRequired": "Yêu cầu khởi động lại Server",
|
||||
"NotificationOptionPluginUpdateInstalled": "Cập nhật Plugin đã được cài đặt",
|
||||
"NotificationOptionPluginUninstalled": "Đã gỡ bỏ Plugin",
|
||||
"NotificationOptionPluginInstalled": "Đã cài đặt Plugin",
|
||||
"NotificationOptionPluginError": "Thất bại Plugin",
|
||||
"NotificationOptionNewLibraryContent": "Nội dung mới được thêm vào",
|
||||
"NotificationOptionInstallationFailed": "Cài đặt thất bại",
|
||||
"NotificationOptionCameraImageUploaded": "Đã tải lên hình ảnh máy ảnh",
|
||||
"NotificationOptionAudioPlaybackStopped": "Phát lại âm thanh đã dừng",
|
||||
"NotificationOptionAudioPlayback": "Phát lại âm thanh đã bắt đầu",
|
||||
"NotificationOptionApplicationUpdateInstalled": "Bản cập nhật ứng dụng đã được cài đặt",
|
||||
"NotificationOptionApplicationUpdateAvailable": "Bản cập nhật ứng dụng hiện sẵn có",
|
||||
"NewVersionIsAvailable": "Một phiên bản mới của Jellyfin Server sẵn có để tải.",
|
||||
"NameSeasonUnknown": "Không Rõ Mùa",
|
||||
"NameSeasonNumber": "Mùa {0}",
|
||||
"NameInstallFailed": "{0} cài đặt thất bại",
|
||||
"MusicVideos": "Video Nhạc",
|
||||
"Music": "Nhạc",
|
||||
"MixedContent": "Nội dung hỗn hợp",
|
||||
"MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật",
|
||||
"MessageNamedServerConfigurationUpdatedWithValue": "Phần cấu hình máy chủ {0} đã được cập nhật",
|
||||
"MessageApplicationUpdatedTo": "Jellyfin Server đã được cập nhật lên {0}",
|
||||
"MessageApplicationUpdated": "Jellyfin Server đã được cập nhật",
|
||||
"Latest": "Gần Nhất",
|
||||
"LabelRunningTimeValue": "Thời Gian Chạy: {0}",
|
||||
"LabelIpAddressValue": "Địa Chỉ IP: {0}",
|
||||
"ItemRemovedWithName": "{0} đã xóa khỏi thư viện",
|
||||
"ItemAddedWithName": "{0} được thêm vào thư viện",
|
||||
"Inherit": "Thừa hưởng",
|
||||
"HomeVideos": "Video nhà",
|
||||
"HeaderRecordingGroups": "Nhóm Ghi Video",
|
||||
"HeaderNextUp": "Tiếp Theo",
|
||||
"HeaderFavoriteSongs": "Bài Hát Yêu Thích",
|
||||
"HeaderFavoriteShows": "Chương Trình Yêu Thích",
|
||||
"HeaderFavoriteEpisodes": "Tập Phim Yêu Thích",
|
||||
"HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích",
|
||||
"HeaderFavoriteAlbums": "Album Ưa Thích",
|
||||
"FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}",
|
||||
"DeviceOnlineWithName": "{0} đã kết nối",
|
||||
"DeviceOfflineWithName": "{0} đã ngắt kết nối",
|
||||
"ChapterNameValue": "Phân Cảnh {0}",
|
||||
"Channels": "Các Kênh",
|
||||
"CameraImageUploadedFrom": "Một hình ảnh máy ảnh mới đã được tải lên từ {0}",
|
||||
"Books": "Sách",
|
||||
"AuthenticationSucceededWithUserName": "{0} xác thực thành công",
|
||||
"Application": "Ứng Dụng",
|
||||
"AppDeviceValues": "Ứng Dụng: {0}, Thiết Bị: {1}"
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "文件夹",
|
||||
"Genres": "风格",
|
||||
"HeaderAlbumArtists": "专辑作家",
|
||||
"HeaderCameraUploads": "相机上传",
|
||||
"HeaderContinueWatching": "继续观影",
|
||||
"HeaderFavoriteAlbums": "收藏的专辑",
|
||||
"HeaderFavoriteArtists": "最爱的艺术家",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "檔案夾",
|
||||
"Genres": "風格",
|
||||
"HeaderAlbumArtists": "專輯藝人",
|
||||
"HeaderCameraUploads": "相機上載",
|
||||
"HeaderContinueWatching": "繼續觀看",
|
||||
"HeaderFavoriteAlbums": "最愛專輯",
|
||||
"HeaderFavoriteArtists": "最愛的藝人",
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
"Folders": "資料夾",
|
||||
"Genres": "風格",
|
||||
"HeaderAlbumArtists": "專輯演出者",
|
||||
"HeaderCameraUploads": "相機上傳",
|
||||
"HeaderContinueWatching": "繼續觀賞",
|
||||
"HeaderFavoriteAlbums": "最愛專輯",
|
||||
"HeaderFavoriteArtists": "最愛演出者",
|
||||
|
@ -96,7 +95,7 @@
|
|||
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
|
||||
"TaskRefreshChannels": "重新整理頻道",
|
||||
"TaskUpdatePlugins": "更新外掛",
|
||||
"TaskRefreshPeople": "重新整理人員",
|
||||
"TaskRefreshPeople": "刷新用戶",
|
||||
"TaskCleanLogsDescription": "刪除超過 {0} 天的舊紀錄檔。",
|
||||
"TaskCleanLogs": "清空紀錄資料夾",
|
||||
"TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新描述資料。",
|
||||
|
|
|
@ -413,6 +413,7 @@ namespace Emby.Server.Implementations.Localization
|
|||
yield return new LocalizationOption("Swedish", "sv");
|
||||
yield return new LocalizationOption("Swiss German", "gsw");
|
||||
yield return new LocalizationOption("Turkish", "tr");
|
||||
yield return new LocalizationOption("Tiếng Việt", "vi");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
60
Emby.Server.Implementations/Plugins/PluginManifest.cs
Normal file
60
Emby.Server.Implementations/Plugins/PluginManifest.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
|
||||
namespace Emby.Server.Implementations.Plugins
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines a Plugin manifest file.
|
||||
/// </summary>
|
||||
public class PluginManifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the category of the plugin.
|
||||
/// </summary>
|
||||
public string Category { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the changelog information.
|
||||
/// </summary>
|
||||
public string Changelog { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the description of the plugin.
|
||||
/// </summary>
|
||||
public string Description { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Global Unique Identifier for the plugin.
|
||||
/// </summary>
|
||||
public Guid Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Name of the plugin.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an overview of the plugin.
|
||||
/// </summary>
|
||||
public string Overview { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the owner of the plugin.
|
||||
/// </summary>
|
||||
public string Owner { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the compatibility version for the plugin.
|
||||
/// </summary>
|
||||
public string TargetAbi { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the timestamp of the plugin.
|
||||
/// </summary>
|
||||
public DateTime Timestamp { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the Version number of the plugin.
|
||||
/// </summary>
|
||||
public string Version { get; set; }
|
||||
}
|
||||
}
|
|
@ -148,7 +148,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
|
|||
public bool IsHidden => false;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsEnabled => false;
|
||||
public bool IsEnabled => true;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool IsLogged => true;
|
||||
|
|
|
@ -666,7 +666,7 @@ namespace Emby.Server.Implementations.Session
|
|||
}
|
||||
}
|
||||
|
||||
var eventArgs = new PlaybackProgressEventArgs
|
||||
var eventArgs = new PlaybackStartEventArgs
|
||||
{
|
||||
Item = libraryItem,
|
||||
Users = users,
|
||||
|
@ -1037,7 +1037,7 @@ namespace Emby.Server.Implementations.Session
|
|||
|
||||
var generalCommand = new GeneralCommand
|
||||
{
|
||||
Name = GeneralCommandType.DisplayMessage.ToString()
|
||||
Name = GeneralCommandType.DisplayMessage
|
||||
};
|
||||
|
||||
generalCommand.Arguments["Header"] = command.Header;
|
||||
|
@ -1064,10 +1064,10 @@ namespace Emby.Server.Implementations.Session
|
|||
AssertCanControl(session, controllingSession);
|
||||
}
|
||||
|
||||
return SendMessageToSession(session, "GeneralCommand", command, cancellationToken);
|
||||
return SendMessageToSession(session, SessionMessageType.GeneralCommand, command, cancellationToken);
|
||||
}
|
||||
|
||||
private static async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
|
||||
private static async Task SendMessageToSession<T>(SessionInfo session, SessionMessageType name, T data, CancellationToken cancellationToken)
|
||||
{
|
||||
var controllers = session.SessionControllers;
|
||||
var messageId = Guid.NewGuid();
|
||||
|
@ -1078,7 +1078,7 @@ namespace Emby.Server.Implementations.Session
|
|||
}
|
||||
}
|
||||
|
||||
private static Task SendMessageToSessions<T>(IEnumerable<SessionInfo> sessions, string name, T data, CancellationToken cancellationToken)
|
||||
private static Task SendMessageToSessions<T>(IEnumerable<SessionInfo> sessions, SessionMessageType name, T data, CancellationToken cancellationToken)
|
||||
{
|
||||
IEnumerable<Task> GetTasks()
|
||||
{
|
||||
|
@ -1178,7 +1178,7 @@ namespace Emby.Server.Implementations.Session
|
|||
}
|
||||
}
|
||||
|
||||
await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false);
|
||||
await SendMessageToSession(session, SessionMessageType.Play, command, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -1186,7 +1186,7 @@ namespace Emby.Server.Implementations.Session
|
|||
{
|
||||
CheckDisposed();
|
||||
var session = GetSessionToRemoteControl(sessionId);
|
||||
await SendMessageToSession(session, "SyncPlayCommand", command, cancellationToken).ConfigureAwait(false);
|
||||
await SendMessageToSession(session, SessionMessageType.SyncPlayCommand, command, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
|
@ -1194,7 +1194,7 @@ namespace Emby.Server.Implementations.Session
|
|||
{
|
||||
CheckDisposed();
|
||||
var session = GetSessionToRemoteControl(sessionId);
|
||||
await SendMessageToSession(session, "SyncPlayGroupUpdate", command, cancellationToken).ConfigureAwait(false);
|
||||
await SendMessageToSession(session, SessionMessageType.SyncPlayGroupUpdate, command, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, User user)
|
||||
|
@ -1268,7 +1268,7 @@ namespace Emby.Server.Implementations.Session
|
|||
{
|
||||
var generalCommand = new GeneralCommand
|
||||
{
|
||||
Name = GeneralCommandType.DisplayContent.ToString(),
|
||||
Name = GeneralCommandType.DisplayContent,
|
||||
Arguments =
|
||||
{
|
||||
["ItemId"] = command.ItemId,
|
||||
|
@ -1297,7 +1297,7 @@ namespace Emby.Server.Implementations.Session
|
|||
}
|
||||
}
|
||||
|
||||
return SendMessageToSession(session, "Playstate", command, cancellationToken);
|
||||
return SendMessageToSession(session, SessionMessageType.PlayState, command, cancellationToken);
|
||||
}
|
||||
|
||||
private static void AssertCanControl(SessionInfo session, SessionInfo controllingSession)
|
||||
|
@ -1322,7 +1322,7 @@ namespace Emby.Server.Implementations.Session
|
|||
{
|
||||
CheckDisposed();
|
||||
|
||||
return SendMessageToSessions(Sessions, "RestartRequired", string.Empty, cancellationToken);
|
||||
return SendMessageToSessions(Sessions, SessionMessageType.RestartRequired, string.Empty, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1334,7 +1334,7 @@ namespace Emby.Server.Implementations.Session
|
|||
{
|
||||
CheckDisposed();
|
||||
|
||||
return SendMessageToSessions(Sessions, "ServerShuttingDown", string.Empty, cancellationToken);
|
||||
return SendMessageToSessions(Sessions, SessionMessageType.ServerShuttingDown, string.Empty, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1348,7 +1348,7 @@ namespace Emby.Server.Implementations.Session
|
|||
|
||||
_logger.LogDebug("Beginning SendServerRestartNotification");
|
||||
|
||||
return SendMessageToSessions(Sessions, "ServerRestarting", string.Empty, cancellationToken);
|
||||
return SendMessageToSessions(Sessions, SessionMessageType.ServerRestarting, string.Empty, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1484,6 +1484,14 @@ namespace Emby.Server.Implementations.Session
|
|||
throw new SecurityException("User is not allowed access from this device.");
|
||||
}
|
||||
|
||||
int sessionsCount = Sessions.Count(i => i.UserId.Equals(user.Id));
|
||||
int maxActiveSessions = user.MaxActiveSessions;
|
||||
_logger.LogInformation("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions);
|
||||
if (maxActiveSessions >= 1 && sessionsCount >= maxActiveSessions)
|
||||
{
|
||||
throw new SecurityException("User is at their maximum number of sessions.");
|
||||
}
|
||||
|
||||
var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName);
|
||||
|
||||
var session = LogSessionActivity(
|
||||
|
@ -1866,7 +1874,7 @@ namespace Emby.Server.Implementations.Session
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken)
|
||||
public Task SendMessageToAdminSessions<T>(SessionMessageType name, T data, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
|
@ -1879,7 +1887,7 @@ namespace Emby.Server.Implementations.Session
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, Func<T> dataFn, CancellationToken cancellationToken)
|
||||
public Task SendMessageToUserSessions<T>(List<Guid> userIds, SessionMessageType name, Func<T> dataFn, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
|
@ -1894,7 +1902,7 @@ namespace Emby.Server.Implementations.Session
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken)
|
||||
public Task SendMessageToUserSessions<T>(List<Guid> userIds, SessionMessageType name, T data, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
|
@ -1903,7 +1911,7 @@ namespace Emby.Server.Implementations.Session
|
|||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
|
||||
public Task SendMessageToUserDeviceSessions<T>(string deviceId, SessionMessageType name, T data, CancellationToken cancellationToken)
|
||||
{
|
||||
CheckDisposed();
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ using Jellyfin.Data.Events;
|
|||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Controller.Session;
|
||||
using MediaBrowser.Model.Net;
|
||||
using MediaBrowser.Model.Session;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
|
@ -316,7 +317,7 @@ namespace Emby.Server.Implementations.Session
|
|||
return webSocket.SendAsync(
|
||||
new WebSocketMessage<int>
|
||||
{
|
||||
MessageType = "ForceKeepAlive",
|
||||
MessageType = SessionMessageType.ForceKeepAlive,
|
||||
Data = WebSocketLostTimeout
|
||||
},
|
||||
CancellationToken.None);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user