Merge pull request #82 from jellyfin/master

Refreshing image
This commit is contained in:
BaronGreenback 2020-10-17 09:54:09 +01:00 committed by GitHub
commit 3ae37deea8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
348 changed files with 4053 additions and 7246 deletions

View 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

View File

@ -63,7 +63,38 @@ jobs:
sshEndpoint: repository sshEndpoint: repository
sourceFolder: '$(Build.SourcesDirectory)/deployment/dist' sourceFolder: '$(Build.SourcesDirectory)/deployment/dist'
contents: '**' 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 - job: BuildDocker
displayName: 'Build Docker' displayName: 'Build Docker'

View File

@ -56,7 +56,7 @@ jobs:
inputs: inputs:
command: "test" command: "test"
projects: ${{ parameters.TestProjects }} 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 publishTestResults: true
testRunTitle: $(Agent.JobName) testRunTitle: $(Agent.JobName)
workingDirectory: "$(Build.SourcesDirectory)" workingDirectory: "$(Build.SourcesDirectory)"

View File

@ -35,6 +35,12 @@ jobs:
Windows: 'windows-latest' Windows: 'windows-latest'
macOS: 'macos-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'))) }}: - ${{ if not(or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))) }}:
- template: azure-pipelines-abi.yml - template: azure-pipelines-abi.yml
parameters: parameters:
@ -55,3 +61,6 @@ jobs:
- ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}: - ${{ if or(startsWith(variables['Build.SourceBranch'], 'refs/tags/v'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master')) }}:
- template: azure-pipelines-package.yml - 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
View File

@ -276,3 +276,4 @@ BenchmarkDotNet.Artifacts
web/ web/
web-src.* web-src.*
MediaBrowser.WebDashboard/jellyfin-web MediaBrowser.WebDashboard/jellyfin-web
apiclient/generated

6
.vscode/launch.json vendored
View File

@ -11,7 +11,11 @@
"cwd": "${workspaceFolder}/Jellyfin.Server", "cwd": "${workspaceFolder}/Jellyfin.Server",
"console": "internalConsole", "console": "internalConsole",
"stopAtEntry": false, "stopAtEntry": false,
"internalConsoleOptions": "openOnSessionStart" "internalConsoleOptions": "openOnSessionStart",
"serverReadyAction": {
"action": "openExternally",
"pattern": "Overriding address\\(es\\) \\'(https?:\\S+)\\'",
}
}, },
{ {
"name": ".NET Core Launch (nowebclient)", "name": ".NET Core Launch (nowebclient)",

View File

@ -103,6 +103,7 @@
- [sl1288](https://github.com/sl1288) - [sl1288](https://github.com/sl1288)
- [sorinyo2004](https://github.com/sorinyo2004) - [sorinyo2004](https://github.com/sorinyo2004)
- [sparky8251](https://github.com/sparky8251) - [sparky8251](https://github.com/sparky8251)
- [spookbits](https://github.com/spookbits)
- [stanionascu](https://github.com/stanionascu) - [stanionascu](https://github.com/stanionascu)
- [stevehayles](https://github.com/stevehayles) - [stevehayles](https://github.com/stevehayles)
- [SuperSandro2000](https://github.com/SuperSandro2000) - [SuperSandro2000](https://github.com/SuperSandro2000)
@ -136,6 +137,7 @@
- [KristupasSavickas](https://github.com/KristupasSavickas) - [KristupasSavickas](https://github.com/KristupasSavickas)
- [Pusta](https://github.com/pusta) - [Pusta](https://github.com/pusta)
- [nielsvanvelzen](https://github.com/nielsvanvelzen) - [nielsvanvelzen](https://github.com/nielsvanvelzen)
- [skyfrk](https://github.com/skyfrk)
# Emby Contributors # Emby Contributors

View File

@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Xml; using System.Xml;
@ -10,8 +8,16 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.MediaReceiverRegistrar namespace Emby.Dlna.MediaReceiverRegistrar
{ {
/// <summary>
/// Defines the <see cref="ControlHandler" />.
/// </summary>
public class ControlHandler : BaseControlHandler public class ControlHandler : BaseControlHandler
{ {
/// <summary>
/// Initializes a new instance of the <see cref="ControlHandler"/> class.
/// </summary>
/// <param name="config">The <see cref="IServerConfigurationManager"/> for use with the <see cref="ControlHandler"/> instance.</param>
/// <param name="logger">The <see cref="ILogger"/> for use with the <see cref="ControlHandler"/> instance.</param>
public ControlHandler(IServerConfigurationManager config, ILogger logger) public ControlHandler(IServerConfigurationManager config, ILogger logger)
: base(config, logger) : base(config, logger)
{ {
@ -35,9 +41,17 @@ namespace Emby.Dlna.MediaReceiverRegistrar
throw new ResourceNotFoundException("Unexpected control request name: " + methodName); throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
} }
/// <summary>
/// Records that the handle is authorized in the xml stream.
/// </summary>
/// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
private static void HandleIsAuthorized(XmlWriter xmlWriter) private static void HandleIsAuthorized(XmlWriter xmlWriter)
=> xmlWriter.WriteElementString("Result", "1"); => xmlWriter.WriteElementString("Result", "1");
/// <summary>
/// Records that the handle is validated in the xml stream.
/// </summary>
/// <param name="xmlWriter">The <see cref="XmlWriter"/>.</param>
private static void HandleIsValidated(XmlWriter xmlWriter) private static void HandleIsValidated(XmlWriter xmlWriter)
=> xmlWriter.WriteElementString("Result", "1"); => xmlWriter.WriteElementString("Result", "1");
} }

View File

@ -1,5 +1,3 @@
#pragma warning disable CS1591
using System.Net.Http; using System.Net.Http;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Dlna.Service; using Emby.Dlna.Service;
@ -8,10 +6,19 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.MediaReceiverRegistrar namespace Emby.Dlna.MediaReceiverRegistrar
{ {
/// <summary>
/// Defines the <see cref="MediaReceiverRegistrarService" />.
/// </summary>
public class MediaReceiverRegistrarService : BaseService, IMediaReceiverRegistrar public class MediaReceiverRegistrarService : BaseService, IMediaReceiverRegistrar
{ {
private readonly IServerConfigurationManager _config; private readonly IServerConfigurationManager _config;
/// <summary>
/// Initializes a new instance of the <see cref="MediaReceiverRegistrarService"/> class.
/// </summary>
/// <param name="logger">The <see cref="ILogger{MediaReceiverRegistrarService}"/> for use with the <see cref="MediaReceiverRegistrarService"/> instance.</param>
/// <param name="httpClientFactory">The <see cref="IHttpClientFactory"/> for use with the <see cref="MediaReceiverRegistrarService"/> instance.</param>
/// <param name="config">The <see cref="IServerConfigurationManager"/> for use with the <see cref="MediaReceiverRegistrarService"/> instance.</param>
public MediaReceiverRegistrarService( public MediaReceiverRegistrarService(
ILogger<MediaReceiverRegistrarService> logger, ILogger<MediaReceiverRegistrarService> logger,
IHttpClientFactory httpClientFactory, IHttpClientFactory httpClientFactory,
@ -24,7 +31,7 @@ namespace Emby.Dlna.MediaReceiverRegistrar
/// <inheritdoc /> /// <inheritdoc />
public string GetServiceXml() public string GetServiceXml()
{ {
return new MediaReceiverRegistrarXmlBuilder().GetXml(); return MediaReceiverRegistrarXmlBuilder.GetXml();
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -1,79 +1,89 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using Emby.Dlna.Service; using Emby.Dlna.Service;
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.MediaReceiverRegistrar namespace Emby.Dlna.MediaReceiverRegistrar
{ {
public class MediaReceiverRegistrarXmlBuilder /// <summary>
/// Defines the <see cref="MediaReceiverRegistrarXmlBuilder" />.
/// See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-drmnd/5d37515e-7a63-4709-8258-8fd4e0ed4482.
/// </summary>
public static class MediaReceiverRegistrarXmlBuilder
{ {
public string GetXml() /// <summary>
/// Retrieves an XML description of the X_MS_MediaReceiverRegistrar.
/// </summary>
/// <returns>An XML representation of this service.</returns>
public static string GetXml()
{ {
return new ServiceXmlBuilder().GetXml( return new ServiceXmlBuilder().GetXml(ServiceActionListBuilder.GetActions(), GetStateVariables());
new ServiceActionListBuilder().GetActions(),
GetStateVariables());
} }
/// <summary>
/// The a list of all the state variables for this invocation.
/// </summary>
/// <returns>The <see cref="IEnumerable{StateVariable}"/>.</returns>
private static IEnumerable<StateVariable> GetStateVariables() private static IEnumerable<StateVariable> GetStateVariables()
{ {
var list = new List<StateVariable>(); var list = new List<StateVariable>
{
list.Add(new StateVariable new StateVariable
{ {
Name = "AuthorizationGrantedUpdateID", Name = "AuthorizationGrantedUpdateID",
DataType = "ui4", DataType = "ui4",
SendsEvents = true SendsEvents = true
}); },
list.Add(new StateVariable new StateVariable
{ {
Name = "A_ARG_TYPE_DeviceID", Name = "A_ARG_TYPE_DeviceID",
DataType = "string", DataType = "string",
SendsEvents = false SendsEvents = false
}); },
list.Add(new StateVariable new StateVariable
{ {
Name = "AuthorizationDeniedUpdateID", Name = "AuthorizationDeniedUpdateID",
DataType = "ui4", DataType = "ui4",
SendsEvents = true SendsEvents = true
}); },
list.Add(new StateVariable new StateVariable
{ {
Name = "ValidationSucceededUpdateID", Name = "ValidationSucceededUpdateID",
DataType = "ui4", DataType = "ui4",
SendsEvents = true SendsEvents = true
}); },
list.Add(new StateVariable new StateVariable
{ {
Name = "A_ARG_TYPE_RegistrationRespMsg", Name = "A_ARG_TYPE_RegistrationRespMsg",
DataType = "bin.base64", DataType = "bin.base64",
SendsEvents = false SendsEvents = false
}); },
list.Add(new StateVariable new StateVariable
{ {
Name = "A_ARG_TYPE_RegistrationReqMsg", Name = "A_ARG_TYPE_RegistrationReqMsg",
DataType = "bin.base64", DataType = "bin.base64",
SendsEvents = false SendsEvents = false
}); },
list.Add(new StateVariable new StateVariable
{ {
Name = "ValidationRevokedUpdateID", Name = "ValidationRevokedUpdateID",
DataType = "ui4", DataType = "ui4",
SendsEvents = true SendsEvents = true
}); },
list.Add(new StateVariable new StateVariable
{ {
Name = "A_ARG_TYPE_Result", Name = "A_ARG_TYPE_Result",
DataType = "int", DataType = "int",
SendsEvents = false SendsEvents = false
}); }
};
return list; return list;
} }

View File

@ -1,13 +1,19 @@
#pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
using Emby.Dlna.Common; using Emby.Dlna.Common;
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.MediaReceiverRegistrar namespace Emby.Dlna.MediaReceiverRegistrar
{ {
public class ServiceActionListBuilder /// <summary>
/// Defines the <see cref="ServiceActionListBuilder" />.
/// </summary>
public static class ServiceActionListBuilder
{ {
public IEnumerable<ServiceAction> GetActions() /// <summary>
/// Returns a list of services that this instance provides.
/// </summary>
/// <returns>An <see cref="IEnumerable{ServiceAction}"/>.</returns>
public static IEnumerable<ServiceAction> GetActions()
{ {
return new[] return new[]
{ {
@ -21,6 +27,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar
}; };
} }
/// <summary>
/// Returns the action details for "IsValidated".
/// </summary>
/// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetIsValidated() private static ServiceAction GetIsValidated()
{ {
var action = new ServiceAction var action = new ServiceAction
@ -43,6 +53,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action; return action;
} }
/// <summary>
/// Returns the action details for "IsAuthorized".
/// </summary>
/// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetIsAuthorized() private static ServiceAction GetIsAuthorized()
{ {
var action = new ServiceAction var action = new ServiceAction
@ -65,6 +79,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action; return action;
} }
/// <summary>
/// Returns the action details for "RegisterDevice".
/// </summary>
/// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetRegisterDevice() private static ServiceAction GetRegisterDevice()
{ {
var action = new ServiceAction var action = new ServiceAction
@ -87,6 +105,10 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action; return action;
} }
/// <summary>
/// Returns the action details for "GetValidationSucceededUpdateID".
/// </summary>
/// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetGetValidationSucceededUpdateID() private static ServiceAction GetGetValidationSucceededUpdateID()
{ {
var action = new ServiceAction var action = new ServiceAction
@ -103,7 +125,11 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action; return action;
} }
private ServiceAction GetGetAuthorizationDeniedUpdateID() /// <summary>
/// Returns the action details for "GetGetAuthorizationDeniedUpdateID".
/// </summary>
/// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetGetAuthorizationDeniedUpdateID()
{ {
var action = new ServiceAction var action = new ServiceAction
{ {
@ -119,7 +145,11 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action; return action;
} }
private ServiceAction GetGetValidationRevokedUpdateID() /// <summary>
/// Returns the action details for "GetValidationRevokedUpdateID".
/// </summary>
/// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetGetValidationRevokedUpdateID()
{ {
var action = new ServiceAction var action = new ServiceAction
{ {
@ -135,7 +165,11 @@ namespace Emby.Dlna.MediaReceiverRegistrar
return action; return action;
} }
private ServiceAction GetGetAuthorizationGrantedUpdateID() /// <summary>
/// Returns the action details for "GetAuthorizationGrantedUpdateID".
/// </summary>
/// <returns>The <see cref="ServiceAction"/>.</returns>
private static ServiceAction GetGetAuthorizationGrantedUpdateID()
{ {
var action = new ServiceAction var action = new ServiceAction
{ {

View File

@ -811,7 +811,7 @@ namespace Emby.Dlna.PlayTo
} }
/// <inheritdoc /> /// <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) if (_disposed)
{ {
@ -823,17 +823,17 @@ namespace Emby.Dlna.PlayTo
return Task.CompletedTask; return Task.CompletedTask;
} }
if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase)) if (name == SessionMessageType.Play)
{ {
return SendPlayCommand(data as PlayRequest, cancellationToken); return SendPlayCommand(data as PlayRequest, cancellationToken);
} }
if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase)) if (name == SessionMessageType.PlayState)
{ {
return SendPlaystateCommand(data as PlaystateRequest, cancellationToken); return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
} }
if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase)) if (name == SessionMessageType.GeneralCommand)
{ {
return SendGeneralCommand(data as GeneralCommand, cancellationToken); return SendGeneralCommand(data as GeneralCommand, cancellationToken);
} }
@ -881,7 +881,10 @@ namespace Emby.Dlna.PlayTo
return null; return null;
} }
if (_mediaSourceManager != null)
{
mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false); mediaSource = await _mediaSourceManager.GetMediaSource(Item, MediaSourceId, LiveStreamId, false, cancellationToken).ConfigureAwait(false);
}
return mediaSource; return mediaSource;
} }

View File

@ -217,15 +217,15 @@ namespace Emby.Dlna.PlayTo
SupportedCommands = new[] SupportedCommands = new[]
{ {
GeneralCommandType.VolumeDown.ToString(), GeneralCommandType.VolumeDown,
GeneralCommandType.VolumeUp.ToString(), GeneralCommandType.VolumeUp,
GeneralCommandType.Mute.ToString(), GeneralCommandType.Mute,
GeneralCommandType.Unmute.ToString(), GeneralCommandType.Unmute,
GeneralCommandType.ToggleMute.ToString(), GeneralCommandType.ToggleMute,
GeneralCommandType.SetVolume.ToString(), GeneralCommandType.SetVolume,
GeneralCommandType.SetAudioStreamIndex.ToString(), GeneralCommandType.SetAudioStreamIndex,
GeneralCommandType.SetSubtitleStreamIndex.ToString(), GeneralCommandType.SetSubtitleStreamIndex,
GeneralCommandType.PlayMediaSource.ToString() GeneralCommandType.PlayMediaSource
}, },
SupportsMediaControl = true SupportsMediaControl = true

View File

@ -60,11 +60,9 @@ namespace Emby.Dlna.Service
Async = true Async = true
}; };
using (var reader = XmlReader.Create(streamReader, readerSettings)) using var reader = XmlReader.Create(streamReader, readerSettings);
{
requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false); requestInfo = await ParseRequestAsync(reader).ConfigureAwait(false);
} }
}
Logger.LogDebug("Received control request {0}", requestInfo.LocalName); Logger.LogDebug("Received control request {0}", requestInfo.LocalName);
@ -124,11 +122,9 @@ namespace Emby.Dlna.Service
{ {
if (!reader.IsEmptyElement) if (!reader.IsEmptyElement)
{ {
using (var subReader = reader.ReadSubtree()) using var subReader = reader.ReadSubtree();
{
return await ParseBodyTagAsync(subReader).ConfigureAwait(false); return await ParseBodyTagAsync(subReader).ConfigureAwait(false);
} }
}
else else
{ {
await reader.ReadAsync().ConfigureAwait(false); await reader.ReadAsync().ConfigureAwait(false);
@ -150,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) private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
{ {
var result = new ControlRequestInfo(); string namespaceURI = null, localName = null;
await reader.MoveToContentAsync().ConfigureAwait(false); await reader.MoveToContentAsync().ConfigureAwait(false);
await reader.ReadAsync().ConfigureAwait(false); await reader.ReadAsync().ConfigureAwait(false);
@ -165,16 +161,14 @@ namespace Emby.Dlna.Service
{ {
if (reader.NodeType == XmlNodeType.Element) if (reader.NodeType == XmlNodeType.Element)
{ {
result.LocalName = reader.LocalName; localName = reader.LocalName;
result.NamespaceURI = reader.NamespaceURI; namespaceURI = reader.NamespaceURI;
if (!reader.IsEmptyElement) if (!reader.IsEmptyElement)
{ {
using (var subReader = reader.ReadSubtree()) var result = new ControlRequestInfo(localName, namespaceURI);
{ using var subReader = reader.ReadSubtree();
await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false); await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
return result;
}
} }
else else
{ {
@ -187,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) private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
@ -234,11 +233,18 @@ namespace Emby.Dlna.Service
private class ControlRequestInfo 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 LocalName { get; set; }
public string NamespaceURI { 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; }
} }
} }
} }

View File

@ -36,7 +36,7 @@ namespace Emby.Drawing
private readonly IImageEncoder _imageEncoder; private readonly IImageEncoder _imageEncoder;
private readonly IMediaEncoder _mediaEncoder; private readonly IMediaEncoder _mediaEncoder;
private bool _disposed = false; private bool _disposed;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageProcessor"/> class. /// Initializes a new instance of the <see cref="ImageProcessor"/> class.
@ -466,11 +466,11 @@ namespace Emby.Drawing
} }
/// <inheritdoc /> /// <inheritdoc />
public void CreateImageCollage(ImageCollageOptions options) public void CreateImageCollage(ImageCollageOptions options, string? libraryName)
{ {
_logger.LogInformation("Creating image collage and saving to {Path}", options.OutputPath); _logger.LogInformation("Creating image collage and saving to {Path}", options.OutputPath);
_imageEncoder.CreateImageCollage(options); _imageEncoder.CreateImageCollage(options, libraryName);
_logger.LogInformation("Completed creation of image collage and saved to {Path}", options.OutputPath); _logger.LogInformation("Completed creation of image collage and saved to {Path}", options.OutputPath);
} }

View File

@ -38,7 +38,7 @@ namespace Emby.Drawing
} }
/// <inheritdoc /> /// <inheritdoc />
public void CreateImageCollage(ImageCollageOptions options) public void CreateImageCollage(ImageCollageOptions options, string? libraryName)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -1,3 +1,4 @@
#nullable enable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
@ -16,21 +17,11 @@ namespace Emby.Naming.AudioBook
_options = options; _options = options;
} }
public AudioBookFileInfo ParseFile(string path) public AudioBookFileInfo? Resolve(string path, bool isDirectory = false)
{ {
return Resolve(path, false); if (path.Length == 0)
}
public AudioBookFileInfo ParseDirectory(string path)
{ {
return Resolve(path, true); throw new ArgumentException("String can't be empty.", nameof(path));
}
public AudioBookFileInfo Resolve(string path, bool isDirectory = false)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
} }
// TODO // TODO

View File

@ -97,6 +97,7 @@ using MediaBrowser.Model.Tasks;
using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Chapters;
using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Plugins.TheTvdb; using MediaBrowser.Providers.Plugins.TheTvdb;
using MediaBrowser.Providers.Plugins.Tmdb;
using MediaBrowser.Providers.Subtitles; using MediaBrowser.Providers.Subtitles;
using MediaBrowser.XbmcMetadata.Providers; using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
@ -127,7 +128,6 @@ namespace Emby.Server.Implementations
private IMediaEncoder _mediaEncoder; private IMediaEncoder _mediaEncoder;
private ISessionManager _sessionManager; private ISessionManager _sessionManager;
private IHttpClientFactory _httpClientFactory; private IHttpClientFactory _httpClientFactory;
private IWebSocketManager _webSocketManager;
private string[] _urlPrefixes; private string[] _urlPrefixes;
@ -537,6 +537,7 @@ namespace Emby.Server.Implementations
ServiceCollection.AddSingleton(_fileSystemManager); ServiceCollection.AddSingleton(_fileSystemManager);
ServiceCollection.AddSingleton<TvdbClientManager>(); ServiceCollection.AddSingleton<TvdbClientManager>();
ServiceCollection.AddSingleton<TmdbClientManager>();
ServiceCollection.AddSingleton(_networkManager); ServiceCollection.AddSingleton(_networkManager);
@ -665,7 +666,6 @@ namespace Emby.Server.Implementations
_mediaEncoder = Resolve<IMediaEncoder>(); _mediaEncoder = Resolve<IMediaEncoder>();
_sessionManager = Resolve<ISessionManager>(); _sessionManager = Resolve<ISessionManager>();
_httpClientFactory = Resolve<IHttpClientFactory>(); _httpClientFactory = Resolve<IHttpClientFactory>();
_webSocketManager = Resolve<IWebSocketManager>();
((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize(); ((AuthenticationRepository)Resolve<IAuthenticationRepository>()).Initialize();
@ -786,7 +786,6 @@ namespace Emby.Server.Implementations
.ToArray(); .ToArray();
_urlPrefixes = GetUrlPrefixes().ToArray(); _urlPrefixes = GetUrlPrefixes().ToArray();
_webSocketManager.Init(GetExports<IWebSocketListener>());
Resolve<ILibraryManager>().AddParts( Resolve<ILibraryManager>().AddParts(
GetExports<IResolverIgnoreRule>(), GetExports<IResolverIgnoreRule>(),
@ -819,38 +818,6 @@ namespace Emby.Server.Implementations
{ {
try try
{ {
if (plugin is IPluginAssembly assemblyPlugin)
{
var assembly = plugin.GetType().Assembly;
var assemblyName = assembly.GetName();
var assemblyFilePath = assembly.Location;
var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
assemblyPlugin.SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);
try
{
var idAttributes = assembly.GetCustomAttributes(typeof(GuidAttribute), true);
if (idAttributes.Length > 0)
{
var attribute = (GuidAttribute)idAttributes[0];
var assemblyId = new Guid(attribute.Value);
assemblyPlugin.SetId(assemblyId);
}
}
catch (Exception ex)
{
Logger.LogError(ex, "Error getting plugin Id from {PluginName}.", plugin.GetType().FullName);
}
}
if (plugin is IHasPluginConfiguration hasPluginConfiguration)
{
hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
}
plugin.RegisterServices(ServiceCollection); plugin.RegisterServices(ServiceCollection);
} }
catch (Exception ex) catch (Exception ex)

View File

@ -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);
}
}
}
}

View File

@ -157,7 +157,8 @@ namespace Emby.Server.Implementations.Data
protected bool TableExists(ManagedConnection connection, string name) protected bool TableExists(ManagedConnection connection, string name)
{ {
return connection.RunInTransaction(db => return connection.RunInTransaction(
db =>
{ {
using (var statement = PrepareStatement(db, "select DISTINCT tbl_name from sqlite_master")) using (var statement = PrepareStatement(db, "select DISTINCT tbl_name from sqlite_master"))
{ {

View File

@ -219,7 +219,8 @@ namespace Emby.Server.Implementations.Data
{ {
connection.RunQueries(queries); connection.RunQueries(queries);
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
var existingColumnNames = GetColumnNames(db, "AncestorIds"); var existingColumnNames = GetColumnNames(db, "AncestorIds");
AddColumn(db, "AncestorIds", "AncestorIdText", "Text", existingColumnNames); AddColumn(db, "AncestorIds", "AncestorIdText", "Text", existingColumnNames);
@ -495,7 +496,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id")) using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id"))
{ {
@ -546,7 +548,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
SaveItemsInTranscation(db, tuples); SaveItemsInTranscation(db, tuples);
}, TransactionMode); }, TransactionMode);
@ -2032,7 +2035,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
// First delete chapters // First delete chapters
db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob); db.Execute("delete from " + ChaptersTableName + " where ItemId=@ItemId", idBlob);
@ -2263,7 +2267,6 @@ namespace Emby.Server.Implementations.Data
return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase); return query.IncludeItemTypes.Contains("Trailer", StringComparer.OrdinalIgnoreCase);
} }
private static readonly HashSet<string> _artistExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) private static readonly HashSet<string> _artistExcludeParentTypes = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{ {
"Series", "Series",
@ -2922,7 +2925,8 @@ namespace Emby.Server.Implementations.Data
var result = new QueryResult<BaseItem>(); var result = new QueryResult<BaseItem>();
using (var connection = GetConnection(true)) using (var connection = GetConnection(true))
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
var statements = PrepareAll(db, statementTexts); var statements = PrepareAll(db, statementTexts);
@ -3291,7 +3295,6 @@ namespace Emby.Server.Implementations.Data
} }
} }
var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0; var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
var statementTexts = new List<string>(); var statementTexts = new List<string>();
@ -3326,7 +3329,8 @@ namespace Emby.Server.Implementations.Data
var result = new QueryResult<Guid>(); var result = new QueryResult<Guid>();
using (var connection = GetConnection(true)) using (var connection = GetConnection(true))
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
var statements = PrepareAll(db, statementTexts); var statements = PrepareAll(db, statementTexts);
@ -4901,7 +4905,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
connection.ExecuteAll(sql); connection.ExecuteAll(sql);
}, TransactionMode); }, TransactionMode);
@ -4952,7 +4957,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
var idBlob = id.ToByteArray(); var idBlob = id.ToByteArray();
@ -5746,7 +5752,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
var itemIdBlob = itemId.ToByteArray(); var itemIdBlob = itemId.ToByteArray();
@ -5900,7 +5907,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
var itemIdBlob = id.ToByteArray(); var itemIdBlob = id.ToByteArray();
@ -6006,7 +6014,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
} }
} }
/// <summary> /// <summary>
/// Gets the chapter. /// Gets the chapter.
/// </summary> /// </summary>
@ -6235,7 +6242,8 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
var itemIdBlob = id.ToByteArray(); var itemIdBlob = id.ToByteArray();

View File

@ -44,7 +44,8 @@ namespace Emby.Server.Implementations.Data
var users = userDatasTableExists ? null : userManager.Users; var users = userDatasTableExists ? null : userManager.Users;
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
db.ExecuteAll(string.Join(";", new[] { db.ExecuteAll(string.Join(";", new[] {
@ -178,7 +179,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
SaveUserData(db, internalUserId, key, userData); SaveUserData(db, internalUserId, key, userData);
}, TransactionMode); }, TransactionMode);
@ -246,7 +248,8 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
connection.RunInTransaction(db => connection.RunInTransaction(
db =>
{ {
foreach (var userItemData in userDataList) foreach (var userItemData in userDataList)
{ {

View File

@ -468,7 +468,6 @@ namespace Emby.Server.Implementations.Dto
IncludeItemTypes = new[] { typeof(MusicAlbum).Name }, IncludeItemTypes = new[] { typeof(MusicAlbum).Name },
Name = item.Album, Name = item.Album,
Limit = 1 Limit = 1
}); });
if (parentAlbumIds.Count > 0) if (parentAlbumIds.Count > 0)
@ -1139,6 +1138,7 @@ namespace Emby.Server.Implementations.Dto
if (episodeSeries != null) if (episodeSeries != null)
{ {
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary); dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, episodeSeries, ImageType.Primary);
AttachPrimaryImageAspectRatio(dto, episodeSeries);
} }
} }
@ -1185,6 +1185,7 @@ namespace Emby.Server.Implementations.Dto
if (series != null) if (series != null)
{ {
dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary); dto.SeriesPrimaryImageTag = GetTagAndFillBlurhash(dto, series, ImageType.Primary);
AttachPrimaryImageAspectRatio(dto, series);
} }
} }
} }

View File

@ -22,7 +22,7 @@
</ItemGroup> </ItemGroup>
<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="Jellyfin.XmlTv" Version="10.6.2" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" /> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" /> <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.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" 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.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.8" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.9" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.8" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.9" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.8" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.9" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.8" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.9" />
<PackageReference Include="Mono.Nat" Version="2.0.2" /> <PackageReference Include="Mono.Nat" Version="3.0.0" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.0" /> <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" /> <PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" />
<PackageReference Include="sharpcompress" Version="0.26.0" /> <PackageReference Include="sharpcompress" Version="0.26.0" />

View File

@ -16,6 +16,7 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints namespace Emby.Server.Implementations.EntryPoints
@ -105,7 +106,7 @@ namespace Emby.Server.Implementations.EntryPoints
try try
{ {
_sessionManager.SendMessageToAdminSessions("RefreshProgress", dict, CancellationToken.None); _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, dict, CancellationToken.None);
} }
catch catch
{ {
@ -123,7 +124,7 @@ namespace Emby.Server.Implementations.EntryPoints
try try
{ {
_sessionManager.SendMessageToAdminSessions("RefreshProgress", collectionFolderDict, CancellationToken.None); _sessionManager.SendMessageToAdminSessions(SessionMessageType.RefreshProgress, collectionFolderDict, CancellationToken.None);
} }
catch catch
{ {
@ -345,7 +346,7 @@ namespace Emby.Server.Implementations.EntryPoints
try 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) catch (Exception ex)
{ {

View File

@ -10,6 +10,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints namespace Emby.Server.Implementations.EntryPoints
@ -46,25 +47,25 @@ namespace Emby.Server.Implementations.EntryPoints
private async void OnLiveTvManagerSeriesTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e) 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) 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) 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) 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(); var users = _userManager.Users.Where(i => i.HasPermission(PermissionKind.EnableLiveTvAccess)).Select(i => i.Id).ToList();

View File

@ -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()
{
}
}
}

View File

@ -28,7 +28,6 @@ namespace Emby.Server.Implementations.EntryPoints
private readonly object _syncLock = new object(); private readonly object _syncLock = new object();
private Timer _updateTimer; private Timer _updateTimer;
public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IUserManager userManager) public UserDataChangeNotifier(IUserDataManager userDataManager, ISessionManager sessionManager, IUserManager userManager)
{ {
_userDataManager = userDataManager; _userDataManager = userDataManager;
@ -116,7 +115,7 @@ namespace Emby.Server.Implementations.EntryPoints
private Task SendNotifications(Guid userId, List<BaseItem> changedItems, CancellationToken cancellationToken) 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) private UserDataChangeInfo GetUserDataChangeInfo(Guid userId, List<BaseItem> changedItems)

View File

@ -11,6 +11,7 @@ using System.Threading.Tasks;
using MediaBrowser.Common.Json; using MediaBrowser.Common.Json;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
@ -227,7 +228,7 @@ namespace Emby.Server.Implementations.HttpServer
Connection = this Connection = this
}; };
if (info.MessageType.Equals("KeepAlive", StringComparison.Ordinal)) if (info.MessageType == SessionMessageType.KeepAlive)
{ {
await SendKeepAliveResponse().ConfigureAwait(false); await SendKeepAliveResponse().ConfigureAwait(false);
} }
@ -244,7 +245,7 @@ namespace Emby.Server.Implementations.HttpServer
new WebSocketMessage<string> new WebSocketMessage<string>
{ {
MessageId = Guid.NewGuid(), MessageId = Guid.NewGuid(),
MessageType = "KeepAlive" MessageType = SessionMessageType.KeepAlive
}, CancellationToken.None); }, CancellationToken.None);
} }

View File

@ -2,7 +2,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events; using Jellyfin.Data.Events;
@ -14,16 +13,18 @@ namespace Emby.Server.Implementations.HttpServer
{ {
public class WebSocketManager : IWebSocketManager public class WebSocketManager : IWebSocketManager
{ {
private readonly Lazy<IEnumerable<IWebSocketListener>> _webSocketListeners;
private readonly ILogger<WebSocketManager> _logger; private readonly ILogger<WebSocketManager> _logger;
private readonly ILoggerFactory _loggerFactory; private readonly ILoggerFactory _loggerFactory;
private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
private bool _disposed = false; private bool _disposed = false;
public WebSocketManager( public WebSocketManager(
Lazy<IEnumerable<IWebSocketListener>> webSocketListeners,
ILogger<WebSocketManager> logger, ILogger<WebSocketManager> logger,
ILoggerFactory loggerFactory) ILoggerFactory loggerFactory)
{ {
_webSocketListeners = webSocketListeners;
_logger = logger; _logger = logger;
_loggerFactory = loggerFactory; _loggerFactory = loggerFactory;
} }
@ -68,15 +69,6 @@ namespace Emby.Server.Implementations.HttpServer
} }
} }
/// <summary>
/// Adds the rest handlers.
/// </summary>
/// <param name="listeners">The web socket listeners.</param>
public void Init(IEnumerable<IWebSocketListener> listeners)
{
_webSocketListeners = listeners.ToArray();
}
/// <summary> /// <summary>
/// Processes the web socket message received. /// Processes the web socket message received.
/// </summary> /// </summary>
@ -90,7 +82,8 @@ namespace Emby.Server.Implementations.HttpServer
IEnumerable<Task> GetTasks() IEnumerable<Task> GetTasks()
{ {
foreach (var x in _webSocketListeners) var listeners = _webSocketListeners.Value;
foreach (var x in listeners)
{ {
yield return x.ProcessMessageAsync(result); yield return x.ProcessMessageAsync(result);
} }

View File

@ -16,11 +16,6 @@ namespace Emby.Server.Implementations
/// </summary> /// </summary>
bool IsService { get; } bool IsService { get; }
/// <summary>
/// Gets the value of the --noautorunwebapp command line option.
/// </summary>
bool NoAutoRunWebApp { get; }
/// <summary> /// <summary>
/// Gets the value of the --package-name command line option. /// Gets the value of the --package-name command line option.
/// </summary> /// </summary>

View File

@ -133,9 +133,20 @@ namespace Emby.Server.Implementations.Images
protected virtual IEnumerable<string> GetStripCollageImagePaths(BaseItem primaryItem, IEnumerable<BaseItem> items) protected virtual IEnumerable<string> GetStripCollageImagePaths(BaseItem primaryItem, IEnumerable<BaseItem> items)
{ {
var useBackdrop = primaryItem is CollectionFolder;
return items return items
.Select(i => .Select(i =>
{ {
// Use Backdrop instead of Primary image for Library images.
if (useBackdrop)
{
var backdrop = i.GetImageInfo(ImageType.Backdrop, 0);
if (backdrop != null && backdrop.IsLocalFile)
{
return backdrop.Path;
}
}
var image = i.GetImageInfo(ImageType.Primary, 0); var image = i.GetImageInfo(ImageType.Primary, 0);
if (image != null && image.IsLocalFile) if (image != null && image.IsLocalFile)
{ {
@ -190,7 +201,7 @@ namespace Emby.Server.Implementations.Images
return null; return null;
} }
ImageProcessor.CreateImageCollage(options); ImageProcessor.CreateImageCollage(options, primaryItem.Name);
return outputPath; return outputPath;
} }

View File

@ -1,6 +1,7 @@
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
@ -43,7 +44,7 @@ namespace Emby.Server.Implementations.Library
private readonly ILocalizationManager _localizationManager; private readonly ILocalizationManager _localizationManager;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly Dictionary<string, ILiveStream> _openStreams = new Dictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase); private readonly ConcurrentDictionary<string, ILiveStream> _openStreams = new ConcurrentDictionary<string, ILiveStream>(StringComparer.OrdinalIgnoreCase);
private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1); private readonly SemaphoreSlim _liveStreamSemaphore = new SemaphoreSlim(1, 1);
private IMediaSourceProvider[] _providers; private IMediaSourceProvider[] _providers;
@ -582,11 +583,7 @@ namespace Emby.Server.Implementations.Library
mediaSource.InferTotalBitrate(); mediaSource.InferTotalBitrate();
} }
public async Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken) public Task<IDirectStreamProvider> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken)
{
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{ {
var info = _openStreams.Values.FirstOrDefault(i => var info = _openStreams.Values.FirstOrDefault(i =>
{ {
@ -599,12 +596,7 @@ namespace Emby.Server.Implementations.Library
return false; return false;
}); });
return info as IDirectStreamProvider; return Task.FromResult(info as IDirectStreamProvider);
}
finally
{
_liveStreamSemaphore.Release();
}
} }
public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken) public async Task<LiveStreamResponse> OpenLiveStream(LiveStreamRequest request, CancellationToken cancellationToken)
@ -793,29 +785,20 @@ namespace Emby.Server.Implementations.Library
return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info as IDirectStreamProvider); return new Tuple<MediaSourceInfo, IDirectStreamProvider>(info.MediaSource, info as IDirectStreamProvider);
} }
private async Task<ILiveStream> GetLiveStreamInfo(string id, CancellationToken cancellationToken) private Task<ILiveStream> GetLiveStreamInfo(string id, CancellationToken cancellationToken)
{ {
if (string.IsNullOrEmpty(id)) if (string.IsNullOrEmpty(id))
{ {
throw new ArgumentNullException(nameof(id)); throw new ArgumentNullException(nameof(id));
} }
await _liveStreamSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
try
{
if (_openStreams.TryGetValue(id, out ILiveStream info)) if (_openStreams.TryGetValue(id, out ILiveStream info))
{ {
return info; return Task.FromResult(info);
} }
else else
{ {
throw new ResourceNotFoundException(); return Task.FromException<ILiveStream>(new ResourceNotFoundException());
}
}
finally
{
_liveStreamSemaphore.Release();
} }
} }
@ -844,7 +827,7 @@ namespace Emby.Server.Implementations.Library
if (liveStream.ConsumerCount <= 0) if (liveStream.ConsumerCount <= 0)
{ {
_openStreams.Remove(id); _openStreams.TryRemove(id, out _);
_logger.LogInformation("Closing live stream {0}", id); _logger.LogInformation("Closing live stream {0}", id);

View File

@ -32,7 +32,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// <value>The priority.</value> /// <value>The priority.</value>
public override ResolverPriority Priority => ResolverPriority.Fourth; public override ResolverPriority Priority => ResolverPriority.Fourth;
public MultiItemResolverResult ResolveMultiple(Folder parent, public MultiItemResolverResult ResolveMultiple(
Folder parent,
List<FileSystemMetadata> files, List<FileSystemMetadata> files,
string collectionType, string collectionType,
IDirectoryService directoryService) IDirectoryService directoryService)
@ -50,7 +51,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
return result; return result;
} }
private MultiItemResolverResult ResolveMultipleInternal(Folder parent, private MultiItemResolverResult ResolveMultipleInternal(
Folder parent,
List<FileSystemMetadata> files, List<FileSystemMetadata> files,
string collectionType, string collectionType,
IDirectoryService directoryService) IDirectoryService directoryService)

View File

@ -1,5 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Emby.Naming.Audio; using Emby.Naming.Audio;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -113,22 +116,29 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
IFileSystem fileSystem, IFileSystem fileSystem,
ILibraryManager libraryManager) 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 discSubfolderCount = 0;
var notMultiDisc = false;
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions(); var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
var parser = new AlbumParser(namingOptions); var parser = new AlbumParser(namingOptions);
foreach (var fileSystemInfo in list)
{
if (fileSystemInfo.IsDirectory)
{
if (allowSubfolders)
{
if (notMultiDisc)
{
continue;
}
var directories = list.Where(fileSystemInfo => fileSystemInfo.IsDirectory);
var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
{
var path = fileSystemInfo.FullName; var path = fileSystemInfo.FullName;
var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager); var hasMusic = ContainsMusic(directoryService.GetFileSystemEntries(path), false, directoryService, logger, fileSystem, libraryManager);
@ -137,28 +147,17 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
if (parser.IsMultiPart(path)) if (parser.IsMultiPart(path))
{ {
logger.LogDebug("Found multi-disc folder: " + path); logger.LogDebug("Found multi-disc folder: " + path);
discSubfolderCount++; Interlocked.Increment(ref discSubfolderCount);
} }
else else
{ {
// If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album // If there are folders underneath with music that are not multidisc, then this can't be a multi-disc album
notMultiDisc = true; state.Stop();
} }
} }
} });
}
else
{
var fullName = fileSystemInfo.FullName;
if (libraryManager.IsAudioFile(fullName)) if (!result.IsCompleted)
{
return true;
}
}
}
if (notMultiDisc)
{ {
return false; return false;
} }

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using System.Threading.Tasks;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -94,7 +95,18 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager); var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
// If we contain an album assume we are an artist folder // 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;
} }
} }
} }

View File

@ -50,7 +50,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Books
var fileExtension = Path.GetExtension(f.FullName) ?? var fileExtension = Path.GetExtension(f.FullName) ??
string.Empty; string.Empty;
return _validExtensions.Contains(fileExtension, return _validExtensions.Contains(
fileExtension,
StringComparer StringComparer
.OrdinalIgnoreCase); .OrdinalIgnoreCase);
}).ToList(); }).ToList();

View File

@ -874,7 +874,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<Lineup> lineups { get; set; } public List<Lineup> lineups { get; set; }
} }
public class Headends public class Headends
{ {
public string headend { get; set; } public string headend { get; set; }
@ -886,8 +885,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<Lineup> lineups { get; set; } public List<Lineup> lineups { get; set; }
} }
public class Map public class Map
{ {
public string stationID { get; set; } public string stationID { get; set; }
@ -971,9 +968,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public List<string> date { get; set; } public List<string> date { get; set; }
} }
public class Rating public class Rating
{ {
public string body { get; set; } public string body { get; set; }
@ -1017,8 +1011,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings
public string isPremiereOrFinale { get; set; } public string isPremiereOrFinale { get; set; }
} }
public class MetadataSchedule public class MetadataSchedule
{ {
public string modified { get; set; } public string modified { get; set; }

View File

@ -2135,6 +2135,7 @@ namespace Emby.Server.Implementations.LiveTv
} }
private bool _disposed = false; private bool _disposed = false;
/// <summary> /// <summary>
/// Releases unmanaged and - optionally - managed resources. /// Releases unmanaged and - optionally - managed resources.
/// </summary> /// </summary>

View File

@ -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) 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]; var profile = streamId.Split('_')[0];
Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelInfo.Id, streamId, profile); Logger.LogInformation("GetChannelStream: channel id: {0}. stream id: {1} profile: {2}", channelInfo.Id, streamId, profile);

View File

@ -85,7 +85,6 @@
"ItemAddedWithName": "{0} is in die versameling", "ItemAddedWithName": "{0} is in die versameling",
"HomeVideos": "Tuis opnames", "HomeVideos": "Tuis opnames",
"HeaderRecordingGroups": "Groep Opnames", "HeaderRecordingGroups": "Groep Opnames",
"HeaderCameraUploads": "Kamera Oplaai",
"Genres": "Genres", "Genres": "Genres",
"FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}", "FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}",
"ChapterNameValue": "Hoofstuk", "ChapterNameValue": "Hoofstuk",

View File

@ -16,7 +16,6 @@
"Folders": "المجلدات", "Folders": "المجلدات",
"Genres": "التضنيفات", "Genres": "التضنيفات",
"HeaderAlbumArtists": "فناني الألبومات", "HeaderAlbumArtists": "فناني الألبومات",
"HeaderCameraUploads": "تحميلات الكاميرا",
"HeaderContinueWatching": "استئناف", "HeaderContinueWatching": "استئناف",
"HeaderFavoriteAlbums": "الألبومات المفضلة", "HeaderFavoriteAlbums": "الألبومات المفضلة",
"HeaderFavoriteArtists": "الفنانون المفضلون", "HeaderFavoriteArtists": "الفنانون المفضلون",

View File

@ -16,7 +16,6 @@
"Folders": "Папки", "Folders": "Папки",
"Genres": "Жанрове", "Genres": "Жанрове",
"HeaderAlbumArtists": "Изпълнители на албуми", "HeaderAlbumArtists": "Изпълнители на албуми",
"HeaderCameraUploads": "Качени от камера",
"HeaderContinueWatching": "Продължаване на гледането", "HeaderContinueWatching": "Продължаване на гледането",
"HeaderFavoriteAlbums": "Любими албуми", "HeaderFavoriteAlbums": "Любими албуми",
"HeaderFavoriteArtists": "Любими изпълнители", "HeaderFavoriteArtists": "Любими изпълнители",

View File

@ -14,7 +14,6 @@
"HeaderFavoriteArtists": "প্রিয় শিল্পীরা", "HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
"HeaderFavoriteAlbums": "প্রিয় এলবামগুলো", "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
"HeaderContinueWatching": "দেখতে থাকুন", "HeaderContinueWatching": "দেখতে থাকুন",
"HeaderCameraUploads": "ক্যামেরার আপলোড সমূহ",
"HeaderAlbumArtists": "এলবাম শিল্পী", "HeaderAlbumArtists": "এলবাম শিল্পী",
"Genres": "জেনার", "Genres": "জেনার",
"Folders": "ফোল্ডারগুলো", "Folders": "ফোল্ডারগুলো",

View File

@ -16,7 +16,6 @@
"Folders": "Carpetes", "Folders": "Carpetes",
"Genres": "Gèneres", "Genres": "Gèneres",
"HeaderAlbumArtists": "Artistes del Àlbum", "HeaderAlbumArtists": "Artistes del Àlbum",
"HeaderCameraUploads": "Pujades de Càmera",
"HeaderContinueWatching": "Continua Veient", "HeaderContinueWatching": "Continua Veient",
"HeaderFavoriteAlbums": "Àlbums Preferits", "HeaderFavoriteAlbums": "Àlbums Preferits",
"HeaderFavoriteArtists": "Artistes Preferits", "HeaderFavoriteArtists": "Artistes Preferits",

View File

@ -16,7 +16,6 @@
"Folders": "Složky", "Folders": "Složky",
"Genres": "Žánry", "Genres": "Žánry",
"HeaderAlbumArtists": "Umělci alba", "HeaderAlbumArtists": "Umělci alba",
"HeaderCameraUploads": "Nahrané fotografie",
"HeaderContinueWatching": "Pokračovat ve sledování", "HeaderContinueWatching": "Pokračovat ve sledování",
"HeaderFavoriteAlbums": "Oblíbená alba", "HeaderFavoriteAlbums": "Oblíbená alba",
"HeaderFavoriteArtists": "Oblíbení interpreti", "HeaderFavoriteArtists": "Oblíbení interpreti",

View File

@ -16,7 +16,6 @@
"Folders": "Mapper", "Folders": "Mapper",
"Genres": "Genrer", "Genres": "Genrer",
"HeaderAlbumArtists": "Albumkunstnere", "HeaderAlbumArtists": "Albumkunstnere",
"HeaderCameraUploads": "Kamera Uploads",
"HeaderContinueWatching": "Fortsæt Afspilning", "HeaderContinueWatching": "Fortsæt Afspilning",
"HeaderFavoriteAlbums": "Favoritalbummer", "HeaderFavoriteAlbums": "Favoritalbummer",
"HeaderFavoriteArtists": "Favoritkunstnere", "HeaderFavoriteArtists": "Favoritkunstnere",

View File

@ -16,7 +16,6 @@
"Folders": "Verzeichnisse", "Folders": "Verzeichnisse",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album-Interpreten", "HeaderAlbumArtists": "Album-Interpreten",
"HeaderCameraUploads": "Kamera-Uploads",
"HeaderContinueWatching": "Fortsetzen", "HeaderContinueWatching": "Fortsetzen",
"HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblings-Interpreten", "HeaderFavoriteArtists": "Lieblings-Interpreten",

View File

@ -16,7 +16,6 @@
"Folders": "Φάκελοι", "Folders": "Φάκελοι",
"Genres": "Είδη", "Genres": "Είδη",
"HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ", "HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
"HeaderCameraUploads": "Μεταφορτώσεις Κάμερας",
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση", "HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ", "HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες", "HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",

View File

@ -16,7 +16,6 @@
"Folders": "Folders", "Folders": "Folders",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads",
"HeaderContinueWatching": "Continue Watching", "HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favourite Albums", "HeaderFavoriteAlbums": "Favourite Albums",
"HeaderFavoriteArtists": "Favourite Artists", "HeaderFavoriteArtists": "Favourite Artists",

View File

@ -16,7 +16,6 @@
"Folders": "Folders", "Folders": "Folders",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Album Artists",
"HeaderCameraUploads": "Camera Uploads",
"HeaderContinueWatching": "Continue Watching", "HeaderContinueWatching": "Continue Watching",
"HeaderFavoriteAlbums": "Favorite Albums", "HeaderFavoriteAlbums": "Favorite Albums",
"HeaderFavoriteArtists": "Favorite Artists", "HeaderFavoriteArtists": "Favorite Artists",

View File

@ -16,7 +16,6 @@
"Folders": "Carpetas", "Folders": "Carpetas",
"Genres": "Géneros", "Genres": "Géneros",
"HeaderAlbumArtists": "Artistas de álbum", "HeaderAlbumArtists": "Artistas de álbum",
"HeaderCameraUploads": "Subidas de cámara",
"HeaderContinueWatching": "Seguir viendo", "HeaderContinueWatching": "Seguir viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteArtists": "Artistas favoritos",

View File

@ -16,7 +16,6 @@
"Folders": "Carpetas", "Folders": "Carpetas",
"Genres": "Géneros", "Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del álbum", "HeaderAlbumArtists": "Artistas del álbum",
"HeaderCameraUploads": "Subidas desde la cámara",
"HeaderContinueWatching": "Continuar viendo", "HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteArtists": "Artistas favoritos",

View File

@ -16,7 +16,6 @@
"Folders": "Carpetas", "Folders": "Carpetas",
"Genres": "Géneros", "Genres": "Géneros",
"HeaderAlbumArtists": "Artistas del álbum", "HeaderAlbumArtists": "Artistas del álbum",
"HeaderCameraUploads": "Subidas desde la cámara",
"HeaderContinueWatching": "Continuar viendo", "HeaderContinueWatching": "Continuar viendo",
"HeaderFavoriteAlbums": "Álbumes favoritos", "HeaderFavoriteAlbums": "Álbumes favoritos",
"HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteArtists": "Artistas favoritos",

View File

@ -105,7 +105,6 @@
"Inherit": "Heredar", "Inherit": "Heredar",
"HomeVideos": "Videos caseros", "HomeVideos": "Videos caseros",
"HeaderRecordingGroups": "Grupos de grabación", "HeaderRecordingGroups": "Grupos de grabación",
"HeaderCameraUploads": "Subidas desde la cámara",
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}", "FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}",
"DeviceOnlineWithName": "{0} está conectado", "DeviceOnlineWithName": "{0} está conectado",
"DeviceOfflineWithName": "{0} se ha desconectado", "DeviceOfflineWithName": "{0} se ha desconectado",

View File

@ -12,7 +12,6 @@
"Application": "Aplicación", "Application": "Aplicación",
"AppDeviceValues": "App: {0}, Dispositivo: {1}", "AppDeviceValues": "App: {0}, Dispositivo: {1}",
"HeaderContinueWatching": "Continuar Viendo", "HeaderContinueWatching": "Continuar Viendo",
"HeaderCameraUploads": "Subidas de Cámara",
"HeaderAlbumArtists": "Artistas del Álbum", "HeaderAlbumArtists": "Artistas del Álbum",
"Genres": "Géneros", "Genres": "Géneros",
"Folders": "Carpetas", "Folders": "Carpetas",

View File

@ -16,7 +16,6 @@
"Folders": "پوشه‌ها", "Folders": "پوشه‌ها",
"Genres": "ژانرها", "Genres": "ژانرها",
"HeaderAlbumArtists": "هنرمندان آلبوم", "HeaderAlbumArtists": "هنرمندان آلبوم",
"HeaderCameraUploads": "آپلودهای دوربین",
"HeaderContinueWatching": "ادامه تماشا", "HeaderContinueWatching": "ادامه تماشا",
"HeaderFavoriteAlbums": "آلبوم‌های مورد علاقه", "HeaderFavoriteAlbums": "آلبوم‌های مورد علاقه",
"HeaderFavoriteArtists": "هنرمندان مورد علاقه", "HeaderFavoriteArtists": "هنرمندان مورد علاقه",

View File

@ -24,7 +24,6 @@
"HeaderFavoriteSongs": "Lempikappaleet", "HeaderFavoriteSongs": "Lempikappaleet",
"HeaderFavoriteShows": "Lempisarjat", "HeaderFavoriteShows": "Lempisarjat",
"HeaderFavoriteEpisodes": "Lempijaksot", "HeaderFavoriteEpisodes": "Lempijaksot",
"HeaderCameraUploads": "Kamerasta Lähetetyt",
"HeaderFavoriteArtists": "Lempiartistit", "HeaderFavoriteArtists": "Lempiartistit",
"HeaderFavoriteAlbums": "Lempialbumit", "HeaderFavoriteAlbums": "Lempialbumit",
"HeaderContinueWatching": "Jatka katsomista", "HeaderContinueWatching": "Jatka katsomista",

View File

@ -73,7 +73,6 @@
"HeaderFavoriteArtists": "Paboritong Artista", "HeaderFavoriteArtists": "Paboritong Artista",
"HeaderFavoriteAlbums": "Paboritong Albums", "HeaderFavoriteAlbums": "Paboritong Albums",
"HeaderContinueWatching": "Ituloy Manood", "HeaderContinueWatching": "Ituloy Manood",
"HeaderCameraUploads": "Camera Uploads",
"HeaderAlbumArtists": "Artista ng Album", "HeaderAlbumArtists": "Artista ng Album",
"Genres": "Kategorya", "Genres": "Kategorya",
"Folders": "Folders", "Folders": "Folders",

View File

@ -16,7 +16,6 @@
"Folders": "Dossiers", "Folders": "Dossiers",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Artistes de l'album", "HeaderAlbumArtists": "Artistes de l'album",
"HeaderCameraUploads": "Photos transférées",
"HeaderContinueWatching": "Continuer à regarder", "HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris", "HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes favoris", "HeaderFavoriteArtists": "Artistes favoris",

View File

@ -16,7 +16,6 @@
"Folders": "Dossiers", "Folders": "Dossiers",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Artistes", "HeaderAlbumArtists": "Artistes",
"HeaderCameraUploads": "Photos transférées",
"HeaderContinueWatching": "Continuer à regarder", "HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris", "HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes préférés", "HeaderFavoriteArtists": "Artistes préférés",

View File

@ -16,7 +16,6 @@
"Folders": "Ordner", "Folders": "Ordner",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album-Künstler", "HeaderAlbumArtists": "Album-Künstler",
"HeaderCameraUploads": "Kamera-Uploads",
"HeaderContinueWatching": "weiter schauen", "HeaderContinueWatching": "weiter schauen",
"HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblings-Künstler", "HeaderFavoriteArtists": "Lieblings-Künstler",

View File

@ -16,7 +16,6 @@
"Folders": "תיקיות", "Folders": "תיקיות",
"Genres": "ז'אנרים", "Genres": "ז'אנרים",
"HeaderAlbumArtists": "אמני האלבום", "HeaderAlbumArtists": "אמני האלבום",
"HeaderCameraUploads": "העלאות ממצלמה",
"HeaderContinueWatching": "המשך לצפות", "HeaderContinueWatching": "המשך לצפות",
"HeaderFavoriteAlbums": "אלבומים מועדפים", "HeaderFavoriteAlbums": "אלבומים מועדפים",
"HeaderFavoriteArtists": "אמנים מועדפים", "HeaderFavoriteArtists": "אמנים מועדפים",

View File

@ -16,7 +16,6 @@
"Folders": "Mape", "Folders": "Mape",
"Genres": "Žanrovi", "Genres": "Žanrovi",
"HeaderAlbumArtists": "Izvođači na albumu", "HeaderAlbumArtists": "Izvođači na albumu",
"HeaderCameraUploads": "Uvoz sa kamere",
"HeaderContinueWatching": "Nastavi gledati", "HeaderContinueWatching": "Nastavi gledati",
"HeaderFavoriteAlbums": "Omiljeni albumi", "HeaderFavoriteAlbums": "Omiljeni albumi",
"HeaderFavoriteArtists": "Omiljeni izvođači", "HeaderFavoriteArtists": "Omiljeni izvođači",

View File

@ -16,7 +16,6 @@
"Folders": "Könyvtárak", "Folders": "Könyvtárak",
"Genres": "Műfajok", "Genres": "Műfajok",
"HeaderAlbumArtists": "Album előadók", "HeaderAlbumArtists": "Album előadók",
"HeaderCameraUploads": "Kamera feltöltések",
"HeaderContinueWatching": "Megtekintés folytatása", "HeaderContinueWatching": "Megtekintés folytatása",
"HeaderFavoriteAlbums": "Kedvenc albumok", "HeaderFavoriteAlbums": "Kedvenc albumok",
"HeaderFavoriteArtists": "Kedvenc előadók", "HeaderFavoriteArtists": "Kedvenc előadók",

View File

@ -20,7 +20,6 @@
"HeaderFavoriteArtists": "Artis Favorit", "HeaderFavoriteArtists": "Artis Favorit",
"HeaderFavoriteAlbums": "Album Favorit", "HeaderFavoriteAlbums": "Album Favorit",
"HeaderContinueWatching": "Lanjut Menonton", "HeaderContinueWatching": "Lanjut Menonton",
"HeaderCameraUploads": "Unggahan Kamera",
"HeaderAlbumArtists": "Album Artis", "HeaderAlbumArtists": "Album Artis",
"Genres": "Aliran", "Genres": "Aliran",
"Folders": "Folder", "Folders": "Folder",

View File

@ -13,7 +13,6 @@
"HeaderFavoriteArtists": "Uppáhalds Listamenn", "HeaderFavoriteArtists": "Uppáhalds Listamenn",
"HeaderFavoriteAlbums": "Uppáhalds Plötur", "HeaderFavoriteAlbums": "Uppáhalds Plötur",
"HeaderContinueWatching": "Halda áfram að horfa", "HeaderContinueWatching": "Halda áfram að horfa",
"HeaderCameraUploads": "Myndavéla upphal",
"HeaderAlbumArtists": "Höfundur plötu", "HeaderAlbumArtists": "Höfundur plötu",
"Genres": "Tegundir", "Genres": "Tegundir",
"Folders": "Möppur", "Folders": "Möppur",

View File

@ -16,7 +16,6 @@
"Folders": "Cartelle", "Folders": "Cartelle",
"Genres": "Generi", "Genres": "Generi",
"HeaderAlbumArtists": "Artisti degli Album", "HeaderAlbumArtists": "Artisti degli Album",
"HeaderCameraUploads": "Caricamenti Fotocamera",
"HeaderContinueWatching": "Continua a guardare", "HeaderContinueWatching": "Continua a guardare",
"HeaderFavoriteAlbums": "Album Preferiti", "HeaderFavoriteAlbums": "Album Preferiti",
"HeaderFavoriteArtists": "Artisti Preferiti", "HeaderFavoriteArtists": "Artisti Preferiti",

View File

@ -16,7 +16,6 @@
"Folders": "フォルダー", "Folders": "フォルダー",
"Genres": "ジャンル", "Genres": "ジャンル",
"HeaderAlbumArtists": "アルバムアーティスト", "HeaderAlbumArtists": "アルバムアーティスト",
"HeaderCameraUploads": "カメラアップロード",
"HeaderContinueWatching": "視聴を続ける", "HeaderContinueWatching": "視聴を続ける",
"HeaderFavoriteAlbums": "お気に入りのアルバム", "HeaderFavoriteAlbums": "お気に入りのアルバム",
"HeaderFavoriteArtists": "お気に入りのアーティスト", "HeaderFavoriteArtists": "お気に入りのアーティスト",

View File

@ -16,7 +16,6 @@
"Folders": "Qaltalar", "Folders": "Qaltalar",
"Genres": "Janrlar", "Genres": "Janrlar",
"HeaderAlbumArtists": "Álbom oryndaýshylary", "HeaderAlbumArtists": "Álbom oryndaýshylary",
"HeaderCameraUploads": "Kameradan júktelgender",
"HeaderContinueWatching": "Qaraýdy jalǵastyrý", "HeaderContinueWatching": "Qaraýdy jalǵastyrý",
"HeaderFavoriteAlbums": "Tańdaýly álbomdar", "HeaderFavoriteAlbums": "Tańdaýly álbomdar",
"HeaderFavoriteArtists": "Tańdaýly oryndaýshylar", "HeaderFavoriteArtists": "Tańdaýly oryndaýshylar",

View File

@ -16,7 +16,6 @@
"Folders": "폴더", "Folders": "폴더",
"Genres": "장르", "Genres": "장르",
"HeaderAlbumArtists": "앨범 아티스트", "HeaderAlbumArtists": "앨범 아티스트",
"HeaderCameraUploads": "카메라 업로드",
"HeaderContinueWatching": "계속 시청하기", "HeaderContinueWatching": "계속 시청하기",
"HeaderFavoriteAlbums": "즐겨찾는 앨범", "HeaderFavoriteAlbums": "즐겨찾는 앨범",
"HeaderFavoriteArtists": "즐겨찾는 아티스트", "HeaderFavoriteArtists": "즐겨찾는 아티스트",

View File

@ -16,7 +16,6 @@
"Folders": "Katalogai", "Folders": "Katalogai",
"Genres": "Žanrai", "Genres": "Žanrai",
"HeaderAlbumArtists": "Albumo atlikėjai", "HeaderAlbumArtists": "Albumo atlikėjai",
"HeaderCameraUploads": "Kameros",
"HeaderContinueWatching": "Žiūrėti toliau", "HeaderContinueWatching": "Žiūrėti toliau",
"HeaderFavoriteAlbums": "Mėgstami Albumai", "HeaderFavoriteAlbums": "Mėgstami Albumai",
"HeaderFavoriteArtists": "Mėgstami Atlikėjai", "HeaderFavoriteArtists": "Mėgstami Atlikėjai",

View File

@ -72,7 +72,6 @@
"ItemAddedWithName": "{0} tika pievienots bibliotēkai", "ItemAddedWithName": "{0} tika pievienots bibliotēkai",
"HeaderLiveTV": "Tiešraides TV", "HeaderLiveTV": "Tiešraides TV",
"HeaderContinueWatching": "Turpināt Skatīšanos", "HeaderContinueWatching": "Turpināt Skatīšanos",
"HeaderCameraUploads": "Kameras augšupielādes",
"HeaderAlbumArtists": "Albumu Izpildītāji", "HeaderAlbumArtists": "Albumu Izpildītāji",
"Genres": "Žanri", "Genres": "Žanri",
"Folders": "Mapes", "Folders": "Mapes",

View File

@ -51,7 +51,6 @@
"HeaderFavoriteArtists": "Омилени Изведувачи", "HeaderFavoriteArtists": "Омилени Изведувачи",
"HeaderFavoriteAlbums": "Омилени Албуми", "HeaderFavoriteAlbums": "Омилени Албуми",
"HeaderContinueWatching": "Продолжи со гледање", "HeaderContinueWatching": "Продолжи со гледање",
"HeaderCameraUploads": "Поставувања од камера",
"HeaderAlbumArtists": "Изведувачи од Албуми", "HeaderAlbumArtists": "Изведувачи од Албуми",
"Genres": "Жанрови", "Genres": "Жанрови",
"Folders": "Папки", "Folders": "Папки",

View File

@ -54,7 +54,6 @@
"ItemAddedWithName": "{0} हे संग्रहालयात जोडले गेले", "ItemAddedWithName": "{0} हे संग्रहालयात जोडले गेले",
"HomeVideos": "घरचे व्हिडीयो", "HomeVideos": "घरचे व्हिडीयो",
"HeaderRecordingGroups": "रेकॉर्डिंग गट", "HeaderRecordingGroups": "रेकॉर्डिंग गट",
"HeaderCameraUploads": "कॅमेरा अपलोड",
"CameraImageUploadedFrom": "एक नवीन कॅमेरा चित्र {0} येथून अपलोड केले आहे", "CameraImageUploadedFrom": "एक नवीन कॅमेरा चित्र {0} येथून अपलोड केले आहे",
"Application": "अ‍ॅप्लिकेशन", "Application": "अ‍ॅप्लिकेशन",
"AppDeviceValues": "अ‍ॅप: {0}, यंत्र: {1}", "AppDeviceValues": "अ‍ॅप: {0}, यंत्र: {1}",

View File

@ -16,7 +16,6 @@
"Folders": "Fail-fail", "Folders": "Fail-fail",
"Genres": "Genre-genre", "Genres": "Genre-genre",
"HeaderAlbumArtists": "Album Artis-artis", "HeaderAlbumArtists": "Album Artis-artis",
"HeaderCameraUploads": "Muatnaik Kamera",
"HeaderContinueWatching": "Terus Menonton", "HeaderContinueWatching": "Terus Menonton",
"HeaderFavoriteAlbums": "Album-album Kegemaran", "HeaderFavoriteAlbums": "Album-album Kegemaran",
"HeaderFavoriteArtists": "Artis-artis Kegemaran", "HeaderFavoriteArtists": "Artis-artis Kegemaran",

View File

@ -16,7 +16,6 @@
"Folders": "Mapper", "Folders": "Mapper",
"Genres": "Sjangre", "Genres": "Sjangre",
"HeaderAlbumArtists": "Albumartister", "HeaderAlbumArtists": "Albumartister",
"HeaderCameraUploads": "Kameraopplastinger",
"HeaderContinueWatching": "Fortsett å se", "HeaderContinueWatching": "Fortsett å se",
"HeaderFavoriteAlbums": "Favorittalbum", "HeaderFavoriteAlbums": "Favorittalbum",
"HeaderFavoriteArtists": "Favorittartister", "HeaderFavoriteArtists": "Favorittartister",

View File

@ -41,7 +41,6 @@
"HeaderFavoriteArtists": "मनपर्ने कलाकारहरू", "HeaderFavoriteArtists": "मनपर्ने कलाकारहरू",
"HeaderFavoriteAlbums": "मनपर्ने एल्बमहरू", "HeaderFavoriteAlbums": "मनपर्ने एल्बमहरू",
"HeaderContinueWatching": "हेर्न जारी राख्नुहोस्", "HeaderContinueWatching": "हेर्न जारी राख्नुहोस्",
"HeaderCameraUploads": "क्यामेरा अपलोडहरू",
"HeaderAlbumArtists": "एल्बमका कलाकारहरू", "HeaderAlbumArtists": "एल्बमका कलाकारहरू",
"Genres": "विधाहरू", "Genres": "विधाहरू",
"Folders": "फोल्डरहरू", "Folders": "फोल्डरहरू",

View File

@ -16,7 +16,6 @@
"Folders": "Mappen", "Folders": "Mappen",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Albumartiesten", "HeaderAlbumArtists": "Albumartiesten",
"HeaderCameraUploads": "Camera-uploads",
"HeaderContinueWatching": "Kijken hervatten", "HeaderContinueWatching": "Kijken hervatten",
"HeaderFavoriteAlbums": "Favoriete albums", "HeaderFavoriteAlbums": "Favoriete albums",
"HeaderFavoriteArtists": "Favoriete artiesten", "HeaderFavoriteArtists": "Favoriete artiesten",

View File

@ -19,7 +19,6 @@
"HeaderFavoriteArtists": "Favoritt Artistar", "HeaderFavoriteArtists": "Favoritt Artistar",
"HeaderFavoriteAlbums": "Favoritt Album", "HeaderFavoriteAlbums": "Favoritt Album",
"HeaderContinueWatching": "Fortsett å sjå", "HeaderContinueWatching": "Fortsett å sjå",
"HeaderCameraUploads": "Kamera Opplastingar",
"HeaderAlbumArtists": "Album Artist", "HeaderAlbumArtists": "Album Artist",
"Genres": "Sjangrar", "Genres": "Sjangrar",
"Folders": "Mapper", "Folders": "Mapper",

View File

@ -16,7 +16,6 @@
"Folders": "Foldery", "Folders": "Foldery",
"Genres": "Gatunki", "Genres": "Gatunki",
"HeaderAlbumArtists": "Wykonawcy albumów", "HeaderAlbumArtists": "Wykonawcy albumów",
"HeaderCameraUploads": "Przekazane obrazy",
"HeaderContinueWatching": "Kontynuuj odtwarzanie", "HeaderContinueWatching": "Kontynuuj odtwarzanie",
"HeaderFavoriteAlbums": "Ulubione albumy", "HeaderFavoriteAlbums": "Ulubione albumy",
"HeaderFavoriteArtists": "Ulubieni wykonawcy", "HeaderFavoriteArtists": "Ulubieni wykonawcy",

View File

@ -16,7 +16,6 @@
"Folders": "Pastas", "Folders": "Pastas",
"Genres": "Gêneros", "Genres": "Gêneros",
"HeaderAlbumArtists": "Artistas do Álbum", "HeaderAlbumArtists": "Artistas do Álbum",
"HeaderCameraUploads": "Envios da Câmera",
"HeaderContinueWatching": "Continuar Assistindo", "HeaderContinueWatching": "Continuar Assistindo",
"HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas favoritos", "HeaderFavoriteArtists": "Artistas favoritos",

View File

@ -16,7 +16,6 @@
"Folders": "Pastas", "Folders": "Pastas",
"Genres": "Géneros", "Genres": "Géneros",
"HeaderAlbumArtists": "Artistas do Álbum", "HeaderAlbumArtists": "Artistas do Álbum",
"HeaderCameraUploads": "Envios a partir da câmara",
"HeaderContinueWatching": "Continuar a Ver", "HeaderContinueWatching": "Continuar a Ver",
"HeaderFavoriteAlbums": "Álbuns Favoritos", "HeaderFavoriteAlbums": "Álbuns Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos", "HeaderFavoriteArtists": "Artistas Favoritos",
@ -26,7 +25,7 @@
"HeaderLiveTV": "TV em Direto", "HeaderLiveTV": "TV em Direto",
"HeaderNextUp": "A Seguir", "HeaderNextUp": "A Seguir",
"HeaderRecordingGroups": "Grupos de Gravação", "HeaderRecordingGroups": "Grupos de Gravação",
"HomeVideos": "Videos caseiros", "HomeVideos": "Vídeos Caseiros",
"Inherit": "Herdar", "Inherit": "Herdar",
"ItemAddedWithName": "{0} foi adicionado à biblioteca", "ItemAddedWithName": "{0} foi adicionado à biblioteca",
"ItemRemovedWithName": "{0} foi removido da biblioteca", "ItemRemovedWithName": "{0} foi removido da biblioteca",

View File

@ -83,7 +83,6 @@
"Playlists": "Listas de Reprodução", "Playlists": "Listas de Reprodução",
"Photos": "Fotografias", "Photos": "Fotografias",
"Movies": "Filmes", "Movies": "Filmes",
"HeaderCameraUploads": "Carregamentos a partir da câmara",
"FailedLoginAttemptWithUserName": "Tentativa de ligação falhada a partir de {0}", "FailedLoginAttemptWithUserName": "Tentativa de ligação falhada a partir de {0}",
"DeviceOnlineWithName": "{0} está connectado", "DeviceOnlineWithName": "{0} está connectado",
"DeviceOfflineWithName": "{0} desconectou-se", "DeviceOfflineWithName": "{0} desconectou-se",

View File

@ -74,7 +74,6 @@
"HeaderFavoriteArtists": "Artiști Favoriți", "HeaderFavoriteArtists": "Artiști Favoriți",
"HeaderFavoriteAlbums": "Albume Favorite", "HeaderFavoriteAlbums": "Albume Favorite",
"HeaderContinueWatching": "Vizionează în continuare", "HeaderContinueWatching": "Vizionează în continuare",
"HeaderCameraUploads": "Incărcări Cameră Foto",
"HeaderAlbumArtists": "Album Artiști", "HeaderAlbumArtists": "Album Artiști",
"Genres": "Genuri", "Genres": "Genuri",
"Folders": "Dosare", "Folders": "Dosare",

View File

@ -16,7 +16,6 @@
"Folders": "Папки", "Folders": "Папки",
"Genres": "Жанры", "Genres": "Жанры",
"HeaderAlbumArtists": "Исполнители альбома", "HeaderAlbumArtists": "Исполнители альбома",
"HeaderCameraUploads": "Камеры",
"HeaderContinueWatching": "Продолжение просмотра", "HeaderContinueWatching": "Продолжение просмотра",
"HeaderFavoriteAlbums": "Избранные альбомы", "HeaderFavoriteAlbums": "Избранные альбомы",
"HeaderFavoriteArtists": "Избранные исполнители", "HeaderFavoriteArtists": "Избранные исполнители",

View File

@ -16,7 +16,6 @@
"Folders": "Priečinky", "Folders": "Priečinky",
"Genres": "Žánre", "Genres": "Žánre",
"HeaderAlbumArtists": "Umelci albumu", "HeaderAlbumArtists": "Umelci albumu",
"HeaderCameraUploads": "Nahrané fotografie",
"HeaderContinueWatching": "Pokračovať v pozeraní", "HeaderContinueWatching": "Pokračovať v pozeraní",
"HeaderFavoriteAlbums": "Obľúbené albumy", "HeaderFavoriteAlbums": "Obľúbené albumy",
"HeaderFavoriteArtists": "Obľúbení umelci", "HeaderFavoriteArtists": "Obľúbení umelci",

View File

@ -16,7 +16,6 @@
"Folders": "Mape", "Folders": "Mape",
"Genres": "Zvrsti", "Genres": "Zvrsti",
"HeaderAlbumArtists": "Izvajalci albuma", "HeaderAlbumArtists": "Izvajalci albuma",
"HeaderCameraUploads": "Posnetki kamere",
"HeaderContinueWatching": "Nadaljuj gledanje", "HeaderContinueWatching": "Nadaljuj gledanje",
"HeaderFavoriteAlbums": "Priljubljeni albumi", "HeaderFavoriteAlbums": "Priljubljeni albumi",
"HeaderFavoriteArtists": "Priljubljeni izvajalci", "HeaderFavoriteArtists": "Priljubljeni izvajalci",

View File

@ -96,7 +96,6 @@
"HeaderFavoriteArtists": "Artistët e preferuar", "HeaderFavoriteArtists": "Artistët e preferuar",
"HeaderFavoriteAlbums": "Albumet e preferuar", "HeaderFavoriteAlbums": "Albumet e preferuar",
"HeaderContinueWatching": "Vazhdo të shikosh", "HeaderContinueWatching": "Vazhdo të shikosh",
"HeaderCameraUploads": "Ngarkimet nga Kamera",
"HeaderAlbumArtists": "Artistët e albumeve", "HeaderAlbumArtists": "Artistët e albumeve",
"Genres": "Zhanre", "Genres": "Zhanre",
"Folders": "Dosje", "Folders": "Dosje",

View File

@ -74,7 +74,6 @@
"HeaderFavoriteArtists": "Омиљени извођачи", "HeaderFavoriteArtists": "Омиљени извођачи",
"HeaderFavoriteAlbums": "Омиљени албуми", "HeaderFavoriteAlbums": "Омиљени албуми",
"HeaderContinueWatching": "Настави гледање", "HeaderContinueWatching": "Настави гледање",
"HeaderCameraUploads": "Слања са камере",
"HeaderAlbumArtists": "Извођачи албума", "HeaderAlbumArtists": "Извођачи албума",
"Genres": "Жанрови", "Genres": "Жанрови",
"Folders": "Фасцикле", "Folders": "Фасцикле",

View File

@ -16,7 +16,6 @@
"Folders": "Mappar", "Folders": "Mappar",
"Genres": "Genrer", "Genres": "Genrer",
"HeaderAlbumArtists": "Albumartister", "HeaderAlbumArtists": "Albumartister",
"HeaderCameraUploads": "Kamerauppladdningar",
"HeaderContinueWatching": "Fortsätt kolla", "HeaderContinueWatching": "Fortsätt kolla",
"HeaderFavoriteAlbums": "Favoritalbum", "HeaderFavoriteAlbums": "Favoritalbum",
"HeaderFavoriteArtists": "Favoritartister", "HeaderFavoriteArtists": "Favoritartister",

View File

@ -20,7 +20,6 @@
"MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது", "MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது",
"Inherit": "மரபுரிமையாகப் பெறு", "Inherit": "மரபுரிமையாகப் பெறு",
"HeaderRecordingGroups": "பதிவு குழுக்கள்", "HeaderRecordingGroups": "பதிவு குழுக்கள்",
"HeaderCameraUploads": "புகைப்பட பதிவேற்றங்கள்",
"Folders": "கோப்புறைகள்", "Folders": "கோப்புறைகள்",
"FailedLoginAttemptWithUserName": "{0} இலிருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது", "FailedLoginAttemptWithUserName": "{0} இலிருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது",
"DeviceOnlineWithName": "{0} இணைக்கப்பட்டது", "DeviceOnlineWithName": "{0} இணைக்கப்பட்டது",

View File

@ -50,7 +50,6 @@
"HeaderFavoriteArtists": "ศิลปินที่ชื่นชอบ", "HeaderFavoriteArtists": "ศิลปินที่ชื่นชอบ",
"HeaderFavoriteAlbums": "อัมบั้มที่ชื่นชอบ", "HeaderFavoriteAlbums": "อัมบั้มที่ชื่นชอบ",
"HeaderContinueWatching": "ดูต่อ", "HeaderContinueWatching": "ดูต่อ",
"HeaderCameraUploads": "อัปโหลดรูปถ่าย",
"HeaderAlbumArtists": "อัลบั้มศิลปิน", "HeaderAlbumArtists": "อัลบั้มศิลปิน",
"Genres": "ประเภท", "Genres": "ประเภท",
"Folders": "โฟลเดอร์", "Folders": "โฟลเดอร์",

View File

@ -16,7 +16,6 @@
"Folders": "Klasörler", "Folders": "Klasörler",
"Genres": "Türler", "Genres": "Türler",
"HeaderAlbumArtists": "Albüm Sanatçıları", "HeaderAlbumArtists": "Albüm Sanatçıları",
"HeaderCameraUploads": "Kamera Yüklemeleri",
"HeaderContinueWatching": "İzlemeye Devam Et", "HeaderContinueWatching": "İzlemeye Devam Et",
"HeaderFavoriteAlbums": "Favori Albümler", "HeaderFavoriteAlbums": "Favori Albümler",
"HeaderFavoriteArtists": "Favori Sanatçılar", "HeaderFavoriteArtists": "Favori Sanatçılar",

View File

@ -16,7 +16,6 @@
"HeaderFavoriteArtists": "Улюблені виконавці", "HeaderFavoriteArtists": "Улюблені виконавці",
"HeaderFavoriteAlbums": "Улюблені альбоми", "HeaderFavoriteAlbums": "Улюблені альбоми",
"HeaderContinueWatching": "Продовжити перегляд", "HeaderContinueWatching": "Продовжити перегляд",
"HeaderCameraUploads": "Завантажено з камери",
"HeaderAlbumArtists": "Виконавці альбому", "HeaderAlbumArtists": "Виконавці альбому",
"Genres": "Жанри", "Genres": "Жанри",
"Folders": "Каталоги", "Folders": "Каталоги",

View File

@ -105,7 +105,6 @@
"Inherit": "وراثت میں", "Inherit": "وراثت میں",
"HomeVideos": "ہوم ویڈیو", "HomeVideos": "ہوم ویڈیو",
"HeaderRecordingGroups": "ریکارڈنگ گروپس", "HeaderRecordingGroups": "ریکارڈنگ گروپس",
"HeaderCameraUploads": "کیمرہ اپلوڈز",
"FailedLoginAttemptWithUserName": "لاگن کئ کوشش ناکام {0}", "FailedLoginAttemptWithUserName": "لاگن کئ کوشش ناکام {0}",
"DeviceOnlineWithName": "{0} متصل ھو چکا ھے", "DeviceOnlineWithName": "{0} متصل ھو چکا ھے",
"DeviceOfflineWithName": "{0} منقطع ھو چکا ھے", "DeviceOfflineWithName": "{0} منقطع ھو چکا ھے",

View File

@ -103,7 +103,6 @@
"HeaderFavoriteEpisodes": "Tập Phim Yêu Thích", "HeaderFavoriteEpisodes": "Tập Phim Yêu Thích",
"HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích", "HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích",
"HeaderFavoriteAlbums": "Album Ưa Thích", "HeaderFavoriteAlbums": "Album Ưa Thích",
"HeaderCameraUploads": "Máy Ảnh Tải Lên",
"FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}", "FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}",
"DeviceOnlineWithName": "{0} đã kết nối", "DeviceOnlineWithName": "{0} đã kết nối",
"DeviceOfflineWithName": "{0} đã ngắt kết nối", "DeviceOfflineWithName": "{0} đã ngắt kết nối",

View File

@ -16,7 +16,6 @@
"Folders": "文件夹", "Folders": "文件夹",
"Genres": "风格", "Genres": "风格",
"HeaderAlbumArtists": "专辑作家", "HeaderAlbumArtists": "专辑作家",
"HeaderCameraUploads": "相机上传",
"HeaderContinueWatching": "继续观影", "HeaderContinueWatching": "继续观影",
"HeaderFavoriteAlbums": "收藏的专辑", "HeaderFavoriteAlbums": "收藏的专辑",
"HeaderFavoriteArtists": "最爱的艺术家", "HeaderFavoriteArtists": "最爱的艺术家",

View File

@ -16,7 +16,6 @@
"Folders": "檔案夾", "Folders": "檔案夾",
"Genres": "風格", "Genres": "風格",
"HeaderAlbumArtists": "專輯藝人", "HeaderAlbumArtists": "專輯藝人",
"HeaderCameraUploads": "相機上載",
"HeaderContinueWatching": "繼續觀看", "HeaderContinueWatching": "繼續觀看",
"HeaderFavoriteAlbums": "最愛專輯", "HeaderFavoriteAlbums": "最愛專輯",
"HeaderFavoriteArtists": "最愛的藝人", "HeaderFavoriteArtists": "最愛的藝人",

View File

@ -16,7 +16,6 @@
"Folders": "資料夾", "Folders": "資料夾",
"Genres": "風格", "Genres": "風格",
"HeaderAlbumArtists": "專輯演出者", "HeaderAlbumArtists": "專輯演出者",
"HeaderCameraUploads": "相機上傳",
"HeaderContinueWatching": "繼續觀賞", "HeaderContinueWatching": "繼續觀賞",
"HeaderFavoriteAlbums": "最愛專輯", "HeaderFavoriteAlbums": "最愛專輯",
"HeaderFavoriteArtists": "最愛演出者", "HeaderFavoriteArtists": "最愛演出者",

Some files were not shown because too many files have changed in this diff Show More