mirror of
				https://github.com/jellyfin/jellyfin.git
				synced 2025-11-04 03:27:21 -05:00 
			
		
		
		
	Merge branch 'master' into Comment4
This commit is contained in:
		
						commit
						0f47b3ec2d
					
				
							
								
								
									
										72
									
								
								.ci/azure-pipelines-api-client.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								.ci/azure-pipelines-api-client.yml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					parameters:
 | 
				
			||||||
 | 
					  - name: LinuxImage
 | 
				
			||||||
 | 
					    type: string
 | 
				
			||||||
 | 
					    default: "ubuntu-latest"
 | 
				
			||||||
 | 
					  - name: GeneratorVersion
 | 
				
			||||||
 | 
					    type: string
 | 
				
			||||||
 | 
					    default: "5.0.0-beta2"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					jobs:
 | 
				
			||||||
 | 
					- job: GenerateApiClients
 | 
				
			||||||
 | 
					  displayName: 'Generate Api Clients'
 | 
				
			||||||
 | 
					  dependsOn: Test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  pool:
 | 
				
			||||||
 | 
					    vmImage: "${{ parameters.LinuxImage }}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  steps:
 | 
				
			||||||
 | 
					    - task: DownloadPipelineArtifact@2
 | 
				
			||||||
 | 
					      displayName: 'Download OpenAPI Spec Artifact'
 | 
				
			||||||
 | 
					      inputs:
 | 
				
			||||||
 | 
					        source: 'current'
 | 
				
			||||||
 | 
					        artifact: "OpenAPI Spec"
 | 
				
			||||||
 | 
					        path: "$(System.ArtifactsDirectory)/openapispec"
 | 
				
			||||||
 | 
					        runVersion: "latest"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    - task: CmdLine@2
 | 
				
			||||||
 | 
					      displayName: 'Download OpenApi Generator'
 | 
				
			||||||
 | 
					      inputs:
 | 
				
			||||||
 | 
					        script: "wget https://repo1.maven.org/maven2/org/openapitools/openapi-generator-cli/${{ parameters.GeneratorVersion }}/openapi-generator-cli-${{ parameters.GeneratorVersion }}.jar -O openapi-generator-cli.jar"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Generate npm api client
 | 
				
			||||||
 | 
					# Unstable
 | 
				
			||||||
 | 
					    - task: CmdLine@2
 | 
				
			||||||
 | 
					      displayName: 'Build unstable typescript axios client'
 | 
				
			||||||
 | 
					      condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
 | 
				
			||||||
 | 
					      inputs:
 | 
				
			||||||
 | 
					        script: "bash ./apiclient/templates/typescript/axios/generate.sh $(System.ArtifactsDirectory) $(Build.BuildNumber)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Stable
 | 
				
			||||||
 | 
					    - task: CmdLine@2
 | 
				
			||||||
 | 
					      displayName: 'Build stable typescript axios client'
 | 
				
			||||||
 | 
					      condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
 | 
				
			||||||
 | 
					      inputs:
 | 
				
			||||||
 | 
					        script: "bash ./apiclient/templates/typescript/axios/generate.sh $(System.ArtifactsDirectory)"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Run npm install
 | 
				
			||||||
 | 
					    - task: Npm@1
 | 
				
			||||||
 | 
					      displayName: 'Install npm dependencies'
 | 
				
			||||||
 | 
					      inputs:
 | 
				
			||||||
 | 
					        command: install
 | 
				
			||||||
 | 
					        workingDir: ./apiclient/generated/typescript/axios
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Publish npm packages
 | 
				
			||||||
 | 
					# Unstable
 | 
				
			||||||
 | 
					    - task: Npm@1
 | 
				
			||||||
 | 
					      displayName: 'Publish unstable typescript axios client'
 | 
				
			||||||
 | 
					      condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
 | 
				
			||||||
 | 
					      inputs:
 | 
				
			||||||
 | 
					        command: publish
 | 
				
			||||||
 | 
					        publishRegistry: useFeed
 | 
				
			||||||
 | 
					        publishFeed: 'jellyfin/unstable'
 | 
				
			||||||
 | 
					        workingDir: ./apiclient/generated/typescript/axios
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Stable
 | 
				
			||||||
 | 
					    - task: Npm@1
 | 
				
			||||||
 | 
					      displayName: 'Publish stable typescript axios client'
 | 
				
			||||||
 | 
					      condition: startsWith(variables['Build.SourceBranch'], 'refs/tags/v')
 | 
				
			||||||
 | 
					      inputs:
 | 
				
			||||||
 | 
					        command: publish
 | 
				
			||||||
 | 
					        publishRegistry: useExternalRegistry
 | 
				
			||||||
 | 
					        publishEndpoint: 'jellyfin-bot for NPM'
 | 
				
			||||||
 | 
					        workingDir: ./apiclient/generated/typescript/axios
 | 
				
			||||||
@ -63,7 +63,38 @@ jobs:
 | 
				
			|||||||
      sshEndpoint: repository
 | 
					      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'
 | 
				
			||||||
 | 
				
			|||||||
@ -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)"
 | 
				
			||||||
 | 
				
			|||||||
@ -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
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -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
									
									
								
							
							
						
						
									
										6
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
								
							@ -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)",
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
@ -135,6 +136,8 @@
 | 
				
			|||||||
 - [YouKnowBlom](https://github.com/YouKnowBlom)
 | 
					 - [YouKnowBlom](https://github.com/YouKnowBlom)
 | 
				
			||||||
 - [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)
 | 
				
			||||||
 | 
					 - [skyfrk](https://github.com/skyfrk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Emby Contributors
 | 
					# Emby Contributors
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -198,3 +201,4 @@
 | 
				
			|||||||
 - [tikuf](https://github.com/tikuf/)
 | 
					 - [tikuf](https://github.com/tikuf/)
 | 
				
			||||||
 - [Tim Hobbs](https://github.com/timhobbs)
 | 
					 - [Tim Hobbs](https://github.com/timhobbs)
 | 
				
			||||||
 - [SvenVandenbrande](https://github.com/SvenVandenbrande)
 | 
					 - [SvenVandenbrande](https://github.com/SvenVandenbrande)
 | 
				
			||||||
 | 
					 - [olsh](https://github.com/olsh)
 | 
				
			||||||
 | 
				
			|||||||
@ -126,14 +126,14 @@ namespace Emby.Dlna
 | 
				
			|||||||
            var builder = new StringBuilder();
 | 
					            var builder = new StringBuilder();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            builder.AppendLine("No matching device profile found. The default will need to be used.");
 | 
					            builder.AppendLine("No matching device profile found. The default will need to be used.");
 | 
				
			||||||
            builder.AppendFormat(CultureInfo.InvariantCulture, "FriendlyName:{0}", profile.FriendlyName ?? string.Empty).AppendLine();
 | 
					            builder.Append("FriendlyName:").AppendLine(profile.FriendlyName);
 | 
				
			||||||
            builder.AppendFormat(CultureInfo.InvariantCulture, "Manufacturer:{0}", profile.Manufacturer ?? string.Empty).AppendLine();
 | 
					            builder.Append("Manufacturer:").AppendLine(profile.Manufacturer);
 | 
				
			||||||
            builder.AppendFormat(CultureInfo.InvariantCulture, "ManufacturerUrl:{0}", profile.ManufacturerUrl ?? string.Empty).AppendLine();
 | 
					            builder.Append("ManufacturerUrl:").AppendLine(profile.ManufacturerUrl);
 | 
				
			||||||
            builder.AppendFormat(CultureInfo.InvariantCulture, "ModelDescription:{0}", profile.ModelDescription ?? string.Empty).AppendLine();
 | 
					            builder.Append("ModelDescription:").AppendLine(profile.ModelDescription);
 | 
				
			||||||
            builder.AppendFormat(CultureInfo.InvariantCulture, "ModelName:{0}", profile.ModelName ?? string.Empty).AppendLine();
 | 
					            builder.Append("ModelName:").AppendLine(profile.ModelName);
 | 
				
			||||||
            builder.AppendFormat(CultureInfo.InvariantCulture, "ModelNumber:{0}", profile.ModelNumber ?? string.Empty).AppendLine();
 | 
					            builder.Append("ModelNumber:").AppendLine(profile.ModelNumber);
 | 
				
			||||||
            builder.AppendFormat(CultureInfo.InvariantCulture, "ModelUrl:{0}", profile.ModelUrl ?? string.Empty).AppendLine();
 | 
					            builder.Append("ModelUrl:").AppendLine(profile.ModelUrl);
 | 
				
			||||||
            builder.AppendFormat(CultureInfo.InvariantCulture, "SerialNumber:{0}", profile.SerialNumber ?? string.Empty).AppendLine();
 | 
					            builder.Append("SerialNumber:").AppendLine(profile.SerialNumber);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _logger.LogInformation(builder.ToString());
 | 
					            _logger.LogInformation(builder.ToString());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 286 B After Width: | Height: | Size: 278 B  | 
@ -669,9 +669,7 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
 | 
					        private Task SendGeneralCommand(GeneralCommand command, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (Enum.TryParse(command.Name, true, out GeneralCommandType commandType))
 | 
					            switch (command.Name)
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                switch (commandType)
 | 
					 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                case GeneralCommandType.VolumeDown:
 | 
					                case GeneralCommandType.VolumeDown:
 | 
				
			||||||
                    return _device.VolumeDown(cancellationToken);
 | 
					                    return _device.VolumeDown(cancellationToken);
 | 
				
			||||||
@ -724,9 +722,6 @@ namespace Emby.Dlna.PlayTo
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return Task.CompletedTask;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private async Task SetAudioStreamIndex(int? newIndex)
 | 
					        private async Task SetAudioStreamIndex(int? newIndex)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var media = _device.CurrentMediaInfo;
 | 
					            var media = _device.CurrentMediaInfo;
 | 
				
			||||||
@ -816,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)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -828,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);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -886,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;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -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
 | 
				
			||||||
 | 
				
			|||||||
@ -146,12 +146,12 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return new ControlRequestInfo();
 | 
					            throw new EndOfStreamException("Stream ended but no body tag found.");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task<ControlRequestInfo> ParseBodyTagAsync(XmlReader reader)
 | 
					        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);
 | 
				
			||||||
@ -161,14 +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)
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
 | 
					                        var result = new ControlRequestInfo(localName, namespaceURI);
 | 
				
			||||||
                        using var subReader = reader.ReadSubtree();
 | 
					                        using var subReader = reader.ReadSubtree();
 | 
				
			||||||
                        await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
 | 
					                        await ParseFirstBodyChildAsync(subReader, result.Headers).ConfigureAwait(false);
 | 
				
			||||||
                        return result;
 | 
					 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
                    {
 | 
					                    {
 | 
				
			||||||
@ -181,7 +181,12 @@ namespace Emby.Dlna.Service
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return result;
 | 
					            if (localName != null && namespaceURI != null)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return new ControlRequestInfo(localName, namespaceURI);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            throw new EndOfStreamException("Stream ended but no control found.");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
 | 
					        private async Task ParseFirstBodyChildAsync(XmlReader reader, IDictionary<string, string> headers)
 | 
				
			||||||
@ -228,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; }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -455,7 +455,7 @@ namespace Emby.Drawing
 | 
				
			|||||||
                throw new ArgumentException("Path can't be empty.", nameof(path));
 | 
					                throw new ArgumentException("Path can't be empty.", nameof(path));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (path.IsEmpty)
 | 
					            if (filename.IsEmpty)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                throw new ArgumentException("Filename can't be empty.", nameof(filename));
 | 
					                throw new ArgumentException("Filename can't be empty.", nameof(filename));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,6 @@
 | 
				
			|||||||
 | 
					#nullable enable
 | 
				
			||||||
#pragma warning disable CS1591
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using System;
 | 
					 | 
				
			||||||
using System.Globalization;
 | 
					using System.Globalization;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Text.RegularExpressions;
 | 
					using System.Text.RegularExpressions;
 | 
				
			||||||
@ -19,12 +19,7 @@ namespace Emby.Naming.AudioBook
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        public AudioBookFilePathParserResult Parse(string path)
 | 
					        public AudioBookFilePathParserResult Parse(string path)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            if (path == null)
 | 
					            AudioBookFilePathParserResult result = default;
 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                throw new ArgumentNullException(nameof(path));
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            var result = new AudioBookFilePathParserResult();
 | 
					 | 
				
			||||||
            var fileName = Path.GetFileNameWithoutExtension(path);
 | 
					            var fileName = Path.GetFileNameWithoutExtension(path);
 | 
				
			||||||
            foreach (var expression in _options.AudioBookPartsExpressions)
 | 
					            foreach (var expression in _options.AudioBookPartsExpressions)
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -50,27 +45,14 @@ namespace Emby.Naming.AudioBook
 | 
				
			|||||||
                        {
 | 
					                        {
 | 
				
			||||||
                            if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
 | 
					                            if (int.TryParse(value.Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var intValue))
 | 
				
			||||||
                            {
 | 
					                            {
 | 
				
			||||||
                                result.ChapterNumber = intValue;
 | 
					                                result.PartNumber = intValue;
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            /*var matches = _iRegexProvider.GetRegex("\\d+", RegexOptions.IgnoreCase).Matches(fileName);
 | 
					            result.Success = result.ChapterNumber.HasValue || result.PartNumber.HasValue;
 | 
				
			||||||
            if (matches.Count > 0)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                if (!result.ChapterNumber.HasValue)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    result.ChapterNumber = int.Parse(matches[0].Groups[0].Value);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                if (matches.Count > 1)
 | 
					 | 
				
			||||||
                {
 | 
					 | 
				
			||||||
                    result.PartNumber = int.Parse(matches[matches.Count - 1].Groups[0].Value);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }*/
 | 
					 | 
				
			||||||
            result.Success = result.PartNumber.HasValue || result.ChapterNumber.HasValue;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,9 @@
 | 
				
			|||||||
 | 
					#nullable enable
 | 
				
			||||||
#pragma warning disable CS1591
 | 
					#pragma warning disable CS1591
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace Emby.Naming.AudioBook
 | 
					namespace Emby.Naming.AudioBook
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
    public class AudioBookFilePathParserResult
 | 
					    public struct AudioBookFilePathParserResult
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
        public int? PartNumber { get; set; }
 | 
					        public int? PartNumber { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -55,8 +55,8 @@ namespace Emby.Naming.AudioBook
 | 
				
			|||||||
            {
 | 
					            {
 | 
				
			||||||
                Path = path,
 | 
					                Path = path,
 | 
				
			||||||
                Container = container,
 | 
					                Container = container,
 | 
				
			||||||
                PartNumber = parsingResult.PartNumber,
 | 
					 | 
				
			||||||
                ChapterNumber = parsingResult.ChapterNumber,
 | 
					                ChapterNumber = parsingResult.ChapterNumber,
 | 
				
			||||||
 | 
					                PartNumber = parsingResult.PartNumber,
 | 
				
			||||||
                IsDirectory = isDirectory
 | 
					                IsDirectory = isDirectory
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
				
			|||||||
@ -4,6 +4,7 @@ using System;
 | 
				
			|||||||
using System.Collections.Concurrent;
 | 
					using System.Collections.Concurrent;
 | 
				
			||||||
using System.Collections.Generic;
 | 
					using System.Collections.Generic;
 | 
				
			||||||
using System.Diagnostics;
 | 
					using System.Diagnostics;
 | 
				
			||||||
 | 
					using System.Globalization;
 | 
				
			||||||
using System.IO;
 | 
					using System.IO;
 | 
				
			||||||
using System.Linq;
 | 
					using System.Linq;
 | 
				
			||||||
using System.Net;
 | 
					using System.Net;
 | 
				
			||||||
@ -37,6 +38,7 @@ using Emby.Server.Implementations.LiveTv;
 | 
				
			|||||||
using Emby.Server.Implementations.Localization;
 | 
					using Emby.Server.Implementations.Localization;
 | 
				
			||||||
using Emby.Server.Implementations.Net;
 | 
					using Emby.Server.Implementations.Net;
 | 
				
			||||||
using Emby.Server.Implementations.Playlists;
 | 
					using Emby.Server.Implementations.Playlists;
 | 
				
			||||||
 | 
					using Emby.Server.Implementations.Plugins;
 | 
				
			||||||
using Emby.Server.Implementations.QuickConnect;
 | 
					using Emby.Server.Implementations.QuickConnect;
 | 
				
			||||||
using Emby.Server.Implementations.ScheduledTasks;
 | 
					using Emby.Server.Implementations.ScheduledTasks;
 | 
				
			||||||
using Emby.Server.Implementations.Security;
 | 
					using Emby.Server.Implementations.Security;
 | 
				
			||||||
@ -95,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;
 | 
				
			||||||
@ -119,6 +122,7 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
        private readonly IFileSystem _fileSystemManager;
 | 
					        private readonly IFileSystem _fileSystemManager;
 | 
				
			||||||
        private readonly INetworkManager _networkManager;
 | 
					        private readonly INetworkManager _networkManager;
 | 
				
			||||||
        private readonly IXmlSerializer _xmlSerializer;
 | 
					        private readonly IXmlSerializer _xmlSerializer;
 | 
				
			||||||
 | 
					        private readonly IJsonSerializer _jsonSerializer;
 | 
				
			||||||
        private readonly IStartupOptions _startupOptions;
 | 
					        private readonly IStartupOptions _startupOptions;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private IMediaEncoder _mediaEncoder;
 | 
					        private IMediaEncoder _mediaEncoder;
 | 
				
			||||||
@ -255,6 +259,8 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
            IServiceCollection serviceCollection)
 | 
					            IServiceCollection serviceCollection)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            _xmlSerializer = new MyXmlSerializer();
 | 
					            _xmlSerializer = new MyXmlSerializer();
 | 
				
			||||||
 | 
					            _jsonSerializer = new JsonSerializer();            
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            ServiceCollection = serviceCollection;
 | 
					            ServiceCollection = serviceCollection;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            _networkManager = networkManager;
 | 
					            _networkManager = networkManager;
 | 
				
			||||||
@ -532,6 +538,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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1021,6 +1028,119 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        protected abstract void RestartInternal();
 | 
					        protected abstract void RestartInternal();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Comparison function used in <see cref="GetPlugins" />.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="a">Item to compare.</param>
 | 
				
			||||||
 | 
					        /// <param name="b">Item to compare with.</param>
 | 
				
			||||||
 | 
					        /// <returns>Boolean result of the operation.</returns>
 | 
				
			||||||
 | 
					        private static int VersionCompare(
 | 
				
			||||||
 | 
					            (Version PluginVersion, string Name, string Path) a,
 | 
				
			||||||
 | 
					            (Version PluginVersion, string Name, string Path) b)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            int compare = string.Compare(a.Name, b.Name, true, CultureInfo.InvariantCulture);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (compare == 0)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                return a.PluginVersion.CompareTo(b.PluginVersion);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return compare;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Returns a list of plugins to install.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        /// <param name="path">Path to check.</param>
 | 
				
			||||||
 | 
					        /// <param name="cleanup">True if an attempt should be made to delete old plugs.</param>
 | 
				
			||||||
 | 
					        /// <returns>Enumerable list of dlls to load.</returns>
 | 
				
			||||||
 | 
					        private IEnumerable<string> GetPlugins(string path, bool cleanup = true)
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            var dllList = new List<string>();
 | 
				
			||||||
 | 
					            var versions = new List<(Version PluginVersion, string Name, string Path)>();
 | 
				
			||||||
 | 
					            var directories = Directory.EnumerateDirectories(path, "*.*", SearchOption.TopDirectoryOnly);
 | 
				
			||||||
 | 
					            string metafile;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            foreach (var dir in directories)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                try
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    metafile = Path.Combine(dir, "meta.json");
 | 
				
			||||||
 | 
					                    if (File.Exists(metafile))
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        var manifest = _jsonSerializer.DeserializeFromFile<PluginManifest>(metafile);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (!Version.TryParse(manifest.TargetAbi, out var targetAbi))
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            targetAbi = new Version(0, 0, 0, 1);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (!Version.TryParse(manifest.Version, out var version))
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            version = new Version(0, 0, 0, 1);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        if (ApplicationVersion >= targetAbi)
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // Only load Plugins if the plugin is built for this version or below.
 | 
				
			||||||
 | 
					                            versions.Add((version, manifest.Name, dir));
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        // No metafile, so lets see if the folder is versioned.
 | 
				
			||||||
 | 
					                        metafile = dir.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries)[^1];
 | 
				
			||||||
 | 
					                        
 | 
				
			||||||
 | 
					                        int versionIndex = dir.LastIndexOf('_');
 | 
				
			||||||
 | 
					                        if (versionIndex != -1 && Version.TryParse(dir.Substring(versionIndex + 1), out Version ver))
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // Versioned folder.
 | 
				
			||||||
 | 
					                            versions.Add((ver, metafile, dir));
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else
 | 
				
			||||||
 | 
					                        {
 | 
				
			||||||
 | 
					                            // Un-versioned folder - Add it under the path name and version 0.0.0.1.                        
 | 
				
			||||||
 | 
					                            versions.Add((new Version(0, 0, 0, 1), metafile, dir));
 | 
				
			||||||
 | 
					                        }   
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                catch
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            string lastName = string.Empty;
 | 
				
			||||||
 | 
					            versions.Sort(VersionCompare);
 | 
				
			||||||
 | 
					            // Traverse backwards through the list.
 | 
				
			||||||
 | 
					            // The first item will be the latest version.
 | 
				
			||||||
 | 
					            for (int x = versions.Count - 1; x >= 0; x--)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                if (!string.Equals(lastName, versions[x].Name, StringComparison.OrdinalIgnoreCase))
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    dllList.AddRange(Directory.EnumerateFiles(versions[x].Path, "*.dll", SearchOption.AllDirectories));
 | 
				
			||||||
 | 
					                    lastName = versions[x].Name;
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!string.IsNullOrEmpty(lastName) && cleanup)
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    // Attempt a cleanup of old folders.
 | 
				
			||||||
 | 
					                    try
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Logger.LogDebug("Deleting {Path}", versions[x].Path);
 | 
				
			||||||
 | 
					                        Directory.Delete(versions[x].Path, true);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    catch (Exception e)
 | 
				
			||||||
 | 
					                    {
 | 
				
			||||||
 | 
					                        Logger.LogWarning(e, "Unable to delete {Path}", versions[x].Path);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return dllList;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Gets the composable part assemblies.
 | 
					        /// Gets the composable part assemblies.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
@ -1029,7 +1149,7 @@ namespace Emby.Server.Implementations
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (Directory.Exists(ApplicationPaths.PluginsPath))
 | 
					            if (Directory.Exists(ApplicationPaths.PluginsPath))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                foreach (var file in Directory.EnumerateFiles(ApplicationPaths.PluginsPath, "*.dll", SearchOption.AllDirectories))
 | 
					                foreach (var file in GetPlugins(ApplicationPaths.PluginsPath))
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    Assembly plugAss;
 | 
					                    Assembly plugAss;
 | 
				
			||||||
                    try
 | 
					                    try
 | 
				
			||||||
 | 
				
			|||||||
@ -1,51 +0,0 @@
 | 
				
			|||||||
using System;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					 | 
				
			||||||
using Microsoft.Extensions.Logging;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Emby.Server.Implementations.Browser
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Assists in opening application URLs in an external browser.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public static class BrowserLauncher
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Opens the home page of the web client.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="appHost">The app host.</param>
 | 
					 | 
				
			||||||
        public static void OpenWebApp(IServerApplicationHost appHost)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            TryOpenUrl(appHost, "/web/index.html");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Opens the swagger API page.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="appHost">The app host.</param>
 | 
					 | 
				
			||||||
        public static void OpenSwaggerPage(IServerApplicationHost appHost)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            TryOpenUrl(appHost, "/api-docs/swagger");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="appHost">The application host.</param>
 | 
					 | 
				
			||||||
        /// <param name="relativeUrl">The URL to open, relative to the server base URL.</param>
 | 
					 | 
				
			||||||
        private static void TryOpenUrl(IServerApplicationHost appHost, string relativeUrl)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            try
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                string baseUrl = appHost.GetLocalApiUrl("localhost");
 | 
					 | 
				
			||||||
                appHost.LaunchUrl(baseUrl + relativeUrl);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            catch (Exception ex)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                var logger = appHost.Resolve<ILogger<IServerApplicationHost>>();
 | 
					 | 
				
			||||||
                logger?.LogError(ex, "Failed to open browser window with URL {URL}", relativeUrl);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -234,7 +234,9 @@ namespace Emby.Server.Implementations.Data
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
 | 
					            if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                bindParam.Bind(value.ToByteArray());
 | 
					                Span<byte> byteValue = stackalloc byte[16];
 | 
				
			||||||
 | 
					                value.TryWriteBytes(byteValue);
 | 
				
			||||||
 | 
					                bindParam.Bind(byteValue);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            else
 | 
					            else
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
				
			|||||||
@ -2263,7 +2263,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",
 | 
				
			||||||
@ -3291,7 +3290,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>();
 | 
				
			||||||
@ -6006,7 +6004,6 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
        /// Gets the chapter.
 | 
					        /// Gets the chapter.
 | 
				
			||||||
        /// </summary>
 | 
					        /// </summary>
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -1431,7 +1432,7 @@ namespace Emby.Server.Implementations.Dto
 | 
				
			|||||||
                return null;
 | 
					                return null;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return width / height;
 | 
					            return (double)width / height;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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.7" />
 | 
					    <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.1.8" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.7" />
 | 
					    <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.1.8" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.7" />
 | 
					    <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.8" />
 | 
				
			||||||
    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.7" />
 | 
					    <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.8" />
 | 
				
			||||||
    <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" />
 | 
				
			||||||
 | 
				
			|||||||
@ -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)
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
 | 
				
			|||||||
@ -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();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,83 +0,0 @@
 | 
				
			|||||||
using System.Threading.Tasks;
 | 
					 | 
				
			||||||
using Emby.Server.Implementations.Browser;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Configuration;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Extensions;
 | 
					 | 
				
			||||||
using MediaBrowser.Controller.Plugins;
 | 
					 | 
				
			||||||
using Microsoft.Extensions.Configuration;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
namespace Emby.Server.Implementations.EntryPoints
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
    /// <summary>
 | 
					 | 
				
			||||||
    /// Class StartupWizard.
 | 
					 | 
				
			||||||
    /// </summary>
 | 
					 | 
				
			||||||
    public sealed class StartupWizard : IServerEntryPoint
 | 
					 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        private readonly IServerApplicationHost _appHost;
 | 
					 | 
				
			||||||
        private readonly IConfiguration _appConfig;
 | 
					 | 
				
			||||||
        private readonly IServerConfigurationManager _config;
 | 
					 | 
				
			||||||
        private readonly IStartupOptions _startupOptions;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <summary>
 | 
					 | 
				
			||||||
        /// Initializes a new instance of the <see cref="StartupWizard"/> class.
 | 
					 | 
				
			||||||
        /// </summary>
 | 
					 | 
				
			||||||
        /// <param name="appHost">The application host.</param>
 | 
					 | 
				
			||||||
        /// <param name="appConfig">The application configuration.</param>
 | 
					 | 
				
			||||||
        /// <param name="config">The configuration manager.</param>
 | 
					 | 
				
			||||||
        /// <param name="startupOptions">The application startup options.</param>
 | 
					 | 
				
			||||||
        public StartupWizard(
 | 
					 | 
				
			||||||
            IServerApplicationHost appHost,
 | 
					 | 
				
			||||||
            IConfiguration appConfig,
 | 
					 | 
				
			||||||
            IServerConfigurationManager config,
 | 
					 | 
				
			||||||
            IStartupOptions startupOptions)
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            _appHost = appHost;
 | 
					 | 
				
			||||||
            _appConfig = appConfig;
 | 
					 | 
				
			||||||
            _config = config;
 | 
					 | 
				
			||||||
            _startupOptions = startupOptions;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <inheritdoc />
 | 
					 | 
				
			||||||
        public Task RunAsync()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            Run();
 | 
					 | 
				
			||||||
            return Task.CompletedTask;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        private void Run()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
            if (!_appHost.CanLaunchWebBrowser)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Always launch the startup wizard if possible when it has not been completed
 | 
					 | 
				
			||||||
            if (!_config.Configuration.IsStartupWizardCompleted && _appConfig.HostWebClient())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                BrowserLauncher.OpenWebApp(_appHost);
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Do nothing if the web app is configured to not run automatically
 | 
					 | 
				
			||||||
            if (!_config.Configuration.AutoRunWebApp || _startupOptions.NoAutoRunWebApp)
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Launch the swagger page if the web client is not hosted, otherwise open the web client
 | 
					 | 
				
			||||||
            if (_appConfig.HostWebClient())
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                BrowserLauncher.OpenWebApp(_appHost);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            else
 | 
					 | 
				
			||||||
            {
 | 
					 | 
				
			||||||
                BrowserLauncher.OpenSwaggerPage(_appHost);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        /// <inheritdoc />
 | 
					 | 
				
			||||||
        public void Dispose()
 | 
					 | 
				
			||||||
        {
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -28,7 +28,6 @@ namespace Emby.Server.Implementations.EntryPoints
 | 
				
			|||||||
        private readonly object _syncLock = new object();
 | 
					        private 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)
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -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>
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -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; }
 | 
				
			||||||
 | 
				
			|||||||
@ -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>
 | 
				
			||||||
 | 
				
			|||||||
@ -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);
 | 
				
			||||||
 | 
				
			|||||||
@ -1,19 +1,19 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "Artists": "Kunstenare",
 | 
					    "Artists": "Kunstenare",
 | 
				
			||||||
    "Channels": "Kanale",
 | 
					    "Channels": "Kanale",
 | 
				
			||||||
    "Folders": "Fouers",
 | 
					    "Folders": "Lêergidse",
 | 
				
			||||||
    "Favorites": "Gunstelinge",
 | 
					    "Favorites": "Gunstellinge",
 | 
				
			||||||
    "HeaderFavoriteShows": "Gunsteling Vertonings",
 | 
					    "HeaderFavoriteShows": "Gunsteling Vertonings",
 | 
				
			||||||
    "ValueSpecialEpisodeName": "Spesiale - {0}",
 | 
					    "ValueSpecialEpisodeName": "Spesiale - {0}",
 | 
				
			||||||
    "HeaderAlbumArtists": "Album Kunstenaars",
 | 
					    "HeaderAlbumArtists": "Album Kunstenaars",
 | 
				
			||||||
    "Books": "Boeke",
 | 
					    "Books": "Boeke",
 | 
				
			||||||
    "HeaderNextUp": "Volgende",
 | 
					    "HeaderNextUp": "Volgende",
 | 
				
			||||||
    "Movies": "Rolprente",
 | 
					    "Movies": "Flieks",
 | 
				
			||||||
    "Shows": "Program",
 | 
					    "Shows": "Televisie Reekse",
 | 
				
			||||||
    "HeaderContinueWatching": "Hou Aan Kyk",
 | 
					    "HeaderContinueWatching": "Kyk Verder",
 | 
				
			||||||
    "HeaderFavoriteEpisodes": "Gunsteling Episodes",
 | 
					    "HeaderFavoriteEpisodes": "Gunsteling Episodes",
 | 
				
			||||||
    "Photos": "Fotos",
 | 
					    "Photos": "Fotos",
 | 
				
			||||||
    "Playlists": "Speellysse",
 | 
					    "Playlists": "Snitlyste",
 | 
				
			||||||
    "HeaderFavoriteArtists": "Gunsteling Kunstenaars",
 | 
					    "HeaderFavoriteArtists": "Gunsteling Kunstenaars",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "Gunsteling Albums",
 | 
					    "HeaderFavoriteAlbums": "Gunsteling Albums",
 | 
				
			||||||
    "Sync": "Sinkroniseer",
 | 
					    "Sync": "Sinkroniseer",
 | 
				
			||||||
@ -23,7 +23,7 @@
 | 
				
			|||||||
    "DeviceOfflineWithName": "{0} is ontkoppel",
 | 
					    "DeviceOfflineWithName": "{0} is ontkoppel",
 | 
				
			||||||
    "Collections": "Versamelings",
 | 
					    "Collections": "Versamelings",
 | 
				
			||||||
    "Inherit": "Ontvang",
 | 
					    "Inherit": "Ontvang",
 | 
				
			||||||
    "HeaderLiveTV": "Live TV",
 | 
					    "HeaderLiveTV": "Lewendige TV",
 | 
				
			||||||
    "Application": "Program",
 | 
					    "Application": "Program",
 | 
				
			||||||
    "AppDeviceValues": "App: {0}, Toestel: {1}",
 | 
					    "AppDeviceValues": "App: {0}, Toestel: {1}",
 | 
				
			||||||
    "VersionNumber": "Weergawe {0}",
 | 
					    "VersionNumber": "Weergawe {0}",
 | 
				
			||||||
@ -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",
 | 
				
			||||||
@ -95,5 +94,23 @@
 | 
				
			|||||||
    "TasksChannelsCategory": "Internet kanale",
 | 
					    "TasksChannelsCategory": "Internet kanale",
 | 
				
			||||||
    "TasksApplicationCategory": "aansoek",
 | 
					    "TasksApplicationCategory": "aansoek",
 | 
				
			||||||
    "TasksLibraryCategory": "biblioteek",
 | 
					    "TasksLibraryCategory": "biblioteek",
 | 
				
			||||||
    "TasksMaintenanceCategory": "onderhoud"
 | 
					    "TasksMaintenanceCategory": "onderhoud",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "Vee kasregister lêers uit wat nie meer deur die stelsel benodig word nie.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Reinig Kasgeheue Lêergids",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "Soek aanlyn vir vermiste onderskrifte gebasseer op metadata verstellings.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "Laai vermiste onderskrifte af",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "Vervris internet kanaal inligting.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "Vervris Kanale",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "Vee transkodering lêers uit wat ouer is as een dag.",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "Reinig Transkoderings Leêrbinder",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "Laai opgedateerde inprop-sagteware af en installeer inprop-sagteware wat verstel is om outomaties op te dateer.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "Dateer Inprop-Sagteware Op",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "Vervris metadata oor akteurs en regisseurs in u media versameling.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "Vervris Mense",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "Vee loglêers wat ouer as {0} dae is uit.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "Reinig Loglêer Lêervouer",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "Skandeer u media versameling vir nuwe lêers en verfris metadata.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "Skandeer Media Versameling",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "Maak kleinkiekeis (fotos) vir films wat hoofstukke het.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "Verkry Hoofstuk Beelde"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "المجلدات",
 | 
					    "Folders": "المجلدات",
 | 
				
			||||||
    "Genres": "التضنيفات",
 | 
					    "Genres": "التضنيفات",
 | 
				
			||||||
    "HeaderAlbumArtists": "فناني الألبومات",
 | 
					    "HeaderAlbumArtists": "فناني الألبومات",
 | 
				
			||||||
    "HeaderCameraUploads": "تحميلات الكاميرا",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "استئناف",
 | 
					    "HeaderContinueWatching": "استئناف",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "الألبومات المفضلة",
 | 
					    "HeaderFavoriteAlbums": "الألبومات المفضلة",
 | 
				
			||||||
    "HeaderFavoriteArtists": "الفنانون المفضلون",
 | 
					    "HeaderFavoriteArtists": "الفنانون المفضلون",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "Папки",
 | 
					    "Folders": "Папки",
 | 
				
			||||||
    "Genres": "Жанрове",
 | 
					    "Genres": "Жанрове",
 | 
				
			||||||
    "HeaderAlbumArtists": "Изпълнители на албуми",
 | 
					    "HeaderAlbumArtists": "Изпълнители на албуми",
 | 
				
			||||||
    "HeaderCameraUploads": "Качени от камера",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "Продължаване на гледането",
 | 
					    "HeaderContinueWatching": "Продължаване на гледането",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "Любими албуми",
 | 
					    "HeaderFavoriteAlbums": "Любими албуми",
 | 
				
			||||||
    "HeaderFavoriteArtists": "Любими изпълнители",
 | 
					    "HeaderFavoriteArtists": "Любими изпълнители",
 | 
				
			||||||
 | 
				
			|||||||
@ -14,7 +14,6 @@
 | 
				
			|||||||
    "HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
 | 
					    "HeaderFavoriteArtists": "প্রিয় শিল্পীরা",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
 | 
					    "HeaderFavoriteAlbums": "প্রিয় এলবামগুলো",
 | 
				
			||||||
    "HeaderContinueWatching": "দেখতে থাকুন",
 | 
					    "HeaderContinueWatching": "দেখতে থাকুন",
 | 
				
			||||||
    "HeaderCameraUploads": "ক্যামেরার আপলোড সমূহ",
 | 
					 | 
				
			||||||
    "HeaderAlbumArtists": "এলবাম শিল্পী",
 | 
					    "HeaderAlbumArtists": "এলবাম শিল্পী",
 | 
				
			||||||
    "Genres": "জেনার",
 | 
					    "Genres": "জেনার",
 | 
				
			||||||
    "Folders": "ফোল্ডারগুলো",
 | 
					    "Folders": "ফোল্ডারগুলো",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "Φάκελοι",
 | 
					    "Folders": "Φάκελοι",
 | 
				
			||||||
    "Genres": "Είδη",
 | 
					    "Genres": "Είδη",
 | 
				
			||||||
    "HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
 | 
					    "HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
 | 
				
			||||||
    "HeaderCameraUploads": "Μεταφορτώσεις Κάμερας",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
 | 
					    "HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
 | 
					    "HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
 | 
				
			||||||
    "HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
 | 
					    "HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "پوشهها",
 | 
					    "Folders": "پوشهها",
 | 
				
			||||||
    "Genres": "ژانرها",
 | 
					    "Genres": "ژانرها",
 | 
				
			||||||
    "HeaderAlbumArtists": "هنرمندان آلبوم",
 | 
					    "HeaderAlbumArtists": "هنرمندان آلبوم",
 | 
				
			||||||
    "HeaderCameraUploads": "آپلودهای دوربین",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "ادامه تماشا",
 | 
					    "HeaderContinueWatching": "ادامه تماشا",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "آلبومهای مورد علاقه",
 | 
					    "HeaderFavoriteAlbums": "آلبومهای مورد علاقه",
 | 
				
			||||||
    "HeaderFavoriteArtists": "هنرمندان مورد علاقه",
 | 
					    "HeaderFavoriteArtists": "هنرمندان مورد علاقه",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
@ -107,7 +106,7 @@
 | 
				
			|||||||
    "TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.",
 | 
					    "TaskCleanLogsDescription": "Supprime les journaux de plus de {0} jours.",
 | 
				
			||||||
    "TaskCleanLogs": "Nettoyer le répertoire des journaux",
 | 
					    "TaskCleanLogs": "Nettoyer le répertoire des journaux",
 | 
				
			||||||
    "TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.",
 | 
					    "TaskRefreshLibraryDescription": "Scanne toute les bibliothèques pour trouver les nouveaux fichiers et rafraîchit les métadonnées.",
 | 
				
			||||||
    "TaskRefreshLibrary": "Scanner toute les Bibliothèques",
 | 
					    "TaskRefreshLibrary": "Scanner toutes les Bibliothèques",
 | 
				
			||||||
    "TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.",
 | 
					    "TaskRefreshChapterImagesDescription": "Crée des images de miniature pour les vidéos ayant des chapitres.",
 | 
				
			||||||
    "TaskRefreshChapterImages": "Extraire les images de chapitre",
 | 
					    "TaskRefreshChapterImages": "Extraire les images de chapitre",
 | 
				
			||||||
    "TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.",
 | 
					    "TaskCleanCacheDescription": "Supprime les fichiers de cache dont le système n'a plus besoin.",
 | 
				
			||||||
 | 
				
			|||||||
@ -1,3 +1,11 @@
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
    "Albums": "Álbumes"
 | 
					    "Albums": "Álbumes",
 | 
				
			||||||
 | 
					    "Collections": "Colecións",
 | 
				
			||||||
 | 
					    "ChapterNameValue": "Capítulos {0}",
 | 
				
			||||||
 | 
					    "Channels": "Canles",
 | 
				
			||||||
 | 
					    "CameraImageUploadedFrom": "Cargouse unha nova imaxe da cámara desde {0}",
 | 
				
			||||||
 | 
					    "Books": "Libros",
 | 
				
			||||||
 | 
					    "AuthenticationSucceededWithUserName": "{0} autenticouse correctamente",
 | 
				
			||||||
 | 
					    "Artists": "Artistas",
 | 
				
			||||||
 | 
					    "Application": "Aplicativo"
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "Ordner",
 | 
					    "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",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "תיקיות",
 | 
					    "Folders": "תיקיות",
 | 
				
			||||||
    "Genres": "ז'אנרים",
 | 
					    "Genres": "ז'אנרים",
 | 
				
			||||||
    "HeaderAlbumArtists": "אמני האלבום",
 | 
					    "HeaderAlbumArtists": "אמני האלבום",
 | 
				
			||||||
    "HeaderCameraUploads": "העלאות ממצלמה",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "המשך לצפות",
 | 
					    "HeaderContinueWatching": "המשך לצפות",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "אלבומים מועדפים",
 | 
					    "HeaderFavoriteAlbums": "אלבומים מועדפים",
 | 
				
			||||||
    "HeaderFavoriteArtists": "אמנים מועדפים",
 | 
					    "HeaderFavoriteArtists": "אמנים מועדפים",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "フォルダー",
 | 
					    "Folders": "フォルダー",
 | 
				
			||||||
    "Genres": "ジャンル",
 | 
					    "Genres": "ジャンル",
 | 
				
			||||||
    "HeaderAlbumArtists": "アルバムアーティスト",
 | 
					    "HeaderAlbumArtists": "アルバムアーティスト",
 | 
				
			||||||
    "HeaderCameraUploads": "カメラアップロード",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "視聴を続ける",
 | 
					    "HeaderContinueWatching": "視聴を続ける",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "お気に入りのアルバム",
 | 
					    "HeaderFavoriteAlbums": "お気に入りのアルバム",
 | 
				
			||||||
    "HeaderFavoriteArtists": "お気に入りのアーティスト",
 | 
					    "HeaderFavoriteArtists": "お気に入りのアーティスト",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "폴더",
 | 
					    "Folders": "폴더",
 | 
				
			||||||
    "Genres": "장르",
 | 
					    "Genres": "장르",
 | 
				
			||||||
    "HeaderAlbumArtists": "앨범 아티스트",
 | 
					    "HeaderAlbumArtists": "앨범 아티스트",
 | 
				
			||||||
    "HeaderCameraUploads": "카메라 업로드",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "계속 시청하기",
 | 
					    "HeaderContinueWatching": "계속 시청하기",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "즐겨찾는 앨범",
 | 
					    "HeaderFavoriteAlbums": "즐겨찾는 앨범",
 | 
				
			||||||
    "HeaderFavoriteArtists": "즐겨찾는 아티스트",
 | 
					    "HeaderFavoriteArtists": "즐겨찾는 아티스트",
 | 
				
			||||||
@ -84,8 +83,8 @@
 | 
				
			|||||||
    "UserDeletedWithName": "사용자 {0} 삭제됨",
 | 
					    "UserDeletedWithName": "사용자 {0} 삭제됨",
 | 
				
			||||||
    "UserDownloadingItemWithValues": "{0}이(가) {1}을 다운로드 중입니다",
 | 
					    "UserDownloadingItemWithValues": "{0}이(가) {1}을 다운로드 중입니다",
 | 
				
			||||||
    "UserLockedOutWithName": "유저 {0} 은(는) 잠금처리 되었습니다",
 | 
					    "UserLockedOutWithName": "유저 {0} 은(는) 잠금처리 되었습니다",
 | 
				
			||||||
    "UserOfflineFromDevice": "{1}로부터 {0}의 연결이 끊겼습니다",
 | 
					    "UserOfflineFromDevice": "{1}에서 {0}의 연결이 끊킴",
 | 
				
			||||||
    "UserOnlineFromDevice": "{0}은 {1}에서 온라인 상태입니다",
 | 
					    "UserOnlineFromDevice": "{0}이 {1}으로 접속",
 | 
				
			||||||
    "UserPasswordChangedWithName": "사용자 {0}의 비밀번호가 변경되었습니다",
 | 
					    "UserPasswordChangedWithName": "사용자 {0}의 비밀번호가 변경되었습니다",
 | 
				
			||||||
    "UserPolicyUpdatedWithName": "{0}의 사용자 정책이 업데이트되었습니다",
 | 
					    "UserPolicyUpdatedWithName": "{0}의 사용자 정책이 업데이트되었습니다",
 | 
				
			||||||
    "UserStartedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생 중",
 | 
					    "UserStartedPlayingItemWithValues": "{2}에서 {0}이 {1} 재생 중",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -51,7 +51,6 @@
 | 
				
			|||||||
    "HeaderFavoriteArtists": "Омилени Изведувачи",
 | 
					    "HeaderFavoriteArtists": "Омилени Изведувачи",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "Омилени Албуми",
 | 
					    "HeaderFavoriteAlbums": "Омилени Албуми",
 | 
				
			||||||
    "HeaderContinueWatching": "Продолжи со гледање",
 | 
					    "HeaderContinueWatching": "Продолжи со гледање",
 | 
				
			||||||
    "HeaderCameraUploads": "Поставувања од камера",
 | 
					 | 
				
			||||||
    "HeaderAlbumArtists": "Изведувачи од Албуми",
 | 
					    "HeaderAlbumArtists": "Изведувачи од Албуми",
 | 
				
			||||||
    "Genres": "Жанрови",
 | 
					    "Genres": "Жанрови",
 | 
				
			||||||
    "Folders": "Папки",
 | 
					    "Folders": "Папки",
 | 
				
			||||||
 | 
				
			|||||||
@ -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}",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
@ -50,7 +49,7 @@
 | 
				
			|||||||
    "NotificationOptionAudioPlayback": "Lydavspilling startet",
 | 
					    "NotificationOptionAudioPlayback": "Lydavspilling startet",
 | 
				
			||||||
    "NotificationOptionAudioPlaybackStopped": "Lydavspilling stoppet",
 | 
					    "NotificationOptionAudioPlaybackStopped": "Lydavspilling stoppet",
 | 
				
			||||||
    "NotificationOptionCameraImageUploaded": "Kamerabilde lastet opp",
 | 
					    "NotificationOptionCameraImageUploaded": "Kamerabilde lastet opp",
 | 
				
			||||||
    "NotificationOptionInstallationFailed": "Installasjonsfeil",
 | 
					    "NotificationOptionInstallationFailed": "Installasjonen feilet",
 | 
				
			||||||
    "NotificationOptionNewLibraryContent": "Nytt innhold lagt til",
 | 
					    "NotificationOptionNewLibraryContent": "Nytt innhold lagt til",
 | 
				
			||||||
    "NotificationOptionPluginError": "Pluginfeil",
 | 
					    "NotificationOptionPluginError": "Pluginfeil",
 | 
				
			||||||
    "NotificationOptionPluginInstalled": "Plugin installert",
 | 
					    "NotificationOptionPluginInstalled": "Plugin installert",
 | 
				
			||||||
 | 
				
			|||||||
@ -41,7 +41,6 @@
 | 
				
			|||||||
    "HeaderFavoriteArtists": "मनपर्ने कलाकारहरू",
 | 
					    "HeaderFavoriteArtists": "मनपर्ने कलाकारहरू",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "मनपर्ने एल्बमहरू",
 | 
					    "HeaderFavoriteAlbums": "मनपर्ने एल्बमहरू",
 | 
				
			||||||
    "HeaderContinueWatching": "हेर्न जारी राख्नुहोस्",
 | 
					    "HeaderContinueWatching": "हेर्न जारी राख्नुहोस्",
 | 
				
			||||||
    "HeaderCameraUploads": "क्यामेरा अपलोडहरू",
 | 
					 | 
				
			||||||
    "HeaderAlbumArtists": "एल्बमका कलाकारहरू",
 | 
					    "HeaderAlbumArtists": "एल्बमका कलाकारहरू",
 | 
				
			||||||
    "Genres": "विधाहरू",
 | 
					    "Genres": "विधाहरू",
 | 
				
			||||||
    "Folders": "फोल्डरहरू",
 | 
					    "Folders": "फोल्डरहरू",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "Папки",
 | 
					    "Folders": "Папки",
 | 
				
			||||||
    "Genres": "Жанры",
 | 
					    "Genres": "Жанры",
 | 
				
			||||||
    "HeaderAlbumArtists": "Исполнители альбома",
 | 
					    "HeaderAlbumArtists": "Исполнители альбома",
 | 
				
			||||||
    "HeaderCameraUploads": "Камеры",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "Продолжение просмотра",
 | 
					    "HeaderContinueWatching": "Продолжение просмотра",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "Избранные альбомы",
 | 
					    "HeaderFavoriteAlbums": "Избранные альбомы",
 | 
				
			||||||
    "HeaderFavoriteArtists": "Избранные исполнители",
 | 
					    "HeaderFavoriteArtists": "Избранные исполнители",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										116
									
								
								Emby.Server.Implementations/Localization/Core/sq.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								Emby.Server.Implementations/Localization/Core/sq.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "MessageApplicationUpdatedTo": "Serveri Jellyfin u përditesua në versionin {0}",
 | 
				
			||||||
 | 
					    "Inherit": "Trashgimi",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "Kërkon në internet për titra që mungojnë bazuar tek konfigurimi i metadata-ve.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "Shkarko titra që mungojnë",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "Rifreskon informacionin e kanaleve të internetit.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "Rifresko Kanalet",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "Fshin skedarët e transkodimit që janë më të vjetër se një ditë.",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "Fshi dosjen e transkodimit",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "Shkarkon dhe instalon përditësimi për plugin që janë konfiguruar të përditësohen automatikisht.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "Përditëso Plugin",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "Përditëson metadata të aktorëve dhe regjizorëve në librarinë tuaj.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "Rifresko aktorët",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "Fshin skëdarët log që janë më të vjetër se {0} ditë.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "Fshi dosjen Log",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "Skanon librarinë media për skedarë të rinj dhe rifreskon metadata.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "Skano librarinë media",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "Krijon imazh për videot që kanë kapituj.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "Ekstrakto Imazhet e Kapitullit",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "Fshi skedarët e cache-s që nuk i duhen më sistemit.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Pastro memorjen cache",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "Kanalet nga interneti",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Aplikacioni",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Libraria",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Mirëmbajtje",
 | 
				
			||||||
 | 
					    "VersionNumber": "Versioni {0}",
 | 
				
			||||||
 | 
					    "ValueSpecialEpisodeName": "Speciale - {0}",
 | 
				
			||||||
 | 
					    "ValueHasBeenAddedToLibrary": "{0} u shtua tek libraria juaj",
 | 
				
			||||||
 | 
					    "UserStoppedPlayingItemWithValues": "{0} mbaroi së shikuari {1} tek {2}",
 | 
				
			||||||
 | 
					    "UserStartedPlayingItemWithValues": "{0} po shikon {1} tek {2}",
 | 
				
			||||||
 | 
					    "UserPolicyUpdatedWithName": "Politika e përdoruesit u përditësua për {0}",
 | 
				
			||||||
 | 
					    "UserPasswordChangedWithName": "Fjalëkalimi u ndryshua për përdoruesin {0}",
 | 
				
			||||||
 | 
					    "UserOnlineFromDevice": "{0} është në linjë nga {1}",
 | 
				
			||||||
 | 
					    "UserOfflineFromDevice": "{0} u shkëput nga {1}",
 | 
				
			||||||
 | 
					    "UserLockedOutWithName": "Përdoruesi {0} u përjashtua",
 | 
				
			||||||
 | 
					    "UserDownloadingItemWithValues": "{0} po shkarkon {1}",
 | 
				
			||||||
 | 
					    "UserDeletedWithName": "Përdoruesi {0} u fshi",
 | 
				
			||||||
 | 
					    "UserCreatedWithName": "Përdoruesi {0} u krijua",
 | 
				
			||||||
 | 
					    "User": "Përdoruesi",
 | 
				
			||||||
 | 
					    "TvShows": "Seriale TV",
 | 
				
			||||||
 | 
					    "System": "Sistemi",
 | 
				
			||||||
 | 
					    "Sync": "Sinkronizo",
 | 
				
			||||||
 | 
					    "SubtitleDownloadFailureFromForItem": "Titrat deshtuan të shkarkohen nga {0} për {1}",
 | 
				
			||||||
 | 
					    "StartupEmbyServerIsLoading": "Serveri Jellyfin po ngarkohet. Ju lutemi provoni përseri pas pak.",
 | 
				
			||||||
 | 
					    "Songs": "Këngë",
 | 
				
			||||||
 | 
					    "Shows": "Seriale",
 | 
				
			||||||
 | 
					    "ServerNameNeedsToBeRestarted": "{0} duhet të ristartoj",
 | 
				
			||||||
 | 
					    "ScheduledTaskStartedWithName": "{0} filloi",
 | 
				
			||||||
 | 
					    "ScheduledTaskFailedWithName": "{0} dështoi",
 | 
				
			||||||
 | 
					    "ProviderValue": "Ofruesi: {0}",
 | 
				
			||||||
 | 
					    "PluginUpdatedWithName": "{0} u përditësua",
 | 
				
			||||||
 | 
					    "PluginUninstalledWithName": "{0} u çinstalua",
 | 
				
			||||||
 | 
					    "PluginInstalledWithName": "{0} u instalua",
 | 
				
			||||||
 | 
					    "Plugin": "Plugin",
 | 
				
			||||||
 | 
					    "Playlists": "Listat për luajtje",
 | 
				
			||||||
 | 
					    "Photos": "Fotografitë",
 | 
				
			||||||
 | 
					    "NotificationOptionVideoPlaybackStopped": "Luajtja e videos ndaloi",
 | 
				
			||||||
 | 
					    "NotificationOptionVideoPlayback": "Luajtja e videos filloi",
 | 
				
			||||||
 | 
					    "NotificationOptionUserLockedOut": "Përdoruesi u përjashtua",
 | 
				
			||||||
 | 
					    "NotificationOptionTaskFailed": "Ushtrimi i planifikuar dështoi",
 | 
				
			||||||
 | 
					    "NotificationOptionServerRestartRequired": "Kërkohet ristartim i serverit",
 | 
				
			||||||
 | 
					    "NotificationOptionPluginUpdateInstalled": "Përditësimi i plugin u instalua",
 | 
				
			||||||
 | 
					    "NotificationOptionPluginUninstalled": "Plugin u çinstalua",
 | 
				
			||||||
 | 
					    "NotificationOptionPluginInstalled": "Plugin u instalua",
 | 
				
			||||||
 | 
					    "NotificationOptionPluginError": "Plugin dështoi",
 | 
				
			||||||
 | 
					    "NotificationOptionNewLibraryContent": "Një përmbajtje e re u shtua",
 | 
				
			||||||
 | 
					    "NotificationOptionInstallationFailed": "Instalimi dështoi",
 | 
				
			||||||
 | 
					    "NotificationOptionCameraImageUploaded": "Fotoja nga kamera u ngarkua",
 | 
				
			||||||
 | 
					    "NotificationOptionAudioPlaybackStopped": "Luajtja e audios ndaloi",
 | 
				
			||||||
 | 
					    "NotificationOptionAudioPlayback": "Luajtja e audios filloi",
 | 
				
			||||||
 | 
					    "NotificationOptionApplicationUpdateInstalled": "Përditësimi i aplikacionit u instalua",
 | 
				
			||||||
 | 
					    "NotificationOptionApplicationUpdateAvailable": "Një perditësim i aplikacionit është gati",
 | 
				
			||||||
 | 
					    "NewVersionIsAvailable": "Një version i ri i Jellyfin është gati për tu shkarkuar.",
 | 
				
			||||||
 | 
					    "NameSeasonUnknown": "Sezon i panjohur",
 | 
				
			||||||
 | 
					    "NameSeasonNumber": "Sezoni {0}",
 | 
				
			||||||
 | 
					    "NameInstallFailed": "Instalimi i {0} dështoi",
 | 
				
			||||||
 | 
					    "MusicVideos": "Video muzikore",
 | 
				
			||||||
 | 
					    "Music": "Muzikë",
 | 
				
			||||||
 | 
					    "Movies": "Filma",
 | 
				
			||||||
 | 
					    "MixedContent": "Përmbajtje e përzier",
 | 
				
			||||||
 | 
					    "MessageServerConfigurationUpdated": "Konfigurimet e serverit u përditësuan",
 | 
				
			||||||
 | 
					    "MessageNamedServerConfigurationUpdatedWithValue": "Seksioni i konfigurimit të serverit {0} u përditësua",
 | 
				
			||||||
 | 
					    "MessageApplicationUpdated": "Serveri Jellyfin u përditësua",
 | 
				
			||||||
 | 
					    "Latest": "Të fundit",
 | 
				
			||||||
 | 
					    "LabelRunningTimeValue": "Kohëzgjatja: {0}",
 | 
				
			||||||
 | 
					    "LabelIpAddressValue": "Adresa IP: {0}",
 | 
				
			||||||
 | 
					    "ItemRemovedWithName": "{0} u fshi nga libraria",
 | 
				
			||||||
 | 
					    "ItemAddedWithName": "{0} u shtua tek libraria",
 | 
				
			||||||
 | 
					    "HomeVideos": "Video personale",
 | 
				
			||||||
 | 
					    "HeaderRecordingGroups": "Grupet e regjistrimit",
 | 
				
			||||||
 | 
					    "HeaderNextUp": "Në vazhdim",
 | 
				
			||||||
 | 
					    "HeaderLiveTV": "TV Live",
 | 
				
			||||||
 | 
					    "HeaderFavoriteSongs": "Kënget e preferuara",
 | 
				
			||||||
 | 
					    "HeaderFavoriteShows": "Serialet e preferuar",
 | 
				
			||||||
 | 
					    "HeaderFavoriteEpisodes": "Episodet e preferuar",
 | 
				
			||||||
 | 
					    "HeaderFavoriteArtists": "Artistët e preferuar",
 | 
				
			||||||
 | 
					    "HeaderFavoriteAlbums": "Albumet e preferuar",
 | 
				
			||||||
 | 
					    "HeaderContinueWatching": "Vazhdo të shikosh",
 | 
				
			||||||
 | 
					    "HeaderAlbumArtists": "Artistët e albumeve",
 | 
				
			||||||
 | 
					    "Genres": "Zhanre",
 | 
				
			||||||
 | 
					    "Folders": "Dosje",
 | 
				
			||||||
 | 
					    "Favorites": "Të preferuara",
 | 
				
			||||||
 | 
					    "FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}",
 | 
				
			||||||
 | 
					    "DeviceOnlineWithName": "{0} u lidh",
 | 
				
			||||||
 | 
					    "DeviceOfflineWithName": "{0} u shkëput",
 | 
				
			||||||
 | 
					    "Collections": "Koleksione",
 | 
				
			||||||
 | 
					    "ChapterNameValue": "Kapituj",
 | 
				
			||||||
 | 
					    "Channels": "Kanale",
 | 
				
			||||||
 | 
					    "CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}",
 | 
				
			||||||
 | 
					    "Books": "Libra",
 | 
				
			||||||
 | 
					    "AuthenticationSucceededWithUserName": "{0} u identifikua me sukses",
 | 
				
			||||||
 | 
					    "Artists": "Artistë",
 | 
				
			||||||
 | 
					    "Application": "Aplikacioni",
 | 
				
			||||||
 | 
					    "AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}",
 | 
				
			||||||
 | 
					    "Albums": "Albumet"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -74,7 +74,6 @@
 | 
				
			|||||||
    "HeaderFavoriteArtists": "Омиљени извођачи",
 | 
					    "HeaderFavoriteArtists": "Омиљени извођачи",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "Омиљени албуми",
 | 
					    "HeaderFavoriteAlbums": "Омиљени албуми",
 | 
				
			||||||
    "HeaderContinueWatching": "Настави гледање",
 | 
					    "HeaderContinueWatching": "Настави гледање",
 | 
				
			||||||
    "HeaderCameraUploads": "Слања са камере",
 | 
					 | 
				
			||||||
    "HeaderAlbumArtists": "Извођачи албума",
 | 
					    "HeaderAlbumArtists": "Извођачи албума",
 | 
				
			||||||
    "Genres": "Жанрови",
 | 
					    "Genres": "Жанрови",
 | 
				
			||||||
    "Folders": "Фасцикле",
 | 
					    "Folders": "Фасцикле",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -20,13 +20,12 @@
 | 
				
			|||||||
    "MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது",
 | 
					    "MessageApplicationUpdated": "ஜெல்லிஃபின் சேவையகம் புதுப்பிக்கப்பட்டது",
 | 
				
			||||||
    "Inherit": "மரபுரிமையாகப் பெறு",
 | 
					    "Inherit": "மரபுரிமையாகப் பெறு",
 | 
				
			||||||
    "HeaderRecordingGroups": "பதிவு குழுக்கள்",
 | 
					    "HeaderRecordingGroups": "பதிவு குழுக்கள்",
 | 
				
			||||||
    "HeaderCameraUploads": "புகைப்பட பதிவேற்றங்கள்",
 | 
					 | 
				
			||||||
    "Folders": "கோப்புறைகள்",
 | 
					    "Folders": "கோப்புறைகள்",
 | 
				
			||||||
    "FailedLoginAttemptWithUserName": "{0} இலிருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது",
 | 
					    "FailedLoginAttemptWithUserName": "{0} இலிருந்து உள்நுழைவு முயற்சி தோல்வியடைந்தது",
 | 
				
			||||||
    "DeviceOnlineWithName": "{0} இணைக்கப்பட்டது",
 | 
					    "DeviceOnlineWithName": "{0} இணைக்கப்பட்டது",
 | 
				
			||||||
    "DeviceOfflineWithName": "{0} துண்டிக்கப்பட்டது",
 | 
					    "DeviceOfflineWithName": "{0} துண்டிக்கப்பட்டது",
 | 
				
			||||||
    "Collections": "தொகுப்புகள்",
 | 
					    "Collections": "தொகுப்புகள்",
 | 
				
			||||||
    "CameraImageUploadedFrom": "{0} இலிருந்து புதிய புகைப்படம் பதிவேற்றப்பட்டது",
 | 
					    "CameraImageUploadedFrom": "{0} இல் இருந்து புதிய புகைப்படம் பதிவேற்றப்பட்டது",
 | 
				
			||||||
    "AppDeviceValues": "செயலி: {0}, சாதனம்: {1}",
 | 
					    "AppDeviceValues": "செயலி: {0}, சாதனம்: {1}",
 | 
				
			||||||
    "TaskDownloadMissingSubtitles": "விடுபட்டுபோன வசன வரிகளைப் பதிவிறக்கு",
 | 
					    "TaskDownloadMissingSubtitles": "விடுபட்டுபோன வசன வரிகளைப் பதிவிறக்கு",
 | 
				
			||||||
    "TaskRefreshChannels": "சேனல்களை புதுப்பி",
 | 
					    "TaskRefreshChannels": "சேனல்களை புதுப்பி",
 | 
				
			||||||
 | 
				
			|||||||
@ -20,8 +20,8 @@
 | 
				
			|||||||
    "NotificationOptionCameraImageUploaded": "อัปโหลดภาพถ่ายแล้ว",
 | 
					    "NotificationOptionCameraImageUploaded": "อัปโหลดภาพถ่ายแล้ว",
 | 
				
			||||||
    "NotificationOptionAudioPlaybackStopped": "หยุดเล่นเสียง",
 | 
					    "NotificationOptionAudioPlaybackStopped": "หยุดเล่นเสียง",
 | 
				
			||||||
    "NotificationOptionAudioPlayback": "เริ่มเล่นเสียง",
 | 
					    "NotificationOptionAudioPlayback": "เริ่มเล่นเสียง",
 | 
				
			||||||
    "NotificationOptionApplicationUpdateInstalled": "ติดตั้งการอัปเดตแอพพลิเคชันแล้ว",
 | 
					    "NotificationOptionApplicationUpdateInstalled": "ติดตั้งการอัปเดตแอปพลิเคชันแล้ว",
 | 
				
			||||||
    "NotificationOptionApplicationUpdateAvailable": "มีการอัปเดตแอพพลิเคชัน",
 | 
					    "NotificationOptionApplicationUpdateAvailable": "มีการอัปเดตแอปพลิเคชัน",
 | 
				
			||||||
    "NewVersionIsAvailable": "เวอร์ชันใหม่ของเซิร์ฟเวอร์ Jellyfin พร้อมให้ดาวน์โหลดแล้ว",
 | 
					    "NewVersionIsAvailable": "เวอร์ชันใหม่ของเซิร์ฟเวอร์ Jellyfin พร้อมให้ดาวน์โหลดแล้ว",
 | 
				
			||||||
    "NameSeasonUnknown": "ไม่ทราบซีซัน",
 | 
					    "NameSeasonUnknown": "ไม่ทราบซีซัน",
 | 
				
			||||||
    "NameSeasonNumber": "ซีซัน {0}",
 | 
					    "NameSeasonNumber": "ซีซัน {0}",
 | 
				
			||||||
@ -50,7 +50,6 @@
 | 
				
			|||||||
    "HeaderFavoriteArtists": "ศิลปินที่ชื่นชอบ",
 | 
					    "HeaderFavoriteArtists": "ศิลปินที่ชื่นชอบ",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "อัมบั้มที่ชื่นชอบ",
 | 
					    "HeaderFavoriteAlbums": "อัมบั้มที่ชื่นชอบ",
 | 
				
			||||||
    "HeaderContinueWatching": "ดูต่อ",
 | 
					    "HeaderContinueWatching": "ดูต่อ",
 | 
				
			||||||
    "HeaderCameraUploads": "อัปโหลดรูปถ่าย",
 | 
					 | 
				
			||||||
    "HeaderAlbumArtists": "อัลบั้มศิลปิน",
 | 
					    "HeaderAlbumArtists": "อัลบั้มศิลปิน",
 | 
				
			||||||
    "Genres": "ประเภท",
 | 
					    "Genres": "ประเภท",
 | 
				
			||||||
    "Folders": "โฟลเดอร์",
 | 
					    "Folders": "โฟลเดอร์",
 | 
				
			||||||
@ -65,8 +64,8 @@
 | 
				
			|||||||
    "Books": "หนังสือ",
 | 
					    "Books": "หนังสือ",
 | 
				
			||||||
    "AuthenticationSucceededWithUserName": "{0} ยืนยันตัวสำเร็จแล้ว",
 | 
					    "AuthenticationSucceededWithUserName": "{0} ยืนยันตัวสำเร็จแล้ว",
 | 
				
			||||||
    "Artists": "ศิลปิน",
 | 
					    "Artists": "ศิลปิน",
 | 
				
			||||||
    "Application": "แอพพลิเคชัน",
 | 
					    "Application": "แอปพลิเคชัน",
 | 
				
			||||||
    "AppDeviceValues": "แอพ: {0}, อุปกรณ์: {1}",
 | 
					    "AppDeviceValues": "แอป: {0}, อุปกรณ์: {1}",
 | 
				
			||||||
    "Albums": "อัลบั้ม",
 | 
					    "Albums": "อัลบั้ม",
 | 
				
			||||||
    "ScheduledTaskStartedWithName": "{0} เริ่มต้น",
 | 
					    "ScheduledTaskStartedWithName": "{0} เริ่มต้น",
 | 
				
			||||||
    "ScheduledTaskFailedWithName": "{0} ล้มเหลว",
 | 
					    "ScheduledTaskFailedWithName": "{0} ล้มเหลว",
 | 
				
			||||||
@ -92,7 +91,7 @@
 | 
				
			|||||||
    "TaskCleanCacheDescription": "ลบไฟล์แคชที่ระบบไม่ต้องการ",
 | 
					    "TaskCleanCacheDescription": "ลบไฟล์แคชที่ระบบไม่ต้องการ",
 | 
				
			||||||
    "TaskCleanCache": "ล้างไดเรกทอรีแคช",
 | 
					    "TaskCleanCache": "ล้างไดเรกทอรีแคช",
 | 
				
			||||||
    "TasksChannelsCategory": "ช่องอินเทอร์เน็ต",
 | 
					    "TasksChannelsCategory": "ช่องอินเทอร์เน็ต",
 | 
				
			||||||
    "TasksApplicationCategory": "แอพพลิเคชัน",
 | 
					    "TasksApplicationCategory": "แอปพลิเคชัน",
 | 
				
			||||||
    "TasksLibraryCategory": "ไลบรารี",
 | 
					    "TasksLibraryCategory": "ไลบรารี",
 | 
				
			||||||
    "TasksMaintenanceCategory": "ปิดซ่อมบำรุง",
 | 
					    "TasksMaintenanceCategory": "ปิดซ่อมบำรุง",
 | 
				
			||||||
    "VersionNumber": "เวอร์ชัน {0}",
 | 
					    "VersionNumber": "เวอร์ชัน {0}",
 | 
				
			||||||
 | 
				
			|||||||
@ -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",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "HeaderFavoriteArtists": "Улюблені виконавці",
 | 
					    "HeaderFavoriteArtists": "Улюблені виконавці",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "Улюблені альбоми",
 | 
					    "HeaderFavoriteAlbums": "Улюблені альбоми",
 | 
				
			||||||
    "HeaderContinueWatching": "Продовжити перегляд",
 | 
					    "HeaderContinueWatching": "Продовжити перегляд",
 | 
				
			||||||
    "HeaderCameraUploads": "Завантажено з камери",
 | 
					 | 
				
			||||||
    "HeaderAlbumArtists": "Виконавці альбому",
 | 
					    "HeaderAlbumArtists": "Виконавці альбому",
 | 
				
			||||||
    "Genres": "Жанри",
 | 
					    "Genres": "Жанри",
 | 
				
			||||||
    "Folders": "Каталоги",
 | 
					    "Folders": "Каталоги",
 | 
				
			||||||
 | 
				
			|||||||
@ -105,7 +105,6 @@
 | 
				
			|||||||
    "Inherit": "وراثت میں",
 | 
					    "Inherit": "وراثت میں",
 | 
				
			||||||
    "HomeVideos": "ہوم ویڈیو",
 | 
					    "HomeVideos": "ہوم ویڈیو",
 | 
				
			||||||
    "HeaderRecordingGroups": "ریکارڈنگ گروپس",
 | 
					    "HeaderRecordingGroups": "ریکارڈنگ گروپس",
 | 
				
			||||||
    "HeaderCameraUploads": "کیمرہ اپلوڈز",
 | 
					 | 
				
			||||||
    "FailedLoginAttemptWithUserName": "لاگن کئ کوشش ناکام {0}",
 | 
					    "FailedLoginAttemptWithUserName": "لاگن کئ کوشش ناکام {0}",
 | 
				
			||||||
    "DeviceOnlineWithName": "{0} متصل ھو چکا ھے",
 | 
					    "DeviceOnlineWithName": "{0} متصل ھو چکا ھے",
 | 
				
			||||||
    "DeviceOfflineWithName": "{0} منقطع ھو چکا ھے",
 | 
					    "DeviceOfflineWithName": "{0} منقطع ھو چکا ھے",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										116
									
								
								Emby.Server.Implementations/Localization/Core/vi.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								Emby.Server.Implementations/Localization/Core/vi.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,116 @@
 | 
				
			|||||||
 | 
					{
 | 
				
			||||||
 | 
					    "Collections": "Bộ Sưu Tập",
 | 
				
			||||||
 | 
					    "Favorites": "Yêu Thích",
 | 
				
			||||||
 | 
					    "Folders": "Thư Mục",
 | 
				
			||||||
 | 
					    "Genres": "Thể Loại",
 | 
				
			||||||
 | 
					    "HeaderAlbumArtists": "Bộ Sưu Tập Nghệ sĩ",
 | 
				
			||||||
 | 
					    "HeaderContinueWatching": "Xem Tiếp",
 | 
				
			||||||
 | 
					    "HeaderLiveTV": "TV Trực Tiếp",
 | 
				
			||||||
 | 
					    "Movies": "Phim",
 | 
				
			||||||
 | 
					    "Photos": "Ảnh",
 | 
				
			||||||
 | 
					    "Playlists": "Danh sách phát",
 | 
				
			||||||
 | 
					    "Shows": "Chương Trình TV",
 | 
				
			||||||
 | 
					    "Songs": "Các Bài Hát",
 | 
				
			||||||
 | 
					    "Sync": "Đồng Bộ",
 | 
				
			||||||
 | 
					    "ValueSpecialEpisodeName": "Đặc Biệt - {0}",
 | 
				
			||||||
 | 
					    "Albums": "Albums",
 | 
				
			||||||
 | 
					    "Artists": "Các Nghệ Sĩ",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitlesDescription": "Tìm kiếm phụ đề bị thiếu trên Internet dựa trên cấu hình dữ liệu mô tả.",
 | 
				
			||||||
 | 
					    "TaskDownloadMissingSubtitles": "Tải xuống phụ đề bị thiếu",
 | 
				
			||||||
 | 
					    "TaskRefreshChannelsDescription": "Làm mới thông tin kênh internet.",
 | 
				
			||||||
 | 
					    "TaskRefreshChannels": "Làm Mới Kênh",
 | 
				
			||||||
 | 
					    "TaskCleanTranscodeDescription": "Xóa các tệp chuyển mã cũ hơn một ngày.",
 | 
				
			||||||
 | 
					    "TaskCleanTranscode": "Làm Sạch Thư Mục Chuyển Mã",
 | 
				
			||||||
 | 
					    "TaskUpdatePluginsDescription": "Tải xuống và cài đặt các bản cập nhật cho các plugin được định cấu hình để cập nhật tự động.",
 | 
				
			||||||
 | 
					    "TaskUpdatePlugins": "Cập Nhật Plugins",
 | 
				
			||||||
 | 
					    "TaskRefreshPeopleDescription": "Cập nhật thông tin chi tiết cho diễn viên và đạo diễn trong thư viện phương tiện của bạn.",
 | 
				
			||||||
 | 
					    "TaskRefreshPeople": "Làm mới Người dùng",
 | 
				
			||||||
 | 
					    "TaskCleanLogsDescription": "Xóa tập tin nhật ký cũ hơn {0} ngày.",
 | 
				
			||||||
 | 
					    "TaskCleanLogs": "Làm sạch nhật ký",
 | 
				
			||||||
 | 
					    "TaskRefreshLibraryDescription": "Quét thư viện phương tiện của bạn để tìm các tệp mới và làm mới thông tin chi tiết.",
 | 
				
			||||||
 | 
					    "TaskRefreshLibrary": "Quét Thư viện Phương tiện",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImagesDescription": "Tạo hình thu nhỏ cho video có các phân cảnh.",
 | 
				
			||||||
 | 
					    "TaskRefreshChapterImages": "Trích Xuất Ảnh Phân Cảnh",
 | 
				
			||||||
 | 
					    "TaskCleanCacheDescription": "Xóa các tệp cache không còn cần thiết của hệ thống.",
 | 
				
			||||||
 | 
					    "TaskCleanCache": "Làm Sạch Thư Mục Cache",
 | 
				
			||||||
 | 
					    "TasksChannelsCategory": "Kênh Internet",
 | 
				
			||||||
 | 
					    "TasksApplicationCategory": "Ứng Dụng",
 | 
				
			||||||
 | 
					    "TasksLibraryCategory": "Thư Viện",
 | 
				
			||||||
 | 
					    "TasksMaintenanceCategory": "Bảo Trì",
 | 
				
			||||||
 | 
					    "VersionNumber": "Phiên Bản {0}",
 | 
				
			||||||
 | 
					    "ValueHasBeenAddedToLibrary": "{0} đã được thêm vào thư viện của bạn",
 | 
				
			||||||
 | 
					    "UserStoppedPlayingItemWithValues": "{0} đã phát xong {1} trên {2}",
 | 
				
			||||||
 | 
					    "UserStartedPlayingItemWithValues": "{0} đang phát {1} trên {2}",
 | 
				
			||||||
 | 
					    "UserPolicyUpdatedWithName": "Chính sách người dùng đã được cập nhật cho {0}",
 | 
				
			||||||
 | 
					    "UserPasswordChangedWithName": "Mật khẩu đã được thay đổi cho người dùng {0}",
 | 
				
			||||||
 | 
					    "UserOnlineFromDevice": "{0} trực tuyến từ {1}",
 | 
				
			||||||
 | 
					    "UserOfflineFromDevice": "{0} đã ngắt kết nối từ {1}",
 | 
				
			||||||
 | 
					    "UserLockedOutWithName": "User {0} đã bị khóa",
 | 
				
			||||||
 | 
					    "UserDownloadingItemWithValues": "{0} đang tải xuống {1}",
 | 
				
			||||||
 | 
					    "UserDeletedWithName": "Người Dùng {0} đã được xóa",
 | 
				
			||||||
 | 
					    "UserCreatedWithName": "Người Dùng {0} đã được tạo",
 | 
				
			||||||
 | 
					    "User": "Người Dùng",
 | 
				
			||||||
 | 
					    "TvShows": "Chương Trình TV",
 | 
				
			||||||
 | 
					    "System": "Hệ Thống",
 | 
				
			||||||
 | 
					    "SubtitleDownloadFailureFromForItem": "Không thể tải xuống phụ đề từ {0} cho {1}",
 | 
				
			||||||
 | 
					    "StartupEmbyServerIsLoading": "Jellyfin Server đang tải. Vui lòng thử lại trong thời gian ngắn.",
 | 
				
			||||||
 | 
					    "ServerNameNeedsToBeRestarted": "{0} cần được khởi động lại",
 | 
				
			||||||
 | 
					    "ScheduledTaskStartedWithName": "{0} đã bắt đầu",
 | 
				
			||||||
 | 
					    "ScheduledTaskFailedWithName": "{0} đã thất bại",
 | 
				
			||||||
 | 
					    "ProviderValue": "Provider: {0}",
 | 
				
			||||||
 | 
					    "PluginUpdatedWithName": "{0} đã cập nhật",
 | 
				
			||||||
 | 
					    "PluginUninstalledWithName": "{0} đã được gỡ bỏ",
 | 
				
			||||||
 | 
					    "PluginInstalledWithName": "{0} đã được cài đặt",
 | 
				
			||||||
 | 
					    "Plugin": "Plugin",
 | 
				
			||||||
 | 
					    "NotificationOptionVideoPlaybackStopped": "Phát lại video đã dừng",
 | 
				
			||||||
 | 
					    "NotificationOptionVideoPlayback": "Đã bắt đầu phát lại video",
 | 
				
			||||||
 | 
					    "NotificationOptionUserLockedOut": "Người dùng bị khóa",
 | 
				
			||||||
 | 
					    "NotificationOptionTaskFailed": "Lỗi tác vụ đã lên lịch",
 | 
				
			||||||
 | 
					    "NotificationOptionServerRestartRequired": "Yêu cầu khởi động lại Server",
 | 
				
			||||||
 | 
					    "NotificationOptionPluginUpdateInstalled": "Cập nhật Plugin đã được cài đặt",
 | 
				
			||||||
 | 
					    "NotificationOptionPluginUninstalled": "Đã gỡ bỏ Plugin",
 | 
				
			||||||
 | 
					    "NotificationOptionPluginInstalled": "Đã cài đặt Plugin",
 | 
				
			||||||
 | 
					    "NotificationOptionPluginError": "Thất bại Plugin",
 | 
				
			||||||
 | 
					    "NotificationOptionNewLibraryContent": "Nội dung mới được thêm vào",
 | 
				
			||||||
 | 
					    "NotificationOptionInstallationFailed": "Cài đặt thất bại",
 | 
				
			||||||
 | 
					    "NotificationOptionCameraImageUploaded": "Đã tải lên hình ảnh máy ảnh",
 | 
				
			||||||
 | 
					    "NotificationOptionAudioPlaybackStopped": "Phát lại âm thanh đã dừng",
 | 
				
			||||||
 | 
					    "NotificationOptionAudioPlayback": "Phát lại âm thanh đã bắt đầu",
 | 
				
			||||||
 | 
					    "NotificationOptionApplicationUpdateInstalled": "Bản cập nhật ứng dụng đã được cài đặt",
 | 
				
			||||||
 | 
					    "NotificationOptionApplicationUpdateAvailable": "Bản cập nhật ứng dụng hiện sẵn có",
 | 
				
			||||||
 | 
					    "NewVersionIsAvailable": "Một phiên bản mới của Jellyfin Server sẵn có để tải.",
 | 
				
			||||||
 | 
					    "NameSeasonUnknown": "Không Rõ Mùa",
 | 
				
			||||||
 | 
					    "NameSeasonNumber": "Mùa {0}",
 | 
				
			||||||
 | 
					    "NameInstallFailed": "{0} cài đặt thất bại",
 | 
				
			||||||
 | 
					    "MusicVideos": "Video Nhạc",
 | 
				
			||||||
 | 
					    "Music": "Nhạc",
 | 
				
			||||||
 | 
					    "MixedContent": "Nội dung hỗn hợp",
 | 
				
			||||||
 | 
					    "MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật",
 | 
				
			||||||
 | 
					    "MessageNamedServerConfigurationUpdatedWithValue": "Phần cấu hình máy chủ {0} đã được cập nhật",
 | 
				
			||||||
 | 
					    "MessageApplicationUpdatedTo": "Jellyfin Server đã được cập nhật lên {0}",
 | 
				
			||||||
 | 
					    "MessageApplicationUpdated": "Jellyfin Server đã được cập nhật",
 | 
				
			||||||
 | 
					    "Latest": "Gần Nhất",
 | 
				
			||||||
 | 
					    "LabelRunningTimeValue": "Thời Gian Chạy: {0}",
 | 
				
			||||||
 | 
					    "LabelIpAddressValue": "Địa Chỉ IP: {0}",
 | 
				
			||||||
 | 
					    "ItemRemovedWithName": "{0} đã xóa khỏi thư viện",
 | 
				
			||||||
 | 
					    "ItemAddedWithName": "{0} được thêm vào thư viện",
 | 
				
			||||||
 | 
					    "Inherit": "Thừa hưởng",
 | 
				
			||||||
 | 
					    "HomeVideos": "Video nhà",
 | 
				
			||||||
 | 
					    "HeaderRecordingGroups": "Nhóm Ghi Video",
 | 
				
			||||||
 | 
					    "HeaderNextUp": "Tiếp Theo",
 | 
				
			||||||
 | 
					    "HeaderFavoriteSongs": "Bài Hát Yêu Thích",
 | 
				
			||||||
 | 
					    "HeaderFavoriteShows": "Chương Trình Yêu Thích",
 | 
				
			||||||
 | 
					    "HeaderFavoriteEpisodes": "Tập Phim Yêu Thích",
 | 
				
			||||||
 | 
					    "HeaderFavoriteArtists": "Nghệ Sĩ Yêu Thích",
 | 
				
			||||||
 | 
					    "HeaderFavoriteAlbums": "Album Ưa Thích",
 | 
				
			||||||
 | 
					    "FailedLoginAttemptWithUserName": "Nỗ lực đăng nhập thất bại từ {0}",
 | 
				
			||||||
 | 
					    "DeviceOnlineWithName": "{0} đã kết nối",
 | 
				
			||||||
 | 
					    "DeviceOfflineWithName": "{0} đã ngắt kết nối",
 | 
				
			||||||
 | 
					    "ChapterNameValue": "Phân Cảnh {0}",
 | 
				
			||||||
 | 
					    "Channels": "Các Kênh",
 | 
				
			||||||
 | 
					    "CameraImageUploadedFrom": "Một hình ảnh máy ảnh mới đã được tải lên từ {0}",
 | 
				
			||||||
 | 
					    "Books": "Sách",
 | 
				
			||||||
 | 
					    "AuthenticationSucceededWithUserName": "{0} xác thực thành công",
 | 
				
			||||||
 | 
					    "Application": "Ứng Dụng",
 | 
				
			||||||
 | 
					    "AppDeviceValues": "Ứng Dụng: {0}, Thiết Bị: {1}"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "文件夹",
 | 
					    "Folders": "文件夹",
 | 
				
			||||||
    "Genres": "风格",
 | 
					    "Genres": "风格",
 | 
				
			||||||
    "HeaderAlbumArtists": "专辑作家",
 | 
					    "HeaderAlbumArtists": "专辑作家",
 | 
				
			||||||
    "HeaderCameraUploads": "相机上传",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "继续观影",
 | 
					    "HeaderContinueWatching": "继续观影",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "收藏的专辑",
 | 
					    "HeaderFavoriteAlbums": "收藏的专辑",
 | 
				
			||||||
    "HeaderFavoriteArtists": "最爱的艺术家",
 | 
					    "HeaderFavoriteArtists": "最爱的艺术家",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "檔案夾",
 | 
					    "Folders": "檔案夾",
 | 
				
			||||||
    "Genres": "風格",
 | 
					    "Genres": "風格",
 | 
				
			||||||
    "HeaderAlbumArtists": "專輯藝人",
 | 
					    "HeaderAlbumArtists": "專輯藝人",
 | 
				
			||||||
    "HeaderCameraUploads": "相機上載",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "繼續觀看",
 | 
					    "HeaderContinueWatching": "繼續觀看",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "最愛專輯",
 | 
					    "HeaderFavoriteAlbums": "最愛專輯",
 | 
				
			||||||
    "HeaderFavoriteArtists": "最愛的藝人",
 | 
					    "HeaderFavoriteArtists": "最愛的藝人",
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,6 @@
 | 
				
			|||||||
    "Folders": "資料夾",
 | 
					    "Folders": "資料夾",
 | 
				
			||||||
    "Genres": "風格",
 | 
					    "Genres": "風格",
 | 
				
			||||||
    "HeaderAlbumArtists": "專輯演出者",
 | 
					    "HeaderAlbumArtists": "專輯演出者",
 | 
				
			||||||
    "HeaderCameraUploads": "相機上傳",
 | 
					 | 
				
			||||||
    "HeaderContinueWatching": "繼續觀賞",
 | 
					    "HeaderContinueWatching": "繼續觀賞",
 | 
				
			||||||
    "HeaderFavoriteAlbums": "最愛專輯",
 | 
					    "HeaderFavoriteAlbums": "最愛專輯",
 | 
				
			||||||
    "HeaderFavoriteArtists": "最愛演出者",
 | 
					    "HeaderFavoriteArtists": "最愛演出者",
 | 
				
			||||||
@ -96,7 +95,7 @@
 | 
				
			|||||||
    "TaskDownloadMissingSubtitles": "下載遺失的字幕",
 | 
					    "TaskDownloadMissingSubtitles": "下載遺失的字幕",
 | 
				
			||||||
    "TaskRefreshChannels": "重新整理頻道",
 | 
					    "TaskRefreshChannels": "重新整理頻道",
 | 
				
			||||||
    "TaskUpdatePlugins": "更新外掛",
 | 
					    "TaskUpdatePlugins": "更新外掛",
 | 
				
			||||||
    "TaskRefreshPeople": "重新整理人員",
 | 
					    "TaskRefreshPeople": "刷新用戶",
 | 
				
			||||||
    "TaskCleanLogsDescription": "刪除超過 {0} 天的舊紀錄檔。",
 | 
					    "TaskCleanLogsDescription": "刪除超過 {0} 天的舊紀錄檔。",
 | 
				
			||||||
    "TaskCleanLogs": "清空紀錄資料夾",
 | 
					    "TaskCleanLogs": "清空紀錄資料夾",
 | 
				
			||||||
    "TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新描述資料。",
 | 
					    "TaskRefreshLibraryDescription": "重新掃描媒體庫的新檔案並更新描述資料。",
 | 
				
			||||||
 | 
				
			|||||||
@ -413,6 +413,7 @@ namespace Emby.Server.Implementations.Localization
 | 
				
			|||||||
            yield return new LocalizationOption("Swedish", "sv");
 | 
					            yield return new LocalizationOption("Swedish", "sv");
 | 
				
			||||||
            yield return new LocalizationOption("Swiss German", "gsw");
 | 
					            yield return new LocalizationOption("Swiss German", "gsw");
 | 
				
			||||||
            yield return new LocalizationOption("Turkish", "tr");
 | 
					            yield return new LocalizationOption("Turkish", "tr");
 | 
				
			||||||
 | 
					            yield return new LocalizationOption("Tiếng Việt", "vi");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										60
									
								
								Emby.Server.Implementations/Plugins/PluginManifest.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								Emby.Server.Implementations/Plugins/PluginManifest.cs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
				
			|||||||
 | 
					using System;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace Emby.Server.Implementations.Plugins
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    /// <summary>
 | 
				
			||||||
 | 
					    /// Defines a Plugin manifest file.
 | 
				
			||||||
 | 
					    /// </summary>
 | 
				
			||||||
 | 
					    public class PluginManifest
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the category of the plugin.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string Category { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the changelog information.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string Changelog { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the description of the plugin.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string Description { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the Global Unique Identifier for the plugin.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public Guid Guid { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the Name of the plugin.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string Name { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets an overview of the plugin.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string Overview { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the owner of the plugin.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string Owner { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the compatibility version for the plugin.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string TargetAbi { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the timestamp of the plugin.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public DateTime Timestamp { get; set; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        /// <summary>
 | 
				
			||||||
 | 
					        /// Gets or sets the Version number of the plugin.
 | 
				
			||||||
 | 
					        /// </summary>
 | 
				
			||||||
 | 
					        public string Version { get; set; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -148,7 +148,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
 | 
				
			|||||||
        public bool IsHidden => false;
 | 
					        public bool IsHidden => false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public bool IsEnabled => false;
 | 
					        public bool IsEnabled => true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public bool IsLogged => true;
 | 
					        public bool IsLogged => true;
 | 
				
			||||||
 | 
				
			|||||||
@ -666,7 +666,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var eventArgs = new PlaybackProgressEventArgs
 | 
					            var eventArgs = new PlaybackStartEventArgs
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Item = libraryItem,
 | 
					                Item = libraryItem,
 | 
				
			||||||
                Users = users,
 | 
					                Users = users,
 | 
				
			||||||
@ -1037,7 +1037,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            var generalCommand = new GeneralCommand
 | 
					            var generalCommand = new GeneralCommand
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Name = GeneralCommandType.DisplayMessage.ToString()
 | 
					                Name = GeneralCommandType.DisplayMessage
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            generalCommand.Arguments["Header"] = command.Header;
 | 
					            generalCommand.Arguments["Header"] = command.Header;
 | 
				
			||||||
@ -1064,10 +1064,10 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
                AssertCanControl(session, controllingSession);
 | 
					                AssertCanControl(session, controllingSession);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return SendMessageToSession(session, "GeneralCommand", command, cancellationToken);
 | 
					            return SendMessageToSession(session, SessionMessageType.GeneralCommand, command, cancellationToken);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
 | 
					        private static async Task SendMessageToSession<T>(SessionInfo session, SessionMessageType name, T data, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            var controllers = session.SessionControllers;
 | 
					            var controllers = session.SessionControllers;
 | 
				
			||||||
            var messageId = Guid.NewGuid();
 | 
					            var messageId = Guid.NewGuid();
 | 
				
			||||||
@ -1078,7 +1078,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static Task SendMessageToSessions<T>(IEnumerable<SessionInfo> sessions, string name, T data, CancellationToken cancellationToken)
 | 
					        private static Task SendMessageToSessions<T>(IEnumerable<SessionInfo> sessions, SessionMessageType name, T data, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            IEnumerable<Task> GetTasks()
 | 
					            IEnumerable<Task> GetTasks()
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
@ -1178,7 +1178,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            await SendMessageToSession(session, "Play", command, cancellationToken).ConfigureAwait(false);
 | 
					            await SendMessageToSession(session, SessionMessageType.Play, command, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
@ -1186,7 +1186,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            CheckDisposed();
 | 
					            CheckDisposed();
 | 
				
			||||||
            var session = GetSessionToRemoteControl(sessionId);
 | 
					            var session = GetSessionToRemoteControl(sessionId);
 | 
				
			||||||
            await SendMessageToSession(session, "SyncPlayCommand", command, cancellationToken).ConfigureAwait(false);
 | 
					            await SendMessageToSession(session, SessionMessageType.SyncPlayCommand, command, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
@ -1194,7 +1194,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            CheckDisposed();
 | 
					            CheckDisposed();
 | 
				
			||||||
            var session = GetSessionToRemoteControl(sessionId);
 | 
					            var session = GetSessionToRemoteControl(sessionId);
 | 
				
			||||||
            await SendMessageToSession(session, "SyncPlayGroupUpdate", command, cancellationToken).ConfigureAwait(false);
 | 
					            await SendMessageToSession(session, SessionMessageType.SyncPlayGroupUpdate, command, cancellationToken).ConfigureAwait(false);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, User user)
 | 
					        private IEnumerable<BaseItem> TranslateItemForPlayback(Guid id, User user)
 | 
				
			||||||
@ -1268,7 +1268,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            var generalCommand = new GeneralCommand
 | 
					            var generalCommand = new GeneralCommand
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                Name = GeneralCommandType.DisplayContent.ToString(),
 | 
					                Name = GeneralCommandType.DisplayContent,
 | 
				
			||||||
                Arguments =
 | 
					                Arguments =
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    ["ItemId"] = command.ItemId,
 | 
					                    ["ItemId"] = command.ItemId,
 | 
				
			||||||
@ -1297,7 +1297,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return SendMessageToSession(session, "Playstate", command, cancellationToken);
 | 
					            return SendMessageToSession(session, SessionMessageType.PlayState, command, cancellationToken);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        private static void AssertCanControl(SessionInfo session, SessionInfo controllingSession)
 | 
					        private static void AssertCanControl(SessionInfo session, SessionInfo controllingSession)
 | 
				
			||||||
@ -1322,7 +1322,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            CheckDisposed();
 | 
					            CheckDisposed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return SendMessageToSessions(Sessions, "RestartRequired", string.Empty, cancellationToken);
 | 
					            return SendMessageToSessions(Sessions, SessionMessageType.RestartRequired, string.Empty, cancellationToken);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
@ -1334,7 +1334,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
        {
 | 
					        {
 | 
				
			||||||
            CheckDisposed();
 | 
					            CheckDisposed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return SendMessageToSessions(Sessions, "ServerShuttingDown", string.Empty, cancellationToken);
 | 
					            return SendMessageToSessions(Sessions, SessionMessageType.ServerShuttingDown, string.Empty, cancellationToken);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
@ -1348,7 +1348,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            _logger.LogDebug("Beginning SendServerRestartNotification");
 | 
					            _logger.LogDebug("Beginning SendServerRestartNotification");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            return SendMessageToSessions(Sessions, "ServerRestarting", string.Empty, cancellationToken);
 | 
					            return SendMessageToSessions(Sessions, SessionMessageType.ServerRestarting, string.Empty, cancellationToken);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <summary>
 | 
					        /// <summary>
 | 
				
			||||||
@ -1484,6 +1484,14 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
                throw new SecurityException("User is not allowed access from this device.");
 | 
					                throw new SecurityException("User is not allowed access from this device.");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int sessionsCount = Sessions.Count(i => i.UserId.Equals(user.Id));
 | 
				
			||||||
 | 
					            int maxActiveSessions = user.MaxActiveSessions;
 | 
				
			||||||
 | 
					            _logger.LogInformation("Current/Max sessions for user {User}: {Sessions}/{Max}", user.Username, sessionsCount, maxActiveSessions);
 | 
				
			||||||
 | 
					            if (maxActiveSessions >= 1 && sessionsCount >= maxActiveSessions)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					                throw new SecurityException("User is at their maximum number of sessions.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName);
 | 
					            var token = GetAuthorizationToken(user, request.DeviceId, request.App, request.AppVersion, request.DeviceName);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            var session = LogSessionActivity(
 | 
					            var session = LogSessionActivity(
 | 
				
			||||||
@ -1866,7 +1874,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public Task SendMessageToAdminSessions<T>(string name, T data, CancellationToken cancellationToken)
 | 
					        public Task SendMessageToAdminSessions<T>(SessionMessageType name, T data, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            CheckDisposed();
 | 
					            CheckDisposed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1879,7 +1887,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, Func<T> dataFn, CancellationToken cancellationToken)
 | 
					        public Task SendMessageToUserSessions<T>(List<Guid> userIds, SessionMessageType name, Func<T> dataFn, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            CheckDisposed();
 | 
					            CheckDisposed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1894,7 +1902,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public Task SendMessageToUserSessions<T>(List<Guid> userIds, string name, T data, CancellationToken cancellationToken)
 | 
					        public Task SendMessageToUserSessions<T>(List<Guid> userIds, SessionMessageType name, T data, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            CheckDisposed();
 | 
					            CheckDisposed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1903,7 +1911,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /// <inheritdoc />
 | 
					        /// <inheritdoc />
 | 
				
			||||||
        public Task SendMessageToUserDeviceSessions<T>(string deviceId, string name, T data, CancellationToken cancellationToken)
 | 
					        public Task SendMessageToUserDeviceSessions<T>(string deviceId, SessionMessageType name, T data, CancellationToken cancellationToken)
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
            CheckDisposed();
 | 
					            CheckDisposed();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -8,6 +8,7 @@ using Jellyfin.Data.Events;
 | 
				
			|||||||
using MediaBrowser.Controller.Net;
 | 
					using MediaBrowser.Controller.Net;
 | 
				
			||||||
using MediaBrowser.Controller.Session;
 | 
					using MediaBrowser.Controller.Session;
 | 
				
			||||||
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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -316,7 +317,7 @@ namespace Emby.Server.Implementations.Session
 | 
				
			|||||||
            return webSocket.SendAsync(
 | 
					            return webSocket.SendAsync(
 | 
				
			||||||
                new WebSocketMessage<int>
 | 
					                new WebSocketMessage<int>
 | 
				
			||||||
                {
 | 
					                {
 | 
				
			||||||
                    MessageType = "ForceKeepAlive",
 | 
					                    MessageType = SessionMessageType.ForceKeepAlive,
 | 
				
			||||||
                    Data = WebSocketLostTimeout
 | 
					                    Data = WebSocketLostTimeout
 | 
				
			||||||
                },
 | 
					                },
 | 
				
			||||||
                CancellationToken.None);
 | 
					                CancellationToken.None);
 | 
				
			||||||
 | 
				
			|||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user