diff --git a/.github/dependabot.yml b/.github/dependabot.yml
deleted file mode 100644
index 70bcd49737..0000000000
--- a/.github/dependabot.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-version: 2
-updates:
-- package-ecosystem: nuget
- directory: "/"
- schedule:
- interval: weekly
- time: '12:00'
- open-pull-requests-limit: 10
-
-- package-ecosystem: github-actions
- directory: '/'
- schedule:
- interval: weekly
- time: '12:00'
- open-pull-requests-limit: 10
diff --git a/.github/renovate.json b/.github/renovate.json
new file mode 100644
index 0000000000..5ca683876a
--- /dev/null
+++ b/.github/renovate.json
@@ -0,0 +1,6 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "github>jellyfin/.github//renovate-presets/dotnet"
+ ]
+}
diff --git a/.github/workflows/automation.yml b/.github/workflows/automation.yml
index 20294843d5..0989df64b9 100644
--- a/.github/workflows/automation.yml
+++ b/.github/workflows/automation.yml
@@ -14,7 +14,7 @@ jobs:
if: ${{ github.repository == 'jellyfin/jellyfin' }}
steps:
- name: Apply label
- uses: eps1lon/actions-label-merge-conflict@v2.0.1
+ uses: eps1lon/actions-label-merge-conflict@fd1f295ee7443d13745804bc49fe158e240f6c6e # tag=v2.1.0
if: ${{ github.event_name == 'push' || github.event_name == 'pull_request_target'}}
with:
dirtyLabel: 'merge conflict'
@@ -26,7 +26,7 @@ jobs:
if: ${{ github.repository == 'jellyfin/jellyfin' }}
steps:
- name: Remove from 'Current Release' project
- uses: alex-page/github-project-automation-plus@v0.8.1
+ uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
continue-on-error: true
with:
@@ -35,7 +35,7 @@ jobs:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add to 'Release Next' project
- uses: alex-page/github-project-automation-plus@v0.8.1
+ uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened'
continue-on-error: true
with:
@@ -44,7 +44,7 @@ jobs:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add to 'Current Release' project
- uses: alex-page/github-project-automation-plus@v0.8.1
+ uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
continue-on-error: true
with:
@@ -58,7 +58,7 @@ jobs:
run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)"
- name: Move issue to needs triage
- uses: alex-page/github-project-automation-plus@v0.8.1
+ uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1
continue-on-error: true
with:
@@ -67,7 +67,7 @@ jobs:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add issue to triage project
- uses: alex-page/github-project-automation-plus@v0.8.1
+ uses: alex-page/github-project-automation-plus@1f8873e97e3c8f58161a323b7c568c1f623a1c4d # tag=v0.8.2
if: github.event.issue.pull_request == '' && github.event.action == 'opened'
continue-on-error: true
with:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 1dbd7fa367..adca9680fb 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -20,18 +20,18 @@ jobs:
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
- name: Setup .NET Core
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # tag=v3
with:
dotnet-version: '6.0.x'
- name: Initialize CodeQL
- uses: github/codeql-action/init@v2
+ uses: github/codeql-action/init@312e093a1892bd801f026f1090904ee8e460b9b6 # v2
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
- uses: github/codeql-action/autobuild@v2
+ uses: github/codeql-action/autobuild@312e093a1892bd801f026f1090904ee8e460b9b6 # v2
- name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
+ uses: github/codeql-action/analyze@312e093a1892bd801f026f1090904ee8e460b9b6 # v2
diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml
index 23873706d2..a29519b296 100644
--- a/.github/workflows/commands.yml
+++ b/.github/workflows/commands.yml
@@ -16,20 +16,20 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Notify as seen
- uses: peter-evans/create-or-update-comment@v2
+ uses: peter-evans/create-or-update-comment@5adcb0bb0f9fb3f95ef05400558bdb3f329ee808 # tag=v2
with:
token: ${{ secrets.JF_BOT_TOKEN }}
comment-id: ${{ github.event.comment.id }}
reactions: '+1'
- name: Checkout the latest code
- uses: actions/checkout@v3
+ uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
with:
token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0
- name: Automatic Rebase
- uses: cirrus-actions/rebase@1.7
+ uses: cirrus-actions/rebase@6e572f08c244e2f04f9beb85a943eb618218714d # tag=1.7
env:
GITHUB_TOKEN: ${{ secrets.JF_BOT_TOKEN }}
@@ -39,7 +39,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Notify as seen
- uses: peter-evans/create-or-update-comment@v2
+ uses: peter-evans/create-or-update-comment@5adcb0bb0f9fb3f95ef05400558bdb3f329ee808 # tag=v2
if: ${{ github.event.comment != null }}
with:
token: ${{ secrets.JF_BOT_TOKEN }}
@@ -47,14 +47,14 @@ jobs:
reactions: eyes
- name: Checkout the latest code
- uses: actions/checkout@v3
+ uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
with:
token: ${{ secrets.JF_BOT_TOKEN }}
fetch-depth: 0
- name: Notify as running
id: comment_running
- uses: peter-evans/create-or-update-comment@v2
+ uses: peter-evans/create-or-update-comment@5adcb0bb0f9fb3f95ef05400558bdb3f329ee808 # tag=v2
if: ${{ github.event.comment != null }}
with:
token: ${{ secrets.JF_BOT_TOKEN }}
@@ -89,7 +89,7 @@ jobs:
exit ${retcode}
- name: Notify with result success
- uses: peter-evans/create-or-update-comment@v2
+ uses: peter-evans/create-or-update-comment@5adcb0bb0f9fb3f95ef05400558bdb3f329ee808 # tag=v2
if: ${{ github.event.comment != null && success() }}
with:
token: ${{ secrets.JF_BOT_TOKEN }}
@@ -104,7 +104,7 @@ jobs:
reactions: hooray
- name: Notify with result failure
- uses: peter-evans/create-or-update-comment@v2
+ uses: peter-evans/create-or-update-comment@5adcb0bb0f9fb3f95ef05400558bdb3f329ee808 # tag=v2
if: ${{ github.event.comment != null && failure() }}
with:
token: ${{ secrets.JF_BOT_TOKEN }}
diff --git a/.github/workflows/openapi.yml b/.github/workflows/openapi.yml
index ceb4e8cdff..390d140fd5 100644
--- a/.github/workflows/openapi.yml
+++ b/.github/workflows/openapi.yml
@@ -12,18 +12,18 @@ jobs:
permissions: read-all
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
with:
ref: ${{ github.event.pull_request.head.sha }}
repository: ${{ github.event.pull_request.head.repo.full_name }}
- name: Setup .NET Core
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # tag=v3
with:
dotnet-version: '6.0.x'
- name: Generate openapi.json
run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
- name: Upload openapi.json
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3
with:
name: openapi-head
retention-days: 14
@@ -37,17 +37,17 @@ jobs:
permissions: read-all
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # tag=v3
with:
ref: ${{ github.base_ref }}
- name: Setup .NET Core
- uses: actions/setup-dotnet@v3
+ uses: actions/setup-dotnet@607fce577a46308457984d59e4954e075820f10a # tag=v3
with:
dotnet-version: '6.0.x'
- name: Generate openapi.json
run: dotnet test tests/Jellyfin.Server.Integration.Tests/Jellyfin.Server.Integration.Tests.csproj -c Release --filter "Jellyfin.Server.Integration.Tests.OpenApiSpecTests"
- name: Upload openapi.json
- uses: actions/upload-artifact@v3
+ uses: actions/upload-artifact@83fd05a356d7e2593de66fc9913b3002723633cb # tag=v3
with:
name: openapi-base
retention-days: 14
@@ -63,12 +63,12 @@ jobs:
- openapi-base
steps:
- name: Download openapi-head
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3
with:
name: openapi-head
path: openapi-head
- name: Download openapi-base
- uses: actions/download-artifact@v3
+ uses: actions/download-artifact@9782bd6a9848b53b110e712e20e42d89988822b7 # tag=v3
with:
name: openapi-base
path: openapi-base
@@ -90,14 +90,14 @@ jobs:
body="${body//$'\r'/'%0D'}"
echo ::set-output name=body::$body
- name: Find difference comment
- uses: peter-evans/find-comment@v2
+ uses: peter-evans/find-comment@f4499a714d59013c74a08789b48abe4b704364a0 # v2
id: find-comment
with:
issue-number: ${{ github.event.pull_request.number }}
direction: last
body-includes: openapi-diff-workflow-comment
- name: Reply or edit difference comment (changed)
- uses: peter-evans/create-or-update-comment@v2
+ uses: peter-evans/create-or-update-comment@5adcb0bb0f9fb3f95ef05400558bdb3f329ee808 # tag=v2
if: ${{ steps.read-diff.outputs.body != '' }}
with:
issue-number: ${{ github.event.pull_request.number }}
@@ -112,7 +112,7 @@ jobs:
- name: Edit difference comment (unchanged)
- uses: peter-evans/create-or-update-comment@v2
+ uses: peter-evans/create-or-update-comment@5adcb0bb0f9fb3f95ef05400558bdb3f329ee808 # tag=v2
if: ${{ steps.read-diff.outputs.body == '' && steps.find-comment.outputs.comment-id != '' }}
with:
issue-number: ${{ github.event.pull_request.number }}
diff --git a/.github/workflows/repo-stale.yaml b/.github/workflows/repo-stale.yaml
index 2578f82cfe..f7a77f02b1 100644
--- a/.github/workflows/repo-stale.yaml
+++ b/.github/workflows/repo-stale.yaml
@@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ contains(github.repository, 'jellyfin/') }}
steps:
- - uses: actions/stale@v6
+ - uses: actions/stale@5ebf00ea0e4c1561e9b43a292ed34424fb1d4578 # tag=v6
with:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
days-before-stale: 120
diff --git a/.gitignore b/.gitignore
index c2ae76c1e3..fd8e1f314b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -150,8 +150,6 @@ publish/
*.pubxml
# NuGet Packages Directory
-## TODO: If you have NuGet Package Restore enabled, uncomment the next line
-# packages/
dlls/
dllssigned/
@@ -166,7 +164,6 @@ AppPackages/
sql/
*.Cache
ClientBin/
-[Ss]tyle[Cc]op.*
~$*
*~
*.dbmdl
diff --git a/Directory.Build.props b/Directory.Build.props
index efcfb72243..44a60ffb5c 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -15,7 +15,8 @@
-
+
+
diff --git a/Dockerfile b/Dockerfile
index 219b958935..7b69a186ff 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -89,4 +89,4 @@ ENTRYPOINT ["./jellyfin/jellyfin", \
"--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"]
HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \
- CMD curl -Lk "${HEALTHCHECK_URL}" || exit 1
+ CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
diff --git a/Dockerfile.arm b/Dockerfile.arm
index 8e0ba7af53..84ddf499a1 100644
--- a/Dockerfile.arm
+++ b/Dockerfile.arm
@@ -78,4 +78,4 @@ ENTRYPOINT ["./jellyfin/jellyfin", \
"--ffmpeg", "/usr/lib/jellyfin-ffmpeg/ffmpeg"]
HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \
- CMD curl -Lk "${HEALTHCHECK_URL}" || exit 1
+ CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
diff --git a/Dockerfile.arm64 b/Dockerfile.arm64
index 790be1c39d..d4ae5802c0 100644
--- a/Dockerfile.arm64
+++ b/Dockerfile.arm64
@@ -72,4 +72,4 @@ ENTRYPOINT ["./jellyfin/jellyfin", \
"--ffmpeg", "/usr/bin/ffmpeg"]
HEALTHCHECK --interval=30s --timeout=30s --start-period=10s --retries=3 \
- CMD curl -Lk "${HEALTHCHECK_URL}" || exit 1
+ CMD curl -Lk -fsS "${HEALTHCHECK_URL}" || exit 1
diff --git a/Emby.Dlna/DlnaManager.cs b/Emby.Dlna/DlnaManager.cs
index 57864d2780..2ea2c130f2 100644
--- a/Emby.Dlna/DlnaManager.cs
+++ b/Emby.Dlna/DlnaManager.cs
@@ -1,4 +1,5 @@
#pragma warning disable CS1591
+
using System;
using System.Collections.Generic;
using System.Globalization;
@@ -61,6 +62,7 @@ namespace Emby.Dlna
try
{
await ExtractSystemProfilesAsync().ConfigureAwait(false);
+ Directory.CreateDirectory(UserProfilesPath);
LoadProfiles();
}
catch (Exception ex)
@@ -322,32 +324,28 @@ namespace Emby.Dlna
var path = Path.Join(
systemProfilesPath,
- Path.GetFileName(name.AsSpan()).Slice(namespaceName.Length));
+ Path.GetFileName(name.AsSpan())[namespaceName.Length..]);
+
+ if (File.Exists(path))
+ {
+ continue;
+ }
// The stream should exist as we just got its name from GetManifestResourceNames
using (var stream = _assembly.GetManifestResourceStream(name)!)
{
- var length = stream.Length;
- var fileInfo = _fileSystem.GetFileInfo(path);
+ Directory.CreateDirectory(systemProfilesPath);
- if (!fileInfo.Exists || fileInfo.Length != length)
+ var fileOptions = AsyncFile.WriteOptions;
+ fileOptions.Mode = FileMode.CreateNew;
+ fileOptions.PreallocationSize = stream.Length;
+ var fileStream = new FileStream(path, fileOptions);
+ await using (fileStream.ConfigureAwait(false))
{
- Directory.CreateDirectory(systemProfilesPath);
-
- var fileOptions = AsyncFile.WriteOptions;
- fileOptions.Mode = FileMode.Create;
- fileOptions.PreallocationSize = length;
- var fileStream = new FileStream(path, fileOptions);
- await using (fileStream.ConfigureAwait(false))
- {
- await stream.CopyToAsync(fileStream).ConfigureAwait(false);
- }
+ await stream.CopyToAsync(fileStream).ConfigureAwait(false);
}
}
}
-
- // Not necessary, but just to make it easy to find
- Directory.CreateDirectory(UserProfilesPath);
}
///
@@ -400,14 +398,20 @@ namespace Emby.Dlna
}
var current = GetProfileInfosInternal().First(i => string.Equals(i.Info.Id, profileId, StringComparison.OrdinalIgnoreCase));
+ if (current.Info.Type == DeviceProfileType.System)
+ {
+ throw new ArgumentException("System profiles can't be edited");
+ }
var newFilename = _fileSystem.GetValidFilename(profile.Name) + ".xml";
- var path = Path.Combine(UserProfilesPath, newFilename);
+ var path = Path.Join(UserProfilesPath, newFilename);
- if (!string.Equals(path, current.Path, StringComparison.Ordinal) &&
- current.Info.Type != DeviceProfileType.System)
+ if (!string.Equals(path, current.Path, StringComparison.Ordinal))
{
- _fileSystem.DeleteFile(current.Path);
+ lock (_profiles)
+ {
+ _profiles.Remove(current.Path);
+ }
}
SaveProfile(profile, path, DeviceProfileType.User);
@@ -490,72 +494,4 @@ namespace Emby.Dlna
internal string Path { get; }
}
}
-
- /*
- class DlnaProfileEntryPoint : IServerEntryPoint
- {
- private readonly IApplicationPaths _appPaths;
- private readonly IFileSystem _fileSystem;
- private readonly IXmlSerializer _xmlSerializer;
-
- public DlnaProfileEntryPoint(IApplicationPaths appPaths, IFileSystem fileSystem, IXmlSerializer xmlSerializer)
- {
- _appPaths = appPaths;
- _fileSystem = fileSystem;
- _xmlSerializer = xmlSerializer;
- }
-
- public void Run()
- {
- DumpProfiles();
- }
-
- private void DumpProfiles()
- {
- DeviceProfile[] list = new[]
- {
- new SamsungSmartTvProfile(),
- new XboxOneProfile(),
- new SonyPs3Profile(),
- new SonyPs4Profile(),
- new SonyBravia2010Profile(),
- new SonyBravia2011Profile(),
- new SonyBravia2012Profile(),
- new SonyBravia2013Profile(),
- new SonyBravia2014Profile(),
- new SonyBlurayPlayer2013(),
- new SonyBlurayPlayer2014(),
- new SonyBlurayPlayer2015(),
- new SonyBlurayPlayer2016(),
- new SonyBlurayPlayerProfile(),
- new PanasonicVieraProfile(),
- new WdtvLiveProfile(),
- new DenonAvrProfile(),
- new LinksysDMA2100Profile(),
- new LgTvProfile(),
- new Foobar2000Profile(),
- new SharpSmartTvProfile(),
- new MediaMonkeyProfile(),
- // new Windows81Profile(),
- // new WindowsMediaCenterProfile(),
- // new WindowsPhoneProfile(),
- new DirectTvProfile(),
- new DishHopperJoeyProfile(),
- new DefaultProfile(),
- new PopcornHourProfile(),
- new MarantzProfile()
- };
-
- foreach (var item in list)
- {
- var path = Path.Combine(_appPaths.ProgramDataPath, _fileSystem.GetValidFilename(item.Name) + ".xml");
-
- _xmlSerializer.SerializeToFile(item, path);
- }
- }
-
- public void Dispose()
- {
- }
- }*/
}
diff --git a/Emby.Dlna/Eventing/DlnaEventManager.cs b/Emby.Dlna/Eventing/DlnaEventManager.cs
index d17e238715..68895a7fed 100644
--- a/Emby.Dlna/Eventing/DlnaEventManager.cs
+++ b/Emby.Dlna/Eventing/DlnaEventManager.cs
@@ -127,8 +127,7 @@ namespace Emby.Dlna.Eventing
public Task TriggerEvent(string notificationType, IDictionary stateVariables)
{
var subs = _subscriptions.Values
- .Where(i => !i.IsExpired && string.Equals(notificationType, i.NotificationType, StringComparison.OrdinalIgnoreCase))
- .ToList();
+ .Where(i => !i.IsExpired && string.Equals(notificationType, i.NotificationType, StringComparison.OrdinalIgnoreCase));
var tasks = subs.Select(i => TriggerEvent(i, stateVariables));
diff --git a/Emby.Dlna/PlayTo/PlayToController.cs b/Emby.Dlna/PlayTo/PlayToController.cs
index b73ce00b6f..65367e24f2 100644
--- a/Emby.Dlna/PlayTo/PlayToController.cs
+++ b/Emby.Dlna/PlayTo/PlayToController.cs
@@ -338,7 +338,6 @@ namespace Emby.Dlna.PlayTo
SubtitleStreamIndex = info.SubtitleStreamIndex,
VolumeLevel = _device.Volume,
- // TODO
CanSeek = true,
PlayMethod = info.IsDirectStream ? PlayMethod.DirectStream : PlayMethod.Transcode
diff --git a/Emby.Dlna/Profiles/DefaultProfile.cs b/Emby.Dlna/Profiles/DefaultProfile.cs
index 8f4f2bd384..23437f1bdf 100644
--- a/Emby.Dlna/Profiles/DefaultProfile.cs
+++ b/Emby.Dlna/Profiles/DefaultProfile.cs
@@ -2,7 +2,6 @@
using System;
using System.Globalization;
-using System.Linq;
using MediaBrowser.Model.Dlna;
namespace Emby.Dlna.Profiles
@@ -164,18 +163,5 @@ namespace Emby.Dlna.Profiles
}
};
}
-
- public void AddXmlRootAttribute(string name, string value)
- {
- var list = XmlRootAttributes.ToList();
-
- list.Add(new XmlAttribute
- {
- Name = name,
- Value = value
- });
-
- XmlRootAttributes = list.ToArray();
- }
}
}
diff --git a/Emby.Dlna/Profiles/DenonAvrProfile.cs b/Emby.Dlna/Profiles/DenonAvrProfile.cs
deleted file mode 100644
index a5ba0f36cc..0000000000
--- a/Emby.Dlna/Profiles/DenonAvrProfile.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class DenonAvrProfile : DefaultProfile
- {
- public DenonAvrProfile()
- {
- Name = "Denon AVR";
-
- SupportedMediaTypes = "Audio";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"Denon:\[AVR:.*",
- Manufacturer = "Denon"
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "mp3,flac,m4a,wma",
- Type = DlnaProfileType.Audio
- },
- };
-
- ResponseProfiles = System.Array.Empty();
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/DirectTvProfile.cs b/Emby.Dlna/Profiles/DirectTvProfile.cs
deleted file mode 100644
index f6f98b07d8..0000000000
--- a/Emby.Dlna/Profiles/DirectTvProfile.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class DirectTvProfile : DefaultProfile
- {
- public DirectTvProfile()
- {
- Name = "DirecTV HD-DVR";
-
- TimelineOffsetSeconds = 10;
- RequiresPlainFolders = true;
- RequiresPlainVideoItems = true;
-
- Identification = new DeviceIdentification
- {
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Match = HeaderMatchType.Substring,
- Name = "User-Agent",
- Value = "DIRECTV"
- }
- },
-
- FriendlyName = "^DIRECTV.*$"
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg2video",
- AudioCodec = "mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "jpeg,jpg",
- Type = DlnaProfileType.Photo
- }
- };
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg2video",
- AudioCodec = "mp2",
- Type = DlnaProfileType.Video
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Codec = "mpeg2video",
- Type = CodecType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "8192000"
- }
- }
- },
- new CodecProfile
- {
- Codec = "mp2",
- Type = CodecType.Audio,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
-
- ResponseProfiles = System.Array.Empty();
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
deleted file mode 100644
index 2a7524a6a2..0000000000
--- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs
+++ /dev/null
@@ -1,228 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class DishHopperJoeyProfile : DefaultProfile
- {
- public DishHopperJoeyProfile()
- {
- Name = "Dish Hopper-Joey";
-
- ProtocolInfo = "http-get:*:video/mp2t:*,http-get:*:video/mpeg:*,http-get:*:video/MP1S:*,http-get:*:video/mpeg2:*,http-get:*:video/mp4:*,http-get:*:video/x-matroska:*,http-get:*:audio/mpeg:*,http-get:*:audio/mpeg3:*,http-get:*:audio/mp3:*,http-get:*:audio/mp4:*,http-get:*:audio/mp4a-latm:*,http-get:*:image/jpeg:*";
-
- Identification = new DeviceIdentification
- {
- Manufacturer = "Echostar Technologies LLC",
- ManufacturerUrl = "http://www.echostar.com",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Match = HeaderMatchType.Substring,
- Name = "User-Agent",
- Value = "Zip_"
- }
- }
- };
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "mp4",
- Type = DlnaProfileType.Video,
- AudioCodec = "aac",
- VideoCodec = "h264"
- },
-
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "mp4,mkv,mpeg,ts",
- VideoCodec = "h264,mpeg2video",
- AudioCodec = "mp3,ac3,aac,he-aac,pcm",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "mp3,alac,flac",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "20000000",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = "41",
- IsRequired = true
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = true
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "20000000",
- IsRequired = true
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3,he-aac",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = true
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2",
- IsRequired = true
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Conditions = new[]
- {
- // The device does not have any audio switching capabilities
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.IsSecondaryAudio,
- Value = "false"
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "mkv,ts,mpegts",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/Foobar2000Profile.cs b/Emby.Dlna/Profiles/Foobar2000Profile.cs
deleted file mode 100644
index 68e959770a..0000000000
--- a/Emby.Dlna/Profiles/Foobar2000Profile.cs
+++ /dev/null
@@ -1,78 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class Foobar2000Profile : DefaultProfile
- {
- public Foobar2000Profile()
- {
- Name = "foobar2000";
-
- SupportedMediaTypes = "Audio";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"foobar",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = "foobar",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp2,mp3",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "mp4",
- AudioCodec = "mp4",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "aac,wav",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "flac",
- AudioCodec = "flac",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "asf",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "ogg",
- AudioCodec = "vorbis",
- Type = DlnaProfileType.Audio
- }
- };
-
- ResponseProfiles = System.Array.Empty();
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/LgTvProfile.cs b/Emby.Dlna/Profiles/LgTvProfile.cs
deleted file mode 100644
index fbb368d3e3..0000000000
--- a/Emby.Dlna/Profiles/LgTvProfile.cs
+++ /dev/null
@@ -1,211 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class LgTvProfile : DefaultProfile
- {
- public LgTvProfile()
- {
- Name = "LG Smart TV";
-
- TimelineOffsetSeconds = 10;
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"LG.*",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = "LG",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "ts",
- AudioCodec = "ac3,aac,mp3",
- VideoCodec = "h264",
- Type = DlnaProfileType.Video
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts,avi,mkv,m2ts",
- VideoCodec = "h264",
- AudioCodec = "aac,ac3,eac3,mp3,dca,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "h264,mpeg4",
- AudioCodec = "aac,ac3,eac3,mp3,dca,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "mpeg4",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = "41"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3,eac3,aac,mp3",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- },
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.External
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- },
- new ResponseProfile
- {
- Container = "ts,mpegts",
- Type = DlnaProfileType.Video,
- MimeType = "video/mpeg"
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs b/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
deleted file mode 100644
index 1a510dfecf..0000000000
--- a/Emby.Dlna/Profiles/LinksysDMA2100Profile.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class LinksysDMA2100Profile : DefaultProfile
- {
- public LinksysDMA2100Profile()
- {
- // Linksys DMA2100us does not need any transcoding of the formats we support statically
- Name = "Linksys DMA2100";
-
- Identification = new DeviceIdentification
- {
- ModelName = "DMA2100us"
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "mp3,flac,m4a,wma",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "avi,mp4,mkv,ts,mpegts,m4v",
- Type = DlnaProfileType.Video
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/MarantzProfile.cs b/Emby.Dlna/Profiles/MarantzProfile.cs
deleted file mode 100644
index 24078ab29c..0000000000
--- a/Emby.Dlna/Profiles/MarantzProfile.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class MarantzProfile : DefaultProfile
- {
- public MarantzProfile()
- {
- Name = "Marantz";
-
- SupportedMediaTypes = "Audio";
-
- Identification = new DeviceIdentification
- {
- Manufacturer = @"Marantz",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = "Marantz",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "aac,mp3,wav,wma,flac",
- Type = DlnaProfileType.Audio
- },
- };
-
- ResponseProfiles = System.Array.Empty();
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/MediaMonkeyProfile.cs b/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
deleted file mode 100644
index 28d639b6db..0000000000
--- a/Emby.Dlna/Profiles/MediaMonkeyProfile.cs
+++ /dev/null
@@ -1,44 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class MediaMonkeyProfile : DefaultProfile
- {
- public MediaMonkeyProfile()
- {
- Name = "MediaMonkey";
-
- SupportedMediaTypes = "Audio";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"MediaMonkey",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = "MediaMonkey",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "aac,mp3,mpa,wav,wma,mp2,ogg,oga,webma,ape,opus,flac,m4a",
- Type = DlnaProfileType.Audio
- }
- };
-
- ResponseProfiles = Array.Empty();
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs b/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
deleted file mode 100644
index 0d536acf39..0000000000
--- a/Emby.Dlna/Profiles/PanasonicVieraProfile.cs
+++ /dev/null
@@ -1,222 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class PanasonicVieraProfile : DefaultProfile
- {
- public PanasonicVieraProfile()
- {
- Name = "Panasonic Viera";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"VIERA",
- Manufacturer = "Panasonic",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = "Panasonic MIL DLNA",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:pv", "http://www.pv.com/pvns/");
-
- TimelineOffsetSeconds = 10;
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "ts",
- AudioCodec = "ac3",
- VideoCodec = "h264",
- Type = DlnaProfileType.Video
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "mpeg,mpg",
- VideoCodec = "mpeg2video,mpeg4",
- AudioCodec = "ac3,mp3,pcm_dvd",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "mkv",
- VideoCodec = "h264,mpeg2video",
- AudioCodec = "aac,ac3,dca,mp3,mp2,pcm,dts",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264,mpeg2video",
- AudioCodec = "aac,mp3,mp2",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "h264",
- AudioCodec = "aac,ac3,mp3,pcm",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "mov",
- VideoCodec = "h264",
- AudioCodec = "aac,pcm",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mpeg4",
- AudioCodec = "pcm",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "flv",
- VideoCodec = "h264",
- AudioCodec = "aac",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "mp4",
- AudioCodec = "aac",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitDepth,
- Value = "8",
- IsRequired = false
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- },
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.External
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Type = DlnaProfileType.Video,
- Container = "ts,mpegts",
- OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
- MimeType = "video/vnd.dlna.mpeg-tts"
- },
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/PopcornHourProfile.cs b/Emby.Dlna/Profiles/PopcornHourProfile.cs
deleted file mode 100644
index 7fbf8c1649..0000000000
--- a/Emby.Dlna/Profiles/PopcornHourProfile.cs
+++ /dev/null
@@ -1,225 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class PopcornHourProfile : DefaultProfile
- {
- public PopcornHourProfile()
- {
- Name = "Popcorn Hour";
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
-
- new TranscodingProfile
- {
- Container = "mp4",
- Type = DlnaProfileType.Video,
- AudioCodec = "aac",
- VideoCodec = "h264"
- },
-
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "mp4,mov,m4v",
- Type = DlnaProfileType.Video,
- VideoCodec = "h264,mpeg4",
- AudioCodec = "aac"
- },
-
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- Type = DlnaProfileType.Video,
- VideoCodec = "h264",
- AudioCodec = "aac,ac3,eac3,mp3,mp2,pcm"
- },
-
- new DirectPlayProfile
- {
- Container = "asf,wmv",
- Type = DlnaProfileType.Video,
- VideoCodec = "wmv3,vc1",
- AudioCodec = "wmav2,wmapro"
- },
-
- new DirectPlayProfile
- {
- Container = "avi",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg4,msmpeg4",
- AudioCodec = "mp3,ac3,eac3,mp2,pcm"
- },
-
- new DirectPlayProfile
- {
- Container = "mkv",
- Type = DlnaProfileType.Video,
- VideoCodec = "h264",
- AudioCodec = "aac,mp3,ac3,eac3,mp2,pcm"
- },
- new DirectPlayProfile
- {
- Container = "aac,mp3,flac,ogg,wma,wav",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg,gif,bmp,png",
- Type = DlnaProfileType.Photo
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition(ProfileConditionType.EqualsAny, ProfileConditionValue.VideoProfile, "baseline|constrained baseline"),
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.IsAnamorphic,
- Value = "true",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.IsAnamorphic,
- Value = "true",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Audio,
- Codec = "aac",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Audio,
- Codec = "mp3",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioBitrate,
- Value = "320000",
- IsRequired = false
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs b/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
deleted file mode 100644
index ddbebba2a4..0000000000
--- a/Emby.Dlna/Profiles/SamsungSmartTvProfile.cs
+++ /dev/null
@@ -1,367 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SamsungSmartTvProfile : DefaultProfile
- {
- public SamsungSmartTvProfile()
- {
- Name = "Samsung Smart TV";
-
- EnableAlbumArtInDidl = true;
-
- // Without this, older samsungs fail to browse
- EnableSingleAlbumArtLimit = true;
-
- Identification = new DeviceIdentification
- {
- ModelUrl = "samsung.com",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = @"SEC_",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:sec", "http://www.sec.co.kr/");
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "ts",
- AudioCodec = "ac3",
- VideoCodec = "h264",
- Type = DlnaProfileType.Video,
- EstimateContentLength = false
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "asf",
- VideoCodec = "h264,mpeg4,mjpeg",
- AudioCodec = "mp3,ac3,wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "h264,mpeg4,mjpeg",
- AudioCodec = "mp3,ac3,dca,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mkv",
- VideoCodec = "h264,mpeg4,mjpeg4",
- AudioCodec = "mp3,ac3,dca,aac,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "h264,mpeg4",
- AudioCodec = "mp3,aac",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "3gp",
- VideoCodec = "h264,mpeg4",
- AudioCodec = "aac,he-aac",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpg,mpeg",
- VideoCodec = "mpeg1video,mpeg2video,h264",
- AudioCodec = "ac3,mp2,mp3,aac",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "vro,vob",
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "ac3,mp2,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "ts",
- VideoCodec = "mpeg2video,h264,vc1",
- AudioCodec = "ac3,aac,mp3,eac3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "asf",
- VideoCodec = "wmv2,wmv3",
- AudioCodec = "wmav2,wmavoice",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3,flac",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "mpeg2video",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "30720000"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "mpeg4",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "8192000"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "37500000"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = "41"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "wmv2,wmv3,vc1",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "25600000"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "wmav2,dca,aac,mp3,dts",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6"
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "avi",
- MimeType = "video/x-msvideo",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mkv",
- MimeType = "video/x-mkv",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "flac",
- MimeType = "audio/x-flac",
- Type = DlnaProfileType.Audio
- },
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- },
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.External,
- DidlMode = "CaptionInfoEx"
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SharpSmartTvProfile.cs b/Emby.Dlna/Profiles/SharpSmartTvProfile.cs
deleted file mode 100644
index aa8d434e3f..0000000000
--- a/Emby.Dlna/Profiles/SharpSmartTvProfile.cs
+++ /dev/null
@@ -1,121 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SharpSmartTvProfile : DefaultProfile
- {
- public SharpSmartTvProfile()
- {
- Name = "Sharp Smart TV";
-
- RequiresPlainFolders = true;
- RequiresPlainVideoItems = true;
-
- Identification = new DeviceIdentification
- {
- Manufacturer = "Sharp",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = "Sharp",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
-
- new TranscodingProfile
- {
- Container = "ts",
- Type = DlnaProfileType.Video,
- AudioCodec = "ac3,aac,mp3,dts,dca",
- VideoCodec = "h264",
- EnableMpegtsM2TsMode = true
- },
-
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "m4v,mkv,avi,mov,mp4",
- VideoCodec = "h264,mpeg4",
- AudioCodec = "aac,mp3,ac3,dts,dca",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "asf,wmv",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "mpg,mpeg",
- VideoCodec = "mpeg2video",
- AudioCodec = "mp3,aac",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "flv",
- VideoCodec = "h264",
- AudioCodec = "mp3,aac",
- Type = DlnaProfileType.Video
- },
-
- new DirectPlayProfile
- {
- Container = "mp3,wav",
- Type = DlnaProfileType.Audio
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- },
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.External
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
deleted file mode 100644
index 765c125045..0000000000
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2013.cs
+++ /dev/null
@@ -1,230 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBlurayPlayer2013 : DefaultProfile
- {
- public SonyBlurayPlayer2013()
- {
- Name = "Sony Blu-ray Player 2013";
-
- Identification = new DeviceIdentification
- {
- ModelNumber = "BDP-2013",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S1100",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S3100",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S5100",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S6100",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S7100",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- Manufacturer = "Microsoft Corporation";
-
- ProtocolInfo = "http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000";
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
-
- new TranscodingProfile
- {
- Container = "mkv",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
-
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg1video,mpeg2video,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg,mpg",
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "ac3,mp3,mp2,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,aac,pcm,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mkv",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,dca,aac,mp3,pcm,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "m2ts,mts",
- VideoCodec = "h264,mpeg4,vc1",
- AudioCodec = "aac,mp3,ac3,dca,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "wmv,asf",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3,m4a,wma,wav",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg,png,gif",
- Type = DlnaProfileType.Photo
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = false
- }
- }
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
-
- ResponseProfiles = Array.Empty();
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
deleted file mode 100644
index 390c8a3e43..0000000000
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2014.cs
+++ /dev/null
@@ -1,230 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBlurayPlayer2014 : DefaultProfile
- {
- public SonyBlurayPlayer2014()
- {
- Name = "Sony Blu-ray Player 2014";
-
- Identification = new DeviceIdentification
- {
- ModelNumber = "BDP-2014",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S1200",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S3200",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S5200",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S6200",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S7200",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- Manufacturer = "Microsoft Corporation";
-
- ProtocolInfo = "http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000";
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
-
- new TranscodingProfile
- {
- Container = "mkv",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
-
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg1video,mpeg2video,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg,mpg",
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "ac3,mp3,mp2,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,aac,pcm,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mkv",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,dca,aac,mp3,pcm,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "m2ts,mts",
- VideoCodec = "h264,mpeg4,vc1",
- AudioCodec = "aac,mp3,ac3,dca,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "wmv,asf",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3,m4a,wma,wav",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg,png,gif",
- Type = DlnaProfileType.Photo
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = false
- }
- }
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
-
- ResponseProfiles = Array.Empty();
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
deleted file mode 100644
index 25adc4d022..0000000000
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2015.cs
+++ /dev/null
@@ -1,218 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBlurayPlayer2015 : DefaultProfile
- {
- public SonyBlurayPlayer2015()
- {
- Name = "Sony Blu-ray Player 2015";
-
- Identification = new DeviceIdentification
- {
- ModelNumber = "BDP-2015",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S1500",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S3500",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S6500",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- Manufacturer = "Microsoft Corporation";
-
- ProtocolInfo = "http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000";
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
-
- new TranscodingProfile
- {
- Container = "mkv",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
-
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg1video,mpeg2video,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg,mpg",
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "ac3,mp3,mp2,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,aac,pcm,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mkv",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,dca,aac,mp3,pcm,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "m2ts,mts",
- VideoCodec = "h264,mpeg4,vc1",
- AudioCodec = "aac,mp3,ac3,dca,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "wmv,asf",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3,m4a,wma,wav",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg,png,gif",
- Type = DlnaProfileType.Photo
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = false
- }
- }
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
-
- ResponseProfiles = Array.Empty();
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs b/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
deleted file mode 100644
index 0a39a5f401..0000000000
--- a/Emby.Dlna/Profiles/SonyBlurayPlayer2016.cs
+++ /dev/null
@@ -1,218 +0,0 @@
-#pragma warning disable CS1591
-
-using System;
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBlurayPlayer2016 : DefaultProfile
- {
- public SonyBlurayPlayer2016()
- {
- Name = "Sony Blu-ray Player 2016";
-
- Identification = new DeviceIdentification
- {
- ModelNumber = "BDP-2016",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S1700",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S3700",
- Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = "BDP-S6700",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- Manufacturer = "Microsoft Corporation";
-
- ProtocolInfo = "http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000";
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
-
- new TranscodingProfile
- {
- Container = "mkv",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
-
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg1video,mpeg2video,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg,mpg",
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "ac3,mp3,mp2,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,aac,pcm,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mkv",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,dca,aac,mp3,pcm,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "m2ts,mts",
- VideoCodec = "h264,mpeg4,vc1",
- AudioCodec = "aac,mp3,ac3,dca,dts",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "wmv,asf",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3,m4a,wma,wav",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg,png,gif",
- Type = DlnaProfileType.Photo
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = false
- }
- }
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
-
- ResponseProfiles = Array.Empty();
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs b/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
deleted file mode 100644
index 05c8ab1c14..0000000000
--- a/Emby.Dlna/Profiles/SonyBlurayPlayerProfile.cs
+++ /dev/null
@@ -1,284 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBlurayPlayerProfile : DefaultProfile
- {
- public SonyBlurayPlayerProfile()
- {
- Name = "Sony Blu-ray Player";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"Blu-ray Disc Player",
- Manufacturer = "Sony",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Client-Info",
- Value = @"(Blu-ray Disc Player|Home Theater System|Home Theatre System|Media Player)",
- Match = HeaderMatchType.Regex
- },
-
- new HttpHeaderInfo
- {
- Name = "X-AV-Physical-Unit-Info",
- Value = @"(Blu-ray Disc Player|Home Theater System|Home Theatre System|Media Player)",
- Match = HeaderMatchType.Regex
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- Manufacturer = "Microsoft Corporation";
-
- ProtocolInfo = "http-get:*:video/divx:DLNA.ORG_PN=MATROSKA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_NTSC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMABASE;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/x-ms-wma:DLNA.ORG_PN=WMAFULL;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mp4:DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=44100;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=1:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/L16;rate=48000;channels=2:DLNA.ORG_PN=LPCM;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/mp4:DLNA.ORG_PN=AAC_ISO_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/vnd.dlna.adts:DLNA.ORG_PN=AAC_ADTS_320;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/flac:DLNA.ORG_PN=FLAC;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:audio/ogg:DLNA.ORG_PN=OGG;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_MED;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/png:DLNA.ORG_PN=PNG_TN;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:image/gif:DLNA.ORG_PN=GIF_LRG;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG1;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_EU_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_EU_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_NA_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_NA_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_SD_KO_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_TS_SD_KO_ISO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.dlna.mpeg-tts:DLNA.ORG_PN=MPEG_TS_JP_T;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-msvideo:DLNA.ORG_PN=AVI;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-flv:DLNA.ORG_PN=FLV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-dvr:DLNA.ORG_PN=DVR_MS;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/wtv:DLNA.ORG_PN=WTV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/ogg:DLNA.ORG_PN=OGV;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/vnd.rn-realvideo:DLNA.ORG_PN=REAL_VIDEO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_BASE;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_FULL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVMED_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-wmv:DLNA.ORG_PN=WMVHIGH_PRO;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L1_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L2_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/x-ms-asf:DLNA.ORG_PN=VC1_ASF_AP_L3_WMA;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_P2_3GPP_SP_L0B_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_3GPP_P0_L10_AMR;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:video/3gpp:DLNA.ORG_PN=MPEG4_H263_MP4_P0_L10_AAC;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000";
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
-
- new TranscodingProfile
- {
- Container = "ts",
- VideoCodec = "mpeg2video",
- AudioCodec = "ac3",
- Type = DlnaProfileType.Video
- },
-
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg1video,mpeg2video,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "ac3,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi,mp4,m4v",
- VideoCodec = "mpeg4,h264",
- AudioCodec = "ac3,aac,mp3,pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "asf",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = "41",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "15360000",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2",
- IsRequired = false
- }
- }
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264,mpeg4,vc1",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "avi",
- MimeType = "video/mpeg",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mkv",
- MimeType = "video/vnd.dlna.mpeg-tts",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- MimeType = "video/vnd.dlna.mpeg-tts",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mp4",
- MimeType = "video/mpeg",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "m4v",
- MimeType = "video/mpeg",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mpeg",
- MimeType = "video/mpeg",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mp3",
- MimeType = "audio/mpeg",
- Type = DlnaProfileType.Audio
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs b/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
deleted file mode 100644
index 9f0d82b8f8..0000000000
--- a/Emby.Dlna/Profiles/SonyBravia2010Profile.cs
+++ /dev/null
@@ -1,366 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBravia2010Profile : DefaultProfile
- {
- public SonyBravia2010Profile()
- {
- Name = "Sony Bravia (2010)";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"KDL-[0-9]{2}[EHLNPB]X[0-9][01][0-9].*",
- Manufacturer = "Sony",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Client-Info",
- Value = @".*KDL-[0-9]{2}[EHLNPB]X[0-9][01][0-9].*",
- Match = HeaderMatchType.Regex
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- AlbumArtPn = "JPEG_TN";
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- ModelUrl = "http://www.microsoft.com/";
- Manufacturer = "Microsoft Corporation";
- ManufacturerUrl = "http://www.microsoft.com/";
- SonyAggregationFlags = "10";
- ProtocolInfo =
- "http-get:*:audio/mpeg:DLNA.ORG_PN=MP3;DLNA.ORG_OP=01;DLNA.ORG_FLAGS=81500000000000000000000000000000,http-get:*:image/jpeg:DLNA.ORG_PN=JPEG_SM;DLNA.ORG_OP=00;DLNA.ORG_FLAGS=00D00000000000000000000000000000,http-get:*:video/mpeg:DLNA.ORG_PN=MPEG_PS_PAL;DLNA.ORG_OP=11;DLNA.ORG_FLAGS=81500000000000000000000000000000";
-
- EnableSingleAlbumArtLimit = true;
- EnableAlbumArtInDidl = true;
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "ts",
- VideoCodec = "h264",
- AudioCodec = "ac3",
- Type = DlnaProfileType.Video,
- EnableMpegtsM2TsMode = true
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg2video,mpeg1video",
- AudioCodec = "mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "192"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.VideoTimestamp,
- Value = "Valid"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/mpeg",
- OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "188"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg2video",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg1video,mpeg2video",
- MimeType = "video/mpeg",
- OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
- Type = DlnaProfileType.Video
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "20000000"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = "41"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "mpeg2video",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "20000000"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.AudioProfile,
- Value = "he-aac"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "mp3,mp2",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs b/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
deleted file mode 100644
index dfb91817ac..0000000000
--- a/Emby.Dlna/Profiles/SonyBravia2011Profile.cs
+++ /dev/null
@@ -1,389 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBravia2011Profile : DefaultProfile
- {
- public SonyBravia2011Profile()
- {
- Name = "Sony Bravia (2011)";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"KDL-[0-9]{2}([A-Z]X[0-9]2[0-9]|CX400).*",
- Manufacturer = "Sony",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Client-Info",
- Value = @".*KDL-[0-9]{2}([A-Z]X[0-9]2[0-9]|CX400).*",
- Match = HeaderMatchType.Regex
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- AlbumArtPn = "JPEG_TN";
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- ModelUrl = "http://www.microsoft.com/";
- Manufacturer = "Microsoft Corporation";
- ManufacturerUrl = "http://www.microsoft.com/";
- SonyAggregationFlags = "10";
- EnableSingleAlbumArtLimit = true;
- EnableAlbumArtInDidl = true;
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "ts",
- VideoCodec = "h264",
- AudioCodec = "ac3",
- Type = DlnaProfileType.Video,
- EnableMpegtsM2TsMode = true
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg2video",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "h264,mpeg4",
- AudioCodec = "ac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg2video,mpeg1video",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "asf",
- VideoCodec = "wmv2,wmv3,vc1",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "asf",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Audio
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "192"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.VideoTimestamp,
- Value = "Valid"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/mpeg",
- OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "188"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg2video",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg1video,mpeg2video",
- MimeType = "video/mpeg",
- OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
- Type = DlnaProfileType.Video
- },
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "20000000"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = "41"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "mpeg2video",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "20000000"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.AudioProfile,
- Value = "he-aac"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "mp3,mp2",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs b/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
deleted file mode 100644
index d59ee38d74..0000000000
--- a/Emby.Dlna/Profiles/SonyBravia2012Profile.cs
+++ /dev/null
@@ -1,307 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBravia2012Profile : DefaultProfile
- {
- public SonyBravia2012Profile()
- {
- Name = "Sony Bravia (2012)";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"KDL-[0-9]{2}[A-Z]X[0-9]5([0-9]|G).*",
- Manufacturer = "Sony",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Client-Info",
- Value = @".*KDL-[0-9]{2}[A-Z]X[0-9]5([0-9]|G).*",
- Match = HeaderMatchType.Regex
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- AlbumArtPn = "JPEG_TN";
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- ModelUrl = "http://www.microsoft.com/";
- Manufacturer = "Microsoft Corporation";
- ManufacturerUrl = "http://www.microsoft.com/";
- SonyAggregationFlags = "10";
- EnableSingleAlbumArtLimit = true;
- EnableAlbumArtInDidl = true;
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "ts",
- VideoCodec = "h264",
- AudioCodec = "ac3",
- Type = DlnaProfileType.Video,
- EnableMpegtsM2TsMode = true
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg2video",
- AudioCodec = "mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "h264,mpeg4",
- AudioCodec = "ac3,aac,mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mpeg4",
- AudioCodec = "ac3,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg2video,mpeg1video",
- AudioCodec = "mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "asf",
- VideoCodec = "wmv2,wmv3,vc1",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "asf",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "192"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.VideoTimestamp,
- Value = "Valid"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/mpeg",
- OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "188"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg2video",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg1video,mpeg2video",
- MimeType = "video/mpeg",
- OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
- Type = DlnaProfileType.Video
- },
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "mp3,mp2",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs b/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
deleted file mode 100644
index 73b0fd67eb..0000000000
--- a/Emby.Dlna/Profiles/SonyBravia2013Profile.cs
+++ /dev/null
@@ -1,324 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBravia2013Profile : DefaultProfile
- {
- public SonyBravia2013Profile()
- {
- Name = "Sony Bravia (2013)";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"KDL-[0-9]{2}[WR][5689][0-9]{2}A.*",
- Manufacturer = "Sony",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Client-Info",
- Value = @".*KDL-[0-9]{2}[WR][5689][0-9]{2}A.*",
- Match = HeaderMatchType.Regex
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- AlbumArtPn = "JPEG_TN";
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- ModelUrl = "http://www.microsoft.com/";
- Manufacturer = "Microsoft Corporation";
- ManufacturerUrl = "http://www.microsoft.com/";
- SonyAggregationFlags = "10";
- EnableSingleAlbumArtLimit = true;
- EnableAlbumArtInDidl = true;
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "ts",
- VideoCodec = "h264",
- AudioCodec = "ac3",
- Type = DlnaProfileType.Video,
- EnableMpegtsM2TsMode = true
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,eac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg2video",
- AudioCodec = "mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "h264,mpeg4",
- AudioCodec = "ac3,eac3,aac,mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mov",
- VideoCodec = "h264,mpeg4,mjpeg",
- AudioCodec = "ac3,eac3,aac,mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mkv",
- VideoCodec = "h264,mpeg4,vp8",
- AudioCodec = "ac3,eac3,aac,mp3,mp2,pcm,vorbis",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mpeg4",
- AudioCodec = "ac3,eac3,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mjpeg",
- AudioCodec = "pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg2video,mpeg1video",
- AudioCodec = "mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "asf",
- VideoCodec = "wmv2,wmv3,vc1",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "mp4",
- AudioCodec = "aac",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "wav",
- AudioCodec = "pcm",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "asf",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "192"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.VideoTimestamp,
- Value = "Valid"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/mpeg",
- OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "188"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg2video",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg1video,mpeg2video",
- MimeType = "video/mpeg",
- OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
- Type = DlnaProfileType.Video
- },
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "mp3,mp2",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs b/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
deleted file mode 100644
index db8ee5750f..0000000000
--- a/Emby.Dlna/Profiles/SonyBravia2014Profile.cs
+++ /dev/null
@@ -1,324 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyBravia2014Profile : DefaultProfile
- {
- public SonyBravia2014Profile()
- {
- Name = "Sony Bravia (2014)";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = @"(KDL-[0-9]{2}W[5-9][0-9]{2}B|KDL-[0-9]{2}R480|XBR-[0-9]{2}X[89][0-9]{2}B|KD-[0-9]{2}[SX][89][0-9]{3}B).*",
- Manufacturer = "Sony",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "X-AV-Client-Info",
- Value = @".*(KDL-[0-9]{2}W[5-9][0-9]{2}B|KDL-[0-9]{2}R480|XBR-[0-9]{2}X[89][0-9]{2}B|KD-[0-9]{2}[SX][89][0-9]{3}B).*",
- Match = HeaderMatchType.Regex
- }
- }
- };
-
- AddXmlRootAttribute("xmlns:av", "urn:schemas-sony-com:av");
-
- AlbumArtPn = "JPEG_TN";
-
- ModelName = "Windows Media Player Sharing";
- ModelNumber = "3.0";
- ModelUrl = "http://www.microsoft.com/";
- Manufacturer = "Microsoft Corporation";
- ManufacturerUrl = "http://www.microsoft.com/";
- SonyAggregationFlags = "10";
- EnableSingleAlbumArtLimit = true;
- EnableAlbumArtInDidl = true;
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "ts",
- VideoCodec = "h264",
- AudioCodec = "ac3",
- Type = DlnaProfileType.Video,
- EnableMpegtsM2TsMode = true
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,eac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg2video",
- AudioCodec = "mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,m4v",
- VideoCodec = "h264,mpeg4",
- AudioCodec = "ac3,eac3,aac,mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mov",
- VideoCodec = "h264,mpeg4,mjpeg",
- AudioCodec = "ac3,eac3,aac,mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mkv",
- VideoCodec = "h264,mpeg4,vp8",
- AudioCodec = "ac3,eac3,aac,mp3,mp2,pcm,vorbis",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mpeg4",
- AudioCodec = "ac3,eac3,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mjpeg",
- AudioCodec = "pcm",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg2video,mpeg1video",
- AudioCodec = "mp3,mp2",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "asf",
- VideoCodec = "wmv2,wmv3,vc1",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "mp4",
- AudioCodec = "aac",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "wav",
- AudioCodec = "pcm",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "asf",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3_T,AVC_TS_HD_50_AC3_T,AVC_TS_HD_60_AC3_T,AVC_TS_HD_EU_T",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "192"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.VideoTimestamp,
- Value = "Valid"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/mpeg",
- OrgPn = "AVC_TS_HD_24_AC3_ISO,AVC_TS_HD_50_AC3_ISO,AVC_TS_HD_60_AC3_ISO,AVC_TS_HD_EU_ISO",
- Type = DlnaProfileType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.PacketLength,
- Value = "188"
- }
- }
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264",
- AudioCodec = "ac3,aac,mp3",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "AVC_TS_HD_24_AC3,AVC_TS_HD_50_AC3,AVC_TS_HD_60_AC3,AVC_TS_HD_EU",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "mpeg2video",
- MimeType = "video/vnd.dlna.mpeg-tts",
- OrgPn = "MPEG_TS_SD_EU,MPEG_TS_SD_NA,MPEG_TS_SD_KO",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "mpeg",
- VideoCodec = "mpeg1video,mpeg2video",
- MimeType = "video/mpeg",
- OrgPn = "MPEG_PS_NTSC,MPEG_PS_PAL",
- Type = DlnaProfileType.Video
- },
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "mp3,mp2",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyPs3Profile.cs b/Emby.Dlna/Profiles/SonyPs3Profile.cs
deleted file mode 100644
index e4a7a3a596..0000000000
--- a/Emby.Dlna/Profiles/SonyPs3Profile.cs
+++ /dev/null
@@ -1,269 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyPs3Profile : DefaultProfile
- {
- public SonyPs3Profile()
- {
- Name = "Sony PlayStation 3";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = "PLAYSTATION 3",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = @"PLAYSTATION 3",
- Match = HeaderMatchType.Substring
- },
-
- new HttpHeaderInfo
- {
- Name = "X-AV-Client-Info",
- Value = @"PLAYSTATION 3",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- AlbumArtPn = "JPEG_TN";
-
- SonyAggregationFlags = "10";
- EnableSingleAlbumArtLimit = true;
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "avi",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg4",
- AudioCodec = "mp2,mp3"
- },
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg1video,mpeg2video,h264",
- AudioCodec = "aac,ac3,mp2"
- },
- new DirectPlayProfile
- {
- Container = "mpeg",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "mp2"
- },
- new DirectPlayProfile
- {
- Container = "mp4",
- Type = DlnaProfileType.Video,
- VideoCodec = "h264,mpeg4",
- AudioCodec = "aac,ac3"
- },
- new DirectPlayProfile
- {
- Container = "aac,mp3,wav",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg,png,gif,bmp,tiff",
- Type = DlnaProfileType.Photo
- }
- };
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "ts",
- VideoCodec = "h264",
- AudioCodec = "aac,ac3,mp2",
- Type = DlnaProfileType.Video
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "15360000",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = "41",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = false
- },
-
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioBitrate,
- Value = "640000",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "wmapro",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.AudioProfile,
- Value = "he-aac",
- IsRequired = false
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "mp4,mov",
- AudioCodec = "aac",
- MimeType = "video/mp4",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "avi",
- MimeType = "video/divx",
- OrgPn = "AVI",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "wav",
- MimeType = "audio/wav",
- Type = DlnaProfileType.Audio
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs
deleted file mode 100644
index 985df0c9a5..0000000000
--- a/Emby.Dlna/Profiles/SonyPs4Profile.cs
+++ /dev/null
@@ -1,278 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class SonyPs4Profile : DefaultProfile
- {
- public SonyPs4Profile()
- {
- Name = "Sony PlayStation 4";
-
- Identification = new DeviceIdentification
- {
- FriendlyName = "PLAYSTATION 4",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = @"PLAYSTATION 4",
- Match = HeaderMatchType.Substring
- },
-
- new HttpHeaderInfo
- {
- Name = "X-AV-Client-Info",
- Value = @"PLAYSTATION 4",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- AlbumArtPn = "JPEG_TN";
-
- SonyAggregationFlags = "10";
- EnableSingleAlbumArtLimit = true;
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "avi",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg4",
- AudioCodec = "mp2,mp3"
- },
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg1video,mpeg2video,h264",
- AudioCodec = "aac,ac3,mp2"
- },
- new DirectPlayProfile
- {
- Container = "mpeg",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "mp2"
- },
- new DirectPlayProfile
- {
- Container = "mp4,mkv,m4v",
- Type = DlnaProfileType.Video,
- VideoCodec = "h264,mpeg4",
- AudioCodec = "aac,ac3"
- },
- new DirectPlayProfile
- {
- Container = "aac,mp3,wav",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg,png,gif,bmp,tiff",
- Type = DlnaProfileType.Photo
- }
- };
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio,
- // Transcoded audio won't be playable at all without this
- TranscodeSeekInfo = TranscodeSeekInfo.Bytes
- },
- new TranscodingProfile
- {
- Container = "ts",
- VideoCodec = "h264",
- AudioCodec = "aac,ac3,mp2",
- Type = DlnaProfileType.Video
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "15360000",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = "41",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = false
- },
-
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioBitrate,
- Value = "640000",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "wmapro",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.AudioProfile,
- Value = "he-aac",
- IsRequired = false
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "mp4,mov",
- AudioCodec = "aac",
- MimeType = "video/mp4",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "avi",
- MimeType = "video/divx",
- OrgPn = "AVI",
- Type = DlnaProfileType.Video
- },
-
- new ResponseProfile
- {
- Container = "wav",
- MimeType = "audio/wav",
- Type = DlnaProfileType.Audio
- },
-
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/WdtvLiveProfile.cs b/Emby.Dlna/Profiles/WdtvLiveProfile.cs
deleted file mode 100644
index 937ca0f425..0000000000
--- a/Emby.Dlna/Profiles/WdtvLiveProfile.cs
+++ /dev/null
@@ -1,266 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class WdtvLiveProfile : DefaultProfile
- {
- public WdtvLiveProfile()
- {
- Name = "WDTV Live";
-
- TimelineOffsetSeconds = 5;
- IgnoreTranscodeByteRangeRequests = true;
-
- Identification = new DeviceIdentification
- {
- ModelName = "WD TV",
-
- Headers = new[]
- {
- new HttpHeaderInfo { Name = "User-Agent", Value = "alphanetworks", Match = HeaderMatchType.Substring },
- new HttpHeaderInfo
- {
- Name = "User-Agent",
- Value = "ALPHA Networks",
- Match = HeaderMatchType.Substring
- }
- }
- };
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- Type = DlnaProfileType.Audio,
- AudioCodec = "mp3"
- },
- new TranscodingProfile
- {
- Container = "ts",
- Type = DlnaProfileType.Video,
- VideoCodec = "h264",
- AudioCodec = "aac"
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "avi",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
- AudioCodec = "ac3,eac3,dca,mp2,mp3,pcm,dts"
- },
-
- new DirectPlayProfile
- {
- Container = "mpeg",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg1video,mpeg2video",
- AudioCodec = "ac3,eac3,dca,mp2,mp3,pcm,dts"
- },
-
- new DirectPlayProfile
- {
- Container = "mkv",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg1video,mpeg2video,mpeg4,h264,vc1",
- AudioCodec = "ac3,eac3,dca,aac,mp2,mp3,pcm,dts"
- },
-
- new DirectPlayProfile
- {
- Container = "ts,m2ts,mpegts",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg1video,mpeg2video,h264,vc1",
- AudioCodec = "ac3,eac3,dca,mp2,mp3,aac,dts"
- },
-
- new DirectPlayProfile
- {
- Container = "mp4,mov,m4v",
- Type = DlnaProfileType.Video,
- VideoCodec = "h264,mpeg4",
- AudioCodec = "ac3,eac3,aac,mp2,mp3,dca,dts"
- },
-
- new DirectPlayProfile
- {
- Container = "asf",
- Type = DlnaProfileType.Video,
- VideoCodec = "vc1",
- AudioCodec = "wmav2,wmapro"
- },
-
- new DirectPlayProfile
- {
- Container = "asf",
- Type = DlnaProfileType.Video,
- VideoCodec = "mpeg2video",
- AudioCodec = "mp2,ac3"
- },
-
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp2,mp3",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "mp4",
- AudioCodec = "mp4",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "flac",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "asf",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Container = "ogg",
- AudioCodec = "vorbis",
- Type = DlnaProfileType.Audio
- },
-
- new DirectPlayProfile
- {
- Type = DlnaProfileType.Photo,
-
- Container = "jpeg,png,gif,bmp,tiff"
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "ts,mpegts",
- OrgPn = "MPEG_TS_SD_NA",
- Type = DlnaProfileType.Video
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Photo,
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- }
- }
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = "41"
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2"
- }
- }
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.External
- },
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- },
- new SubtitleProfile
- {
- Format = "sub",
- Method = SubtitleDeliveryMethod.Embed
- },
- new SubtitleProfile
- {
- Format = "subrip",
- Method = SubtitleDeliveryMethod.Embed
- },
- new SubtitleProfile
- {
- Format = "idx",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Dlna/Profiles/XboxOneProfile.cs b/Emby.Dlna/Profiles/XboxOneProfile.cs
deleted file mode 100644
index 84d8184a22..0000000000
--- a/Emby.Dlna/Profiles/XboxOneProfile.cs
+++ /dev/null
@@ -1,372 +0,0 @@
-#pragma warning disable CS1591
-
-using MediaBrowser.Model.Dlna;
-
-namespace Emby.Dlna.Profiles
-{
- [System.Xml.Serialization.XmlRoot("Profile")]
- public class XboxOneProfile : DefaultProfile
- {
- public XboxOneProfile()
- {
- Name = "Xbox One";
-
- TimelineOffsetSeconds = 40;
-
- Identification = new DeviceIdentification
- {
- ModelName = "Xbox One",
-
- Headers = new[]
- {
- new HttpHeaderInfo
- {
- Name = "FriendlyName.DLNA.ORG", Value = "XboxOne", Match = HeaderMatchType.Substring
- },
- new HttpHeaderInfo
- {
- Name = "User-Agent", Value = "NSPlayer/12", Match = HeaderMatchType.Substring
- }
- }
- };
-
- var videoProfile = "high|main|baseline|constrained baseline";
- var videoLevel = "41";
-
- TranscodingProfiles = new[]
- {
- new TranscodingProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new TranscodingProfile
- {
- Container = "jpeg",
- VideoCodec = "jpeg",
- Type = DlnaProfileType.Photo
- },
- new TranscodingProfile
- {
- Container = "ts",
- VideoCodec = "h264",
- AudioCodec = "aac",
- Type = DlnaProfileType.Video
- }
- };
-
- DirectPlayProfiles = new[]
- {
- new DirectPlayProfile
- {
- Container = "ts,mpegts",
- VideoCodec = "h264,mpeg2video,hevc",
- AudioCodec = "ac3,aac,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "mpeg4",
- AudioCodec = "ac3,mp3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "avi",
- VideoCodec = "h264",
- AudioCodec = "aac",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "mp4,mov,mkv,m4v",
- VideoCodec = "h264,mpeg4,mpeg2video,hevc",
- AudioCodec = "aac,ac3",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "asf",
- VideoCodec = "wmv2,wmv3,vc1",
- AudioCodec = "wmav2,wmapro",
- Type = DlnaProfileType.Video
- },
- new DirectPlayProfile
- {
- Container = "asf",
- AudioCodec = "wmav2,wmapro,wmavoice",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "mp3",
- AudioCodec = "mp3",
- Type = DlnaProfileType.Audio
- },
- new DirectPlayProfile
- {
- Container = "jpeg",
- Type = DlnaProfileType.Photo
- }
- };
-
- ContainerProfiles = new[]
- {
- new ContainerProfile
- {
- Type = DlnaProfileType.Video,
- Container = "mp4,mov",
-
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.Has64BitOffsets,
- Value = "false",
- IsRequired = false
- }
- }
- }
- };
-
- CodecProfiles = new[]
- {
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "mpeg4",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.IsAnamorphic,
- Value = "true",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitDepth,
- Value = "8",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "5120000",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "h264",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.IsAnamorphic,
- Value = "true",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitDepth,
- Value = "8",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoLevel,
- Value = videoLevel,
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.EqualsAny,
- Property = ProfileConditionValue.VideoProfile,
- Value = videoProfile,
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Codec = "wmv2,wmv3,vc1",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.IsAnamorphic,
- Value = "true",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitDepth,
- Value = "8",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Width,
- Value = "1920"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.Height,
- Value = "1080"
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoFramerate,
- Value = "30",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitrate,
- Value = "15360000",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.Video,
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.NotEquals,
- Property = ProfileConditionValue.IsAnamorphic,
- Value = "true",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.VideoBitDepth,
- Value = "8",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "ac3,wmav2,wmapro",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "6",
- IsRequired = false
- }
- }
- },
-
- new CodecProfile
- {
- Type = CodecType.VideoAudio,
- Codec = "aac",
- Conditions = new[]
- {
- new ProfileCondition
- {
- Condition = ProfileConditionType.LessThanEqual,
- Property = ProfileConditionValue.AudioChannels,
- Value = "2",
- IsRequired = false
- },
- new ProfileCondition
- {
- Condition = ProfileConditionType.Equals,
- Property = ProfileConditionValue.AudioProfile,
- Value = "lc",
- IsRequired = false
- }
- }
- }
- };
-
- ResponseProfiles = new[]
- {
- new ResponseProfile
- {
- Container = "avi",
- MimeType = "video/avi",
- Type = DlnaProfileType.Video
- },
- new ResponseProfile
- {
- Container = "m4v",
- Type = DlnaProfileType.Video,
- MimeType = "video/mp4"
- }
- };
-
- SubtitleProfiles = new[]
- {
- new SubtitleProfile
- {
- Format = "srt",
- Method = SubtitleDeliveryMethod.Embed
- }
- };
- }
- }
-}
diff --git a/Emby.Naming/AudioBook/AudioBookListResolver.cs b/Emby.Naming/AudioBook/AudioBookListResolver.cs
index 2efe7d526f..6e491185d8 100644
--- a/Emby.Naming/AudioBook/AudioBookListResolver.cs
+++ b/Emby.Naming/AudioBook/AudioBookListResolver.cs
@@ -36,8 +36,7 @@ namespace Emby.Naming.AudioBook
// File with empty fullname will be sorted out here.
var audiobookFileInfos = files
.Select(i => _audioBookResolver.Resolve(i.FullName))
- .OfType()
- .ToList();
+ .OfType();
var stackResult = StackResolver.ResolveAudioBooks(audiobookFileInfos);
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index e016d7e51f..0119fa38c8 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/Emby.Naming/Common/NamingOptions.cs
@@ -153,7 +153,7 @@ namespace Emby.Naming.Common
CleanStrings = new[]
{
- @"^\s*(?.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
+ @"^\s*(?.+?)[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
@"^(?.+?)(\[.*\])",
@"^\s*(?.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)",
@"^\s*\[[^\]]+\](?!\.\w+$)\s*(?.+)",
@@ -175,12 +175,31 @@ namespace Emby.Naming.Common
AlbumStackingPrefixes = new[]
{
"cd",
+ "digital media",
"disc",
"disk",
"vol",
"volume"
};
+ ArtistSubfolders = new[]
+ {
+ "albums",
+ "broadcasts",
+ "bootlegs",
+ "compilations",
+ "dj-mixes",
+ "eps",
+ "live",
+ "mixtapes",
+ "others",
+ "remixes",
+ "singles",
+ "soundtracks",
+ "spokenwords",
+ "streets"
+ };
+
AudioFileExtensions = new[]
{
".669",
@@ -280,6 +299,13 @@ namespace Emby.Naming.Common
"default"
};
+ MediaHearingImpairedFlags = new[]
+ {
+ "cc",
+ "hi",
+ "sdh"
+ };
+
EpisodeExpressions = new[]
{
// *** Begin Kodi Standard Naming
@@ -487,13 +513,13 @@ namespace Emby.Naming.Common
MediaType.Video),
new ExtraRule(
- ExtraType.Clip,
+ ExtraType.Short,
ExtraRuleType.DirectoryName,
"shorts",
MediaType.Video),
new ExtraRule(
- ExtraType.Clip,
+ ExtraType.Featurette,
ExtraRuleType.DirectoryName,
"featurettes",
MediaType.Video),
@@ -504,6 +530,18 @@ namespace Emby.Naming.Common
"extras",
MediaType.Video),
+ new ExtraRule(
+ ExtraType.Unknown,
+ ExtraRuleType.DirectoryName,
+ "other",
+ MediaType.Video),
+
+ new ExtraRule(
+ ExtraType.Clip,
+ ExtraRuleType.DirectoryName,
+ "clips",
+ MediaType.Video),
+
new ExtraRule(
ExtraType.Trailer,
ExtraRuleType.Filename,
@@ -607,13 +645,13 @@ namespace Emby.Naming.Common
MediaType.Video),
new ExtraRule(
- ExtraType.Clip,
+ ExtraType.Featurette,
ExtraRuleType.Suffix,
"-featurette",
MediaType.Video),
new ExtraRule(
- ExtraType.Clip,
+ ExtraType.Short,
ExtraRuleType.Suffix,
"-short",
MediaType.Video),
@@ -622,6 +660,12 @@ namespace Emby.Naming.Common
ExtraType.Unknown,
ExtraRuleType.Suffix,
"-extra",
+ MediaType.Video),
+
+ new ExtraRule(
+ ExtraType.Unknown,
+ ExtraRuleType.Suffix,
+ "-other",
MediaType.Video)
};
@@ -727,11 +771,21 @@ namespace Emby.Naming.Common
///
public string[] MediaDefaultFlags { get; set; }
+ ///
+ /// Gets or sets list of external media hearing impaired flags.
+ ///
+ public string[] MediaHearingImpairedFlags { get; set; }
+
///
/// Gets or sets list of album stacking prefixes.
///
public string[] AlbumStackingPrefixes { get; set; }
+ ///
+ /// Gets or sets list of artist subfolders.
+ ///
+ public string[] ArtistSubfolders { get; set; }
+
///
/// Gets or sets list of subtitle file extensions.
///
diff --git a/Emby.Naming/ExternalFiles/ExternalPathParser.cs b/Emby.Naming/ExternalFiles/ExternalPathParser.cs
index 3bde3a1cf9..1fa4fa5371 100644
--- a/Emby.Naming/ExternalFiles/ExternalPathParser.cs
+++ b/Emby.Naming/ExternalFiles/ExternalPathParser.cs
@@ -99,6 +99,18 @@ namespace Emby.Naming.ExternalFiles
pathInfo.Language = culture.ThreeLetterISOLanguageName;
extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase);
}
+ else if (culture != null && pathInfo.Language == "hin")
+ {
+ // Hindi language code "hi" collides with a hearing impaired flag - use as Hindi only if no other language is set
+ pathInfo.IsHearingImpaired = true;
+ pathInfo.Language = culture.ThreeLetterISOLanguageName;
+ extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase);
+ }
+ else if (_namingOptions.MediaHearingImpairedFlags.Any(s => currentSliceWithoutSeparator.Contains(s, StringComparison.OrdinalIgnoreCase)))
+ {
+ pathInfo.IsHearingImpaired = true;
+ extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase);
+ }
else
{
titleString = currentSlice + titleString;
diff --git a/Emby.Naming/ExternalFiles/ExternalPathParserResult.cs b/Emby.Naming/ExternalFiles/ExternalPathParserResult.cs
index 1cc773a2e1..b0d9e7a9fd 100644
--- a/Emby.Naming/ExternalFiles/ExternalPathParserResult.cs
+++ b/Emby.Naming/ExternalFiles/ExternalPathParserResult.cs
@@ -11,11 +11,13 @@ namespace Emby.Naming.ExternalFiles
/// Path to file.
/// Is default.
/// Is forced.
- public ExternalPathParserResult(string path, bool isDefault = false, bool isForced = false)
+ /// For the hearing impaired.
+ public ExternalPathParserResult(string path, bool isDefault = false, bool isForced = false, bool isHearingImpaired = false)
{
Path = path;
IsDefault = isDefault;
IsForced = isForced;
+ IsHearingImpaired = isHearingImpaired;
}
///
@@ -47,5 +49,11 @@ namespace Emby.Naming.ExternalFiles
///
/// true if this instance is forced; otherwise, false.
public bool IsForced { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether this instance is for the hearing impaired.
+ ///
+ /// true if this instance is for the hearing impaired; otherwise, false.
+ public bool IsHearingImpaired { get; set; }
}
}
diff --git a/Emby.Notifications/NotificationManager.cs b/Emby.Notifications/NotificationManager.cs
index 8b281e487f..ac90cc8ec5 100644
--- a/Emby.Notifications/NotificationManager.cs
+++ b/Emby.Notifications/NotificationManager.cs
@@ -88,8 +88,7 @@ namespace Emby.Notifications
string description,
CancellationToken cancellationToken)
{
- users = users.Where(i => IsEnabledForUser(service, i))
- .ToList();
+ users = users.Where(i => IsEnabledForUser(service, i));
var tasks = users.Select(i => SendNotification(request, service, title, description, i, cancellationToken));
diff --git a/Emby.Server.Implementations/ApplicationHost.cs b/Emby.Server.Implementations/ApplicationHost.cs
index 7004af3d43..1398bb373b 100644
--- a/Emby.Server.Implementations/ApplicationHost.cs
+++ b/Emby.Server.Implementations/ApplicationHost.cs
@@ -22,7 +22,6 @@ using Emby.Drawing;
using Emby.Naming.Common;
using Emby.Notifications;
using Emby.Photos;
-using Emby.Server.Implementations.Archiving;
using Emby.Server.Implementations.Channels;
using Emby.Server.Implementations.Collections;
using Emby.Server.Implementations.Configuration;
@@ -49,6 +48,7 @@ using Jellyfin.Api.Helpers;
using Jellyfin.MediaEncoding.Hls.Playlist;
using Jellyfin.Networking.Configuration;
using Jellyfin.Networking.Manager;
+using Jellyfin.Server.Implementations;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Events;
@@ -67,6 +67,7 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.Lyrics;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Notifications;
@@ -94,12 +95,14 @@ using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.System;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Providers.Chapters;
+using MediaBrowser.Providers.Lyric;
using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Plugins.Tmdb;
using MediaBrowser.Providers.Subtitles;
using MediaBrowser.XbmcMetadata.Providers;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@@ -559,8 +562,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
-
serviceCollection.AddSingleton(this);
serviceCollection.AddSingleton(ApplicationPaths);
@@ -598,6 +599,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
+ serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
@@ -652,6 +654,17 @@ namespace Emby.Server.Implementations
/// A task representing the service initialization operation.
public async Task InitializeServices()
{
+ var jellyfinDb = await Resolve>().CreateDbContextAsync().ConfigureAwait(false);
+ await using (jellyfinDb.ConfigureAwait(false))
+ {
+ if ((await jellyfinDb.Database.GetPendingMigrationsAsync().ConfigureAwait(false)).Any())
+ {
+ Logger.LogInformation("There are pending EFCore migrations in the database. Applying... (This may take a while, do not stop Jellyfin)");
+ await jellyfinDb.Database.MigrateAsync().ConfigureAwait(false);
+ Logger.LogInformation("EFCore migrations applied successfully");
+ }
+ }
+
var localizationManager = (LocalizationManager)Resolve();
await localizationManager.LoadAll().ConfigureAwait(false);
@@ -1089,15 +1102,7 @@ namespace Emby.Server.Implementations
return GetLocalApiUrl(request.Host.Host, request.Scheme, requestPort);
}
- // Published server ends with a /
- if (!string.IsNullOrEmpty(PublishedServerUrl))
- {
- // Published server ends with a '/', so we need to remove it.
- return PublishedServerUrl.Trim('/');
- }
-
- string smart = NetManager.GetBindInterface(request, out var port);
- return GetLocalApiUrl(smart.Trim('/'), request.Scheme, port);
+ return GetSmartApiUrl(request.HttpContext.Connection.RemoteIpAddress ?? IPAddress.Loopback);
}
///
diff --git a/Emby.Server.Implementations/Archiving/ZipClient.cs b/Emby.Server.Implementations/Archiving/ZipClient.cs
deleted file mode 100644
index 6a3b250d25..0000000000
--- a/Emby.Server.Implementations/Archiving/ZipClient.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System.IO;
-using MediaBrowser.Model.IO;
-using SharpCompress.Common;
-using SharpCompress.Readers;
-using SharpCompress.Readers.GZip;
-
-namespace Emby.Server.Implementations.Archiving
-{
- ///
- /// Class DotNetZipClient.
- ///
- public class ZipClient : IZipClient
- {
- ///
- public void ExtractAllFromGz(Stream source, string targetPath, bool overwriteExistingFiles)
- {
- using var reader = GZipReader.Open(source);
- var options = new ExtractionOptions
- {
- ExtractFullPath = true,
- Overwrite = overwriteExistingFiles
- };
-
- Directory.CreateDirectory(targetPath);
- reader.WriteAllToDirectory(targetPath, options);
- }
-
- ///
- public void ExtractFirstFileFromGz(Stream source, string targetPath, string defaultFileName)
- {
- using var reader = GZipReader.Open(source);
- if (reader.MoveToNextEntry())
- {
- var entry = reader.Entry;
-
- var filename = entry.Key;
- if (string.IsNullOrWhiteSpace(filename))
- {
- filename = defaultFileName;
- }
-
- reader.WriteEntryToFile(Path.Combine(targetPath, filename));
- }
- }
- }
-}
diff --git a/Emby.Server.Implementations/Collections/CollectionManager.cs b/Emby.Server.Implementations/Collections/CollectionManager.cs
index 5fc2e39a7a..187e0c9b3b 100644
--- a/Emby.Server.Implementations/Collections/CollectionManager.cs
+++ b/Emby.Server.Implementations/Collections/CollectionManager.cs
@@ -232,10 +232,10 @@ namespace Emby.Server.Implementations.Collections
if (list.Count > 0)
{
- var newList = collection.LinkedChildren.ToList();
- newList.AddRange(list);
- collection.LinkedChildren = newList.ToArray();
-
+ LinkedChild[] newChildren = new LinkedChild[collection.LinkedChildren.Length + list.Count];
+ collection.LinkedChildren.CopyTo(newChildren, 0);
+ list.CopyTo(newChildren, collection.LinkedChildren.Length);
+ collection.LinkedChildren = newChildren;
collection.UpdateRatingToItems(linkedChildrenList);
await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
index 9c9fa73830..4f0a15df18 100644
--- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs
+++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs
@@ -178,7 +178,8 @@ namespace Emby.Server.Implementations.Data
"RpuPresentFlag",
"ElPresentFlag",
"BlPresentFlag",
- "DvBlSignalCompatibilityId"
+ "DvBlSignalCompatibilityId",
+ "IsHearingImpaired"
};
private static readonly string _mediaStreamSaveColumnsInsertQuery =
@@ -349,7 +350,8 @@ namespace Emby.Server.Implementations.Data
public void Initialize(SqliteUserDataRepository userDataRepo, IUserManager userManager)
{
const string CreateMediaStreamsTableCommand
- = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, PRIMARY KEY (ItemId, StreamIndex))";
+ = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, CodecTag TEXT NULL, Comment TEXT NULL, NalLengthSize TEXT NULL, IsAvc BIT NULL, Title TEXT NULL, TimeBase TEXT NULL, CodecTimeBase TEXT NULL, ColorPrimaries TEXT NULL, ColorSpace TEXT NULL, ColorTransfer TEXT NULL, DvVersionMajor INT NULL, DvVersionMinor INT NULL, DvProfile INT NULL, DvLevel INT NULL, RpuPresentFlag INT NULL, ElPresentFlag INT NULL, BlPresentFlag INT NULL, DvBlSignalCompatibilityId INT NULL, IsHearingImpaired BIT NULL, PRIMARY KEY (ItemId, StreamIndex))";
+
const string CreateMediaAttachmentsTableCommand
= "create table if not exists mediaattachments (ItemId GUID, AttachmentIndex INT, Codec TEXT, CodecTag TEXT NULL, Comment TEXT NULL, Filename TEXT NULL, MIMEType TEXT NULL, PRIMARY KEY (ItemId, AttachmentIndex))";
@@ -572,6 +574,8 @@ namespace Emby.Server.Implementations.Data
AddColumn(db, "MediaStreams", "ElPresentFlag", "INT", existingColumnNames);
AddColumn(db, "MediaStreams", "BlPresentFlag", "INT", existingColumnNames);
AddColumn(db, "MediaStreams", "DvBlSignalCompatibilityId", "INT", existingColumnNames);
+
+ AddColumn(db, "MediaStreams", "IsHearingImpaired", "BIT", existingColumnNames);
},
TransactionMode);
@@ -2452,6 +2456,8 @@ namespace Emby.Server.Implementations.Data
builder.Append('(');
builder.Append("((CleanName like @SearchTermStartsWith or (OriginalTitle not null and OriginalTitle like @SearchTermStartsWith)) * 10)");
+ builder.Append("+ ((CleanName = @SearchTermStartsWith COLLATE NOCASE or (OriginalTitle not null and OriginalTitle = @SearchTermStartsWith COLLATE NOCASE)) * 10)");
+
if (query.SearchTerm.Length > 1)
{
@@ -3150,6 +3156,11 @@ namespace Emby.Server.Implementations.Data
return ItemSortBy.SimilarityScore;
}
+ if (string.Equals(name, ItemSortBy.SearchScore, StringComparison.OrdinalIgnoreCase))
+ {
+ return ItemSortBy.SearchScore;
+ }
+
// Unknown SortBy, just sort by the SortName.
return ItemSortBy.SortName;
}
@@ -3520,6 +3531,13 @@ namespace Emby.Server.Implementations.Data
statement?.TryBind("@MinIndexNumber", query.MinIndexNumber.Value);
}
+ if (query.MinParentAndIndexNumber.HasValue)
+ {
+ whereClauses.Add("((ParentIndexNumber=@MinParentAndIndexNumberParent and IndexNumber>=@MinParentAndIndexNumberIndex) or ParentIndexNumber>@MinParentAndIndexNumberParent)");
+ statement?.TryBind("@MinParentAndIndexNumberParent", query.MinParentAndIndexNumber.Value.ParentIndexNumber);
+ statement?.TryBind("@MinParentAndIndexNumberIndex", query.MinParentAndIndexNumber.Value.IndexNumber);
+ }
+
if (query.MinDateCreated.HasValue)
{
whereClauses.Add("DateCreated>=@MinDateCreated");
@@ -5836,6 +5854,8 @@ AND Type = @InternalPersonType)");
statement.TryBind("@ElPresentFlag" + index, stream.ElPresentFlag);
statement.TryBind("@BlPresentFlag" + index, stream.BlPresentFlag);
statement.TryBind("@DvBlSignalCompatibilityId" + index, stream.DvBlSignalCompatibilityId);
+
+ statement.TryBind("@IsHearingImpaired" + index, stream.IsHearingImpaired);
}
statement.Reset();
@@ -6047,12 +6067,15 @@ AND Type = @InternalPersonType)");
item.DvBlSignalCompatibilityId = dvBlSignalCompatibilityId;
}
+ item.IsHearingImpaired = reader.GetBoolean(43);
+
if (item.Type == MediaStreamType.Subtitle)
{
item.LocalizedUndefined = _localization.GetLocalizedString("Undefined");
item.LocalizedDefault = _localization.GetLocalizedString("Default");
item.LocalizedForced = _localization.GetLocalizedString("Forced");
item.LocalizedExternal = _localization.GetLocalizedString("External");
+ item.LocalizedHearingImpaired = _localization.GetLocalizedString("HearingImpaired");
}
return item;
diff --git a/Emby.Server.Implementations/Dto/DtoService.cs b/Emby.Server.Implementations/Dto/DtoService.cs
index 3d2b8f7f63..f6d37421a8 100644
--- a/Emby.Server.Implementations/Dto/DtoService.cs
+++ b/Emby.Server.Implementations/Dto/DtoService.cs
@@ -7,6 +7,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
+using Jellyfin.Api.Helpers;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums;
using Jellyfin.Extensions;
@@ -18,6 +19,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.Lyrics;
using MediaBrowser.Controller.Persistence;
using MediaBrowser.Controller.Playlists;
using MediaBrowser.Controller.Providers;
@@ -50,6 +52,8 @@ namespace Emby.Server.Implementations.Dto
private readonly IMediaSourceManager _mediaSourceManager;
private readonly Lazy _livetvManagerFactory;
+ private readonly ILyricManager _lyricManager;
+
public DtoService(
ILogger logger,
ILibraryManager libraryManager,
@@ -59,7 +63,8 @@ namespace Emby.Server.Implementations.Dto
IProviderManager providerManager,
IApplicationHost appHost,
IMediaSourceManager mediaSourceManager,
- Lazy livetvManagerFactory)
+ Lazy livetvManagerFactory,
+ ILyricManager lyricManager)
{
_logger = logger;
_libraryManager = libraryManager;
@@ -70,6 +75,7 @@ namespace Emby.Server.Implementations.Dto
_appHost = appHost;
_mediaSourceManager = mediaSourceManager;
_livetvManagerFactory = livetvManagerFactory;
+ _lyricManager = lyricManager;
}
private ILiveTvManager LivetvManager => _livetvManagerFactory.Value;
@@ -139,6 +145,10 @@ namespace Emby.Server.Implementations.Dto
{
LivetvManager.AddInfoToProgramDto(new[] { (item, dto) }, options.Fields, user).GetAwaiter().GetResult();
}
+ else if (item is Audio)
+ {
+ dto.HasLyrics = _lyricManager.HasLyricFile(item);
+ }
if (item is IItemByName itemByName
&& options.ContainsField(ItemFields.ItemCounts))
diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
index 2792a4c7c5..eeaa3346d9 100644
--- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj
+++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj
@@ -25,14 +25,13 @@
-
+
-
-
-
-
+
+
+
diff --git a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
index 9e35d83aa5..d5e4a636ef 100644
--- a/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
+++ b/Emby.Server.Implementations/EntryPoints/LibraryChangedNotifier.cs
@@ -115,7 +115,7 @@ namespace Emby.Server.Implementations.EntryPoints
{
}
- var collectionFolders = _libraryManager.GetCollectionFolders(item).ToList();
+ var collectionFolders = _libraryManager.GetCollectionFolders(item);
foreach (var collectionFolder in collectionFolders)
{
diff --git a/Emby.Server.Implementations/IO/LibraryMonitor.cs b/Emby.Server.Implementations/IO/LibraryMonitor.cs
index 657daac3fc..c1422c43da 100644
--- a/Emby.Server.Implementations/IO/LibraryMonitor.cs
+++ b/Emby.Server.Implementations/IO/LibraryMonitor.cs
@@ -79,14 +79,6 @@ namespace Emby.Server.Implementations.IO
TemporarilyIgnore(path);
}
- public bool IsPathLocked(string path)
- {
- // This method is not used by the core but it used by auto-organize
-
- var lockedPaths = _tempIgnoredPaths.Keys.ToList();
- return lockedPaths.Any(i => _fileSystem.AreEqual(i, path) || _fileSystem.ContainsSubPath(i, path));
- }
-
public async void ReportFileSystemChangeComplete(string path, bool refreshPath)
{
if (string.IsNullOrEmpty(path))
@@ -145,8 +137,7 @@ namespace Emby.Server.Implementations.IO
.OfType()
.SelectMany(f => f.PhysicalLocations)
.Distinct(StringComparer.OrdinalIgnoreCase)
- .OrderBy(i => i)
- .ToList();
+ .OrderBy(i => i);
foreach (var path in paths)
{
@@ -372,11 +363,8 @@ namespace Emby.Server.Implementations.IO
var monitorPath = !IgnorePatterns.ShouldIgnore(path);
- // Ignore certain files
- var tempIgnorePaths = _tempIgnoredPaths.Keys.ToList();
-
- // If the parent of an ignored path has a change event, ignore that too
- if (tempIgnorePaths.Any(i =>
+ // Ignore certain files, If the parent of an ignored path has a change event, ignore that too
+ if (_tempIgnoredPaths.Keys.Any(i =>
{
if (_fileSystem.AreEqual(i, path))
{
@@ -491,7 +479,7 @@ namespace Emby.Server.Implementations.IO
{
lock (_activeRefreshers)
{
- foreach (var refresher in _activeRefreshers.ToList())
+ foreach (var refresher in _activeRefreshers)
{
refresher.Completed -= OnNewRefresherCompleted;
refresher.Dispose();
diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs
index ee94670eb0..b688af5286 100644
--- a/Emby.Server.Implementations/Library/LibraryManager.cs
+++ b/Emby.Server.Implementations/Library/LibraryManager.cs
@@ -665,11 +665,7 @@ namespace Emby.Server.Implementations.Library
if (result?.Items.Count > 0)
{
var items = result.Items;
- foreach (var item in items)
- {
- ResolverHelper.SetInitialItemValues(item, parent, this, directoryService);
- }
-
+ items.RemoveAll(item => !ResolverHelper.SetInitialItemValues(item, parent, this, directoryService));
items.AddRange(ResolveFileList(result.ExtraFiles, directoryService, parent, collectionType, resolvers, libraryOptions));
return items;
}
@@ -2594,9 +2590,9 @@ namespace Emby.Server.Implementations.Library
{
/*
Anime series don't generally have a season in their file name, however,
- tvdb needs a season to correctly get the metadata.
+ TVDb needs a season to correctly get the metadata.
Hence, a null season needs to be filled with something. */
- // FIXME perhaps this would be better for tvdb parser to ask for season 1 if no season is specified
+ // FIXME perhaps this would be better for TVDb parser to ask for season 1 if no season is specified
episode.ParentIndexNumber = 1;
}
diff --git a/Emby.Server.Implementations/Library/MediaStreamSelector.cs b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
index 20a2edb05a..609b957727 100644
--- a/Emby.Server.Implementations/Library/MediaStreamSelector.cs
+++ b/Emby.Server.Implementations/Library/MediaStreamSelector.cs
@@ -45,42 +45,42 @@ namespace Emby.Server.Implementations.Library
.ThenByDescending(x => x.IsForced && string.Equals(x.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
.ThenByDescending(x => x.IsForced)
.ThenByDescending(x => x.IsDefault)
+ .ThenByDescending(x => preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase))
.ToList();
MediaStream? stream = null;
if (mode == SubtitlePlaybackMode.Default)
{
- // Prefer embedded metadata over smart logic
- stream = sortedStreams.FirstOrDefault(s => s.IsExternal || s.IsForced || s.IsDefault);
-
- // if the audio language is not understood by the user, load their preferred subs, if there are any
- if (stream == null && !preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
- {
- stream = sortedStreams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase));
- }
+ // Load subtitles according to external, forced and default flags.
+ stream = sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault);
}
else if (mode == SubtitlePlaybackMode.Smart)
{
- // if the audio language is not understood by the user, load their preferred subs, if there are any
+ // Only attempt to load subtitles if the audio language is not one of the user's preferred subtitle languages.
+ // If no subtitles of preferred language available, use default behaviour.
if (!preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
{
- stream = streams.FirstOrDefault(s => !s.IsForced && preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase)) ??
- streams.FirstOrDefault(s => preferredLanguages.Contains(s.Language, StringComparison.OrdinalIgnoreCase));
+ stream = sortedStreams.FirstOrDefault(x => preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) ??
+ sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault);
+ }
+ else
+ {
+ // Respect forced flag.
+ stream = sortedStreams.FirstOrDefault(x => x.IsForced);
}
}
else if (mode == SubtitlePlaybackMode.Always)
{
- // always load the most suitable full subtitles
- stream = sortedStreams.FirstOrDefault(s => !s.IsForced);
+ // Always load (full/non-forced) subtitles of the user's preferred subtitle language if possible, otherwise default behaviour.
+ stream = sortedStreams.FirstOrDefault(x => !x.IsForced && preferredLanguages.Contains(x.Language, StringComparison.OrdinalIgnoreCase)) ??
+ sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsForced || x.IsDefault);
}
else if (mode == SubtitlePlaybackMode.OnlyForced)
{
- // always load the most suitable full subtitles
+ // Only load subtitles that are flagged forced.
stream = sortedStreams.FirstOrDefault(x => x.IsForced);
}
- // load forced subs if we have found no suitable full subtitles
- stream ??= sortedStreams.FirstOrDefault(s => s.IsForced && string.Equals(s.Language, audioTrackLanguage, StringComparison.OrdinalIgnoreCase));
return stream?.Index;
}
diff --git a/Emby.Server.Implementations/Library/ResolverHelper.cs b/Emby.Server.Implementations/Library/ResolverHelper.cs
index ac75e5d3a1..4100a74a58 100644
--- a/Emby.Server.Implementations/Library/ResolverHelper.cs
+++ b/Emby.Server.Implementations/Library/ResolverHelper.cs
@@ -20,8 +20,9 @@ namespace Emby.Server.Implementations.Library
/// The parent.
/// The library manager.
/// The directory service.
+ /// True if initializing was successful.
/// Item must have a path.
- public static void SetInitialItemValues(BaseItem item, Folder? parent, ILibraryManager libraryManager, IDirectoryService directoryService)
+ public static bool SetInitialItemValues(BaseItem item, Folder? parent, ILibraryManager libraryManager, IDirectoryService directoryService)
{
// This version of the below method has no ItemResolveArgs, so we have to require the path already being set
if (string.IsNullOrEmpty(item.Path))
@@ -44,12 +45,14 @@ namespace Emby.Server.Implementations.Library
var fileInfo = directoryService.GetFile(item.Path);
if (fileInfo == null)
{
- throw new FileNotFoundException("Can't find item path.", item.Path);
+ return false;
}
SetDateCreated(item, fileInfo);
EnsureName(item, fileInfo);
+
+ return true;
}
///
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index da00b9cfa8..a922e36855 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
@@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@@ -18,7 +19,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
///
- /// Class MusicAlbumResolver.
+ /// The music album resolver.
///
public class MusicAlbumResolver : ItemResolver
{
@@ -82,7 +83,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
///
/// The path to check.
/// The directory service.
- /// true if the provided path points to a music album, false otherwise.
+ /// true if the provided path points to a music album; otherwise, false.
public bool IsMusicAlbum(string path, IDirectoryService directoryService)
{
return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService);
@@ -95,10 +96,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// true if [is music album] [the specified args]; otherwise, false.
private bool IsMusicAlbum(ItemResolveArgs args)
{
- // Args points to an album if parent is an Artist folder or it directly contains music
if (args.IsDirectory)
{
- // if (args.Parent is MusicArtist) return true; // saves us from testing children twice
+ // If args is a artist subfolder it's not a music album
+ foreach (var subfolder in _namingOptions.ArtistSubfolders)
+ {
+ if (Path.GetDirectoryName(args.Path.AsSpan()).Equals(subfolder, StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.LogDebug("Found release folder: {Path}", args.Path);
+ return false;
+ }
+ }
+
+ // If args contains music it's a music album
if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService))
{
return true;
@@ -111,22 +121,23 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
///
/// Determine if the supplied list contains what we should consider music.
///
+ /// true if the provided path list contains music; otherwise, false.
private bool ContainsMusic(
ICollection list,
bool allowSubfolders,
IDirectoryService directoryService)
{
- // check for audio files before digging down into directories
+ // Check for audio files before digging down into directories
var foundAudioFile = list.Any(fileSystemInfo => !fileSystemInfo.IsDirectory && AudioFileParser.IsAudioFile(fileSystemInfo.FullName, _namingOptions));
if (foundAudioFile)
{
- // at least one audio file exists
+ // At least one audio file exists
return true;
}
if (!allowSubfolders)
{
- // not music since no audio file exists and we're not looking into subfolders
+ // Not music since no audio file exists and we're not looking into subfolders
return false;
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
index 210ed0953a..2538c2b5b4 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicArtistResolver.cs
@@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
///
- /// Class MusicArtistResolver.
+ /// The music artist resolver.
///
public class MusicArtistResolver : ItemResolver
{
@@ -23,8 +23,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
///
/// Initializes a new instance of the class.
///
- /// The logger for the created instances.
- /// The naming options.
+ /// Instance of the interface.
+ /// The .
public MusicArtistResolver(
ILogger logger,
NamingOptions namingOptions)
@@ -40,10 +40,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
public override ResolverPriority Priority => ResolverPriority.Second;
///
- /// Resolves the specified args.
+ /// Resolves the specified resolver arguments.
///
- /// The args.
- /// MusicArtist.
+ /// The resolver arguments.
+ /// A .
protected override MusicArtist Resolve(ItemResolveArgs args)
{
if (!args.IsDirectory)
@@ -61,7 +61,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var isMusicMediaFolder = string.Equals(collectionType, CollectionType.Music, StringComparison.OrdinalIgnoreCase);
- // If there's a collection type and it's not music, it can't be a series
+ // If there's a collection type and it's not music, it can't be a music artist
if (!isMusicMediaFolder)
{
return null;
@@ -82,14 +82,24 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
var albumResolver = new MusicAlbumResolver(_logger, _namingOptions);
- // If we contain an album assume we are an artist folder
var directories = args.FileSystemChildren.Where(i => i.IsDirectory);
var result = Parallel.ForEach(directories, (fileSystemInfo, state) =>
{
+ // If we contain a artist subfolder assume we are an artist folder
+ foreach (var subfolder in _namingOptions.ArtistSubfolders)
+ {
+ if (fileSystemInfo.Name.Equals(subfolder, StringComparison.OrdinalIgnoreCase))
+ {
+ // Stop once we see an artist subfolder
+ state.Stop();
+ }
+ }
+
+ // If we contain a music album assume we are an artist folder
if (albumResolver.IsMusicAlbum(fileSystemInfo.FullName, directoryService))
{
- // stop once we see a music album
+ // Stop once we see a music album
state.Stop();
}
});
diff --git a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
index 3d6b9f3b62..b2a7abb1bd 100644
--- a/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
@@ -38,7 +38,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
///
/// The args.
/// `0.
- public override T Resolve(ItemResolveArgs args)
+ protected override T Resolve(ItemResolveArgs args)
{
return ResolveVideo(args, false);
}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
index 8f224f547a..6fc200e3b1 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Books/BookResolver.cs
@@ -8,15 +8,16 @@ using System.Linq;
using Jellyfin.Extensions;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library.Resolvers.Books
{
- public class BookResolver : MediaBrowser.Controller.Resolvers.ItemResolver
+ public class BookResolver : ItemResolver
{
private readonly string[] _validExtensions = { ".azw", ".azw3", ".cb7", ".cbr", ".cbt", ".cbz", ".epub", ".mobi", ".pdf" };
- public override Book Resolve(ItemResolveArgs args)
+ protected override Book Resolve(ItemResolveArgs args)
{
var collectionType = args.GetCollectionType();
diff --git a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs
index f109a5e9a2..0799622825 100644
--- a/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/GenericFolderResolver.cs
@@ -2,6 +2,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Resolvers;
namespace Emby.Server.Implementations.Library.Resolvers
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs b/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
deleted file mode 100644
index 3f29ab191f..0000000000
--- a/Emby.Server.Implementations/Library/Resolvers/ItemResolver.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-#nullable disable
-
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Resolvers;
-
-namespace Emby.Server.Implementations.Library.Resolvers
-{
- ///
- /// Class ItemResolver.
- ///
- /// The type of BaseItem.
- public abstract class ItemResolver : IItemResolver
- where T : BaseItem, new()
- {
- ///
- /// Gets the priority.
- ///
- /// The priority.
- public virtual ResolverPriority Priority => ResolverPriority.First;
-
- ///
- /// Resolves the specified args.
- ///
- /// The args.
- /// `0.
- protected virtual T Resolve(ItemResolveArgs args)
- {
- return null;
- }
-
- ///
- /// Sets initial values on the newly resolved item.
- ///
- /// The item.
- /// The args.
- protected virtual void SetInitialItemValues(T item, ItemResolveArgs args)
- {
- }
-
- ///
- /// Resolves the path.
- ///
- /// The args.
- /// BaseItem.
- BaseItem IItemResolver.ResolvePath(ItemResolveArgs args)
- {
- var item = Resolve(args);
-
- if (item != null)
- {
- SetInitialItemValues(item, args);
- }
-
- return item;
- }
- }
-}
diff --git a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
index b2f388a667..84d4688aff 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
@@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
///
/// The args.
/// Video.
- public override Video Resolve(ItemResolveArgs args)
+ protected override Video Resolve(ItemResolveArgs args)
{
var collectionType = args.GetCollectionType();
@@ -376,7 +376,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!justName.IsEmpty)
{
- // check for tmdb id
+ // Check for TMDb id
var tmdbid = justName.GetAttributeValue("tmdbid");
if (!string.IsNullOrWhiteSpace(tmdbid))
@@ -387,7 +387,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
if (!string.IsNullOrEmpty(item.Path))
{
- // check for imdb id - we use full media path, as we can assume, that this will match in any use case (either id in parent dir or in file name)
+ // Check for IMDb id - we use full media path, as we can assume that this will match in any use case (whether id in parent dir or in file name)
var imdbid = item.Path.AsSpan().GetAttributeValue("imdbid");
if (!string.IsNullOrWhiteSpace(imdbid))
diff --git a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
index af4abfb805..e11fb262eb 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PhotoResolver.cs
@@ -12,6 +12,7 @@ using Jellyfin.Extensions;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Model.Entities;
namespace Emby.Server.Implementations.Library.Resolvers
diff --git a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
index 6b0dfe9864..7a2b3da3a9 100644
--- a/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/PlaylistResolver.cs
@@ -31,16 +31,18 @@ namespace Emby.Server.Implementations.Library.Resolvers
if (args.IsDirectory)
{
// It's a boxset if the path is a directory with [playlist] in it's the name
- // TODO: Should this use Path.GetDirectoryName() instead?
- bool isBoxSet = Path.GetFileName(args.Path)
- ?.Contains("[playlist]", StringComparison.OrdinalIgnoreCase)
- ?? false;
- if (isBoxSet)
+ var filename = Path.GetFileName(Path.TrimEndingDirectorySeparator(args.Path));
+ if (string.IsNullOrEmpty(filename))
+ {
+ return null;
+ }
+
+ if (filename.Contains("[playlist]", StringComparison.OrdinalIgnoreCase))
{
return new Playlist
{
Path = args.Path,
- Name = Path.GetFileName(args.Path).Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim()
+ Name = filename.Replace("[playlist]", string.Empty, StringComparison.OrdinalIgnoreCase).Trim()
};
}
@@ -51,7 +53,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
return new Playlist
{
Path = args.Path,
- Name = Path.GetFileName(args.Path)
+ Name = filename
};
}
}
@@ -60,8 +62,8 @@ namespace Emby.Server.Implementations.Library.Resolvers
// It should have the correct collection type and a supported file extension
else if (_musicPlaylistCollectionTypes.Contains(args.CollectionType ?? string.Empty, StringComparison.OrdinalIgnoreCase))
{
- var extension = Path.GetExtension(args.Path);
- if (Playlist.SupportedExtensions.Contains(extension ?? string.Empty, StringComparison.OrdinalIgnoreCase))
+ var extension = Path.GetExtension(args.Path.AsSpan());
+ if (Playlist.SupportedExtensions.Contains(extension, StringComparison.OrdinalIgnoreCase))
{
return new Playlist
{
diff --git a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
index bfa73af2f4..9ba079edfd 100644
--- a/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
+++ b/Emby.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
@@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
///
/// The args.
/// Episode.
- public override Episode Resolve(ItemResolveArgs args)
+ protected override Episode Resolve(ItemResolveArgs args)
{
var parent = args.Parent;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
index 4da6776369..cf9be5a54a 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs
@@ -2192,16 +2192,15 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private void HandleDuplicateShowIds(List timers)
{
- foreach (var timer in timers.Skip(1))
+ // sort showings by HD channels first, then by startDate, record earliest showing possible
+ foreach (var timer in timers.OrderByDescending(t => _liveTvManager.GetLiveTvChannel(t, this).IsHD).ThenBy(t => t.StartDate).Skip(1))
{
- // TODO: Get smarter, prefer HD, etc
-
timer.Status = RecordingStatus.Cancelled;
_timerProvider.Update(timer);
}
}
- private void SearchForDuplicateShowIds(List timers)
+ private void SearchForDuplicateShowIds(IEnumerable timers)
{
var groups = timers.ToLookup(i => i.ShowId ?? string.Empty).ToList();
@@ -2219,6 +2218,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
continue;
}
+ // Skip ShowId without SubKey from duplicate removal actions - https://github.com/jellyfin/jellyfin/issues/5856
+ if (group.Key.EndsWith("0000", StringComparison.Ordinal))
+ {
+ continue;
+ }
+
HandleDuplicateShowIds(groupTimers);
}
}
@@ -2276,39 +2281,13 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (updateTimerSettings)
{
- // Only update if not currently active - test both new timer and existing in case Id's are different
- // Id's could be different if the timer was created manually prior to series timer creation
- if (!_activeRecordings.TryGetValue(timer.Id, out _) && !_activeRecordings.TryGetValue(existingTimer.Id, out _))
- {
- UpdateExistingTimerWithNewMetadata(existingTimer, timer);
-
- // Needed by ShouldCancelTimerForSeriesTimer
- timer.IsManual = existingTimer.IsManual;
-
- if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer))
- {
- existingTimer.Status = RecordingStatus.Cancelled;
- }
- else if (!existingTimer.IsManual)
- {
- existingTimer.Status = RecordingStatus.New;
- }
-
- if (existingTimer.Status != RecordingStatus.Cancelled)
- {
- enabledTimersForSeries.Add(existingTimer);
- }
-
- existingTimer.KeepUntil = seriesTimer.KeepUntil;
- existingTimer.IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired;
- existingTimer.IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired;
- existingTimer.PostPaddingSeconds = seriesTimer.PostPaddingSeconds;
- existingTimer.PrePaddingSeconds = seriesTimer.PrePaddingSeconds;
- existingTimer.Priority = seriesTimer.Priority;
- existingTimer.SeriesTimerId = seriesTimer.Id;
-
- _timerProvider.Update(existingTimer);
- }
+ existingTimer.KeepUntil = seriesTimer.KeepUntil;
+ existingTimer.IsPostPaddingRequired = seriesTimer.IsPostPaddingRequired;
+ existingTimer.IsPrePaddingRequired = seriesTimer.IsPrePaddingRequired;
+ existingTimer.PostPaddingSeconds = seriesTimer.PostPaddingSeconds;
+ existingTimer.PrePaddingSeconds = seriesTimer.PrePaddingSeconds;
+ existingTimer.Priority = seriesTimer.Priority;
+ existingTimer.SeriesTimerId = seriesTimer.Id;
}
existingTimer.SeriesTimerId = seriesTimer.Id;
diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
index a861e6ae44..f612565d1b 100644
--- a/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
+++ b/Emby.Server.Implementations/LiveTv/EmbyTV/TimerManager.cs
@@ -122,11 +122,28 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
if (_timers.TryAdd(item.Id, timer))
{
- Logger.LogInformation(
- "Creating recording timer for {Id}, {Name}. Timer will fire in {Minutes} minutes",
+ if (item.IsSeries)
+ {
+ Logger.LogInformation(
+ "Creating recording timer for {Id}, {Name} {SeasonNumber}x{EpisodeNumber:D2} on channel {ChannelId}. Timer will fire in {Minutes} minutes at {StartDate}",
item.Id,
item.Name,
- dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+ item.SeasonNumber,
+ item.EpisodeNumber,
+ item.ChannelId,
+ dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture),
+ item.StartDate);
+ }
+ else
+ {
+ Logger.LogInformation(
+ "Creating recording timer for {Id}, {Name} on channel {ChannelId}. Timer will fire in {Minutes} minutes at {StartDate}",
+ item.Id,
+ item.Name,
+ item.ChannelId,
+ dueTime.TotalMinutes.ToString(CultureInfo.InvariantCulture),
+ item.StartDate);
+ }
}
else
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
index 4311db28d2..b981ad81a7 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs
@@ -166,12 +166,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
const double DesiredAspect = 2.0 / 3;
- programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, DesiredAspect) ??
- GetProgramImage(ApiUrl, allImages, DesiredAspect);
+ programEntry.PrimaryImage = GetProgramImage(ApiUrl, imagesWithText, DesiredAspect, token) ??
+ GetProgramImage(ApiUrl, allImages, DesiredAspect, token);
const double WideAspect = 16.0 / 9;
- programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, WideAspect);
+ programEntry.ThumbImage = GetProgramImage(ApiUrl, imagesWithText, WideAspect, token);
// Don't supply the same image twice
if (string.Equals(programEntry.PrimaryImage, programEntry.ThumbImage, StringComparison.Ordinal))
@@ -179,7 +179,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
programEntry.ThumbImage = null;
}
- programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, WideAspect);
+ programEntry.BackdropImage = GetProgramImage(ApiUrl, imagesWithoutText, WideAspect, token);
// programEntry.bannerImage = GetProgramImage(ApiUrl, data, "Banner", false) ??
// GetProgramImage(ApiUrl, data, "Banner-L1", false) ??
@@ -400,7 +400,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return info;
}
- private static string GetProgramImage(string apiUrl, IEnumerable images, double desiredAspect)
+ private static string GetProgramImage(string apiUrl, IEnumerable images, double desiredAspect, string token)
{
var match = images
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
@@ -424,7 +424,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
else
{
- return apiUrl + "/image/" + uri;
+ return apiUrl + "/image/" + uri + "?token=" + token;
}
}
@@ -458,6 +458,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
IReadOnlyList programIds,
CancellationToken cancellationToken)
{
+ var token = await GetToken(info, cancellationToken).ConfigureAwait(false);
+
if (programIds.Count == 0)
{
return Array.Empty();
@@ -479,6 +481,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
Content = new StringContent(str.ToString(), Encoding.UTF8, MediaTypeNames.Application.Json)
};
+ message.Headers.TryAddWithoutValidation("token", token);
try
{
diff --git a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
index bd1cd1e1de..82f0baf32e 100644
--- a/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
+++ b/Emby.Server.Implementations/LiveTv/Listings/XmlTvListingsProvider.cs
@@ -6,9 +6,9 @@ using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
+using System.IO.Compression;
using System.Linq;
using System.Net.Http;
-using System.Security.Cryptography;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions;
@@ -32,21 +32,15 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private readonly IServerConfigurationManager _config;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILogger _logger;
- private readonly IFileSystem _fileSystem;
- private readonly IZipClient _zipClient;
public XmlTvListingsProvider(
IServerConfigurationManager config,
IHttpClientFactory httpClientFactory,
- ILogger logger,
- IFileSystem fileSystem,
- IZipClient zipClient)
+ ILogger logger)
{
_config = config;
_httpClientFactory = httpClientFactory;
_logger = logger;
- _fileSystem = fileSystem;
- _zipClient = zipClient;
}
public string Name => "XmlTV";
@@ -67,16 +61,12 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
_logger.LogInformation("xmltv path: {Path}", info.Path);
- if (!info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
- {
- return UnzipIfNeeded(info.Path, info.Path);
- }
-
string cacheFilename = info.Id + ".xml";
string cacheFile = Path.Combine(_config.ApplicationPaths.CachePath, "xmltv", cacheFilename);
+
if (File.Exists(cacheFile) && File.GetLastWriteTimeUtc(cacheFile) >= DateTime.UtcNow.Subtract(_maxCacheAge))
{
- return UnzipIfNeeded(info.Path, cacheFile);
+ return cacheFile;
}
// Must check if file exists as parent directory may not exist.
@@ -84,95 +74,50 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{
File.Delete(cacheFile);
}
+ else
+ {
+ Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
+ }
- _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path);
+ if (info.Path.StartsWith("http", StringComparison.OrdinalIgnoreCase))
+ {
+ _logger.LogInformation("Downloading xmltv listings from {Path}", info.Path);
- Directory.CreateDirectory(Path.GetDirectoryName(cacheFile));
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false);
+ await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
+ return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false);
+ }
+ else
+ {
+ await using var stream = AsyncFile.OpenRead(info.Path);
+ return await UnzipIfNeededAndCopy(info.Path, stream, cacheFile, cancellationToken).ConfigureAwait(false);
+ }
+ }
- using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(info.Path, cancellationToken).ConfigureAwait(false);
- await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
- await using (var fileStream = new FileStream(cacheFile, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.CopyToBufferSize, FileOptions.Asynchronous))
+ private async Task UnzipIfNeededAndCopy(string originalUrl, Stream stream, string file, CancellationToken cancellationToken)
+ {
+ await using var fileStream = new FileStream(file, FileMode.CreateNew, FileAccess.Write, FileShare.None, IODefaults.FileStreamBufferSize, FileOptions.Asynchronous);
+
+ if (Path.GetExtension(originalUrl.AsSpan().LeftPart('?')).Equals(".gz", StringComparison.OrdinalIgnoreCase))
+ {
+ try
+ {
+ using var reader = new GZipStream(stream, CompressionMode.Decompress);
+ await reader.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error extracting from gz file {File}", originalUrl);
+ }
+ }
+ else
{
await stream.CopyToAsync(fileStream, cancellationToken).ConfigureAwait(false);
}
- return UnzipIfNeeded(info.Path, cacheFile);
- }
-
- private string UnzipIfNeeded(ReadOnlySpan originalUrl, string file)
- {
- ReadOnlySpan ext = Path.GetExtension(originalUrl.LeftPart('?'));
-
- if (ext.Equals(".gz", StringComparison.OrdinalIgnoreCase))
- {
- try
- {
- string tempFolder = ExtractGz(file);
- return FindXmlFile(tempFolder);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error extracting from gz file {File}", file);
- }
-
- try
- {
- string tempFolder = ExtractFirstFileFromGz(file);
- return FindXmlFile(tempFolder);
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error extracting from zip file {File}", file);
- }
- }
-
return file;
}
- private string ExtractFirstFileFromGz(string file)
- {
- using (var stream = File.OpenRead(file))
- {
- string tempFolder = GetTempFolderPath(stream);
- Directory.CreateDirectory(tempFolder);
-
- _zipClient.ExtractFirstFileFromGz(stream, tempFolder, "data.xml");
-
- return tempFolder;
- }
- }
-
- private string ExtractGz(string file)
- {
- using (var stream = File.OpenRead(file))
- {
- string tempFolder = GetTempFolderPath(stream);
- Directory.CreateDirectory(tempFolder);
-
- _zipClient.ExtractAllFromGz(stream, tempFolder, true);
-
- return tempFolder;
- }
- }
-
- private string GetTempFolderPath(Stream stream)
- {
-#pragma warning disable CA5351
- using var md5 = MD5.Create();
-#pragma warning restore CA5351
- var checksum = Convert.ToHexString(md5.ComputeHash(stream));
- stream.Position = 0;
- return Path.Combine(_config.ApplicationPaths.TempDirectory, checksum);
- }
-
- private string FindXmlFile(string directory)
- {
- return _fileSystem.GetFiles(directory, true)
- .Where(i => string.Equals(i.Extension, ".xml", StringComparison.OrdinalIgnoreCase))
- .Select(i => i.FullName)
- .FirstOrDefault();
- }
-
public async Task> GetProgramsAsync(ListingsProviderInfo info, string channelId, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(channelId))
@@ -213,16 +158,16 @@ namespace Emby.Server.Implementations.LiveTv.Listings
IsMovie = program.Categories.Any(c => info.MovieCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
IsNews = program.Categories.Any(c => info.NewsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
IsSports = program.Categories.Any(c => info.SportsCategories.Contains(c, StringComparison.OrdinalIgnoreCase)),
- ImageUrl = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source) ? program.Icon.Source : null,
- HasImage = program.Icon != null && !string.IsNullOrEmpty(program.Icon.Source),
- OfficialRating = program.Rating != null && !string.IsNullOrEmpty(program.Rating.Value) ? program.Rating.Value : null,
+ ImageUrl = string.IsNullOrEmpty(program.Icon?.Source) ? null : program.Icon.Source,
+ HasImage = !string.IsNullOrEmpty(program.Icon?.Source),
+ OfficialRating = string.IsNullOrEmpty(program.Rating?.Value) ? null : program.Rating.Value,
CommunityRating = program.StarRating,
- SeriesId = program.Episode == null ? null : program.Title.GetMD5().ToString("N", CultureInfo.InvariantCulture)
+ SeriesId = program.Episode == null ? null : program.Title?.GetMD5().ToString("N", CultureInfo.InvariantCulture)
};
if (string.IsNullOrWhiteSpace(program.ProgramId))
{
- string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty) /*+ (p.IceTvEpisodeNumber ?? string.Empty)*/;
+ string uniqueString = (program.Title ?? string.Empty) + (episodeTitle ?? string.Empty);
if (programInfo.SeasonNumber.HasValue)
{
diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
index 48d9e316de..e67b5846aa 100644
--- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
+++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs
@@ -67,7 +67,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
- return VerifyReturnValueOfGetSet(buffer.AsSpan(receivedBytes), "none");
+ return VerifyReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), "none");
}
finally
{
diff --git a/Emby.Server.Implementations/Localization/Core/ar.json b/Emby.Server.Implementations/Localization/Core/ar.json
index 9dc2fe7996..ada3c77301 100644
--- a/Emby.Server.Implementations/Localization/Core/ar.json
+++ b/Emby.Server.Implementations/Localization/Core/ar.json
@@ -97,7 +97,7 @@
"TasksChannelsCategory": "قنوات الإنترنت",
"TasksLibraryCategory": "مكتبة",
"TasksMaintenanceCategory": "صيانة",
- "TaskRefreshLibraryDescription": "يفصح مكتبة الوسائط الخاصة بك بحثًا عن ملفات جديدة، ومن ثم يتحدث البيانات الوصفية.",
+ "TaskRefreshLibraryDescription": "يفحص مكتبة الوسائط الخاصة بك باحثا عن ملفات جديدة، ومن ثم يتحدث البيانات الوصفية.",
"TaskRefreshLibrary": "افحص مكتبة الوسائط",
"TaskRefreshChapterImagesDescription": "يُنشئ صور مصغرة لمقاطع الفيديو التي تحتوي على فصول.",
"TaskRefreshChapterImages": "استخراج صور الفصل",
diff --git a/Emby.Server.Implementations/Localization/Core/ca.json b/Emby.Server.Implementations/Localization/Core/ca.json
index 644d2676ee..c0ed01fdff 100644
--- a/Emby.Server.Implementations/Localization/Core/ca.json
+++ b/Emby.Server.Implementations/Localization/Core/ca.json
@@ -40,16 +40,16 @@
"Movies": "Pel·lícules",
"Music": "Música",
"MusicVideos": "Vídeos Musicals",
- "NameInstallFailed": "Instalació de {0} fallida",
+ "NameInstallFailed": "Instal·lació de {0} fallida",
"NameSeasonNumber": "Temporada {0}",
"NameSeasonUnknown": "Temporada Desconeguda",
"NewVersionIsAvailable": "Una nova versió del Servidor Jellyfin està disponible per descarregar.",
"NotificationOptionApplicationUpdateAvailable": "Actualització d'aplicació disponible",
"NotificationOptionApplicationUpdateInstalled": "Actualització d'aplicació instal·lada",
- "NotificationOptionAudioPlayback": "Reproducció d'audio iniciada",
- "NotificationOptionAudioPlaybackStopped": "Reproducció d'audio aturada",
+ "NotificationOptionAudioPlayback": "Reproducció d'àudio iniciada",
+ "NotificationOptionAudioPlaybackStopped": "Reproducció d'àudio aturada",
"NotificationOptionCameraImageUploaded": "Imatge de càmera pujada",
- "NotificationOptionInstallationFailed": "Instalació fallida",
+ "NotificationOptionInstallationFailed": "Instal·lació fallida",
"NotificationOptionNewLibraryContent": "Nou contingut afegit",
"NotificationOptionPluginError": "Un connector ha fallat",
"NotificationOptionPluginInstalled": "Connector instal·lat",
@@ -72,13 +72,13 @@
"ServerNameNeedsToBeRestarted": "{0} necessita ser reiniciat",
"Shows": "Sèries",
"Songs": "Cançons",
- "StartupEmbyServerIsLoading": "El Servidor d'Jellyfin està carregant. Si et plau, prova de nou en breus.",
+ "StartupEmbyServerIsLoading": "El Servidor de Jellyfin està carregant. Si et plau, prova de nou ben aviat.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Els subtítols no s'han pogut baixar de {0} per {1}",
"Sync": "Sincronitzar",
"System": "Sistema",
- "TvShows": "Espectacles de TV",
- "User": "User",
+ "TvShows": "Sèries de TV",
+ "User": "Usuari",
"UserCreatedWithName": "S'ha creat l'usuari {0}",
"UserDeletedWithName": "L'usuari {0} ha estat eliminat",
"UserDownloadingItemWithValues": "{0} està descarregant {1}",
@@ -89,12 +89,12 @@
"UserPolicyUpdatedWithName": "La política d'usuari s'ha actualitzat per {0}",
"UserStartedPlayingItemWithValues": "{0} ha començat a reproduir {1}",
"UserStoppedPlayingItemWithValues": "{0} ha parat de reproduir {1}",
- "ValueHasBeenAddedToLibrary": "{0} ha sigut afegit a la teva llibreria",
+ "ValueHasBeenAddedToLibrary": "{0} ha sigut afegit a la teva biblioteca",
"ValueSpecialEpisodeName": "Especial - {0}",
"VersionNumber": "Versió {0}",
"TaskDownloadMissingSubtitlesDescription": "Cerca a internet els subtítols que faltin a partir de la configuració de metadades.",
"TaskDownloadMissingSubtitles": "Descarrega els subtítols que faltin",
- "TaskRefreshChannelsDescription": "Actualitza la informació dels canals d'internet.",
+ "TaskRefreshChannelsDescription": "Actualitza la informació dels canals d'Internet.",
"TaskRefreshChannels": "Actualitza Canals",
"TaskCleanTranscodeDescription": "Elimina els arxius temporals de transcodificacions que tinguin més d'un dia.",
"TaskCleanTranscode": "Neteja les transcodificacions",
@@ -110,7 +110,7 @@
"TaskRefreshChapterImages": "Extreure les imatges dels capítols",
"TaskCleanCacheDescription": "Elimina els arxius temporals que ja no són necessaris per al servidor.",
"TaskCleanCache": "Elimina arxius temporals",
- "TasksChannelsCategory": "Canals d'internet",
+ "TasksChannelsCategory": "Canals d'Internet",
"TasksApplicationCategory": "Aplicació",
"TasksLibraryCategory": "Biblioteca",
"TasksMaintenanceCategory": "Manteniment",
@@ -118,10 +118,11 @@
"TaskCleanActivityLog": "Buidar Registre d'Activitat",
"Undefined": "Indefinit",
"Forced": "Forçat",
- "Default": "Defecto",
+ "Default": "Defecte",
"TaskOptimizeDatabaseDescription": "Compacta la base de dades i trunca l'espai lliure. Executar aquesta tasca després d’escanejar la biblioteca o fer altres canvis que impliquin modificacions a la base de dades pot millorar el rendiment.",
"TaskOptimizeDatabase": "Optimitzar la base de dades",
"TaskKeyframeExtractorDescription": "Extreu fotogrames clau dels fitxers de vídeo per crear llistes de reproducció HLS més precises. Aquesta tasca pot durar molt de temps.",
"TaskKeyframeExtractor": "Extractor de fotogrames clau",
- "External": "Extern"
+ "External": "Extern",
+ "HearingImpaired": "Discapacitat Auditiva"
}
diff --git a/Emby.Server.Implementations/Localization/Core/cs.json b/Emby.Server.Implementations/Localization/Core/cs.json
index 943fc651f7..08db5a30e0 100644
--- a/Emby.Server.Implementations/Localization/Core/cs.json
+++ b/Emby.Server.Implementations/Localization/Core/cs.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Optimalizovat databázi",
"TaskKeyframeExtractorDescription": "Vytahuje klíčové snímky ze souborů videa za účelem vytváření přesnějších seznamů přehrávání HLS. Tento úkol může trvat velmi dlouho.",
"TaskKeyframeExtractor": "Vytahovač klíčových snímků",
- "External": "Externí"
+ "External": "Externí",
+ "HearingImpaired": "Sluchově postižení"
}
diff --git a/Emby.Server.Implementations/Localization/Core/da.json b/Emby.Server.Implementations/Localization/Core/da.json
index 57455587d2..34655ace69 100644
--- a/Emby.Server.Implementations/Localization/Core/da.json
+++ b/Emby.Server.Implementations/Localization/Core/da.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Optimér database",
"TaskKeyframeExtractorDescription": "Udtrækker billeder fra videofiler for at lave mere præcise HLS playlister. Denne opgave kan godt tage lang tid.",
"TaskKeyframeExtractor": "Billedramme udtrækker",
- "External": "Ekstern"
+ "External": "Ekstern",
+ "HearingImpaired": "Hørehæmmet"
}
diff --git a/Emby.Server.Implementations/Localization/Core/de.json b/Emby.Server.Implementations/Localization/Core/de.json
index 9c278db4d2..e1c3e9de12 100644
--- a/Emby.Server.Implementations/Localization/Core/de.json
+++ b/Emby.Server.Implementations/Localization/Core/de.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Datenbank optimieren",
"TaskKeyframeExtractorDescription": "Extrahiere Keyframes aus Videodateien, um präzisere HLS-Playlisten zu erzeugen. Dieser Vorgang kann sehr lange dauern.",
"TaskKeyframeExtractor": "Keyframe Extraktor",
- "External": "Extern"
+ "External": "Extern",
+ "HearingImpaired": "Hörgeschädigt"
}
diff --git a/Emby.Server.Implementations/Localization/Core/el.json b/Emby.Server.Implementations/Localization/Core/el.json
index 9e216a1660..8e9287af48 100644
--- a/Emby.Server.Implementations/Localization/Core/el.json
+++ b/Emby.Server.Implementations/Localization/Core/el.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Βελτιστοποίηση βάσης δεδομένων",
"TaskKeyframeExtractorDescription": "Εξάγει καρέ από αρχεία βίντεο για να δημιουργήσει πιο ακριβείς λίστες αναπαραγωγής HLS. Αυτή η διεργασία μπορεί να πάρει χρόνο.",
"TaskKeyframeExtractor": "Εξαγωγέας βασικών καρέ βίντεο",
- "External": "Εξωτερικό"
+ "External": "Εξωτερικό",
+ "HearingImpaired": "Με προβλήματα ακοής"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-GB.json b/Emby.Server.Implementations/Localization/Core/en-GB.json
index 862410c54b..2436883883 100644
--- a/Emby.Server.Implementations/Localization/Core/en-GB.json
+++ b/Emby.Server.Implementations/Localization/Core/en-GB.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Optimise database",
"TaskKeyframeExtractorDescription": "Extracts keyframes from video files to create more precise HLS playlists. This task may run for a long time.",
"TaskKeyframeExtractor": "Keyframe Extractor",
- "External": "External"
+ "External": "External",
+ "HearingImpaired": "Hearing Impaired"
}
diff --git a/Emby.Server.Implementations/Localization/Core/en-US.json b/Emby.Server.Implementations/Localization/Core/en-US.json
index d8c33d51bd..15088384cc 100644
--- a/Emby.Server.Implementations/Localization/Core/en-US.json
+++ b/Emby.Server.Implementations/Localization/Core/en-US.json
@@ -28,6 +28,7 @@
"HeaderLiveTV": "Live TV",
"HeaderNextUp": "Next Up",
"HeaderRecordingGroups": "Recording Groups",
+ "HearingImpaired": "Hearing Impaired",
"HomeVideos": "Home Videos",
"Inherit": "Inherit",
"ItemAddedWithName": "{0} was added to the library",
diff --git a/Emby.Server.Implementations/Localization/Core/es-AR.json b/Emby.Server.Implementations/Localization/Core/es-AR.json
index 1289172bac..8ad9e8c716 100644
--- a/Emby.Server.Implementations/Localization/Core/es-AR.json
+++ b/Emby.Server.Implementations/Localization/Core/es-AR.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Optimización de base de datos",
"External": "Externo",
"TaskKeyframeExtractorDescription": "Extrae Fotogramas Clave de los archivos de vídeo para crear Listas de Reprodución HLS más precisas. Esta tarea puede durar mucho tiempo.",
- "TaskKeyframeExtractor": "Extractor de Fotogramas Clave"
+ "TaskKeyframeExtractor": "Extractor de Fotogramas Clave",
+ "HearingImpaired": "Personas con discapacidad auditiva"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es-MX.json b/Emby.Server.Implementations/Localization/Core/es-MX.json
index a7391cc882..d677cc46c7 100644
--- a/Emby.Server.Implementations/Localization/Core/es-MX.json
+++ b/Emby.Server.Implementations/Localization/Core/es-MX.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabaseDescription": "Compacta la base de datos y trunca el espacio libre. Puede mejorar el rendimiento si se realiza esta tarea después de escanear la biblioteca o después de realizar otros cambios que impliquen modificar la base de datos.",
"TaskKeyframeExtractorDescription": "Extrae los cuadros clave de los archivos de vídeo para crear listas HLS más precisas. Esta tarea puede tardar un buen rato.",
"TaskKeyframeExtractor": "Extractor de Cuadros Clave",
- "External": "Externo"
+ "External": "Externo",
+ "HearingImpaired": "Discapacidad Auditiva"
}
diff --git a/Emby.Server.Implementations/Localization/Core/es.json b/Emby.Server.Implementations/Localization/Core/es.json
index db65a0c6d5..afffdf3bfa 100644
--- a/Emby.Server.Implementations/Localization/Core/es.json
+++ b/Emby.Server.Implementations/Localization/Core/es.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabaseDescription": "Optimiza y libera el espacio libre en la base de datos. Ejecutar esta tarea tras escanear la biblioteca o hacer cambios que impliquen modificaciones en la base de datos puede mejorar el rendimiento.",
"TaskKeyframeExtractorDescription": "Extrae los fotogramas clave de los archivos de vídeo para crear listas HLS más precisas. Esta tarea puede tardar mucho tiempo.",
"TaskKeyframeExtractor": "Extractor de Fotogramas Clave",
- "External": "Externo"
+ "External": "Externo",
+ "HearingImpaired": "Discapacidad Auditiva"
}
diff --git a/Emby.Server.Implementations/Localization/Core/et.json b/Emby.Server.Implementations/Localization/Core/et.json
index da44e53d00..081462407d 100644
--- a/Emby.Server.Implementations/Localization/Core/et.json
+++ b/Emby.Server.Implementations/Localization/Core/et.json
@@ -120,5 +120,8 @@
"UserPolicyUpdatedWithName": "Kasutaja {0} õigusi värskendati",
"UserStoppedPlayingItemWithValues": "{0} lõpetas {1} taasesituse seadmes {2}",
"UserOnlineFromDevice": "{0} on ühendatud seadmest {1}",
- "External": "Väline"
+ "External": "Väline",
+ "HearingImpaired": "Kuulmispuudega",
+ "TaskKeyframeExtractorDescription": "Eraldab videofailidest võtmekaadreid, et luua täpsemaid HLS-i esitusloendeid. See ülesanne võib kesta pikka aega.",
+ "TaskKeyframeExtractor": "Võtmekaadri ekstraktor"
}
diff --git a/Emby.Server.Implementations/Localization/Core/eu.json b/Emby.Server.Implementations/Localization/Core/eu.json
index dfedce7b3a..d657ac7b69 100644
--- a/Emby.Server.Implementations/Localization/Core/eu.json
+++ b/Emby.Server.Implementations/Localization/Core/eu.json
@@ -116,5 +116,12 @@
"CameraImageUploadedFrom": "{0}-tik kamera irudi berri bat igo da",
"AuthenticationSucceededWithUserName": "{0} ongi autentifikatu da",
"Application": "Aplikazioa",
- "AppDeviceValues": "App: {0}, Gailua: {1}"
+ "AppDeviceValues": "App: {0}, Gailua: {1}",
+ "HearingImpaired": "Entzunaldia aldatua",
+ "ProviderValue": "Hornitzailea: {0}",
+ "TaskKeyframeExtractorDescription": "Bideo fitxategietako fotograma gakoak ateratzen ditu HLS erreprodukzio-zerrenda zehatzagoak sortzeko. Zeregin honek denbora asko iraun dezake.",
+ "HeaderRecordingGroups": "Grabaketa taldeak",
+ "Inherit": "Oinordetu",
+ "TaskOptimizeDatabaseDescription": "Datu-basea trinkotu eta bertatik espazioa askatzen du. Liburutegia eskaneatu ondoren edo datu-basean aldaketak egin ondoren ataza hau exekutatzeak errendimendua hobetu lezake.",
+ "TaskKeyframeExtractor": "Fotograma gakoen erauzgailua"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fi.json b/Emby.Server.Implementations/Localization/Core/fi.json
index f0cafd1c0d..ec72d58dd6 100644
--- a/Emby.Server.Implementations/Localization/Core/fi.json
+++ b/Emby.Server.Implementations/Localization/Core/fi.json
@@ -122,5 +122,6 @@
"TaskOptimizeDatabase": "Optimoi tietokanta",
"TaskKeyframeExtractorDescription": "Purkaa videotiedostojen avainkuvat tarkempien HLS-toistolistojen luomiseksi. Tehtävä saattaa kestää huomattavan pitkään.",
"TaskKeyframeExtractor": "Avainkuvien purkain",
- "External": "Ulkoinen"
+ "External": "Ulkoinen",
+ "HearingImpaired": "Kuulorajoitteinen"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr-CA.json b/Emby.Server.Implementations/Localization/Core/fr-CA.json
index 24ca8f8611..3ee045d89e 100644
--- a/Emby.Server.Implementations/Localization/Core/fr-CA.json
+++ b/Emby.Server.Implementations/Localization/Core/fr-CA.json
@@ -5,7 +5,7 @@
"Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} authentifié avec succès",
"Books": "Livres",
- "CameraImageUploadedFrom": "Une nouvelle image de caméra a été téléchargée depuis {0}",
+ "CameraImageUploadedFrom": "Une nouvelle photo a été téléversée depuis {0}",
"Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}",
"Collections": "Collections",
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Optimiser la base de données",
"TaskKeyframeExtractorDescription": "Extrait les images clés des fichiers vidéo pour créer des listes de lecture HLS plus précises. Cette tâche peut durer très longtemps.",
"TaskKeyframeExtractor": "Extracteur d'image clé",
- "External": "Externe"
+ "External": "Externe",
+ "HearingImpaired": "Malentendants"
}
diff --git a/Emby.Server.Implementations/Localization/Core/fr.json b/Emby.Server.Implementations/Localization/Core/fr.json
index 648c878e9c..4877bcd7a7 100644
--- a/Emby.Server.Implementations/Localization/Core/fr.json
+++ b/Emby.Server.Implementations/Localization/Core/fr.json
@@ -15,7 +15,7 @@
"Favorites": "Favoris",
"Folders": "Dossiers",
"Genres": "Genres",
- "HeaderAlbumArtists": "Artistes d'album",
+ "HeaderAlbumArtists": "Artistes de l'album",
"HeaderContinueWatching": "Reprendre le visionnage",
"HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes préférés",
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Optimiser la base de données",
"TaskKeyframeExtractorDescription": "Extrait les images clés des fichiers vidéo pour créer des listes de lecture HLS plus précises. Cette tâche peut durer très longtemps.",
"TaskKeyframeExtractor": "Extracteur d'image clé",
- "External": "Externe"
+ "External": "Externe",
+ "HearingImpaired": "Malentendants"
}
diff --git a/Emby.Server.Implementations/Localization/Core/gl.json b/Emby.Server.Implementations/Localization/Core/gl.json
index b433c6f68c..76a98aa54b 100644
--- a/Emby.Server.Implementations/Localization/Core/gl.json
+++ b/Emby.Server.Implementations/Localization/Core/gl.json
@@ -47,7 +47,7 @@
"HeaderFavoriteEpisodes": "Episodios Favoritos",
"HeaderFavoriteArtists": "Artistas Favoritos",
"HeaderFavoriteAlbums": "Álbunes Favoritos",
- "HeaderContinueWatching": "Seguir mirando",
+ "HeaderContinueWatching": "Seguir vendo",
"HeaderAlbumArtists": "Artistas do Album",
"Genres": "Xéneros",
"Forced": "Forzado",
@@ -119,5 +119,9 @@
"UserOnlineFromDevice": "{0} está en liña desde {1}",
"UserOfflineFromDevice": "{0} desconectouse desde {1}",
"TaskOptimizeDatabaseDescription": "Compacta e libera o espazo libre da base de datos. Executar esta tarefa logo de realizar mudanzas que impliquen modificacións da base de datos ou despois de escanear a biblioteca pode traer mellorías de desempeño.",
- "TaskOptimizeDatabase": "Optimizar base de datos"
+ "TaskOptimizeDatabase": "Optimizar base de datos",
+ "TaskKeyframeExtractorDescription": "Extrae fragmentos do vídeo para crear listas de reprodución HLS máis precisas. Podería levarlle bastante tempo.",
+ "External": "Externo",
+ "HearingImpaired": "Problemas de audición",
+ "TaskKeyframeExtractor": "Extractor de fragmentos"
}
diff --git a/Emby.Server.Implementations/Localization/Core/he.json b/Emby.Server.Implementations/Localization/Core/he.json
index c635dab23a..694a3d688c 100644
--- a/Emby.Server.Implementations/Localization/Core/he.json
+++ b/Emby.Server.Implementations/Localization/Core/he.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabaseDescription": "דוחס את מסד הנתונים ומוריד את שטח האחסון שבשימוש. הרצה של פעולה זו לאחר סריקת הספרייה או שינויים אחרים שמשפיעים על מסד הנתונים יכולה לשפר ביצועים.",
"TaskKeyframeExtractorDescription": "חלץ תמונות מפתח מקבצי וידאו בכדי ליצור רשימות השמעה מדויקות יותר של HLS. משימה זו עלולה להימשך זמן רב.",
"TaskKeyframeExtractor": "מחלץ תמונות מפתח",
- "External": "חיצוני"
+ "External": "חיצוני",
+ "HearingImpaired": "לקוי שמיעה"
}
diff --git a/Emby.Server.Implementations/Localization/Core/hr.json b/Emby.Server.Implementations/Localization/Core/hr.json
index c63cd2b94f..d01295419e 100644
--- a/Emby.Server.Implementations/Localization/Core/hr.json
+++ b/Emby.Server.Implementations/Localization/Core/hr.json
@@ -123,5 +123,6 @@
"External": "Vanjski",
"TaskKeyframeExtractorDescription": "Izvlačenje ključnih okvira iz videozapisa za stvaranje objektivnije HLS liste za reprodukciju. Pokretanje ovog zadatka može potrajati.",
"TaskKeyframeExtractor": "Izvoditelj ključnog okvira",
- "TaskOptimizeDatabaseDescription": "Sažima bazu podataka i uklanja prazan prostor. Pokretanje ovog zadatka, može poboljšati performanse nakon provođenja indeksiranja biblioteke ili provođenja drugih promjena koje utječu na bazu podataka."
+ "TaskOptimizeDatabaseDescription": "Sažima bazu podataka i uklanja prazan prostor. Pokretanje ovog zadatka, može poboljšati performanse nakon provođenja indeksiranja biblioteke ili provođenja drugih promjena koje utječu na bazu podataka.",
+ "HearingImpaired": "Oštećen sluh"
}
diff --git a/Emby.Server.Implementations/Localization/Core/hu.json b/Emby.Server.Implementations/Localization/Core/hu.json
index c7f2f9c85e..62d48cebd8 100644
--- a/Emby.Server.Implementations/Localization/Core/hu.json
+++ b/Emby.Server.Implementations/Localization/Core/hu.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Adatbázis optimalizálása",
"TaskKeyframeExtractor": "Kulcskockák kibontása",
"TaskKeyframeExtractorDescription": "Kulcskockákat bont ki a videofájlokból, hogy pontosabb HLS lejátszási listákat hozzon létre. Ez a feladat hosszú ideig tarthat.",
- "External": "Külső"
+ "External": "Külső",
+ "HearingImpaired": "Hallássérült"
}
diff --git a/Emby.Server.Implementations/Localization/Core/id.json b/Emby.Server.Implementations/Localization/Core/id.json
index 3e05525c87..695c0f4048 100644
--- a/Emby.Server.Implementations/Localization/Core/id.json
+++ b/Emby.Server.Implementations/Localization/Core/id.json
@@ -122,5 +122,6 @@
"TaskOptimizeDatabase": "Optimalkan basis data",
"TaskKeyframeExtractorDescription": "Ekstrak bingkai utama dari file video untuk membuat daftar putar HLS yang lebih tepat. Tugas ini dapat berjalan untuk waktu yang lama.",
"TaskKeyframeExtractor": "Ekstraktor Bingkai Utama",
- "External": "Luar"
+ "External": "Luar",
+ "HearingImpaired": "Gangguan Pendengaran"
}
diff --git a/Emby.Server.Implementations/Localization/Core/it.json b/Emby.Server.Implementations/Localization/Core/it.json
index 2aa84c536f..3710f03e07 100644
--- a/Emby.Server.Implementations/Localization/Core/it.json
+++ b/Emby.Server.Implementations/Localization/Core/it.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Ottimizza Database",
"TaskKeyframeExtractor": "Estrattore di Keyframe",
"TaskKeyframeExtractorDescription": "Estrae i keyframe dai video per creare migliori playlist HLS. Questa procedura potrebbe richiedere molto tempo.",
- "External": "Esterno"
+ "External": "Esterno",
+ "HearingImpaired": "con problemi di udito"
}
diff --git a/Emby.Server.Implementations/Localization/Core/jbo.json b/Emby.Server.Implementations/Localization/Core/jbo.json
new file mode 100644
index 0000000000..1b47bb2f23
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/jbo.json
@@ -0,0 +1,7 @@
+{
+ "Albums": "lo albuma",
+ "Artists": "lo larpra",
+ "Books": "lo cukta",
+ "HeaderAlbumArtists": "lo albuma larpra",
+ "Playlists": "lo zgipor"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/ka.json b/Emby.Server.Implementations/Localization/Core/ka.json
new file mode 100644
index 0000000000..78cfda3bdc
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/ka.json
@@ -0,0 +1,31 @@
+{
+ "Genres": "ჟანრები",
+ "HeaderAlbumArtists": "ალბომის შემსრულებლები",
+ "HeaderFavoriteAlbums": "რჩეული ალბომები",
+ "TasksApplicationCategory": "აპლიკაცია",
+ "Albums": "ალბომები",
+ "AppDeviceValues": "აპი: {0}, მოწყობილობა: {1}",
+ "Application": "აპლიკაცია",
+ "Artists": "შემსრულებლები",
+ "AuthenticationSucceededWithUserName": "{0} -ის ავთენტიკაცია წარმატებულია",
+ "Books": "წიგნები",
+ "Forced": "ძალით",
+ "Inherit": "მემკვიდრეობით",
+ "Latest": "უახლესი",
+ "Movies": "ფილმები",
+ "Music": "მუსიკა",
+ "Photos": "ფოტოები",
+ "Playlists": "დასაკრავი სიები",
+ "Plugin": "დამატება",
+ "Shows": "სერიალები",
+ "Songs": "სიმღერები",
+ "Sync": "სინქრონიზაცია",
+ "System": "სისტემა",
+ "Undefined": "აღუწერელი",
+ "User": "მომხმარებელი",
+ "TasksMaintenanceCategory": "რემონტი",
+ "TasksLibraryCategory": "ბიბლიოთეკა",
+ "ChapterNameValue": "თავი {0}",
+ "HeaderContinueWatching": "ყურების გაგრძელება",
+ "HeaderFavoriteArtists": "რჩეული შემსრულებლები"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/km.json b/Emby.Server.Implementations/Localization/Core/km.json
new file mode 100644
index 0000000000..02f9d44436
--- /dev/null
+++ b/Emby.Server.Implementations/Localization/Core/km.json
@@ -0,0 +1,3 @@
+{
+ "Albums": "Albums"
+}
diff --git a/Emby.Server.Implementations/Localization/Core/lt-LT.json b/Emby.Server.Implementations/Localization/Core/lt-LT.json
index 232b3ec934..e1c937b6cd 100644
--- a/Emby.Server.Implementations/Localization/Core/lt-LT.json
+++ b/Emby.Server.Implementations/Localization/Core/lt-LT.json
@@ -123,5 +123,6 @@
"TaskKeyframeExtractorDescription": "Iš vaizdo įrašo paruošia reikšminius kadrus, kad būtų sukuriamas tikslenis HLS grojaraštis. Šios užduoties vykdymas gali ilgai užtrukti.",
"TaskKeyframeExtractor": "Pagrindinių kadrų ištraukėjas",
"TaskOptimizeDatabaseDescription": "Suspaudžia duomenų bazę ir atlaisvina vietą. Paleidžiant šią užduotį, po bibliotekos skenavimo arba kitų veiksmų kurie galimai modifikuoja duomenų bazė, gali pagerinti greitaveiką.",
- "External": "Išorinis"
+ "External": "Išorinis",
+ "HearingImpaired": "Su klausos sutrikimais"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nb.json b/Emby.Server.Implementations/Localization/Core/nb.json
index 77ee46a4f7..5c7dec7efe 100644
--- a/Emby.Server.Implementations/Localization/Core/nb.json
+++ b/Emby.Server.Implementations/Localization/Core/nb.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabaseDescription": "Komprimerer database og frigjør plass. Denne prosessen kan forbedre ytelsen etter skanning av bibliotek eller andre handlinger som fører til databaseendringer.",
"TaskKeyframeExtractorDescription": "Trekker ut nøkkelbilder fra videofiler for å skape mere nøyaktige HLS-spillelister. Denne oppgaven kan ta lang tid.",
"TaskKeyframeExtractor": "Nøkkelbilde-uttrekker",
- "External": "Ekstern"
+ "External": "Ekstern",
+ "HearingImpaired": "Hørselshemmet"
}
diff --git a/Emby.Server.Implementations/Localization/Core/nl.json b/Emby.Server.Implementations/Localization/Core/nl.json
index 3f22355d6a..c05114f013 100644
--- a/Emby.Server.Implementations/Localization/Core/nl.json
+++ b/Emby.Server.Implementations/Localization/Core/nl.json
@@ -5,7 +5,7 @@
"Artists": "Artiesten",
"AuthenticationSucceededWithUserName": "{0} is succesvol geauthenticeerd",
"Books": "Boeken",
- "CameraImageUploadedFrom": "Nieuwe camera afbeelding toegevoegd vanaf {0}",
+ "CameraImageUploadedFrom": "Nieuwe camera-afbeelding toegevoegd vanaf {0}",
"Channels": "Kanalen",
"ChapterNameValue": "Hoofdstuk {0}",
"Collections": "Verzamelingen",
@@ -15,7 +15,7 @@
"Favorites": "Favorieten",
"Folders": "Mappen",
"Genres": "Genres",
- "HeaderAlbumArtists": "Album Artiesten",
+ "HeaderAlbumArtists": "Albumartiesten",
"HeaderContinueWatching": "Kijken hervatten",
"HeaderFavoriteAlbums": "Favoriete albums",
"HeaderFavoriteArtists": "Favoriete artiesten",
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Database optimaliseren",
"TaskKeyframeExtractorDescription": "Haalt keyframes uit videobestanden om preciezere HLS afspeellijsten te maken. Dit kan lang duren.",
"TaskKeyframeExtractor": "Keyframe Extractor",
- "External": "Extern"
+ "External": "Extern",
+ "HearingImpaired": "Slechthorend"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt-BR.json b/Emby.Server.Implementations/Localization/Core/pt-BR.json
index 38a36a7e01..b9b93b7b6f 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-BR.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-BR.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Otimizar base de dados",
"TaskKeyframeExtractor": "Extrator de quadro-chave",
"TaskKeyframeExtractorDescription": "Extrai quadros-chave de arquivos de vídeo para criar listas de reprodução HLS mais precisas. Esta tarefa pode ser executada por um longo tempo.",
- "External": "Externo"
+ "External": "Externo",
+ "HearingImpaired": "Deficiência Auditiva"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt-PT.json b/Emby.Server.Implementations/Localization/Core/pt-PT.json
index 7047f1c282..a75182f220 100644
--- a/Emby.Server.Implementations/Localization/Core/pt-PT.json
+++ b/Emby.Server.Implementations/Localization/Core/pt-PT.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Otimizar base de dados",
"TaskKeyframeExtractorDescription": "Extrai quadros-chave de ficheiros de video para criar listas de reprodução HLS mais precisas. Esta tarefa pode demorar algum tempo.",
"TaskKeyframeExtractor": "Extrator de Quadros-chave",
- "External": "Externo"
+ "External": "Externo",
+ "HearingImpaired": "Surdo"
}
diff --git a/Emby.Server.Implementations/Localization/Core/pt.json b/Emby.Server.Implementations/Localization/Core/pt.json
index c2c77ccab1..39229f45f9 100644
--- a/Emby.Server.Implementations/Localization/Core/pt.json
+++ b/Emby.Server.Implementations/Localization/Core/pt.json
@@ -120,5 +120,6 @@
"TaskCleanActivityLogDescription": "Apaga itens no registro com idade acima do que é configurado.",
"TaskOptimizeDatabase": "Otimizar base de dados",
"TaskOptimizeDatabaseDescription": "Base de dados compacta e corta espaço livre. A execução desta tarefa depois de digitalizar a biblioteca ou de fazer outras alterações que impliquem modificações na base de dados pode melhorar o desempenho.",
- "External": "Externo"
+ "External": "Externo",
+ "HearingImpaired": "Problemas auditivos"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ro.json b/Emby.Server.Implementations/Localization/Core/ro.json
index 53456269af..2c10bb4779 100644
--- a/Emby.Server.Implementations/Localization/Core/ro.json
+++ b/Emby.Server.Implementations/Localization/Core/ro.json
@@ -11,7 +11,7 @@
"UserOfflineFromDevice": "{0} s-a deconectat de la {1}",
"UserLockedOutWithName": "Utilizatorul {0} a fost blocat",
"UserDownloadingItemWithValues": "{0} descarcă {1}",
- "UserDeletedWithName": "Utilizatorul {0} a fost eliminat",
+ "UserDeletedWithName": "Utilizatorul {0} a fost șters",
"UserCreatedWithName": "Utilizatorul {0} a fost creat",
"User": "Utilizator",
"TvShows": "Seriale TV",
@@ -20,33 +20,33 @@
"SubtitleDownloadFailureFromForItem": "Subtitrările nu au putut fi descărcate de la {0} pentru {1}",
"StartupEmbyServerIsLoading": "Se încarcă serverul Jellyfin. Încercați din nou în scurt timp.",
"Songs": "Melodii",
- "Shows": "Spectacole",
- "ServerNameNeedsToBeRestarted": "{0} trebuie repornit",
+ "Shows": "Seriale",
+ "ServerNameNeedsToBeRestarted": "{0} trebuie să fie repornit",
"ScheduledTaskStartedWithName": "{0} pornit/ă",
"ScheduledTaskFailedWithName": "{0} eșuat/ă",
"ProviderValue": "Furnizor: {0}",
"PluginUpdatedWithName": "{0} a fost actualizat/ă",
"PluginUninstalledWithName": "{0} a fost dezinstalat",
"PluginInstalledWithName": "{0} a fost instalat",
- "Plugin": "Plugin",
- "Playlists": "Liste redare",
+ "Plugin": "Extensie",
+ "Playlists": "Liste de redare",
"Photos": "Fotografii",
"NotificationOptionVideoPlaybackStopped": "Redarea video oprită",
"NotificationOptionVideoPlayback": "Redare video începută",
"NotificationOptionUserLockedOut": "Utilizatorul a fost blocat",
- "NotificationOptionTaskFailed": "Activitate programata eșuată",
+ "NotificationOptionTaskFailed": "Activitate programată eșuată",
"NotificationOptionServerRestartRequired": "Este necesară repornirea serverului",
- "NotificationOptionPluginUpdateInstalled": "Actualizare plugin instalată",
- "NotificationOptionPluginUninstalled": "Plugin dezinstalat",
- "NotificationOptionPluginInstalled": "Plugin instalat",
- "NotificationOptionPluginError": "Plugin-ul a eșuat",
- "NotificationOptionNewLibraryContent": "Adăugat conținut nou",
- "NotificationOptionInstallationFailed": "Eșec la instalare",
- "NotificationOptionCameraImageUploaded": "Încarcată imagine cameră",
+ "NotificationOptionPluginUpdateInstalled": "Actualizarea extensiei este instalată",
+ "NotificationOptionPluginUninstalled": "Extensie dezinstalată",
+ "NotificationOptionPluginInstalled": "Extensie instalată",
+ "NotificationOptionPluginError": "Eroare de extensie",
+ "NotificationOptionNewLibraryContent": "A fost adăugat conținut nou",
+ "NotificationOptionInstallationFailed": "Instalare eșuată",
+ "NotificationOptionCameraImageUploaded": "Imagine încarcată",
"NotificationOptionAudioPlaybackStopped": "Redare audio oprită",
"NotificationOptionAudioPlayback": "A început redarea audio",
"NotificationOptionApplicationUpdateInstalled": "Actualizarea aplicației a fost instalată",
- "NotificationOptionApplicationUpdateAvailable": "Disponibilă o actualizare a aplicației",
+ "NotificationOptionApplicationUpdateAvailable": "Este disponibilă o actualizare a aplicației",
"NewVersionIsAvailable": "O nouă versiune a Jellyfin Server este disponibilă pentru descărcare.",
"NameSeasonUnknown": "Sezon Necunoscut",
"NameSeasonNumber": "Sezonul {0}",
@@ -54,8 +54,8 @@
"MusicVideos": "Videoclipuri muzicale",
"Music": "Muzică",
"Movies": "Filme",
- "MixedContent": "Conținut mixt",
- "MessageServerConfigurationUpdated": "Configurația serverului a fost actualizată",
+ "MixedContent": "Conținut amestecat",
+ "MessageServerConfigurationUpdated": "Configurarea serverului a fost actualizată",
"MessageNamedServerConfigurationUpdatedWithValue": "Secțiunea de configurare a serverului {0} a fost acualizata",
"MessageApplicationUpdatedTo": "Jellyfin Server a fost actualizat la {0}",
"MessageApplicationUpdated": "Jellyfin Server a fost actualizat",
@@ -69,7 +69,7 @@
"HeaderRecordingGroups": "Grupuri de înregistrare",
"HeaderLiveTV": "TV în Direct",
"HeaderFavoriteSongs": "Melodii Favorite",
- "HeaderFavoriteShows": "Spectacole Favorite",
+ "HeaderFavoriteShows": "Seriale TV Favorite",
"HeaderFavoriteEpisodes": "Episoade Favorite",
"HeaderFavoriteArtists": "Artiști Favoriți",
"HeaderFavoriteAlbums": "Albume Favorite",
@@ -97,10 +97,10 @@
"TaskRefreshChannels": "Actualizează canale",
"TaskCleanTranscodeDescription": "Șterge fișierele de transcodare mai vechi de o zi.",
"TaskCleanTranscode": "Curățați directorul de transcodare",
- "TaskUpdatePluginsDescription": "Descarcă și instalează actualizări pentru pluginuri care sunt configurate să se actualizeze automat.",
- "TaskUpdatePlugins": "Actualizați plugin-uri",
+ "TaskUpdatePluginsDescription": "Descarcă și instalează actualizări pentru extensiile care sunt configurate să se actualizeze automat.",
+ "TaskUpdatePlugins": "Actualizați Extensile",
"TaskRefreshPeopleDescription": "Actualizează metadatele pentru actori și regizori din biblioteca media.",
- "TaskRefreshPeople": "Actualizează oamenii",
+ "TaskRefreshPeople": "Actualizează Persoanele",
"TaskCleanLogsDescription": "Șterge fișierele jurnal care au mai mult de {0} zile.",
"TaskCleanLogs": "Curățare director jurnal",
"TaskRefreshLibraryDescription": "Scanează biblioteca media pentru fișiere noi și reîmprospătează metadatele.",
@@ -114,13 +114,14 @@
"TasksLibraryCategory": "Librărie",
"TasksMaintenanceCategory": "Mentenanță",
"TaskCleanActivityLogDescription": "Șterge intrările din jurnalul de activitate mai vechi de data configurată.",
- "TaskCleanActivityLog": "Curăță Jurnalul de Activitate",
+ "TaskCleanActivityLog": "Curăță Jurnalul de Activități",
"Undefined": "Nedefinit",
"Forced": "Forțat",
"Default": "Implicit",
- "TaskOptimizeDatabaseDescription": "Compactează baza de date și trunchiază spațiul liber. Rularea acestei sarcini după scanarea bibliotecii sau după efectuarea altor modificări care implică modificări ale bazei de date poate îmbunătăți performanța.",
+ "TaskOptimizeDatabaseDescription": "Comprimă baza de date și trunchiază spațiul liber. Rularea acestei sarcini după scanarea bibliotecii sau după efectuarea altor modificări care implică modificări ale bazei de date poate îmbunătăți performanța.",
"TaskOptimizeDatabase": "Optimizează baza de date",
"TaskKeyframeExtractorDescription": "Extrage cadrele cheie din fișierele video pentru a crea liste de redare HLS mai precise. Această sarcină poate rula o perioadă lungă de timp.",
"External": "Extern",
- "TaskKeyframeExtractor": "Extractor de cadre cheie"
+ "TaskKeyframeExtractor": "Extractor de cadre cheie",
+ "HearingImpaired": "Ascultare Impară"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ru.json b/Emby.Server.Implementations/Localization/Core/ru.json
index ea9a82d2bf..dc45a8264d 100644
--- a/Emby.Server.Implementations/Localization/Core/ru.json
+++ b/Emby.Server.Implementations/Localization/Core/ru.json
@@ -75,7 +75,7 @@
"StartupEmbyServerIsLoading": "Jellyfin Server загружается. Повторите попытку в ближайшее время.",
"SubtitleDownloadFailureForItem": "Субтитры к {0} не удалось загрузить",
"SubtitleDownloadFailureFromForItem": "Субтитры к {1} не удалось загрузить с {0}",
- "Sync": "Синхро",
+ "Sync": "Синхронизация",
"System": "Система",
"TvShows": "ТВ",
"User": "Пользователь",
@@ -117,11 +117,12 @@
"TaskCleanActivityLogDescription": "Удаляет записи журнала активности старше установленного возраста.",
"TaskCleanActivityLog": "Очистка журнала активности",
"Undefined": "Не определено",
- "Forced": "Форсир-ые",
+ "Forced": "Принудительно",
"Default": "По умолчанию",
"TaskOptimizeDatabaseDescription": "Сжимает базу данных и вырезает свободные места. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.",
"TaskOptimizeDatabase": "Оптимизация базы данных",
"TaskKeyframeExtractorDescription": "Извлекаются ключевые кадры из видеофайлов для создания более точных списков плей-листов HLS. Эта задача может выполняться в течение длительного времени.",
"TaskKeyframeExtractor": "Извлечение ключевых кадров",
- "External": "Внешние"
+ "External": "Внешние",
+ "HearingImpaired": "Для слабослышащих"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sk.json b/Emby.Server.Implementations/Localization/Core/sk.json
index 7502969a64..858cc40dd8 100644
--- a/Emby.Server.Implementations/Localization/Core/sk.json
+++ b/Emby.Server.Implementations/Localization/Core/sk.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "Optimalizovať databázu",
"TaskKeyframeExtractorDescription": "Extrahuje kľúčové snímky z video súborov na vytvorenie presnejších HLS playlistov. Táto úloha môže trvať dlhšiu dobu.",
"TaskKeyframeExtractor": "Extraktor kľúčových snímkov",
- "External": "Externé"
+ "External": "Externé",
+ "HearingImpaired": "Sluchovo Postihnutý"
}
diff --git a/Emby.Server.Implementations/Localization/Core/sq.json b/Emby.Server.Implementations/Localization/Core/sq.json
index 2766dab06a..d1b73a3eb9 100644
--- a/Emby.Server.Implementations/Localization/Core/sq.json
+++ b/Emby.Server.Implementations/Localization/Core/sq.json
@@ -119,5 +119,9 @@
"Forced": "I detyruar",
"Default": "Parazgjedhur",
"TaskOptimizeDatabaseDescription": "Kompakton bazën e të dhënave dhe shkurton hapësirën e lirë. Drejtimi i kësaj detyre pasi skanoni bibliotekën ose bëni ndryshime të tjera që nënkuptojnë modifikime të bazës së të dhënave mund të përmirësojë performancën.",
- "TaskOptimizeDatabase": "Optimizo databazën"
+ "TaskOptimizeDatabase": "Optimizo databazën",
+ "TaskKeyframeExtractorDescription": "Nxjerrë kornizat kryesore nga skedarët video për të krijuar lista luajtjeje më të sakta HLS. Ky veprim mund të dojë një kohë të gjatë për tu kompletuar.",
+ "TaskKeyframeExtractor": "Nxjerrës i kornizës kryesore",
+ "External": "Jashtem",
+ "HearingImpaired": "Dëgjimi i dëmtuar"
}
diff --git a/Emby.Server.Implementations/Localization/Core/uk.json b/Emby.Server.Implementations/Localization/Core/uk.json
index 3e0fd11c8e..92ce616f2e 100644
--- a/Emby.Server.Implementations/Localization/Core/uk.json
+++ b/Emby.Server.Implementations/Localization/Core/uk.json
@@ -122,5 +122,6 @@
"TaskOptimizeDatabaseDescription": "Стискає базу даних та збільшує вільний простір. Виконання цього завдання після сканування медіатеки або внесення інших змін, які передбачають модифікацію бази даних може покращити продуктивність.",
"TaskKeyframeExtractorDescription": "Витягує ключові кадри з відеофайлів для створення більш точних списків відтворення HLS. Це завдання може виконуватися протягом тривалого часу.",
"TaskKeyframeExtractor": "Екстрактор ключових кадрів",
- "External": "Зовнішній"
+ "External": "Зовнішній",
+ "HearingImpaired": "З порушеннями слуху"
}
diff --git a/Emby.Server.Implementations/Localization/Core/ur_PK.json b/Emby.Server.Implementations/Localization/Core/ur_PK.json
index e7f3e492c6..5413346d41 100644
--- a/Emby.Server.Implementations/Localization/Core/ur_PK.json
+++ b/Emby.Server.Implementations/Localization/Core/ur_PK.json
@@ -5,18 +5,18 @@
"HeaderAlbumArtists": "البم کے فنکار",
"Movies": "فلمیں",
"HeaderFavoriteEpisodes": "پسندیدہ اقساط",
- "Collections": "مجموعہ",
+ "Collections": "مجموعے",
"Folders": "فولڈرز",
"HeaderLiveTV": "براہ راست ٹی وی",
"Channels": "چینلز",
"HeaderContinueWatching": "دیکھنا جاری رکھیں",
"Playlists": "پلے لسٹس",
- "ValueSpecialEpisodeName": "خاص - {0}",
- "Shows": "شوز",
+ "ValueSpecialEpisodeName": "خصوصی - {0}",
+ "Shows": "دکھاتا ہے۔",
"Genres": "انواع",
"Artists": "فنکار",
- "Sync": "مطابقت",
- "Photos": "تصوریں",
+ "Sync": "مطابقت پذیری",
+ "Photos": "تصاویر",
"Albums": "البمز",
"Favorites": "پسندیدہ",
"Songs": "گانے",
diff --git a/Emby.Server.Implementations/Localization/Core/vi.json b/Emby.Server.Implementations/Localization/Core/vi.json
index b9e2f1e6c2..44ce4ac5b2 100644
--- a/Emby.Server.Implementations/Localization/Core/vi.json
+++ b/Emby.Server.Implementations/Localization/Core/vi.json
@@ -122,5 +122,6 @@
"TaskOptimizeDatabase": "Tối ưu hóa cơ sở dữ liệu",
"TaskKeyframeExtractor": "Trích Xuất Khung Hình",
"TaskKeyframeExtractorDescription": "Trích xuất khung hình chính từ các tệp video để tạo danh sách phát HLS chính xác hơn. Tác vụ này có thể chạy trong một thời gian dài.",
- "External": "Bên ngoài"
+ "External": "Bên ngoài",
+ "HearingImpaired": "Khiếm Thính"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-CN.json b/Emby.Server.Implementations/Localization/Core/zh-CN.json
index a121fc376d..ccfbeef0ce 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-CN.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-CN.json
@@ -123,5 +123,6 @@
"TaskOptimizeDatabase": "优化数据库",
"TaskKeyframeExtractorDescription": "从视频文件中提取关键帧以创建更准确的HLS播放列表。这项任务可能需要很长时间。",
"TaskKeyframeExtractor": "关键帧提取器",
- "External": "外部"
+ "External": "外部",
+ "HearingImpaired": "听力障碍"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-HK.json b/Emby.Server.Implementations/Localization/Core/zh-HK.json
index 6c8bf76274..baa9ecc1c1 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-HK.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-HK.json
@@ -123,5 +123,6 @@
"TaskCleanActivityLogDescription": "刪除早於設定時間的日誌記錄。",
"TaskKeyframeExtractorDescription": "提取關鍵格以創建更準確的HLS播放列表。次指示可能用時很長。",
"TaskKeyframeExtractor": "關鍵幀提取器",
- "External": "外部"
+ "External": "外部",
+ "HearingImpaired": "聽力障礙"
}
diff --git a/Emby.Server.Implementations/Localization/Core/zh-TW.json b/Emby.Server.Implementations/Localization/Core/zh-TW.json
index 102a266f8e..4949c5ab6d 100644
--- a/Emby.Server.Implementations/Localization/Core/zh-TW.json
+++ b/Emby.Server.Implementations/Localization/Core/zh-TW.json
@@ -37,7 +37,7 @@
"MixedContent": "混合內容",
"Movies": "電影",
"Music": "音樂",
- "MusicVideos": "音樂錄影帶",
+ "MusicVideos": "MV",
"NameInstallFailed": "{0} 安裝失敗",
"NameSeasonNumber": "第 {0} 季",
"NameSeasonUnknown": "未知季數",
@@ -122,5 +122,6 @@
"TaskOptimizeDatabase": "最佳化資料庫",
"TaskKeyframeExtractorDescription": "將關鍵幀從影片檔案提取出來並建立更精準的HLS播放清單。這可能需要很長時間。",
"TaskKeyframeExtractor": "關鍵幀提取器",
- "External": "外部"
+ "External": "外部",
+ "HearingImpaired": "聽力障礙"
}
diff --git a/Emby.Server.Implementations/Localization/LocalizationManager.cs b/Emby.Server.Implementations/Localization/LocalizationManager.cs
index 281dbb00b9..b771681266 100644
--- a/Emby.Server.Implementations/Localization/LocalizationManager.cs
+++ b/Emby.Server.Implementations/Localization/LocalizationManager.cs
@@ -386,6 +386,7 @@ namespace Emby.Server.Implementations.Localization
yield return new LocalizationOption("Español (Dominicana)", "es_DO");
yield return new LocalizationOption("Español (México)", "es-MX");
yield return new LocalizationOption("Eesti", "et");
+ yield return new LocalizationOption("Basque", "eu");
yield return new LocalizationOption("فارسی", "fa");
yield return new LocalizationOption("Suomi", "fi");
yield return new LocalizationOption("Filipino", "fil");
@@ -433,8 +434,8 @@ namespace Emby.Server.Implementations.Localization
yield return new LocalizationOption("Українська", "uk");
yield return new LocalizationOption("اُردُو", "ur_PK");
yield return new LocalizationOption("Tiếng Việt", "vi");
- yield return new LocalizationOption("汉语 (简化字)", "zh-CN");
- yield return new LocalizationOption("漢語 (繁体字)", "zh-TW");
+ yield return new LocalizationOption("汉语 (简体字)", "zh-CN");
+ yield return new LocalizationOption("漢語 (繁體字)", "zh-TW");
yield return new LocalizationOption("廣東話 (香港)", "zh-HK");
}
}
diff --git a/Emby.Server.Implementations/Plugins/PluginManager.cs b/Emby.Server.Implementations/Plugins/PluginManager.cs
index ec4e0dbeb9..3f7d46822e 100644
--- a/Emby.Server.Implementations/Plugins/PluginManager.cs
+++ b/Emby.Server.Implementations/Plugins/PluginManager.cs
@@ -715,6 +715,7 @@ namespace Emby.Server.Implementations.Plugins
{
// This value is memory only - so that the web will show restart required.
plugin.Manifest.Status = PluginStatus.Restart;
+ plugin.Manifest.AutoUpdate = false;
return;
}
@@ -729,6 +730,7 @@ namespace Emby.Server.Implementations.Plugins
// This value is memory only - so that the web will show restart required.
plugin.Manifest.Status = PluginStatus.Restart;
+ plugin.Manifest.AutoUpdate = false;
}
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
index 0bf0838fac..da7c8732a8 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/ChapterImagesTask.cs
@@ -16,6 +16,7 @@ using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Tasks;
+using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
@@ -24,15 +25,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
///
public class ChapterImagesTask : IScheduledTask
{
- ///
- /// The _library manager.
- ///
+ private readonly ILogger _logger;
private readonly ILibraryManager _libraryManager;
-
private readonly IItemRepository _itemRepo;
-
private readonly IApplicationPaths _appPaths;
-
private readonly IEncodingManager _encodingManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
@@ -40,6 +36,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
///
/// Initializes a new instance of the class.
///
+ /// The logger..
/// The library manager..
/// The item repository.
/// The application paths.
@@ -47,6 +44,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
/// The filesystem.
/// The localization manager.
public ChapterImagesTask(
+ ILogger logger,
ILibraryManager libraryManager,
IItemRepository itemRepo,
IApplicationPaths appPaths,
@@ -54,6 +52,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
IFileSystem fileSystem,
ILocalizationManager localization)
{
+ _logger = logger;
_libraryManager = libraryManager;
_itemRepo = itemRepo;
_appPaths = appPaths;
@@ -167,9 +166,10 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
progress.Report(100 * percent);
}
- catch (ObjectDisposedException)
+ catch (ObjectDisposedException ex)
{
// TODO Investigate and properly fix.
+ _logger.LogError(ex, "Object Disposed");
break;
}
}
diff --git a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
index 98e45fa460..1efacd8562 100644
--- a/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
+++ b/Emby.Server.Implementations/ScheduledTasks/Tasks/OptimizeDatabaseTask.cs
@@ -17,7 +17,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
{
private readonly ILogger _logger;
private readonly ILocalizationManager _localization;
- private readonly JellyfinDbProvider _provider;
+ private readonly IDbContextFactory _provider;
///
/// Initializes a new instance of the class.
@@ -28,7 +28,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
public OptimizeDatabaseTask(
ILogger logger,
ILocalizationManager localization,
- JellyfinDbProvider provider)
+ IDbContextFactory provider)
{
_logger = logger;
_localization = localization;
@@ -70,30 +70,31 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
}
///
- public Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken)
+ public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken)
{
_logger.LogInformation("Optimizing and vacuuming jellyfin.db...");
try
{
- using var context = _provider.CreateContext();
- if (context.Database.IsSqlite())
+ var context = await _provider.CreateDbContextAsync(cancellationToken).ConfigureAwait(false);
+ await using (context.ConfigureAwait(false))
{
- context.Database.ExecuteSqlRaw("PRAGMA optimize");
- context.Database.ExecuteSqlRaw("VACUUM");
- _logger.LogInformation("jellyfin.db optimized successfully!");
- }
- else
- {
- _logger.LogInformation("This database doesn't support optimization");
+ if (context.Database.IsSqlite())
+ {
+ await context.Database.ExecuteSqlRawAsync("PRAGMA optimize", cancellationToken).ConfigureAwait(false);
+ await context.Database.ExecuteSqlRawAsync("VACUUM", cancellationToken).ConfigureAwait(false);
+ _logger.LogInformation("jellyfin.db optimized successfully!");
+ }
+ else
+ {
+ _logger.LogInformation("This database doesn't support optimization");
+ }
}
}
catch (Exception e)
{
_logger.LogError(e, "Error while optimizing jellyfin.db");
}
-
- return Task.CompletedTask;
}
}
}
diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs
index 6005896ad9..5c9b9df153 100644
--- a/Emby.Server.Implementations/TV/TVSeriesManager.cs
+++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs
@@ -192,7 +192,6 @@ namespace Emby.Server.Implementations.TV
AncestorWithPresentationUniqueKey = null,
SeriesPresentationUniqueKey = seriesKey,
IncludeItemTypes = new[] { BaseItemKind.Episode },
- OrderBy = new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) },
IsPlayed = true,
Limit = 1,
ParentIndexNumberNotEquals = 0,
@@ -203,11 +202,10 @@ namespace Emby.Server.Implementations.TV
}
};
- if (rewatching)
- {
- // find last watched by date played, not by newest episode watched
- lastQuery.OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) };
- }
+ // If rewatching is enabled, sort first by date played and then by season and episode numbers
+ lastQuery.OrderBy = rewatching
+ ? new[] { (ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) }
+ : new[] { (ItemSortBy.ParentIndexNumber, SortOrder.Descending), (ItemSortBy.IndexNumber, SortOrder.Descending) };
var lastWatchedEpisode = _libraryManager.GetItemList(lastQuery).Cast().FirstOrDefault();
@@ -226,18 +224,16 @@ namespace Emby.Server.Implementations.TV
DtoOptions = dtoOptions
};
- Episode nextEpisode;
- if (rewatching)
+ // Locate the next up episode based on the last watched episode's season and episode number
+ var lastWatchedParentIndexNumber = lastWatchedEpisode?.ParentIndexNumber;
+ var lastWatchedIndexNumber = lastWatchedEpisode?.IndexNumberEnd ?? lastWatchedEpisode?.IndexNumber;
+ if (lastWatchedParentIndexNumber.HasValue && lastWatchedIndexNumber.HasValue)
{
- nextQuery.Limit = 2;
- // get watched episode after most recently watched
- nextEpisode = _libraryManager.GetItemList(nextQuery).Cast().ElementAtOrDefault(1);
- }
- else
- {
- nextEpisode = _libraryManager.GetItemList(nextQuery).Cast().FirstOrDefault();
+ nextQuery.MinParentAndIndexNumber = (lastWatchedParentIndexNumber.Value, lastWatchedIndexNumber.Value + 1);
}
+ var nextEpisode = _libraryManager.GetItemList(nextQuery).Cast().FirstOrDefault();
+
if (_configurationManager.Configuration.DisplaySpecialsWithinSeasons)
{
var consideredEpisodes = _libraryManager.GetItemList(new InternalItemsQuery(user)
diff --git a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
index 64ee5680ce..14fd7eb3c1 100644
--- a/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
+++ b/Jellyfin.Api/Controllers/DisplayPreferencesController.cs
@@ -178,7 +178,7 @@ namespace Jellyfin.Api.Controllers
foreach (var key in displayPreferences.CustomPrefs.Keys.Where(key => key.StartsWith("homesection", StringComparison.OrdinalIgnoreCase)))
{
- var order = int.Parse(key.AsSpan().Slice("homesection".Length));
+ var order = int.Parse(key.AsSpan().Slice("homesection".Length), NumberStyles.Any, CultureInfo.InvariantCulture);
if (!Enum.TryParse(displayPreferences.CustomPrefs[key], true, out var type))
{
type = order < 8 ? defaults[order] : HomeSectionType.None;
diff --git a/Jellyfin.Api/Controllers/ItemsController.cs b/Jellyfin.Api/Controllers/ItemsController.cs
index 80ae5abcbf..3ee5b8d737 100644
--- a/Jellyfin.Api/Controllers/ItemsController.cs
+++ b/Jellyfin.Api/Controllers/ItemsController.cs
@@ -87,9 +87,9 @@ namespace Jellyfin.Api.Controllers
/// Optional. The minimum last saved date for the current user. Format = ISO.
/// Optional. The maximum premiere date. Format = ISO.
/// Optional filter by items that have an overview or not.
- /// Optional filter by items that have an imdb id or not.
- /// Optional filter by items that have a tmdb id or not.
- /// Optional filter by items that have a tvdb id or not.
+ /// Optional filter by items that have an IMDb id or not.
+ /// Optional filter by items that have a TMDb id or not.
+ /// Optional filter by items that have a TVDb id or not.
/// Optional filter for live tv movies.
/// Optional filter for live tv series.
/// Optional filter for live tv news.
@@ -100,7 +100,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The maximum number of records to return.
/// When searching within folders, this determines whether or not the search will be recursive. true/false.
/// Optional. Filter based on a search term.
- /// Sort Order - Ascending,Descending.
+ /// Sort Order - Ascending, Descending.
/// Specify this to localize the search to a specific item or folder. Omit to use the root.
/// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.
/// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited.
@@ -282,39 +282,13 @@ namespace Jellyfin.Api.Controllers
includeItemTypes = new[] { BaseItemKind.Playlist };
}
- var enabledChannels = isApiKey
- ? Array.Empty()
- : user!.GetPreferenceValues(PreferenceKind.EnabledChannels);
-
- // api keys are always enabled for all folders
- bool isInEnabledFolder = isApiKey
- || Array.IndexOf(user!.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1
- // Assume all folders inside an EnabledChannel are enabled
- || Array.IndexOf(enabledChannels, item.Id) != -1
- // Assume all items inside an EnabledChannel are enabled
- || Array.IndexOf(enabledChannels, item.ChannelId) != -1;
-
- if (!isInEnabledFolder)
- {
- var collectionFolders = _libraryManager.GetCollectionFolders(item);
- foreach (var collectionFolder in collectionFolders)
- {
- // api keys never enter this block, so user is never null
- if (user!.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id))
- {
- isInEnabledFolder = true;
- }
- }
- }
-
- // api keys are always enabled for all folders, so user is never null
if (item is not UserRootFolder
- && !isInEnabledFolder
- && !user!.HasPermission(PermissionKind.EnableAllFolders)
- && !user.HasPermission(PermissionKind.EnableAllChannels)
- && !string.Equals(collectionType, CollectionType.Folders, StringComparison.OrdinalIgnoreCase))
+ // api keys can always access all folders
+ && !isApiKey
+ // check the item is visible for the user
+ && !item.IsVisible(user))
{
- _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}", user.Username, item.Name);
+ _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}", user!.Username, item.Name);
return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}.");
}
@@ -562,9 +536,9 @@ namespace Jellyfin.Api.Controllers
/// Optional. The minimum last saved date for the current user. Format = ISO.
/// Optional. The maximum premiere date. Format = ISO.
/// Optional filter by items that have an overview or not.
- /// Optional filter by items that have an imdb id or not.
- /// Optional filter by items that have a tmdb id or not.
- /// Optional filter by items that have a tvdb id or not.
+ /// Optional filter by items that have an IMDb id or not.
+ /// Optional filter by items that have a TMDb id or not.
+ /// Optional filter by items that have a TVDb id or not.
/// Optional filter for live tv movies.
/// Optional filter for live tv series.
/// Optional filter for live tv news.
@@ -575,7 +549,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The maximum number of records to return.
/// When searching within folders, this determines whether or not the search will be recursive. true/false.
/// Optional. Filter based on a search term.
- /// Sort Order - Ascending,Descending.
+ /// Sort Order - Ascending, Descending.
/// Specify this to localize the search to a specific item or folder. Omit to use the root.
/// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.
/// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited.
diff --git a/Jellyfin.Api/Controllers/LibraryController.cs b/Jellyfin.Api/Controllers/LibraryController.cs
index e9492a6a47..7a57bf1a21 100644
--- a/Jellyfin.Api/Controllers/LibraryController.cs
+++ b/Jellyfin.Api/Controllers/LibraryController.cs
@@ -485,7 +485,7 @@ namespace Jellyfin.Api.Controllers
/// Media folders returned.
/// List of user media folders.
[HttpGet("Library/MediaFolders")]
- [Authorize(Policy = Policies.DefaultAuthorization)]
+ [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult> GetMediaFolders([FromQuery] bool? isHidden)
{
diff --git a/Jellyfin.Api/Controllers/MoviesController.cs b/Jellyfin.Api/Controllers/MoviesController.cs
index 8195fc7609..03f864b4a7 100644
--- a/Jellyfin.Api/Controllers/MoviesController.cs
+++ b/Jellyfin.Api/Controllers/MoviesController.cs
@@ -193,7 +193,7 @@ namespace Jellyfin.Api.Controllers
new InternalItemsQuery(user)
{
Person = name,
- // Account for duplicates by imdb id, since the database doesn't support this yet
+ // Account for duplicates by IMDb id, since the database doesn't support this yet
Limit = itemLimit + 2,
PersonTypes = new[] { PersonType.Director },
IncludeItemTypes = itemTypes.ToArray(),
@@ -232,15 +232,15 @@ namespace Jellyfin.Api.Controllers
foreach (var name in names)
{
var items = _libraryManager.GetItemList(new InternalItemsQuery(user)
- {
- Person = name,
- // Account for duplicates by imdb id, since the database doesn't support this yet
- Limit = itemLimit + 2,
- IncludeItemTypes = itemTypes.ToArray(),
- IsMovie = true,
- EnableGroupByMetadataKey = true,
- DtoOptions = dtoOptions
- }).GroupBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
+ {
+ Person = name,
+ // Account for duplicates by IMDb id, since the database doesn't support this yet
+ Limit = itemLimit + 2,
+ IncludeItemTypes = itemTypes.ToArray(),
+ IsMovie = true,
+ EnableGroupByMetadataKey = true,
+ DtoOptions = dtoOptions
+ }).GroupBy(i => i.GetProviderId(MediaBrowser.Model.Entities.MetadataProvider.Imdb) ?? Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture))
.Select(x => x.First())
.Take(itemLimit)
.ToList();
diff --git a/Jellyfin.Api/Controllers/PlaystateController.cs b/Jellyfin.Api/Controllers/PlaystateController.cs
index 0dd4bf8035..3a2ba033ea 100644
--- a/Jellyfin.Api/Controllers/PlaystateController.cs
+++ b/Jellyfin.Api/Controllers/PlaystateController.cs
@@ -274,7 +274,7 @@ namespace Jellyfin.Api.Controllers
};
playbackProgressInfo.PlayMethod = ValidatePlayMethod(playbackProgressInfo.PlayMethod, playbackProgressInfo.PlaySessionId);
- playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);;
+ playbackProgressInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
await _sessionManager.OnPlaybackProgress(playbackProgressInfo).ConfigureAwait(false);
return NoContent();
}
@@ -319,7 +319,7 @@ namespace Jellyfin.Api.Controllers
await _transcodingJobHelper.KillTranscodingJobs(User.GetDeviceId()!, playbackStopInfo.PlaySessionId, s => true).ConfigureAwait(false);
}
- playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);;
+ playbackStopInfo.SessionId = await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false);
await _sessionManager.OnPlaybackStopped(playbackStopInfo).ConfigureAwait(false);
return NoContent();
}
diff --git a/Jellyfin.Api/Controllers/SessionController.cs b/Jellyfin.Api/Controllers/SessionController.cs
index 28415555ed..31b95162d4 100644
--- a/Jellyfin.Api/Controllers/SessionController.cs
+++ b/Jellyfin.Api/Controllers/SessionController.cs
@@ -182,7 +182,7 @@ namespace Jellyfin.Api.Controllers
};
await _sessionManager.SendPlayCommand(
- await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
+ await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
sessionId,
playRequest,
CancellationToken.None)
@@ -210,7 +210,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? controllingUserId)
{
await _sessionManager.SendPlaystateCommand(
- await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
+ await RequestHelpers.GetSessionId(_sessionManager, _userManager, HttpContext).ConfigureAwait(false),
sessionId,
new PlaystateRequest()
{
diff --git a/Jellyfin.Api/Controllers/TrailersController.cs b/Jellyfin.Api/Controllers/TrailersController.cs
index b296d1c960..53a839e431 100644
--- a/Jellyfin.Api/Controllers/TrailersController.cs
+++ b/Jellyfin.Api/Controllers/TrailersController.cs
@@ -55,9 +55,9 @@ namespace Jellyfin.Api.Controllers
/// Optional. The minimum last saved date for the current user. Format = ISO.
/// Optional. The maximum premiere date. Format = ISO.
/// Optional filter by items that have an overview or not.
- /// Optional filter by items that have an imdb id or not.
- /// Optional filter by items that have a tmdb id or not.
- /// Optional filter by items that have a tvdb id or not.
+ /// Optional filter by items that have an IMDb id or not.
+ /// Optional filter by items that have a TMDb id or not.
+ /// Optional filter by items that have a TVDb id or not.
/// Optional filter for live tv movies.
/// Optional filter for live tv series.
/// Optional filter for live tv news.
@@ -68,7 +68,7 @@ namespace Jellyfin.Api.Controllers
/// Optional. The maximum number of records to return.
/// When searching within folders, this determines whether or not the search will be recursive. true/false.
/// Optional. Filter based on a search term.
- /// Sort Order - Ascending,Descending.
+ /// Sort Order - Ascending, Descending.
/// Specify this to localize the search to a specific item or folder. Omit to use the root.
/// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines.
/// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited.
diff --git a/Jellyfin.Api/Controllers/UniversalAudioController.cs b/Jellyfin.Api/Controllers/UniversalAudioController.cs
index 01e13b4fe3..d77126a353 100644
--- a/Jellyfin.Api/Controllers/UniversalAudioController.cs
+++ b/Jellyfin.Api/Controllers/UniversalAudioController.cs
@@ -108,7 +108,7 @@ namespace Jellyfin.Api.Controllers
{
var deviceProfile = GetDeviceProfile(container, transcodingContainer, audioCodec, transcodingProtocol, breakOnNonKeyFrames, transcodingAudioChannels, maxAudioSampleRate, maxAudioBitDepth, maxAudioChannels);
- if (!userId.HasValue || userId.Value.Equals(Guid.Empty))
+ if (!userId.HasValue || userId.Value.Equals(default))
{
userId = User.GetUserId();
}
diff --git a/Jellyfin.Api/Controllers/UserLibraryController.cs b/Jellyfin.Api/Controllers/UserLibraryController.cs
index ee8a17b62d..8a2d5a27d9 100644
--- a/Jellyfin.Api/Controllers/UserLibraryController.cs
+++ b/Jellyfin.Api/Controllers/UserLibraryController.cs
@@ -7,11 +7,13 @@ using System.Threading.Tasks;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.ModelBinders;
+using Jellyfin.Api.Models.UserDtos;
using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Lyrics;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@@ -36,6 +38,7 @@ namespace Jellyfin.Api.Controllers
private readonly IDtoService _dtoService;
private readonly IUserViewManager _userViewManager;
private readonly IFileSystem _fileSystem;
+ private readonly ILyricManager _lyricManager;
///
/// Initializes a new instance of the class.
@@ -46,13 +49,15 @@ namespace Jellyfin.Api.Controllers
/// Instance of the interface.
/// Instance of the interface.
/// Instance of the interface.
+ /// Instance of the interface.
public UserLibraryController(
IUserManager userManager,
IUserDataManager userDataRepository,
ILibraryManager libraryManager,
IDtoService dtoService,
IUserViewManager userViewManager,
- IFileSystem fileSystem)
+ IFileSystem fileSystem,
+ ILyricManager lyricManager)
{
_userManager = userManager;
_userDataRepository = userDataRepository;
@@ -60,6 +65,7 @@ namespace Jellyfin.Api.Controllers
_dtoService = dtoService;
_userViewManager = userViewManager;
_fileSystem = fileSystem;
+ _lyricManager = lyricManager;
}
///
@@ -381,5 +387,42 @@ namespace Jellyfin.Api.Controllers
return _userDataRepository.GetUserDataDto(item, user);
}
+
+ ///
+ /// Gets an item's lyrics.
+ ///
+ /// User id.
+ /// Item id.
+ /// Lyrics returned.
+ /// Something went wrong. No Lyrics will be returned.
+ /// An containing the item's lyrics.
+ [HttpGet("Users/{userId}/Items/{itemId}/Lyrics")]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ public async Task> GetLyrics([FromRoute, Required] Guid userId, [FromRoute, Required] Guid itemId)
+ {
+ var user = _userManager.GetUserById(userId);
+
+ if (user == null)
+ {
+ return NotFound();
+ }
+
+ var item = itemId.Equals(default)
+ ? _libraryManager.GetUserRootFolder()
+ : _libraryManager.GetItemById(itemId);
+
+ if (item == null)
+ {
+ return NotFound();
+ }
+
+ var result = await _lyricManager.GetLyrics(item).ConfigureAwait(false);
+ if (result is not null)
+ {
+ return Ok(result);
+ }
+
+ return NotFound();
+ }
}
}
diff --git a/Jellyfin.Api/Helpers/StreamingHelpers.cs b/Jellyfin.Api/Helpers/StreamingHelpers.cs
index 3705737739..c8e62999c4 100644
--- a/Jellyfin.Api/Helpers/StreamingHelpers.cs
+++ b/Jellyfin.Api/Helpers/StreamingHelpers.cs
@@ -351,7 +351,7 @@ namespace Jellyfin.Api.Helpers
try
{
// Parses npt times in the format of '10:19:25.7'
- return TimeSpan.Parse(value).Ticks;
+ return TimeSpan.Parse(value, CultureInfo.InvariantCulture).Ticks;
}
catch
{
diff --git a/Jellyfin.Api/Jellyfin.Api.csproj b/Jellyfin.Api/Jellyfin.Api.csproj
index 7e64cf645b..a4502b6129 100644
--- a/Jellyfin.Api/Jellyfin.Api.csproj
+++ b/Jellyfin.Api/Jellyfin.Api.csproj
@@ -17,7 +17,7 @@
-
+
diff --git a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
index cbbeee024e..89de998ca7 100644
--- a/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
+++ b/Jellyfin.Drawing.Skia/Jellyfin.Drawing.Skia.csproj
@@ -18,8 +18,8 @@
-
-
+
+
diff --git a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
index db4f78398c..2a37299428 100644
--- a/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
+++ b/Jellyfin.Drawing.Skia/PlayedIndicatorDrawer.cs
@@ -42,7 +42,7 @@ namespace Jellyfin.Drawing.Skia
// ask the font manager for a font with that character
paint.Typeface = SKFontManager.Default.MatchCharacter(emojiChar);
- canvas.DrawText(Text, (float)x - 20, OffsetFromTopRightCorner + 12, paint);
+ canvas.DrawText(Text, (float)x - 12, OffsetFromTopRightCorner + 12, paint);
}
}
}
diff --git a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
index 592c53fe55..9d6ca6aabe 100644
--- a/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
+++ b/Jellyfin.Server.Implementations/Activity/ActivityManager.cs
@@ -15,13 +15,13 @@ namespace Jellyfin.Server.Implementations.Activity
///
public class ActivityManager : IActivityManager
{
- private readonly JellyfinDbProvider _provider;
+ private readonly IDbContextFactory _provider;
///
/// Initializes a new instance of the class.
///
/// The Jellyfin database provider.
- public ActivityManager(JellyfinDbProvider provider)
+ public ActivityManager(IDbContextFactory provider)
{
_provider = provider;
}
@@ -32,10 +32,12 @@ namespace Jellyfin.Server.Implementations.Activity
///
public async Task CreateAsync(ActivityLog entry)
{
- await using var dbContext = _provider.CreateContext();
-
- dbContext.ActivityLogs.Add(entry);
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ var dbContext = await _provider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ dbContext.ActivityLogs.Add(entry);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
EntryCreated?.Invoke(this, new GenericEventArgs(ConvertToOldModel(entry)));
}
@@ -43,44 +45,47 @@ namespace Jellyfin.Server.Implementations.Activity
///
public async Task> GetPagedResultAsync(ActivityLogQuery query)
{
- await using var dbContext = _provider.CreateContext();
-
- IQueryable entries = dbContext.ActivityLogs
- .AsQueryable()
- .OrderByDescending(entry => entry.DateCreated);
-
- if (query.MinDate.HasValue)
+ var dbContext = await _provider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
{
- entries = entries.Where(entry => entry.DateCreated >= query.MinDate);
- }
+ IQueryable entries = dbContext.ActivityLogs
+ .OrderByDescending(entry => entry.DateCreated);
- if (query.HasUserId.HasValue)
- {
- entries = entries.Where(entry => (!entry.UserId.Equals(default)) == query.HasUserId.Value);
- }
+ if (query.MinDate.HasValue)
+ {
+ entries = entries.Where(entry => entry.DateCreated >= query.MinDate);
+ }
- return new QueryResult(
- query.Skip,
- await entries.CountAsync().ConfigureAwait(false),
- await entries
- .Skip(query.Skip ?? 0)
- .Take(query.Limit ?? 100)
- .AsAsyncEnumerable()
- .Select(ConvertToOldModel)
- .ToListAsync()
- .ConfigureAwait(false));
+ if (query.HasUserId.HasValue)
+ {
+ entries = entries.Where(entry => (!entry.UserId.Equals(default)) == query.HasUserId.Value);
+ }
+
+ return new QueryResult(
+ query.Skip,
+ await entries.CountAsync().ConfigureAwait(false),
+ await entries
+ .Skip(query.Skip ?? 0)
+ .Take(query.Limit ?? 100)
+ .AsAsyncEnumerable()
+ .Select(ConvertToOldModel)
+ .ToListAsync()
+ .ConfigureAwait(false));
+ }
}
///
public async Task CleanAsync(DateTime startDate)
{
- await using var dbContext = _provider.CreateContext();
- var entries = dbContext.ActivityLogs
- .AsQueryable()
- .Where(entry => entry.DateCreated <= startDate);
+ var dbContext = await _provider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ var entries = dbContext.ActivityLogs
+ .Where(entry => entry.DateCreated <= startDate);
- dbContext.RemoveRange(entries);
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ dbContext.RemoveRange(entries);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
}
private static ActivityLogEntry ConvertToOldModel(ActivityLog entry)
diff --git a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs
index 0728f11799..eeb958c620 100644
--- a/Jellyfin.Server.Implementations/Devices/DeviceManager.cs
+++ b/Jellyfin.Server.Implementations/Devices/DeviceManager.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
@@ -22,7 +23,7 @@ namespace Jellyfin.Server.Implementations.Devices
///
public class DeviceManager : IDeviceManager
{
- private readonly JellyfinDbProvider _dbProvider;
+ private readonly IDbContextFactory _dbProvider;
private readonly IUserManager _userManager;
private readonly ConcurrentDictionary _capabilitiesMap = new();
@@ -31,7 +32,7 @@ namespace Jellyfin.Server.Implementations.Devices
///
/// The database provider.
/// The user manager.
- public DeviceManager(JellyfinDbProvider dbProvider, IUserManager userManager)
+ public DeviceManager(IDbContextFactory dbProvider, IUserManager userManager)
{
_dbProvider = dbProvider;
_userManager = userManager;
@@ -49,16 +50,20 @@ namespace Jellyfin.Server.Implementations.Devices
///
public async Task UpdateDeviceOptions(string deviceId, string deviceName)
{
- await using var dbContext = _dbProvider.CreateContext();
- var deviceOptions = await dbContext.DeviceOptions.AsQueryable().FirstOrDefaultAsync(dev => dev.DeviceId == deviceId).ConfigureAwait(false);
- if (deviceOptions == null)
+ DeviceOptions? deviceOptions;
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
{
- deviceOptions = new DeviceOptions(deviceId);
- dbContext.DeviceOptions.Add(deviceOptions);
- }
+ deviceOptions = await dbContext.DeviceOptions.AsQueryable().FirstOrDefaultAsync(dev => dev.DeviceId == deviceId).ConfigureAwait(false);
+ if (deviceOptions == null)
+ {
+ deviceOptions = new DeviceOptions(deviceId);
+ dbContext.DeviceOptions.Add(deviceOptions);
+ }
- deviceOptions.CustomName = deviceName;
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ deviceOptions.CustomName = deviceName;
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
DeviceOptionsUpdated?.Invoke(this, new GenericEventArgs>(new Tuple(deviceId, deviceOptions)));
}
@@ -66,22 +71,29 @@ namespace Jellyfin.Server.Implementations.Devices
///
public async Task CreateDevice(Device device)
{
- await using var dbContext = _dbProvider.CreateContext();
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ dbContext.Devices.Add(device);
- dbContext.Devices.Add(device);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
return device;
}
///
public async Task GetDeviceOptions(string deviceId)
{
- await using var dbContext = _dbProvider.CreateContext();
- var deviceOptions = await dbContext.DeviceOptions
- .AsQueryable()
- .FirstOrDefaultAsync(d => d.DeviceId == deviceId)
- .ConfigureAwait(false);
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ DeviceOptions? deviceOptions;
+ await using (dbContext.ConfigureAwait(false))
+ {
+ deviceOptions = await dbContext.DeviceOptions
+ .AsNoTracking()
+ .FirstOrDefaultAsync(d => d.DeviceId == deviceId)
+ .ConfigureAwait(false);
+ }
return deviceOptions ?? new DeviceOptions(deviceId);
}
@@ -97,14 +109,17 @@ namespace Jellyfin.Server.Implementations.Devices
///
public async Task GetDevice(string id)
{
- await using var dbContext = _dbProvider.CreateContext();
- var device = await dbContext.Devices
- .AsQueryable()
- .Where(d => d.DeviceId == id)
- .OrderByDescending(d => d.DateLastActivity)
- .Include(d => d.User)
- .FirstOrDefaultAsync()
- .ConfigureAwait(false);
+ Device? device;
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ device = await dbContext.Devices
+ .Where(d => d.DeviceId == id)
+ .OrderByDescending(d => d.DateLastActivity)
+ .Include(d => d.User)
+ .FirstOrDefaultAsync()
+ .ConfigureAwait(false);
+ }
var deviceInfo = device == null ? null : ToDeviceInfo(device);
@@ -114,41 +129,40 @@ namespace Jellyfin.Server.Implementations.Devices
///
public async Task> GetDevices(DeviceQuery query)
{
- await using var dbContext = _dbProvider.CreateContext();
-
- var devices = dbContext.Devices.AsQueryable();
-
- if (query.UserId.HasValue)
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
{
- devices = devices.Where(device => device.UserId.Equals(query.UserId.Value));
+ var devices = dbContext.Devices.AsQueryable();
+
+ if (query.UserId.HasValue)
+ {
+ devices = devices.Where(device => device.UserId.Equals(query.UserId.Value));
+ }
+
+ if (query.DeviceId != null)
+ {
+ devices = devices.Where(device => device.DeviceId == query.DeviceId);
+ }
+
+ if (query.AccessToken != null)
+ {
+ devices = devices.Where(device => device.AccessToken == query.AccessToken);
+ }
+
+ var count = await devices.CountAsync().ConfigureAwait(false);
+
+ if (query.Skip.HasValue)
+ {
+ devices = devices.Skip(query.Skip.Value);
+ }
+
+ if (query.Limit.HasValue)
+ {
+ devices = devices.Take(query.Limit.Value);
+ }
+
+ return new QueryResult(query.Skip, count, await devices.ToListAsync().ConfigureAwait(false));
}
-
- if (query.DeviceId != null)
- {
- devices = devices.Where(device => device.DeviceId == query.DeviceId);
- }
-
- if (query.AccessToken != null)
- {
- devices = devices.Where(device => device.AccessToken == query.AccessToken);
- }
-
- var count = await devices.CountAsync().ConfigureAwait(false);
-
- if (query.Skip.HasValue)
- {
- devices = devices.Skip(query.Skip.Value);
- }
-
- if (query.Limit.HasValue)
- {
- devices = devices.Take(query.Limit.Value);
- }
-
- return new QueryResult(
- query.Skip,
- count,
- await devices.ToListAsync().ConfigureAwait(false));
}
///
@@ -165,37 +179,43 @@ namespace Jellyfin.Server.Implementations.Devices
///
public async Task> GetDevicesForUser(Guid? userId, bool? supportsSync)
{
- await using var dbContext = _dbProvider.CreateContext();
- var sessions = dbContext.Devices
- .Include(d => d.User)
- .AsQueryable()
- .OrderByDescending(d => d.DateLastActivity)
- .ThenBy(d => d.DeviceId)
- .AsAsyncEnumerable();
-
- if (supportsSync.HasValue)
+ IAsyncEnumerable sessions;
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
{
- sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == supportsSync.Value);
+ sessions = dbContext.Devices
+ .Include(d => d.User)
+ .OrderByDescending(d => d.DateLastActivity)
+ .ThenBy(d => d.DeviceId)
+ .AsAsyncEnumerable();
+
+ if (supportsSync.HasValue)
+ {
+ sessions = sessions.Where(i => GetCapabilities(i.DeviceId).SupportsSync == supportsSync.Value);
+ }
+
+ if (userId.HasValue)
+ {
+ var user = _userManager.GetUserById(userId.Value);
+
+ sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId));
+ }
+
+ var array = await sessions.Select(device => ToDeviceInfo(device)).ToArrayAsync().ConfigureAwait(false);
+
+ return new QueryResult(array);
}
-
- if (userId.HasValue)
- {
- var user = _userManager.GetUserById(userId.Value);
-
- sessions = sessions.Where(i => CanAccessDevice(user, i.DeviceId));
- }
-
- var array = await sessions.Select(device => ToDeviceInfo(device)).ToArrayAsync().ConfigureAwait(false);
-
- return new QueryResult(array);
}
///
public async Task DeleteDevice(Device device)
{
- await using var dbContext = _dbProvider.CreateContext();
- dbContext.Devices.Remove(device);
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ dbContext.Devices.Remove(device);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
}
///
diff --git a/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs b/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000000..f98a0aede8
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Extensions/ServiceCollectionExtensions.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+using EFCoreSecondLevelCacheInterceptor;
+using MediaBrowser.Common.Configuration;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+
+namespace Jellyfin.Server.Implementations.Extensions;
+
+///
+/// Extensions for the interface.
+///
+public static class ServiceCollectionExtensions
+{
+ ///
+ /// Adds the interface to the service collection with second level caching enabled.
+ ///
+ /// An instance of the interface.
+ /// The updated service collection.
+ public static IServiceCollection AddJellyfinDbContext(this IServiceCollection serviceCollection)
+ {
+ serviceCollection.AddEFSecondLevelCache(options =>
+ options.UseMemoryCacheProvider()
+ .CacheAllQueries(CacheExpirationMode.Sliding, TimeSpan.FromMinutes(10))
+ .DisableLogging(true)
+ .UseCacheKeyPrefix("EF_")
+ // Don't cache null values. Remove this optional setting if it's not necessary.
+ .SkipCachingResults(result =>
+ result.Value == null || (result.Value is EFTableRows rows && rows.RowsCount == 0)));
+
+ serviceCollection.AddPooledDbContextFactory((serviceProvider, opt) =>
+ {
+ var applicationPaths = serviceProvider.GetRequiredService();
+ var loggerFactory = serviceProvider.GetRequiredService();
+ opt.UseSqlite($"Filename={Path.Combine(applicationPaths.DataPath, "jellyfin.db")}")
+ .AddInterceptors(serviceProvider.GetRequiredService())
+ .UseLoggerFactory(loggerFactory);
+ });
+
+ return serviceCollection;
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
index 83b2262782..73e9a5882a 100644
--- a/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
+++ b/Jellyfin.Server.Implementations/Jellyfin.Server.Implementations.csproj
@@ -26,14 +26,15 @@
+
-
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
diff --git a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs b/Jellyfin.Server.Implementations/JellyfinDbProvider.cs
deleted file mode 100644
index c2c5198d14..0000000000
--- a/Jellyfin.Server.Implementations/JellyfinDbProvider.cs
+++ /dev/null
@@ -1,51 +0,0 @@
-using System;
-using System.IO;
-using System.Linq;
-using MediaBrowser.Common.Configuration;
-using Microsoft.EntityFrameworkCore;
-using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
-
-namespace Jellyfin.Server.Implementations
-{
- ///
- /// Factory class for generating new instances.
- ///
- public class JellyfinDbProvider
- {
- private readonly IServiceProvider _serviceProvider;
- private readonly IApplicationPaths _appPaths;
- private readonly ILogger _logger;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The application's service provider.
- /// The application paths.
- /// The logger.
- public JellyfinDbProvider(IServiceProvider serviceProvider, IApplicationPaths appPaths, ILogger logger)
- {
- _serviceProvider = serviceProvider;
- _appPaths = appPaths;
- _logger = logger;
-
- using var jellyfinDb = CreateContext();
- if (jellyfinDb.Database.GetPendingMigrations().Any())
- {
- _logger.LogInformation("There are pending EFCore migrations in the database. Applying... (This may take a while, do not stop Jellyfin)");
- jellyfinDb.Database.Migrate();
- _logger.LogInformation("EFCore migrations applied successfully");
- }
- }
-
- ///
- /// Creates a new context.
- ///
- /// The newly created context.
- public JellyfinDb CreateContext()
- {
- var contextOptions = new DbContextOptionsBuilder().UseSqlite($"Filename={Path.Combine(_appPaths.DataPath, "jellyfin.db")}");
- return ActivatorUtilities.CreateInstance(_serviceProvider, contextOptions.Options);
- }
- }
-}
diff --git a/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs
new file mode 100644
index 0000000000..03e3f3c921
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.Designer.cs
@@ -0,0 +1,657 @@
+#pragma warning disable CS1591
+
+//
+using System;
+using Jellyfin.Server.Implementations;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+
+#nullable disable
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+ [DbContext(typeof(JellyfinDb))]
+ [Migration("20221022080052_AddIndexActivityLogsDateCreated")]
+ partial class AddIndexActivityLogsDateCreated
+ {
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder
+ .HasDefaultSchema("jellyfin")
+ .HasAnnotation("ProductVersion", "6.0.9");
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("DayOfWeek")
+ .HasColumnType("INTEGER");
+
+ b.Property("EndHour")
+ .HasColumnType("REAL");
+
+ b.Property("StartHour")
+ .HasColumnType("REAL");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("AccessSchedules", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("DateCreated")
+ .HasColumnType("TEXT");
+
+ b.Property("ItemId")
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("LogSeverity")
+ .HasColumnType("INTEGER");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("TEXT");
+
+ b.Property("Overview")
+ .HasMaxLength(512)
+ .HasColumnType("TEXT");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property("ShortOverview")
+ .HasMaxLength(512)
+ .HasColumnType("TEXT");
+
+ b.Property("Type")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DateCreated");
+
+ b.ToTable("ActivityLogs", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Client")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property("ItemId")
+ .HasColumnType("TEXT");
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "ItemId", "Client", "Key")
+ .IsUnique();
+
+ b.ToTable("CustomItemDisplayPreferences", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("ChromecastVersion")
+ .HasColumnType("INTEGER");
+
+ b.Property("Client")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property("DashboardTheme")
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property("EnableNextVideoInfoOverlay")
+ .HasColumnType("INTEGER");
+
+ b.Property("IndexBy")
+ .HasColumnType("INTEGER");
+
+ b.Property("ItemId")
+ .HasColumnType("TEXT");
+
+ b.Property("ScrollDirection")
+ .HasColumnType("INTEGER");
+
+ b.Property("ShowBackdrop")
+ .HasColumnType("INTEGER");
+
+ b.Property("ShowSidebar")
+ .HasColumnType("INTEGER");
+
+ b.Property("SkipBackwardLength")
+ .HasColumnType("INTEGER");
+
+ b.Property("SkipForwardLength")
+ .HasColumnType("INTEGER");
+
+ b.Property("TvHome")
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "ItemId", "Client")
+ .IsUnique();
+
+ b.ToTable("DisplayPreferences", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("DisplayPreferencesId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Order")
+ .HasColumnType("INTEGER");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DisplayPreferencesId");
+
+ b.ToTable("HomeSection", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("LastModified")
+ .HasColumnType("TEXT");
+
+ b.Property("Path")
+ .IsRequired()
+ .HasMaxLength(512)
+ .HasColumnType("TEXT");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId")
+ .IsUnique();
+
+ b.ToTable("ImageInfos", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Client")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property("IndexBy")
+ .HasColumnType("INTEGER");
+
+ b.Property("ItemId")
+ .HasColumnType("TEXT");
+
+ b.Property("RememberIndexing")
+ .HasColumnType("INTEGER");
+
+ b.Property("RememberSorting")
+ .HasColumnType("INTEGER");
+
+ b.Property("SortBy")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("TEXT");
+
+ b.Property("SortOrder")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property("ViewType")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId");
+
+ b.ToTable("ItemDisplayPreferences", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Kind")
+ .HasColumnType("INTEGER");
+
+ b.Property("Permission_Permissions_Guid")
+ .HasColumnType("TEXT");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "Kind")
+ .IsUnique()
+ .HasFilter("[UserId] IS NOT NULL");
+
+ b.ToTable("Permissions", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Kind")
+ .HasColumnType("INTEGER");
+
+ b.Property("Preference_Preferences_Guid")
+ .HasColumnType("TEXT");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.Property("Value")
+ .IsRequired()
+ .HasMaxLength(65535)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("UserId", "Kind")
+ .IsUnique()
+ .HasFilter("[UserId] IS NOT NULL");
+
+ b.ToTable("Preferences", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Security.ApiKey", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AccessToken")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("DateCreated")
+ .HasColumnType("TEXT");
+
+ b.Property("DateLastActivity")
+ .HasColumnType("TEXT");
+
+ b.Property("Name")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("AccessToken")
+ .IsUnique();
+
+ b.ToTable("ApiKeys", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Security.Device", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AccessToken")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("AppName")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("TEXT");
+
+ b.Property("AppVersion")
+ .IsRequired()
+ .HasMaxLength(32)
+ .HasColumnType("TEXT");
+
+ b.Property("DateCreated")
+ .HasColumnType("TEXT");
+
+ b.Property("DateLastActivity")
+ .HasColumnType("TEXT");
+
+ b.Property("DateModified")
+ .HasColumnType("TEXT");
+
+ b.Property("DeviceId")
+ .IsRequired()
+ .HasMaxLength(256)
+ .HasColumnType("TEXT");
+
+ b.Property("DeviceName")
+ .IsRequired()
+ .HasMaxLength(64)
+ .HasColumnType("TEXT");
+
+ b.Property("IsActive")
+ .HasColumnType("INTEGER");
+
+ b.Property("UserId")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DeviceId");
+
+ b.HasIndex("AccessToken", "DateLastActivity");
+
+ b.HasIndex("DeviceId", "DateLastActivity");
+
+ b.HasIndex("UserId", "DeviceId");
+
+ b.ToTable("Devices", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Security.DeviceOptions", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CustomName")
+ .HasColumnType("TEXT");
+
+ b.Property("DeviceId")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("DeviceId")
+ .IsUnique();
+
+ b.ToTable("DeviceOptions", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("TEXT");
+
+ b.Property("AudioLanguagePreference")
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property("AuthenticationProviderId")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property("DisplayCollectionsView")
+ .HasColumnType("INTEGER");
+
+ b.Property("DisplayMissingEpisodes")
+ .HasColumnType("INTEGER");
+
+ b.Property("EasyPassword")
+ .HasMaxLength(65535)
+ .HasColumnType("TEXT");
+
+ b.Property("EnableAutoLogin")
+ .HasColumnType("INTEGER");
+
+ b.Property("EnableLocalPassword")
+ .HasColumnType("INTEGER");
+
+ b.Property("EnableNextEpisodeAutoPlay")
+ .HasColumnType("INTEGER");
+
+ b.Property("EnableUserPreferenceAccess")
+ .HasColumnType("INTEGER");
+
+ b.Property("HidePlayedInLatest")
+ .HasColumnType("INTEGER");
+
+ b.Property("InternalId")
+ .HasColumnType("INTEGER");
+
+ b.Property("InvalidLoginAttemptCount")
+ .HasColumnType("INTEGER");
+
+ b.Property("LastActivityDate")
+ .HasColumnType("TEXT");
+
+ b.Property("LastLoginDate")
+ .HasColumnType("TEXT");
+
+ b.Property("LoginAttemptsBeforeLockout")
+ .HasColumnType("INTEGER");
+
+ b.Property("MaxActiveSessions")
+ .HasColumnType("INTEGER");
+
+ b.Property("MaxParentalAgeRating")
+ .HasColumnType("INTEGER");
+
+ b.Property("MustUpdatePassword")
+ .HasColumnType("INTEGER");
+
+ b.Property("Password")
+ .HasMaxLength(65535)
+ .HasColumnType("TEXT");
+
+ b.Property("PasswordResetProviderId")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property("PlayDefaultAudioTrack")
+ .HasColumnType("INTEGER");
+
+ b.Property("RememberAudioSelections")
+ .HasColumnType("INTEGER");
+
+ b.Property("RememberSubtitleSelections")
+ .HasColumnType("INTEGER");
+
+ b.Property("RemoteClientBitrateLimit")
+ .HasColumnType("INTEGER");
+
+ b.Property("RowVersion")
+ .IsConcurrencyToken()
+ .HasColumnType("INTEGER");
+
+ b.Property("SubtitleLanguagePreference")
+ .HasMaxLength(255)
+ .HasColumnType("TEXT");
+
+ b.Property("SubtitleMode")
+ .HasColumnType("INTEGER");
+
+ b.Property("SyncPlayAccess")
+ .HasColumnType("INTEGER");
+
+ b.Property("Username")
+ .IsRequired()
+ .HasMaxLength(255)
+ .HasColumnType("TEXT")
+ .UseCollation("NOCASE");
+
+ b.HasKey("Id");
+
+ b.HasIndex("Username")
+ .IsUnique();
+
+ b.ToTable("Users", "jellyfin");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("AccessSchedules")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("DisplayPreferences")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.DisplayPreferences", null)
+ .WithMany("HomeSections")
+ .HasForeignKey("DisplayPreferencesId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithOne("ProfileImage")
+ .HasForeignKey("Jellyfin.Data.Entities.ImageInfo", "UserId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("ItemDisplayPreferences")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Permissions")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", null)
+ .WithMany("Preferences")
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade);
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.Security.Device", b =>
+ {
+ b.HasOne("Jellyfin.Data.Entities.User", "User")
+ .WithMany()
+ .HasForeignKey("UserId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("User");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
+ {
+ b.Navigation("HomeSections");
+ });
+
+ modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
+ {
+ b.Navigation("AccessSchedules");
+
+ b.Navigation("DisplayPreferences");
+
+ b.Navigation("ItemDisplayPreferences");
+
+ b.Navigation("Permissions");
+
+ b.Navigation("Preferences");
+
+ b.Navigation("ProfileImage");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.cs b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.cs
new file mode 100644
index 0000000000..f09ad2709a
--- /dev/null
+++ b/Jellyfin.Server.Implementations/Migrations/20221022080052_AddIndexActivityLogsDateCreated.cs
@@ -0,0 +1,28 @@
+#pragma warning disable CS1591, SA1601
+
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace Jellyfin.Server.Implementations.Migrations
+{
+ public partial class AddIndexActivityLogsDateCreated : Migration
+ {
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateIndex(
+ name: "IX_ActivityLogs_DateCreated",
+ schema: "jellyfin",
+ table: "ActivityLogs",
+ column: "DateCreated");
+ }
+
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropIndex(
+ name: "IX_ActivityLogs_DateCreated",
+ schema: "jellyfin",
+ table: "ActivityLogs");
+ }
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
index fcc360e260..2dd7b094aa 100644
--- a/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
+++ b/Jellyfin.Server.Implementations/Migrations/JellyfinDbModelSnapshot.cs
@@ -5,6 +5,8 @@ using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+#nullable disable
+
namespace Jellyfin.Server.Implementations.Migrations
{
[DbContext(typeof(JellyfinDb))]
@@ -15,7 +17,7 @@ namespace Jellyfin.Server.Implementations.Migrations
#pragma warning disable 612, 618
modelBuilder
.HasDefaultSchema("jellyfin")
- .HasAnnotation("ProductVersion", "5.0.7");
+ .HasAnnotation("ProductVersion", "6.0.9");
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
{
@@ -39,7 +41,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("UserId");
- b.ToTable("AccessSchedules");
+ b.ToTable("AccessSchedules", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.ActivityLog", b =>
@@ -85,7 +87,9 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasKey("Id");
- b.ToTable("ActivityLogs");
+ b.HasIndex("DateCreated");
+
+ b.ToTable("ActivityLogs", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.CustomItemDisplayPreferences", b =>
@@ -117,7 +121,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("UserId", "ItemId", "Client", "Key")
.IsUnique();
- b.ToTable("CustomItemDisplayPreferences");
+ b.ToTable("CustomItemDisplayPreferences", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.DisplayPreferences", b =>
@@ -174,7 +178,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("UserId", "ItemId", "Client")
.IsUnique();
- b.ToTable("DisplayPreferences");
+ b.ToTable("DisplayPreferences", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.HomeSection", b =>
@@ -196,7 +200,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("DisplayPreferencesId");
- b.ToTable("HomeSection");
+ b.ToTable("HomeSection", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.ImageInfo", b =>
@@ -221,7 +225,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("UserId")
.IsUnique();
- b.ToTable("ImageInfos");
+ b.ToTable("ImageInfos", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.ItemDisplayPreferences", b =>
@@ -265,7 +269,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("UserId");
- b.ToTable("ItemDisplayPreferences");
+ b.ToTable("ItemDisplayPreferences", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.Permission", b =>
@@ -296,7 +300,7 @@ namespace Jellyfin.Server.Implementations.Migrations
.IsUnique()
.HasFilter("[UserId] IS NOT NULL");
- b.ToTable("Permissions");
+ b.ToTable("Permissions", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.Preference", b =>
@@ -329,7 +333,7 @@ namespace Jellyfin.Server.Implementations.Migrations
.IsUnique()
.HasFilter("[UserId] IS NOT NULL");
- b.ToTable("Preferences");
+ b.ToTable("Preferences", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.Security.ApiKey", b =>
@@ -358,7 +362,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("AccessToken")
.IsUnique();
- b.ToTable("ApiKeys");
+ b.ToTable("ApiKeys", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.Security.Device", b =>
@@ -416,7 +420,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("UserId", "DeviceId");
- b.ToTable("Devices");
+ b.ToTable("Devices", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.Security.DeviceOptions", b =>
@@ -437,7 +441,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("DeviceId")
.IsUnique();
- b.ToTable("DeviceOptions");
+ b.ToTable("DeviceOptions", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.User", b =>
@@ -550,7 +554,7 @@ namespace Jellyfin.Server.Implementations.Migrations
b.HasIndex("Username")
.IsUnique();
- b.ToTable("Users");
+ b.ToTable("Users", "jellyfin");
});
modelBuilder.Entity("Jellyfin.Data.Entities.AccessSchedule", b =>
diff --git a/Jellyfin.Server.Implementations/ModelConfiguration/ActivityLogConfiguration.cs b/Jellyfin.Server.Implementations/ModelConfiguration/ActivityLogConfiguration.cs
new file mode 100644
index 0000000000..9a63ed9f21
--- /dev/null
+++ b/Jellyfin.Server.Implementations/ModelConfiguration/ActivityLogConfiguration.cs
@@ -0,0 +1,17 @@
+using Jellyfin.Data.Entities;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Metadata.Builders;
+
+namespace Jellyfin.Server.Implementations.ModelConfiguration;
+
+///
+/// FluentAPI configuration for the ActivityLog entity.
+///
+public class ActivityLogConfiguration : IEntityTypeConfiguration
+{
+ ///
+ public void Configure(EntityTypeBuilder builder)
+ {
+ builder.HasIndex(entity => entity.DateCreated);
+ }
+}
diff --git a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs
index b79e46469c..33c08c8c29 100644
--- a/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs
+++ b/Jellyfin.Server.Implementations/Security/AuthenticationManager.cs
@@ -10,13 +10,13 @@ namespace Jellyfin.Server.Implementations.Security
///
public class AuthenticationManager : IAuthenticationManager
{
- private readonly JellyfinDbProvider _dbProvider;
+ private readonly IDbContextFactory _dbProvider;
///
/// Initializes a new instance of the class.
///
/// The database provider.
- public AuthenticationManager(JellyfinDbProvider dbProvider)
+ public AuthenticationManager(IDbContextFactory dbProvider)
{
_dbProvider = dbProvider;
}
@@ -24,50 +24,56 @@ namespace Jellyfin.Server.Implementations.Security
///
public async Task CreateApiKey(string name)
{
- await using var dbContext = _dbProvider.CreateContext();
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ dbContext.ApiKeys.Add(new ApiKey(name));
- dbContext.ApiKeys.Add(new ApiKey(name));
-
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
}
///
public async Task> GetApiKeys()
{
- await using var dbContext = _dbProvider.CreateContext();
-
- return await dbContext.ApiKeys
- .AsAsyncEnumerable()
- .Select(key => new AuthenticationInfo
- {
- AppName = key.Name,
- AccessToken = key.AccessToken,
- DateCreated = key.DateCreated,
- DeviceId = string.Empty,
- DeviceName = string.Empty,
- AppVersion = string.Empty
- }).ToListAsync().ConfigureAwait(false);
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ return await dbContext.ApiKeys
+ .AsAsyncEnumerable()
+ .Select(key => new AuthenticationInfo
+ {
+ AppName = key.Name,
+ AccessToken = key.AccessToken,
+ DateCreated = key.DateCreated,
+ DeviceId = string.Empty,
+ DeviceName = string.Empty,
+ AppVersion = string.Empty
+ }).ToListAsync().ConfigureAwait(false);
+ }
}
///
public async Task DeleteApiKey(string accessToken)
{
- await using var dbContext = _dbProvider.CreateContext();
-
- var key = await dbContext.ApiKeys
- .AsQueryable()
- .Where(apiKey => apiKey.AccessToken == accessToken)
- .FirstOrDefaultAsync()
- .ConfigureAwait(false);
-
- if (key == null)
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
{
- return;
+ var key = await dbContext.ApiKeys
+ .AsQueryable()
+ .Where(apiKey => apiKey.AccessToken == accessToken)
+ .FirstOrDefaultAsync()
+ .ConfigureAwait(false);
+
+ if (key == null)
+ {
+ return;
+ }
+
+ dbContext.Remove(key);
+
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
-
- dbContext.Remove(key);
-
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
}
}
diff --git a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
index 9f813f532c..4d1a1b3cf8 100644
--- a/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
+++ b/Jellyfin.Server.Implementations/Security/AuthorizationContext.cs
@@ -4,6 +4,7 @@ using System;
using System.Collections.Generic;
using System.Net;
using System.Threading.Tasks;
+using EFCoreSecondLevelCacheInterceptor;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
@@ -15,12 +16,12 @@ namespace Jellyfin.Server.Implementations.Security
{
public class AuthorizationContext : IAuthorizationContext
{
- private readonly JellyfinDbProvider _jellyfinDbProvider;
+ private readonly IDbContextFactory _jellyfinDbProvider;
private readonly IUserManager _userManager;
private readonly IServerApplicationHost _serverApplicationHost;
public AuthorizationContext(
- JellyfinDbProvider jellyfinDb,
+ IDbContextFactory jellyfinDb,
IUserManager userManager,
IServerApplicationHost serverApplicationHost)
{
@@ -121,96 +122,99 @@ namespace Jellyfin.Server.Implementations.Security
#pragma warning restore CA1508
authInfo.HasToken = true;
- await using var dbContext = _jellyfinDbProvider.CreateContext();
- var device = await dbContext.Devices.FirstOrDefaultAsync(d => d.AccessToken == token).ConfigureAwait(false);
-
- if (device != null)
+ var dbContext = await _jellyfinDbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
{
- authInfo.IsAuthenticated = true;
- var updateToken = false;
+ var device = await dbContext.Devices.FirstOrDefaultAsync(d => d.AccessToken == token).ConfigureAwait(false);
- // TODO: Remove these checks for IsNullOrWhiteSpace
- if (string.IsNullOrWhiteSpace(authInfo.Client))
- {
- authInfo.Client = device.AppName;
- }
-
- if (string.IsNullOrWhiteSpace(authInfo.DeviceId))
- {
- authInfo.DeviceId = device.DeviceId;
- }
-
- // Temporary. TODO - allow clients to specify that the token has been shared with a casting device
- var allowTokenInfoUpdate = !authInfo.Client.Contains("chromecast", StringComparison.OrdinalIgnoreCase);
-
- if (string.IsNullOrWhiteSpace(authInfo.Device))
- {
- authInfo.Device = device.DeviceName;
- }
- else if (!string.Equals(authInfo.Device, device.DeviceName, StringComparison.OrdinalIgnoreCase))
- {
- if (allowTokenInfoUpdate)
- {
- updateToken = true;
- device.DeviceName = authInfo.Device;
- }
- }
-
- if (string.IsNullOrWhiteSpace(authInfo.Version))
- {
- authInfo.Version = device.AppVersion;
- }
- else if (!string.Equals(authInfo.Version, device.AppVersion, StringComparison.OrdinalIgnoreCase))
- {
- if (allowTokenInfoUpdate)
- {
- updateToken = true;
- device.AppVersion = authInfo.Version;
- }
- }
-
- if ((DateTime.UtcNow - device.DateLastActivity).TotalMinutes > 3)
- {
- device.DateLastActivity = DateTime.UtcNow;
- updateToken = true;
- }
-
- authInfo.User = _userManager.GetUserById(device.UserId);
-
- if (updateToken)
- {
- dbContext.Devices.Update(device);
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
- }
- }
- else
- {
- var key = await dbContext.ApiKeys.FirstOrDefaultAsync(apiKey => apiKey.AccessToken == token).ConfigureAwait(false);
- if (key != null)
+ if (device != null)
{
authInfo.IsAuthenticated = true;
- authInfo.Client = key.Name;
- authInfo.Token = key.AccessToken;
+ var updateToken = false;
+
+ // TODO: Remove these checks for IsNullOrWhiteSpace
+ if (string.IsNullOrWhiteSpace(authInfo.Client))
+ {
+ authInfo.Client = device.AppName;
+ }
+
if (string.IsNullOrWhiteSpace(authInfo.DeviceId))
{
- authInfo.DeviceId = _serverApplicationHost.SystemId;
+ authInfo.DeviceId = device.DeviceId;
}
+ // Temporary. TODO - allow clients to specify that the token has been shared with a casting device
+ var allowTokenInfoUpdate = !authInfo.Client.Contains("chromecast", StringComparison.OrdinalIgnoreCase);
+
if (string.IsNullOrWhiteSpace(authInfo.Device))
{
- authInfo.Device = _serverApplicationHost.Name;
+ authInfo.Device = device.DeviceName;
+ }
+ else if (!string.Equals(authInfo.Device, device.DeviceName, StringComparison.OrdinalIgnoreCase))
+ {
+ if (allowTokenInfoUpdate)
+ {
+ updateToken = true;
+ device.DeviceName = authInfo.Device;
+ }
}
if (string.IsNullOrWhiteSpace(authInfo.Version))
{
- authInfo.Version = _serverApplicationHost.ApplicationVersionString;
+ authInfo.Version = device.AppVersion;
+ }
+ else if (!string.Equals(authInfo.Version, device.AppVersion, StringComparison.OrdinalIgnoreCase))
+ {
+ if (allowTokenInfoUpdate)
+ {
+ updateToken = true;
+ device.AppVersion = authInfo.Version;
+ }
}
- authInfo.IsApiKey = true;
- }
- }
+ if ((DateTime.UtcNow - device.DateLastActivity).TotalMinutes > 3)
+ {
+ device.DateLastActivity = DateTime.UtcNow;
+ updateToken = true;
+ }
- return authInfo;
+ authInfo.User = _userManager.GetUserById(device.UserId);
+
+ if (updateToken)
+ {
+ dbContext.Devices.Update(device);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
+ }
+ else
+ {
+ var key = await dbContext.ApiKeys.FirstOrDefaultAsync(apiKey => apiKey.AccessToken == token).ConfigureAwait(false);
+ if (key != null)
+ {
+ authInfo.IsAuthenticated = true;
+ authInfo.Client = key.Name;
+ authInfo.Token = key.AccessToken;
+ if (string.IsNullOrWhiteSpace(authInfo.DeviceId))
+ {
+ authInfo.DeviceId = _serverApplicationHost.SystemId;
+ }
+
+ if (string.IsNullOrWhiteSpace(authInfo.Device))
+ {
+ authInfo.Device = _serverApplicationHost.Name;
+ }
+
+ if (string.IsNullOrWhiteSpace(authInfo.Version))
+ {
+ authInfo.Version = _serverApplicationHost.ApplicationVersionString;
+ }
+
+ authInfo.IsApiKey = true;
+ }
+ }
+
+ return authInfo;
+ }
}
///
diff --git a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs
index 65edb30ad2..87babc05c8 100644
--- a/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs
+++ b/Jellyfin.Server.Implementations/Users/DisplayPreferencesManager.cs
@@ -20,10 +20,10 @@ namespace Jellyfin.Server.Implementations.Users
///
/// Initializes a new instance of the class.
///
- /// The database context.
- public DisplayPreferencesManager(JellyfinDb dbContext)
+ /// The database context factory.
+ public DisplayPreferencesManager(IDbContextFactory dbContextFactory)
{
- _dbContext = dbContext;
+ _dbContext = dbContextFactory.CreateDbContext();
}
///
diff --git a/Jellyfin.Server.Implementations/Users/UserManager.cs b/Jellyfin.Server.Implementations/Users/UserManager.cs
index ed90e81c6f..25560707ac 100644
--- a/Jellyfin.Server.Implementations/Users/UserManager.cs
+++ b/Jellyfin.Server.Implementations/Users/UserManager.cs
@@ -33,7 +33,7 @@ namespace Jellyfin.Server.Implementations.Users
///
public class UserManager : IUserManager
{
- private readonly JellyfinDbProvider _dbProvider;
+ private readonly IDbContextFactory _dbProvider;
private readonly IEventManager _eventManager;
private readonly ICryptoProvider _cryptoProvider;
private readonly INetworkManager _networkManager;
@@ -59,7 +59,7 @@ namespace Jellyfin.Server.Implementations.Users
/// The image processor.
/// The logger.
public UserManager(
- JellyfinDbProvider dbProvider,
+ IDbContextFactory dbProvider,
IEventManager eventManager,
ICryptoProvider cryptoProvider,
INetworkManager networkManager,
@@ -83,7 +83,7 @@ namespace Jellyfin.Server.Implementations.Users
_defaultPasswordResetProvider = _passwordResetProviders.OfType().First();
_users = new ConcurrentDictionary();
- using var dbContext = _dbProvider.CreateContext();
+ using var dbContext = _dbProvider.CreateDbContext();
foreach (var user in dbContext.Users
.Include(user => user.Permissions)
.Include(user => user.Preferences)
@@ -139,31 +139,35 @@ namespace Jellyfin.Server.Implementations.Users
throw new ArgumentException("The new and old names must be different.");
}
- await using var dbContext = _dbProvider.CreateContext();
-
- if (await dbContext.Users
- .AsQueryable()
- .AnyAsync(u => u.Username == newName && !u.Id.Equals(user.Id))
- .ConfigureAwait(false))
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
{
- throw new ArgumentException(string.Format(
- CultureInfo.InvariantCulture,
- "A user with the name '{0}' already exists.",
- newName));
+ if (await dbContext.Users
+ .AsQueryable()
+ .AnyAsync(u => u.Username == newName && !u.Id.Equals(user.Id))
+ .ConfigureAwait(false))
+ {
+ throw new ArgumentException(string.Format(
+ CultureInfo.InvariantCulture,
+ "A user with the name '{0}' already exists.",
+ newName));
+ }
+
+ user.Username = newName;
+ await UpdateUserInternalAsync(dbContext, user).ConfigureAwait(false);
}
- user.Username = newName;
- await UpdateUserAsync(user).ConfigureAwait(false);
OnUserUpdated?.Invoke(this, new GenericEventArgs(user));
}
///
public async Task UpdateUserAsync(User user)
{
- await using var dbContext = _dbProvider.CreateContext();
- dbContext.Users.Update(user);
- _users[user.Id] = user;
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ await UpdateUserInternalAsync(dbContext, user).ConfigureAwait(false);
+ }
}
internal async Task CreateUserInternalAsync(string name, JellyfinDb dbContext)
@@ -202,12 +206,15 @@ namespace Jellyfin.Server.Implementations.Users
name));
}
- await using var dbContext = _dbProvider.CreateContext();
+ User newUser;
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false);
- var newUser = await CreateUserInternalAsync(name, dbContext).ConfigureAwait(false);
-
- dbContext.Users.Add(newUser);
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ dbContext.Users.Add(newUser);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
await _eventManager.PublishAsync(new UserCreatedEventArgs(newUser)).ConfigureAwait(false);
@@ -241,9 +248,13 @@ namespace Jellyfin.Server.Implementations.Users
nameof(userId));
}
- await using var dbContext = _dbProvider.CreateContext();
- dbContext.Users.Remove(user);
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ dbContext.Users.Remove(user);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
+
_users.Remove(userId);
await _eventManager.PublishAsync(new UserDeletedEventArgs(user)).ConfigureAwait(false);
@@ -288,7 +299,7 @@ namespace Jellyfin.Server.Implementations.Users
user.EasyPassword = newPasswordSha1;
await UpdateUserAsync(user).ConfigureAwait(false);
- _eventManager.Publish(new UserPasswordChangedEventArgs(user));
+ await _eventManager.PublishAsync(new UserPasswordChangedEventArgs(user)).ConfigureAwait(false);
}
///
@@ -541,14 +552,17 @@ namespace Jellyfin.Server.Implementations.Users
_logger.LogWarning("No users, creating one with username {UserName}", defaultName);
- await using var dbContext = _dbProvider.CreateContext();
- var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false);
- newUser.SetPermission(PermissionKind.IsAdministrator, true);
- newUser.SetPermission(PermissionKind.EnableContentDeletion, true);
- newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true);
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ var newUser = await CreateUserInternalAsync(defaultName, dbContext).ConfigureAwait(false);
+ newUser.SetPermission(PermissionKind.IsAdministrator, true);
+ newUser.SetPermission(PermissionKind.EnableContentDeletion, true);
+ newUser.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, true);
- dbContext.Users.Add(newUser);
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ dbContext.Users.Add(newUser);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
}
///
@@ -584,105 +598,111 @@ namespace Jellyfin.Server.Implementations.Users
///
public async Task UpdateConfigurationAsync(Guid userId, UserConfiguration config)
{
- await using var dbContext = _dbProvider.CreateContext();
- var user = dbContext.Users
- .Include(u => u.Permissions)
- .Include(u => u.Preferences)
- .Include(u => u.AccessSchedules)
- .Include(u => u.ProfileImage)
- .FirstOrDefault(u => u.Id.Equals(userId))
- ?? throw new ArgumentException("No user exists with given Id!");
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ var user = dbContext.Users
+ .Include(u => u.Permissions)
+ .Include(u => u.Preferences)
+ .Include(u => u.AccessSchedules)
+ .Include(u => u.ProfileImage)
+ .FirstOrDefault(u => u.Id.Equals(userId))
+ ?? throw new ArgumentException("No user exists with given Id!");
- user.SubtitleMode = config.SubtitleMode;
- user.HidePlayedInLatest = config.HidePlayedInLatest;
- user.EnableLocalPassword = config.EnableLocalPassword;
- user.PlayDefaultAudioTrack = config.PlayDefaultAudioTrack;
- user.DisplayCollectionsView = config.DisplayCollectionsView;
- user.DisplayMissingEpisodes = config.DisplayMissingEpisodes;
- user.AudioLanguagePreference = config.AudioLanguagePreference;
- user.RememberAudioSelections = config.RememberAudioSelections;
- user.EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay;
- user.RememberSubtitleSelections = config.RememberSubtitleSelections;
- user.SubtitleLanguagePreference = config.SubtitleLanguagePreference;
+ user.SubtitleMode = config.SubtitleMode;
+ user.HidePlayedInLatest = config.HidePlayedInLatest;
+ user.EnableLocalPassword = config.EnableLocalPassword;
+ user.PlayDefaultAudioTrack = config.PlayDefaultAudioTrack;
+ user.DisplayCollectionsView = config.DisplayCollectionsView;
+ user.DisplayMissingEpisodes = config.DisplayMissingEpisodes;
+ user.AudioLanguagePreference = config.AudioLanguagePreference;
+ user.RememberAudioSelections = config.RememberAudioSelections;
+ user.EnableNextEpisodeAutoPlay = config.EnableNextEpisodeAutoPlay;
+ user.RememberSubtitleSelections = config.RememberSubtitleSelections;
+ user.SubtitleLanguagePreference = config.SubtitleLanguagePreference;
- user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
- user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
- user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
- user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
+ user.SetPreference(PreferenceKind.OrderedViews, config.OrderedViews);
+ user.SetPreference(PreferenceKind.GroupedFolders, config.GroupedFolders);
+ user.SetPreference(PreferenceKind.MyMediaExcludes, config.MyMediaExcludes);
+ user.SetPreference(PreferenceKind.LatestItemExcludes, config.LatestItemsExcludes);
- dbContext.Update(user);
- _users[user.Id] = user;
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ dbContext.Update(user);
+ _users[user.Id] = user;
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
}
///
public async Task UpdatePolicyAsync(Guid userId, UserPolicy policy)
{
- await using var dbContext = _dbProvider.CreateContext();
- var user = dbContext.Users
- .Include(u => u.Permissions)
- .Include(u => u.Preferences)
- .Include(u => u.AccessSchedules)
- .Include(u => u.ProfileImage)
- .FirstOrDefault(u => u.Id.Equals(userId))
- ?? throw new ArgumentException("No user exists with given Id!");
-
- // The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0"
- int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
{
- -1 => null,
- 0 => 3,
- _ => policy.LoginAttemptsBeforeLockout
- };
+ var user = dbContext.Users
+ .Include(u => u.Permissions)
+ .Include(u => u.Preferences)
+ .Include(u => u.AccessSchedules)
+ .Include(u => u.ProfileImage)
+ .FirstOrDefault(u => u.Id.Equals(userId))
+ ?? throw new ArgumentException("No user exists with given Id!");
- user.MaxParentalAgeRating = policy.MaxParentalRating;
- user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess;
- user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit;
- user.AuthenticationProviderId = policy.AuthenticationProviderId;
- user.PasswordResetProviderId = policy.PasswordResetProviderId;
- user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount;
- user.LoginAttemptsBeforeLockout = maxLoginAttempts;
- user.MaxActiveSessions = policy.MaxActiveSessions;
- user.SyncPlayAccess = policy.SyncPlayAccess;
- user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
- user.SetPermission(PermissionKind.IsHidden, policy.IsHidden);
- user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled);
- user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl);
- user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess);
- user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement);
- user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess);
- user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback);
- user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscoding);
- user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscoding);
- user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion);
- user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading);
- user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding);
- user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion);
- user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels);
- user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices);
- user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
- user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers);
- user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
- user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
- user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
+ // The default number of login attempts is 3, but for some god forsaken reason it's sent to the server as "0"
+ int? maxLoginAttempts = policy.LoginAttemptsBeforeLockout switch
+ {
+ -1 => null,
+ 0 => 3,
+ _ => policy.LoginAttemptsBeforeLockout
+ };
- user.AccessSchedules.Clear();
- foreach (var policyAccessSchedule in policy.AccessSchedules)
- {
- user.AccessSchedules.Add(policyAccessSchedule);
+ user.MaxParentalAgeRating = policy.MaxParentalRating;
+ user.EnableUserPreferenceAccess = policy.EnableUserPreferenceAccess;
+ user.RemoteClientBitrateLimit = policy.RemoteClientBitrateLimit;
+ user.AuthenticationProviderId = policy.AuthenticationProviderId;
+ user.PasswordResetProviderId = policy.PasswordResetProviderId;
+ user.InvalidLoginAttemptCount = policy.InvalidLoginAttemptCount;
+ user.LoginAttemptsBeforeLockout = maxLoginAttempts;
+ user.MaxActiveSessions = policy.MaxActiveSessions;
+ user.SyncPlayAccess = policy.SyncPlayAccess;
+ user.SetPermission(PermissionKind.IsAdministrator, policy.IsAdministrator);
+ user.SetPermission(PermissionKind.IsHidden, policy.IsHidden);
+ user.SetPermission(PermissionKind.IsDisabled, policy.IsDisabled);
+ user.SetPermission(PermissionKind.EnableSharedDeviceControl, policy.EnableSharedDeviceControl);
+ user.SetPermission(PermissionKind.EnableRemoteAccess, policy.EnableRemoteAccess);
+ user.SetPermission(PermissionKind.EnableLiveTvManagement, policy.EnableLiveTvManagement);
+ user.SetPermission(PermissionKind.EnableLiveTvAccess, policy.EnableLiveTvAccess);
+ user.SetPermission(PermissionKind.EnableMediaPlayback, policy.EnableMediaPlayback);
+ user.SetPermission(PermissionKind.EnableAudioPlaybackTranscoding, policy.EnableAudioPlaybackTranscoding);
+ user.SetPermission(PermissionKind.EnableVideoPlaybackTranscoding, policy.EnableVideoPlaybackTranscoding);
+ user.SetPermission(PermissionKind.EnableContentDeletion, policy.EnableContentDeletion);
+ user.SetPermission(PermissionKind.EnableContentDownloading, policy.EnableContentDownloading);
+ user.SetPermission(PermissionKind.EnableSyncTranscoding, policy.EnableSyncTranscoding);
+ user.SetPermission(PermissionKind.EnableMediaConversion, policy.EnableMediaConversion);
+ user.SetPermission(PermissionKind.EnableAllChannels, policy.EnableAllChannels);
+ user.SetPermission(PermissionKind.EnableAllDevices, policy.EnableAllDevices);
+ user.SetPermission(PermissionKind.EnableAllFolders, policy.EnableAllFolders);
+ user.SetPermission(PermissionKind.EnableRemoteControlOfOtherUsers, policy.EnableRemoteControlOfOtherUsers);
+ user.SetPermission(PermissionKind.EnablePlaybackRemuxing, policy.EnablePlaybackRemuxing);
+ user.SetPermission(PermissionKind.ForceRemoteSourceTranscoding, policy.ForceRemoteSourceTranscoding);
+ user.SetPermission(PermissionKind.EnablePublicSharing, policy.EnablePublicSharing);
+
+ user.AccessSchedules.Clear();
+ foreach (var policyAccessSchedule in policy.AccessSchedules)
+ {
+ user.AccessSchedules.Add(policyAccessSchedule);
+ }
+
+ // TODO: fix this at some point
+ user.SetPreference(PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems ?? Array.Empty());
+ user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
+ user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
+ user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
+ user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
+ user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
+
+ dbContext.Update(user);
+ _users[user.Id] = user;
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
-
- // TODO: fix this at some point
- user.SetPreference(PreferenceKind.BlockUnratedItems, policy.BlockUnratedItems ?? Array.Empty());
- user.SetPreference(PreferenceKind.BlockedTags, policy.BlockedTags);
- user.SetPreference(PreferenceKind.EnabledChannels, policy.EnabledChannels);
- user.SetPreference(PreferenceKind.EnabledDevices, policy.EnabledDevices);
- user.SetPreference(PreferenceKind.EnabledFolders, policy.EnabledFolders);
- user.SetPreference(PreferenceKind.EnableContentDeletionFromFolders, policy.EnableContentDeletionFromFolders);
-
- dbContext.Update(user);
- _users[user.Id] = user;
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
}
///
@@ -693,9 +713,13 @@ namespace Jellyfin.Server.Implementations.Users
return;
}
- await using var dbContext = _dbProvider.CreateContext();
- dbContext.Remove(user.ProfileImage);
- await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ var dbContext = await _dbProvider.CreateDbContextAsync().ConfigureAwait(false);
+ await using (dbContext.ConfigureAwait(false))
+ {
+ dbContext.Remove(user.ProfileImage);
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
+
user.ProfileImage = null;
_users[user.Id] = user;
}
@@ -859,5 +883,12 @@ namespace Jellyfin.Server.Implementations.Users
await UpdateUserAsync(user).ConfigureAwait(false);
}
+
+ private async Task UpdateUserInternalAsync(JellyfinDb dbContext, User user)
+ {
+ dbContext.Users.Update(user);
+ _users[user.Id] = user;
+ await dbContext.SaveChangesAsync().ConfigureAwait(false);
+ }
}
}
diff --git a/Jellyfin.Server/CoreAppHost.cs b/Jellyfin.Server/CoreAppHost.cs
index 67e50b92d9..002193baf5 100644
--- a/Jellyfin.Server/CoreAppHost.cs
+++ b/Jellyfin.Server/CoreAppHost.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.IO;
using System.Reflection;
using Emby.Drawing;
using Emby.Server.Implementations;
@@ -19,6 +18,7 @@ using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Lyrics;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Activity;
@@ -70,19 +70,13 @@ namespace Jellyfin.Server
Logger.LogWarning("Skia not available. Will fallback to {ImageEncoder}.", nameof(NullImageEncoder));
}
- serviceCollection.AddDbContextPool(
- options => options
- .UseLoggerFactory(LoggerFactory)
- .UseSqlite($"Filename={Path.Combine(ApplicationPaths.DataPath, "jellyfin.db")}"));
-
serviceCollection.AddEventServices();
serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
serviceCollection.AddSingleton();
- serviceCollection.AddSingleton();
+ serviceCollection.AddScoped();
serviceCollection.AddSingleton();
// TODO search the assemblies instead of adding them manually?
@@ -95,6 +89,11 @@ namespace Jellyfin.Server
serviceCollection.AddScoped();
+ foreach (var type in GetExportTypes())
+ {
+ serviceCollection.AddSingleton(typeof(ILyricProvider), type);
+ }
+
base.RegisterServices(serviceCollection);
}
diff --git a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
index 439147bfd9..e8a51c2aa6 100644
--- a/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
+++ b/Jellyfin.Server/Extensions/ApiServiceCollectionExtensions.cs
@@ -438,11 +438,15 @@ namespace Jellyfin.Server.Extensions
options.MapType(() =>
new OpenApiSchema
{
- Type = "string",
- Enum = Enum.GetNames()
- .Select(e => new OpenApiString(e))
- .Cast()
- .ToArray()
+ Type = "array",
+ Items = new OpenApiSchema
+ {
+ Reference = new OpenApiReference
+ {
+ Id = nameof(TranscodeReason),
+ Type = ReferenceType.Schema,
+ }
+ }
});
// Swashbuckle doesn't use JsonOptions to describe responses, so we need to manually describe it.
diff --git a/Jellyfin.Server/Filters/AdditionalModelFilter.cs b/Jellyfin.Server/Filters/AdditionalModelFilter.cs
index 487948f815..645696e319 100644
--- a/Jellyfin.Server/Filters/AdditionalModelFilter.cs
+++ b/Jellyfin.Server/Filters/AdditionalModelFilter.cs
@@ -1,4 +1,5 @@
using System;
+using System.Linq;
using Jellyfin.Extensions;
using Jellyfin.Server.Migrations;
using MediaBrowser.Common.Plugins;
@@ -8,6 +9,7 @@ using MediaBrowser.Model.ApiClient;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Session;
using MediaBrowser.Model.SyncPlay;
+using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
@@ -56,6 +58,15 @@ namespace Jellyfin.Server.Filters
context.SchemaGenerator.GenerateSchema(configuration.ConfigurationType, context.SchemaRepository);
}
+
+ context.SchemaRepository.AddDefinition(nameof(TranscodeReason), new OpenApiSchema
+ {
+ Type = "string",
+ Enum = Enum.GetNames()
+ .Select(e => new OpenApiString(e))
+ .Cast()
+ .ToArray()
+ });
}
}
}
diff --git a/Jellyfin.Server/Jellyfin.Server.csproj b/Jellyfin.Server/Jellyfin.Server.csproj
index b2d79050b1..44f92cf833 100644
--- a/Jellyfin.Server/Jellyfin.Server.csproj
+++ b/Jellyfin.Server/Jellyfin.Server.csproj
@@ -37,10 +37,10 @@
-
-
-
-
+
+
+
+
diff --git a/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs b/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs
index e0c112d60d..6ee5bf38a4 100644
--- a/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs
+++ b/Jellyfin.Server/Middleware/BaseUrlRedirectionMiddleware.cs
@@ -45,36 +45,33 @@ namespace Jellyfin.Server.Middleware
var localPath = httpContext.Request.Path.ToString();
var baseUrlPrefix = serverConfigurationManager.GetNetworkConfiguration().BaseUrl;
- if (!string.IsNullOrEmpty(baseUrlPrefix))
+ if (string.IsNullOrEmpty(localPath)
+ || string.Equals(localPath, baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
+ || string.Equals(localPath, baseUrlPrefix + "/", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(localPath, baseUrlPrefix + "/web", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(localPath, baseUrlPrefix + "/web/", StringComparison.OrdinalIgnoreCase)
+ || !localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase)
+ )
{
- var startsWithBaseUrl = localPath.StartsWith(baseUrlPrefix, StringComparison.OrdinalIgnoreCase);
-
- if (!startsWithBaseUrl
- && (localPath.Equals("/health", StringComparison.OrdinalIgnoreCase)
- || localPath.Equals("/health/", StringComparison.OrdinalIgnoreCase)))
+ // Redirect health endpoint
+ if (string.Equals(localPath, "/health", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(localPath, "/health/", StringComparison.OrdinalIgnoreCase))
{
_logger.LogDebug("Redirecting /health check");
httpContext.Response.Redirect(baseUrlPrefix + "/health");
return;
}
- if (!startsWithBaseUrl
- || localPath.Length == baseUrlPrefix.Length
- // Local path is /baseUrl/
- || (localPath.Length == baseUrlPrefix.Length + 1 && localPath[^1] == '/'))
- {
- // Always redirect back to the default path if the base prefix is invalid, missing, or is the full path.
- _logger.LogDebug("Normalizing an URL at {LocalPath}", localPath);
- httpContext.Response.Redirect(baseUrlPrefix + "/" + _configuration[DefaultRedirectKey]);
- return;
- }
- }
- else if (string.IsNullOrEmpty(localPath)
- || localPath.Equals("/", StringComparison.Ordinal))
- {
- // Always redirect back to the default path if root is requested.
+ // Always redirect back to the default path if the base prefix is invalid or missing
_logger.LogDebug("Normalizing an URL at {LocalPath}", localPath);
- httpContext.Response.Redirect("/" + _configuration[DefaultRedirectKey]);
+
+ var port = httpContext.Request.Host.Port ?? -1;
+ var uri = new UriBuilder(httpContext.Request.Scheme, httpContext.Request.Host.Host, port, localPath).Uri;
+ var redirectUri = new UriBuilder(httpContext.Request.Scheme, httpContext.Request.Host.Host, port, baseUrlPrefix + "/" + _configuration[DefaultRedirectKey]).Uri;
+ var target = uri.MakeRelativeUri(redirectUri).ToString();
+ _logger.LogDebug("Redirecting to {Target}", target);
+
+ httpContext.Response.Redirect(target);
return;
}
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs
index 9e22978aee..bf66f75ff9 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateActivityLogDb.cs
@@ -19,7 +19,7 @@ namespace Jellyfin.Server.Migrations.Routines
private const string DbFilename = "activitylog.db";
private readonly ILogger _logger;
- private readonly JellyfinDbProvider _provider;
+ private readonly IDbContextFactory _provider;
private readonly IServerApplicationPaths _paths;
///
@@ -28,7 +28,7 @@ namespace Jellyfin.Server.Migrations.Routines
/// The logger.
/// The server application paths.
/// The database provider.
- public MigrateActivityLogDb(ILogger logger, IServerApplicationPaths paths, JellyfinDbProvider provider)
+ public MigrateActivityLogDb(ILogger logger, IServerApplicationPaths paths, IDbContextFactory provider)
{
_logger = logger;
_provider = provider;
@@ -68,7 +68,7 @@ namespace Jellyfin.Server.Migrations.Routines
{
using var userDbConnection = SQLite3.Open(Path.Combine(dataPath, "users.db"), ConnectionFlags.ReadOnly, null);
_logger.LogWarning("Migrating the activity database may take a while, do not stop Jellyfin.");
- using var dbContext = _provider.CreateContext();
+ using var dbContext = _provider.CreateDbContext();
var queryResult = connection.Query("SELECT * FROM ActivityLog ORDER BY Id");
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs
index ba0e33585c..bf1ea8233d 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateAuthenticationDb.cs
@@ -6,6 +6,7 @@ using Jellyfin.Data.Entities.Security;
using Jellyfin.Server.Implementations;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
@@ -19,7 +20,7 @@ namespace Jellyfin.Server.Migrations.Routines
private const string DbFilename = "authentication.db";
private readonly ILogger _logger;
- private readonly JellyfinDbProvider _dbProvider;
+ private readonly IDbContextFactory _dbProvider;
private readonly IServerApplicationPaths _appPaths;
private readonly IUserManager _userManager;
@@ -32,7 +33,7 @@ namespace Jellyfin.Server.Migrations.Routines
/// The user manager.
public MigrateAuthenticationDb(
ILogger logger,
- JellyfinDbProvider dbProvider,
+ IDbContextFactory dbProvider,
IServerApplicationPaths appPaths,
IUserManager userManager)
{
@@ -60,7 +61,7 @@ namespace Jellyfin.Server.Migrations.Routines
ConnectionFlags.ReadOnly,
null))
{
- using var dbContext = _dbProvider.CreateContext();
+ using var dbContext = _dbProvider.CreateDbContext();
var authenticatedDevices = connection.Query("SELECT * FROM Tokens");
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
index 74f2349f5f..37716482c3 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateDisplayPreferencesDb.cs
@@ -10,6 +10,7 @@ using Jellyfin.Server.Implementations;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
@@ -24,7 +25,7 @@ namespace Jellyfin.Server.Migrations.Routines
private readonly ILogger _logger;
private readonly IServerApplicationPaths _paths;
- private readonly JellyfinDbProvider _provider;
+ private readonly IDbContextFactory _provider;
private readonly JsonSerializerOptions _jsonOptions;
private readonly IUserManager _userManager;
@@ -38,7 +39,7 @@ namespace Jellyfin.Server.Migrations.Routines
public MigrateDisplayPreferencesDb(
ILogger logger,
IServerApplicationPaths paths,
- JellyfinDbProvider provider,
+ IDbContextFactory provider,
IUserManager userManager)
{
_logger = logger;
@@ -84,7 +85,7 @@ namespace Jellyfin.Server.Migrations.Routines
var dbFilePath = Path.Combine(_paths.DataPath, DbFilename);
using (var connection = SQLite3.Open(dbFilePath, ConnectionFlags.ReadOnly, null))
{
- using var dbContext = _provider.CreateContext();
+ using var dbContext = _provider.CreateDbContext();
var results = connection.Query("SELECT * FROM userdisplaypreferences");
foreach (var result in results)
diff --git a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
index 9b2d603c7f..0c2cc69a7e 100644
--- a/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
+++ b/Jellyfin.Server/Migrations/Routines/MigrateUserDb.cs
@@ -11,6 +11,7 @@ using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users;
+using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using SQLitePCL.pretty;
using JsonSerializer = System.Text.Json.JsonSerializer;
@@ -26,7 +27,7 @@ namespace Jellyfin.Server.Migrations.Routines
private readonly ILogger _logger;
private readonly IServerApplicationPaths _paths;
- private readonly JellyfinDbProvider _provider;
+ private readonly IDbContextFactory _provider;
private readonly IXmlSerializer _xmlSerializer;
///
@@ -39,7 +40,7 @@ namespace Jellyfin.Server.Migrations.Routines
public MigrateUserDb(
ILogger logger,
IServerApplicationPaths paths,
- JellyfinDbProvider provider,
+ IDbContextFactory provider,
IXmlSerializer xmlSerializer)
{
_logger = logger;
@@ -65,7 +66,7 @@ namespace Jellyfin.Server.Migrations.Routines
using (var connection = SQLite3.Open(Path.Combine(dataPath, DbFilename), ConnectionFlags.ReadOnly, null))
{
- var dbContext = _provider.CreateContext();
+ var dbContext = _provider.CreateDbContext();
var queryResult = connection.Query("SELECT * FROM LocalUsersv2");
diff --git a/Jellyfin.Server/Program.cs b/Jellyfin.Server/Program.cs
index a77e790f96..1e63b02041 100644
--- a/Jellyfin.Server/Program.cs
+++ b/Jellyfin.Server/Program.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
@@ -192,6 +193,7 @@ namespace Jellyfin.Server
// Re-use the web host service provider in the app host since ASP.NET doesn't allow a custom service collection.
appHost.ServiceProvider = webHost.Services;
+
await appHost.InitializeServices().ConfigureAwait(false);
Migrations.MigrationRunner.Run(appHost, _loggerFactory);
@@ -236,10 +238,13 @@ namespace Jellyfin.Server
{
_logger.LogInformation("Running query planner optimizations in the database... This might take a while");
// Run before disposing the application
- using var context = appHost.Resolve().CreateContext();
- if (context.Database.IsSqlite())
+ var context = await appHost.ServiceProvider.GetRequiredService>().CreateDbContextAsync().ConfigureAwait(false);
+ await using (context.ConfigureAwait(false))
{
- context.Database.ExecuteSqlRaw("PRAGMA optimize");
+ if (context.Database.IsSqlite())
+ {
+ await context.Database.ExecuteSqlRawAsync("PRAGMA optimize").ConfigureAwait(false);
+ }
}
}
@@ -601,11 +606,14 @@ namespace Jellyfin.Server
catch (Exception ex)
{
Log.Logger = new LoggerConfiguration()
- .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
+ .WriteTo.Console(
+ outputTemplate: "[{Timestamp:HH:mm:ss}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message:lj}{NewLine}{Exception}",
+ formatProvider: CultureInfo.InvariantCulture)
.WriteTo.Async(x => x.File(
Path.Combine(appPaths.LogDirectoryPath, "log_.log"),
rollingInterval: RollingInterval.Day,
outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz}] [{Level:u3}] [{ThreadId}] {SourceContext}: {Message}{NewLine}{Exception}",
+ formatProvider: CultureInfo.InvariantCulture,
encoding: Encoding.UTF8))
.Enrich.FromLogContext()
.Enrich.WithThreadId()
diff --git a/Jellyfin.Server/Startup.cs b/Jellyfin.Server/Startup.cs
index 1954a5c558..49a57aa688 100644
--- a/Jellyfin.Server/Startup.cs
+++ b/Jellyfin.Server/Startup.cs
@@ -9,6 +9,7 @@ using Jellyfin.MediaEncoding.Hls.Extensions;
using Jellyfin.Networking.Configuration;
using Jellyfin.Server.Extensions;
using Jellyfin.Server.Implementations;
+using Jellyfin.Server.Implementations.Extensions;
using Jellyfin.Server.Infrastructure;
using Jellyfin.Server.Middleware;
using MediaBrowser.Common.Net;
@@ -65,7 +66,7 @@ namespace Jellyfin.Server
// TODO remove once this is fixed upstream https://github.com/dotnet/aspnetcore/issues/34371
services.AddSingleton, SymlinkFollowingPhysicalFileResultExecutor>();
services.AddJellyfinApi(_serverApplicationHost.GetApiPluginAssemblies(), _serverConfigurationManager.GetNetworkConfiguration());
-
+ services.AddJellyfinDbContext();
services.AddJellyfinApiSwagger();
// configure custom legacy authentication
diff --git a/MediaBrowser.Common/Providers/ProviderIdParsers.cs b/MediaBrowser.Common/Providers/ProviderIdParsers.cs
index 487b5a6d29..d569167b1d 100644
--- a/MediaBrowser.Common/Providers/ProviderIdParsers.cs
+++ b/MediaBrowser.Common/Providers/ProviderIdParsers.cs
@@ -20,7 +20,7 @@ namespace MediaBrowser.Common.Providers
/// True if parsing was successful, false otherwise.
public static bool TryFindImdbId(ReadOnlySpan text, out ReadOnlySpan imdbId)
{
- // imdb id is at least 9 chars (tt + 7 numbers)
+ // IMDb id is at least 9 chars (tt + 7 numbers)
while (text.Length >= 2 + ImdbMinNumbers)
{
var ttPos = text.IndexOf(ImdbPrefix);
@@ -42,7 +42,7 @@ namespace MediaBrowser.Common.Providers
}
}
- // skip if more than 8 digits + 2 chars for tt
+ // Skip if more than 8 digits + 2 chars for tt
if (i <= ImdbMaxNumbers + 2 && i >= ImdbMinNumbers + 2)
{
imdbId = text.Slice(0, i);
diff --git a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
index d273b54fc6..61539cae56 100644
--- a/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
+++ b/MediaBrowser.Controller/BaseItemManager/BaseItemManager.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.Controller.BaseItemManager
public SemaphoreSlim MetadataRefreshThrottler { get; private set; }
///
- public bool IsMetadataFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name)
+ public bool IsMetadataFetcherEnabled(BaseItem baseItem, TypeOptions? libraryTypeOptions, string name)
{
if (baseItem is Channel)
{
@@ -49,10 +49,9 @@ namespace MediaBrowser.Controller.BaseItemManager
return !baseItem.EnableMediaSourceDisplay;
}
- var typeOptions = libraryOptions.GetTypeOptions(baseItem.GetType().Name);
- if (typeOptions != null)
+ if (libraryTypeOptions != null)
{
- return typeOptions.MetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
+ return libraryTypeOptions.MetadataFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
}
var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase));
@@ -61,7 +60,7 @@ namespace MediaBrowser.Controller.BaseItemManager
}
///
- public bool IsImageFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name)
+ public bool IsImageFetcherEnabled(BaseItem baseItem, TypeOptions? libraryTypeOptions, string name)
{
if (baseItem is Channel)
{
@@ -75,10 +74,9 @@ namespace MediaBrowser.Controller.BaseItemManager
return !baseItem.EnableMediaSourceDisplay;
}
- var typeOptions = libraryOptions.GetTypeOptions(baseItem.GetType().Name);
- if (typeOptions != null)
+ if (libraryTypeOptions != null)
{
- return typeOptions.ImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
+ return libraryTypeOptions.ImageFetchers.Contains(name.AsSpan(), StringComparison.OrdinalIgnoreCase);
}
var itemConfig = _serverConfigurationManager.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, baseItem.GetType().Name, StringComparison.OrdinalIgnoreCase));
diff --git a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
index e18994214e..b07c80879d 100644
--- a/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
+++ b/MediaBrowser.Controller/BaseItemManager/IBaseItemManager.cs
@@ -18,18 +18,18 @@ namespace MediaBrowser.Controller.BaseItemManager
/// Is metadata fetcher enabled.
///
/// The base item.
- /// The library options.
+ /// The type options for baseItem from the library (if defined).
/// The metadata fetcher name.
/// true if metadata fetcher is enabled, else false.
- bool IsMetadataFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name);
+ bool IsMetadataFetcherEnabled(BaseItem baseItem, TypeOptions? libraryTypeOptions, string name);
///
/// Is image fetcher enabled.
///
/// The base item.
- /// The library options.
+ /// The type options for baseItem from the library (if defined).
/// The image fetcher name.
/// true if image fetcher is enabled, else false.
- bool IsImageFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name);
+ bool IsImageFetcherEnabled(BaseItem baseItem, TypeOptions? libraryTypeOptions, string name);
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index bd397bdd13..6555de8554 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
@@ -54,7 +54,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public string AlbumArtist => AlbumArtists.FirstOrDefault();
[JsonIgnore]
- public override bool SupportsPeople => false;
+ public override bool SupportsPeople => true;
///
/// Gets the tracks.
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs
index 41fce67fa7..7f5f9f74bd 100644
--- a/MediaBrowser.Controller/Entities/BaseItem.cs
+++ b/MediaBrowser.Controller/Entities/BaseItem.cs
@@ -75,7 +75,9 @@ namespace MediaBrowser.Controller.Entities
Model.Entities.ExtraType.DeletedScene,
Model.Entities.ExtraType.Interview,
Model.Entities.ExtraType.Sample,
- Model.Entities.ExtraType.Scene
+ Model.Entities.ExtraType.Scene,
+ Model.Entities.ExtraType.Featurette,
+ Model.Entities.ExtraType.Short
};
private string _sortName;
@@ -775,36 +777,6 @@ namespace MediaBrowser.Controller.Entities
return Id.ToString("N", CultureInfo.InvariantCulture);
}
- private List> GetSortChunks(string s1)
- {
- var list = new List>();
-
- int thisMarker = 0;
-
- while (thisMarker < s1.Length)
- {
- char thisCh = s1[thisMarker];
-
- var thisChunk = new StringBuilder();
- bool isNumeric = char.IsDigit(thisCh);
-
- while (thisMarker < s1.Length && char.IsDigit(thisCh) == isNumeric)
- {
- thisChunk.Append(thisCh);
- thisMarker++;
-
- if (thisMarker < s1.Length)
- {
- thisCh = s1[thisMarker];
- }
- }
-
- list.Add(new Tuple(thisChunk, isNumeric));
- }
-
- return list;
- }
-
public virtual bool CanDelete()
{
if (SourceType == SourceType.Channel)
@@ -951,28 +923,40 @@ namespace MediaBrowser.Controller.Entities
return ModifySortChunks(sortable);
}
- private string ModifySortChunks(string name)
+ internal static string ModifySortChunks(string name)
{
- var chunks = GetSortChunks(name);
-
- var builder = new StringBuilder();
-
- foreach (var chunk in chunks)
+ void AppendChunk(StringBuilder builder, bool isDigitChunk, ReadOnlySpan chunk)
{
- var chunkBuilder = chunk.Item1;
-
- // This chunk is numeric
- if (chunk.Item2)
+ if (isDigitChunk && chunk.Length < 10)
{
- while (chunkBuilder.Length < 10)
- {
- chunkBuilder.Insert(0, '0');
- }
+ builder.Append('0', 10 - chunk.Length);
}
- builder.Append(chunkBuilder);
+ builder.Append(chunk);
}
+ if (name.Length == 0)
+ {
+ return string.Empty;
+ }
+
+ var builder = new StringBuilder(name.Length);
+
+ int chunkStart = 0;
+ bool isDigitChunk = char.IsDigit(name[0]);
+ for (int i = 0; i < name.Length; i++)
+ {
+ var isDigit = char.IsDigit(name[i]);
+ if (isDigit != isDigitChunk)
+ {
+ AppendChunk(builder, isDigitChunk, name.AsSpan(chunkStart, i - chunkStart));
+ chunkStart = i;
+ isDigitChunk = isDigit;
+ }
+ }
+
+ AppendChunk(builder, isDigitChunk, name.AsSpan(chunkStart));
+
// logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString());
return builder.ToString().RemoveDiacritics();
}
diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
index 13bfd07c34..1bf5285382 100644
--- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
+++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs
@@ -205,6 +205,16 @@ namespace MediaBrowser.Controller.Entities
public int? MinIndexNumber { get; set; }
+ ///
+ /// Gets or sets the minimum ParentIndexNumber and IndexNumber.
+ ///
+ ///
+ /// It produces this where clause:
+ /// (ParentIndexNumber = X and IndexNumber >= Y) or ParentIndexNumber > X.
+ ///
+ ///
+ public (int ParentIndexNumber, int IndexNumber)? MinParentAndIndexNumber { get; set; }
+
public int? AiredDuringSeason { get; set; }
public double? MinCriticRating { get; set; }
diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs
index 77e70f8fbd..3c12acd90d 100644
--- a/MediaBrowser.Controller/Entities/Movies/Movie.cs
+++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs
@@ -33,9 +33,9 @@ namespace MediaBrowser.Controller.Entities.Movies
.ToArray();
///
- /// Gets or sets the name of the TMDB collection.
+ /// Gets or sets the name of the TMDb collection.
///
- /// The name of the TMDB collection.
+ /// The name of the TMDb collection.
public string TmdbCollectionName { get; set; }
[JsonIgnore]
diff --git a/MediaBrowser.Controller/Library/ILibraryMonitor.cs b/MediaBrowser.Controller/Library/ILibraryMonitor.cs
index 455054bd12..de74aa5a11 100644
--- a/MediaBrowser.Controller/Library/ILibraryMonitor.cs
+++ b/MediaBrowser.Controller/Library/ILibraryMonitor.cs
@@ -34,12 +34,5 @@ namespace MediaBrowser.Controller.Library
///
/// The path.
void ReportFileSystemChanged(string path);
-
- ///
- /// Determines whether [is path locked] [the specified path].
- ///
- /// The path.
- /// true if [is path locked] [the specified path]; otherwise, false.
- bool IsPathLocked(string path);
}
}
diff --git a/MediaBrowser.Controller/Lyrics/ILyricManager.cs b/MediaBrowser.Controller/Lyrics/ILyricManager.cs
new file mode 100644
index 0000000000..bb93e1e4c6
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/ILyricManager.cs
@@ -0,0 +1,24 @@
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+///
+/// Interface ILyricManager.
+///
+public interface ILyricManager
+{
+ ///
+ /// Gets the lyrics.
+ ///
+ /// The media item.
+ /// A task representing found lyrics the passed item.
+ Task GetLyrics(BaseItem item);
+
+ ///
+ /// Checks if requested item has a matching local lyric file.
+ ///
+ /// The media item.
+ /// True if item has a matching lyric file; otherwise false.
+ bool HasLyricFile(BaseItem item);
+}
diff --git a/MediaBrowser.Controller/Lyrics/ILyricProvider.cs b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs
new file mode 100644
index 0000000000..2a04c61520
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/ILyricProvider.cs
@@ -0,0 +1,36 @@
+using System.Collections.Generic;
+using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Resolvers;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+///
+/// Interface ILyricsProvider.
+///
+public interface ILyricProvider
+{
+ ///
+ /// Gets a value indicating the provider name.
+ ///
+ string Name { get; }
+
+ ///
+ /// Gets the priority.
+ ///
+ /// The priority.
+ ResolverPriority Priority { get; }
+
+ ///
+ /// Gets the supported media types for this provider.
+ ///
+ /// The supported media types.
+ IReadOnlyCollection SupportedMediaTypes { get; }
+
+ ///
+ /// Gets the lyrics.
+ ///
+ /// The media item.
+ /// A task representing found lyrics.
+ Task GetLyrics(BaseItem item);
+}
diff --git a/MediaBrowser.Controller/Lyrics/LyricInfo.cs b/MediaBrowser.Controller/Lyrics/LyricInfo.cs
new file mode 100644
index 0000000000..6ec6df5825
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/LyricInfo.cs
@@ -0,0 +1,49 @@
+using System;
+using System.IO;
+using Jellyfin.Extensions;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+///
+/// Lyric helper methods.
+///
+public static class LyricInfo
+{
+ ///
+ /// Gets matching lyric file for a requested item.
+ ///
+ /// The lyricProvider interface to use.
+ /// Path of requested item.
+ /// Lyric file path if passed lyric provider's supported media type is found; otherwise, null.
+ public static string? GetLyricFilePath(this ILyricProvider lyricProvider, string itemPath)
+ {
+ // Ensure we have a provider
+ if (lyricProvider is null)
+ {
+ return null;
+ }
+
+ // Ensure the path to the item is not null
+ string? itemDirectoryPath = Path.GetDirectoryName(itemPath);
+ if (itemDirectoryPath is null)
+ {
+ return null;
+ }
+
+ // Ensure the directory path exists
+ if (!Directory.Exists(itemDirectoryPath))
+ {
+ return null;
+ }
+
+ foreach (var lyricFilePath in Directory.GetFiles(itemDirectoryPath, $"{Path.GetFileNameWithoutExtension(itemPath)}.*"))
+ {
+ if (lyricProvider.SupportedMediaTypes.Contains(Path.GetExtension(lyricFilePath.AsSpan())[1..], StringComparison.OrdinalIgnoreCase))
+ {
+ return lyricFilePath;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/MediaBrowser.Controller/Lyrics/LyricLine.cs b/MediaBrowser.Controller/Lyrics/LyricLine.cs
new file mode 100644
index 0000000000..c406f92fcc
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/LyricLine.cs
@@ -0,0 +1,28 @@
+namespace MediaBrowser.Controller.Lyrics;
+
+///
+/// Lyric model.
+///
+public class LyricLine
+{
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The lyric text.
+ /// The lyric start time in ticks.
+ public LyricLine(string text, long? start = null)
+ {
+ Text = text;
+ Start = start;
+ }
+
+ ///
+ /// Gets the text of this lyric line.
+ ///
+ public string Text { get; }
+
+ ///
+ /// Gets the start time in ticks.
+ ///
+ public long? Start { get; }
+}
diff --git a/MediaBrowser.Controller/Lyrics/LyricMetadata.cs b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs
new file mode 100644
index 0000000000..6091ede52a
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/LyricMetadata.cs
@@ -0,0 +1,54 @@
+using System;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+///
+/// LyricMetadata model.
+///
+public class LyricMetadata
+{
+ ///
+ /// Gets or sets the song artist.
+ ///
+ public string? Artist { get; set; }
+
+ ///
+ /// Gets or sets the album this song is on.
+ ///
+ public string? Album { get; set; }
+
+ ///
+ /// Gets or sets the title of the song.
+ ///
+ public string? Title { get; set; }
+
+ ///
+ /// Gets or sets the author of the lyric data.
+ ///
+ public string? Author { get; set; }
+
+ ///
+ /// Gets or sets the length of the song in ticks.
+ ///
+ public long? Length { get; set; }
+
+ ///
+ /// Gets or sets who the LRC file was created by.
+ ///
+ public string? By { get; set; }
+
+ ///
+ /// Gets or sets the lyric offset compared to audio in ticks.
+ ///
+ public long? Offset { get; set; }
+
+ ///
+ /// Gets or sets the software used to create the LRC file.
+ ///
+ public string? Creator { get; set; }
+
+ ///
+ /// Gets or sets the version of the creator used.
+ ///
+ public string? Version { get; set; }
+}
diff --git a/MediaBrowser.Controller/Lyrics/LyricResponse.cs b/MediaBrowser.Controller/Lyrics/LyricResponse.cs
new file mode 100644
index 0000000000..0d52b5ec50
--- /dev/null
+++ b/MediaBrowser.Controller/Lyrics/LyricResponse.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Controller.Lyrics;
+
+///
+/// LyricResponse model.
+///
+public class LyricResponse
+{
+ ///
+ /// Gets or sets Metadata for the lyrics.
+ ///
+ public LyricMetadata Metadata { get; set; } = new();
+
+ ///
+ /// Gets or sets a collection of individual lyric lines.
+ ///
+ public IReadOnlyList Lyrics { get; set; } = Array.Empty();
+}
diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
index d0362b128c..5e924e4bfb 100644
--- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
+++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs
@@ -31,10 +31,13 @@ namespace MediaBrowser.Controller.MediaEncoding
private const string VideotoolboxAlias = "vt";
private const string OpenclAlias = "ocl";
private const string CudaAlias = "cu";
+ private const string DrmAlias = "dr";
+ private const string VulkanAlias = "vk";
private readonly IApplicationPaths _appPaths;
private readonly IMediaEncoder _mediaEncoder;
private readonly ISubtitleEncoder _subtitleEncoder;
private readonly IConfiguration _config;
+ private readonly Version _minKernelVersionAmdVkFmtModifier = new Version(5, 15);
private readonly Version _minKernelVersioni915Hang = new Version(5, 18);
private static readonly string[] _videoProfilesH264 = new[]
@@ -149,6 +152,14 @@ namespace MediaBrowser.Controller.MediaEncoding
&& _mediaEncoder.SupportsFilter("hwupload_cuda");
}
+ private bool IsVulkanFullSupported()
+ {
+ return _mediaEncoder.SupportsHwaccel("vulkan")
+ && _mediaEncoder.SupportsFilter("libplacebo")
+ && _mediaEncoder.SupportsFilter("scale_vulkan")
+ && _mediaEncoder.SupportsFilterWithOption(FilterOptionType.OverlayVulkanFrameSync);
+ }
+
private bool IsHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
{
if (state.VideoStream == null
@@ -176,6 +187,19 @@ namespace MediaBrowser.Controller.MediaEncoding
|| string.Equals(state.VideoStream.VideoRangeType, "HLG", StringComparison.OrdinalIgnoreCase));
}
+ private bool IsVulkanHwTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
+ {
+ if (state.VideoStream == null)
+ {
+ return false;
+ }
+
+ // libplacebo has partial Dolby Vision to SDR tonemapping support.
+ return options.EnableTonemapping
+ && string.Equals(state.VideoStream.VideoRange, "HDR", StringComparison.OrdinalIgnoreCase)
+ && GetVideoColorBitDepth(state) == 10;
+ }
+
private bool IsVaapiVppTonemapAvailable(EncodingJobInfo state, EncodingOptions options)
{
if (state.VideoStream == null
@@ -756,8 +780,13 @@ namespace MediaBrowser.Controller.MediaEncoding
}
else if (_mediaEncoder.IsVaapiDeviceAmd)
{
- args.Append(GetOpenclDeviceArgs(0, "Advanced Micro Devices", null, OpenclAlias));
- filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
+ if (!IsVulkanFullSupported()
+ || !_mediaEncoder.IsVaapiDeviceSupportVulkanFmtModifier
+ || Environment.OSVersion.Version < _minKernelVersionAmdVkFmtModifier)
+ {
+ args.Append(GetOpenclDeviceArgs(0, "Advanced Micro Devices", null, OpenclAlias));
+ filterDevArgs = GetFilterHwDeviceArgs(OpenclAlias);
+ }
}
else
{
@@ -948,7 +977,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// Disable auto inserted SW scaler for HW decoders in case of changed resolution.
var isSwDecoder = string.IsNullOrEmpty(GetHardwareVideoDecoder(state, options));
- if (!isSwDecoder)
+ if (!isSwDecoder && _mediaEncoder.EncoderVersion >= new Version(4, 4))
{
arg.Append(" -autoscale 0");
}
@@ -1147,24 +1176,6 @@ namespace MediaBrowser.Controller.MediaEncoding
":fontsdir='{0}'",
_mediaEncoder.EscapeSubtitleFilterPath(fontPath));
- // TODO
- // var fallbackFontPath = Path.Combine(_appPaths.ProgramDataPath, "fonts", "DroidSansFallback.ttf");
- // string fallbackFontParam = string.Empty;
-
- // if (!File.Exists(fallbackFontPath))
- // {
- // _fileSystem.CreateDirectory(_fileSystem.GetDirectoryName(fallbackFontPath));
- // using (var stream = _assemblyInfo.GetManifestResourceStream(GetType(), GetType().Namespace + ".DroidSansFallback.ttf"))
- // {
- // using (var fileStream = new FileStream(fallbackFontPath, FileMode.Create, FileAccess.Write, FileShare.Read))
- // {
- // stream.CopyTo(fileStream);
- // }
- // }
- // }
-
- // fallbackFontParam = string.Format(CultureInfo.InvariantCulture, ":force_style='FontName=Droid Sans Fallback':fontsdir='{0}'", _mediaEncoder.EscapeSubtitleFilterPath(_fileSystem.GetDirectoryName(fallbackFontPath)));
-
if (state.SubtitleStream.IsExternal)
{
var charsetParam = string.Empty;
@@ -1192,7 +1203,6 @@ namespace MediaBrowser.Controller.MediaEncoding
alphaParam,
sub2videoParam,
fontParam,
- // fallbackFontParam,
setPtsParam);
}
@@ -1430,7 +1440,11 @@ namespace MediaBrowser.Controller.MediaEncoding
param += " -preset 7";
}
- param += " -look_ahead 0";
+ // Only h264_qsv has look_ahead option
+ if (string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase))
+ {
+ param += " -look_ahead 0";
+ }
}
else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase) // h264 (h264_nvenc)
|| string.Equals(videoEncoder, "hevc_nvenc", StringComparison.OrdinalIgnoreCase)) // hevc (hevc_nvenc)
@@ -1468,7 +1482,7 @@ namespace MediaBrowser.Controller.MediaEncoding
break;
default:
- param += " -preset p4";
+ param += " -preset p1";
break;
}
}
@@ -2774,22 +2788,41 @@ namespace MediaBrowser.Controller.MediaEncoding
return string.Empty;
}
- var args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709";
+ var args = string.Empty;
+ var algorithm = options.TonemappingAlgorithm;
- if (hwTonemapSuffix.Contains("vaapi", StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(hwTonemapSuffix, "vaapi", StringComparison.OrdinalIgnoreCase))
{
- args += ",procamp_vaapi=b={2}:c={3}:extra_hw_frames=16";
+ args = "tonemap_vaapi=format={0}:p=bt709:t=bt709:m=bt709,procamp_vaapi=b={1}:c={2}:extra_hw_frames=16";
return string.Format(
CultureInfo.InvariantCulture,
args,
- hwTonemapSuffix,
videoFormat ?? "nv12",
options.VppTonemappingBrightness,
options.VppTonemappingContrast);
}
+ else if (string.Equals(hwTonemapSuffix, "vulkan", StringComparison.OrdinalIgnoreCase))
+ {
+ args = "libplacebo=format={1}:tonemapping={2}:color_primaries=bt709:color_trc=bt709:colorspace=bt709:peak_detect=0:upscaler=none:downscaler=none";
+
+ if (!string.Equals(options.TonemappingRange, "auto", StringComparison.OrdinalIgnoreCase))
+ {
+ args += ":range={6}";
+ }
+
+ if (string.Equals(options.TonemappingAlgorithm, "bt2390", StringComparison.OrdinalIgnoreCase))
+ {
+ algorithm = "bt.2390";
+ }
+
+ else if (string.Equals(options.TonemappingAlgorithm, "none", StringComparison.OrdinalIgnoreCase))
+ {
+ algorithm = "clip";
+ }
+ }
else
{
- args += ":tonemap={2}:peak={3}:desat={4}";
+ args = "tonemap_{0}=format={1}:p=bt709:t=bt709:m=bt709:tonemap={2}:peak={3}:desat={4}";
if (options.TonemappingParam != 0)
{
@@ -2807,7 +2840,7 @@ namespace MediaBrowser.Controller.MediaEncoding
args,
hwTonemapSuffix,
videoFormat ?? "nv12",
- options.TonemappingAlgorithm,
+ algorithm,
options.TonemappingPeak,
options.TonemappingDesat,
options.TonemappingParam,
@@ -3419,6 +3452,12 @@ namespace MediaBrowser.Controller.MediaEncoding
// map from d3d11va to qsv.
mainFilters.Add("hwmap=derive_device=qsv");
}
+ else
+ {
+ // Insert a qsv scaler to sync the decoder surface,
+ // msdk will passthrough this internally.
+ mainFilters.Add("hwmap=derive_device=qsv,scale_qsv");
+ }
}
// hw deint
@@ -3770,7 +3809,9 @@ namespace MediaBrowser.Controller.MediaEncoding
var vidDecoder = GetHardwareVideoDecoder(state, options) ?? string.Empty;
var isSwDecoder = string.IsNullOrEmpty(vidDecoder);
var isSwEncoder = !vidEncoder.Contains("vaapi", StringComparison.OrdinalIgnoreCase);
- var isVaapiOclSupported = isLinux && IsVaapiSupported(state) && IsVaapiFullSupported() && IsOpenclFullSupported();
+ var isVaapiFullSupported = isLinux && IsVaapiSupported(state) && IsVaapiFullSupported();
+ var isVaapiOclSupported = isVaapiFullSupported && IsOpenclFullSupported();
+ var isVaapiVkSupported = isVaapiFullSupported && IsVulkanFullSupported();
// legacy vaapi pipeline(copy-back)
if ((isSwDecoder && isSwEncoder)
@@ -3798,14 +3839,24 @@ namespace MediaBrowser.Controller.MediaEncoding
if (_mediaEncoder.IsVaapiDeviceInteliHD)
{
// Intel iHD path, with extra vpp tonemap and overlay support.
- return GetVaapiFullVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
+ return GetIntelVaapiFullVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
}
- // Intel i965 and Amd radeonsi/r600 path, only featuring scale and deinterlace support.
+ // prefered vaapi + vulkan filters pipeline
+ if (_mediaEncoder.IsVaapiDeviceAmd
+ && isVaapiVkSupported
+ && _mediaEncoder.IsVaapiDeviceSupportVulkanFmtModifier
+ && Environment.OSVersion.Version >= _minKernelVersionAmdVkFmtModifier)
+ {
+ // AMD radeonsi path(Vega/gfx9+, kernel>=5.15), with extra vulkan tonemap and overlay support.
+ return GetAmdVaapiFullVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
+ }
+
+ // Intel i965 and Amd radeonsi/r600 path(Polaris/gfx8-), only featuring scale and deinterlace support.
return GetVaapiLimitedVidFiltersPrefered(state, options, vidDecoder, vidEncoder);
}
- public (List MainFilters, List SubFilters, List OverlayFilters) GetVaapiFullVidFiltersPrefered(
+ public (List MainFilters, List SubFilters, List OverlayFilters) GetIntelVaapiFullVidFiltersPrefered(
EncodingJobInfo state,
EncodingOptions options,
string vidDecoder,
@@ -4003,6 +4054,203 @@ namespace MediaBrowser.Controller.MediaEncoding
return (mainFilters, subFilters, overlayFilters);
}
+ public (List MainFilters, List SubFilters, List