Merge branch 'jellyfin:master' into master

This commit is contained in:
Negulici-R. Barnabas 2022-11-13 15:29:16 +02:00 committed by GitHub
commit b7aa5ed862
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
427 changed files with 5456 additions and 10225 deletions

View File

@ -26,6 +26,8 @@ jobs:
BuildConfiguration: linux.amd64-musl
Linux.arm64:
BuildConfiguration: linux.arm64
Linux.musl-linux-arm64:
BuildConfiguration: linux.musl-linux-arm64
Linux.armhf:
BuildConfiguration: linux.armhf
Windows.amd64:

View File

@ -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

6
.github/renovate.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"github>jellyfin/.github//renovate-presets/dotnet"
]
}

View File

@ -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:

View File

@ -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@v2
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@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # tag=v2
with:
languages: ${{ matrix.language }}
queries: +security-extended
- name: Autobuild
uses: github/codeql-action/autobuild@v2
uses: github/codeql-action/autobuild@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # tag=v2
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
uses: github/codeql-action/analyze@c3b6fce4ee2ca25bc1066aa3bf73962fda0e8898 # tag=v2

View File

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

View File

@ -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@v2
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@v2
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@b657a70ff16d17651703a84bee1cb9ad9d2be2ea # tag=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:
</details>
- 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 }}

View File

@ -10,7 +10,7 @@ jobs:
runs-on: ubuntu-latest
if: ${{ contains(github.repository, 'jellyfin/') }}
steps:
- uses: actions/stale@v5
- uses: actions/stale@5ebf00ea0e4c1561e9b43a292ed34424fb1d4578 # tag=v6
with:
repo-token: ${{ secrets.JF_BOT_TOKEN }}
days-before-stale: 120

View File

@ -36,6 +36,7 @@
- [dmitrylyzo](https://github.com/dmitrylyzo)
- [DMouse10462](https://github.com/DMouse10462)
- [DrPandemic](https://github.com/DrPandemic)
- [eglia](https://github.com/eglia)
- [EraYaN](https://github.com/EraYaN)
- [escabe](https://github.com/escabe)
- [excelite](https://github.com/excelite)
@ -147,6 +148,7 @@
- [xosdy](https://github.com/xosdy)
- [XVicarious](https://github.com/XVicarious)
- [YouKnowBlom](https://github.com/YouKnowBlom)
- [ZachPhelan](https://github.com/ZachPhelan)
- [KristupasSavickas](https://github.com/KristupasSavickas)
- [Pusta](https://github.com/pusta)
- [nielsvanvelzen](https://github.com/nielsvanvelzen)

View File

@ -31,7 +31,7 @@ ARG LEVEL_ZERO_VERSION=1.3.22549
# mesa-va-drivers: needed for AMD VAAPI. Mesa >= 20.1 is required for HEVC transcoding.
# curl: healthcheck
RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget apt-transport-https curl \
&& apt-get install --no-install-recommends --no-install-suggests -y ca-certificates gnupg wget curl \
&& wget -O - https://repo.jellyfin.org/jellyfin_team.gpg.key | apt-key add - \
&& echo "deb [arch=$( dpkg --print-architecture )] https://repo.jellyfin.org/$( awk -F'=' '/^ID=/{ print $NF }' /etc/os-release ) $( awk -F'=' '/^VERSION_CODENAME=/{ print $NF }' /etc/os-release ) main" | tee /etc/apt/sources.list.d/jellyfin.list \
&& apt-get update \
@ -53,7 +53,7 @@ RUN apt-get update \
&& dpkg -i *.deb \
&& cd .. \
&& rm -rf intel-compute-runtime \
&& apt-get remove gnupg wget apt-transport-https -y \
&& apt-get remove gnupg wget -y \
&& apt-get clean autoclean -y \
&& apt-get autoremove -y \
&& rm -rf /var/lib/apt/lists/* \
@ -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

View File

@ -38,9 +38,6 @@ RUN apt-get update \
libssl-dev \
libfontconfig1 \
libfreetype6 \
libomxil-bellagio0 \
libomxil-bellagio-bin \
libraspberrypi0 \
vainfo \
libva2 \
locales \
@ -81,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

View File

@ -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

View File

@ -103,10 +103,7 @@ namespace Emby.Dlna.ContentDirectory
/// <inheritdoc />
public Task<ControlResponse> ProcessControlRequestAsync(ControlRequest request)
{
if (request == null)
{
throw new ArgumentNullException(nameof(request));
}
ArgumentNullException.ThrowIfNull(request);
var profile = _dlna.GetProfile(request.Headers) ?? _dlna.GetDefaultProfile();

View File

@ -114,15 +114,9 @@ namespace Emby.Dlna.ContentDirectory
/// <inheritdoc />
protected override void WriteResult(string methodName, IReadOnlyDictionary<string, string> methodParams, XmlWriter xmlWriter)
{
if (xmlWriter == null)
{
throw new ArgumentNullException(nameof(xmlWriter));
}
ArgumentNullException.ThrowIfNull(xmlWriter);
if (methodParams == null)
{
throw new ArgumentNullException(nameof(methodParams));
}
ArgumentNullException.ThrowIfNull(methodParams);
const string DeviceId = "test";

View File

@ -446,7 +446,7 @@ namespace Emby.Dlna.Didl
/// </summary>
/// <remarks>
/// If context is a season, this will return a string containing just episode number and name.
/// Otherwise the result will include series nams and season number.
/// Otherwise the result will include series names and season number.
/// </remarks>
/// <param name="episode">The episode.</param>
/// <param name="context">Current context.</param>

View File

@ -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)
@ -100,10 +102,7 @@ namespace Emby.Dlna
/// <inheritdoc />
public DeviceProfile? GetProfile(DeviceIdentification deviceInfo)
{
if (deviceInfo == null)
{
throw new ArgumentNullException(nameof(deviceInfo));
}
ArgumentNullException.ThrowIfNull(deviceInfo);
var profile = GetProfiles()
.FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification));
@ -123,7 +122,7 @@ namespace Emby.Dlna
/// <summary>
/// Attempts to match a device with a profile.
/// Rules:
/// - If the profile field has no value, the field matches irregardless of its contents.
/// - If the profile field has no value, the field matches regardless of its contents.
/// - the profile field can be an exact match, or a reg exp.
/// </summary>
/// <param name="deviceInfo">The <see cref="DeviceIdentification"/> of the device.</param>
@ -170,10 +169,7 @@ namespace Emby.Dlna
/// <inheritdoc />
public DeviceProfile? GetProfile(IHeaderDictionary headers)
{
if (headers == null)
{
throw new ArgumentNullException(nameof(headers));
}
ArgumentNullException.ThrowIfNull(headers);
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
if (profile == null)
@ -328,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);
}
/// <inheritdoc />
@ -406,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);
@ -496,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()
{
}
}*/
}

View File

@ -127,8 +127,7 @@ namespace Emby.Dlna.Eventing
public Task TriggerEvent(string notificationType, IDictionary<string, string> 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));

View File

@ -16,7 +16,7 @@ namespace Emby.Dlna
/// </summary>
/// <param name="subscriptionId">The subscription identifier.</param>
/// <param name="notificationType">The notification type.</param>
/// <param name="requestedTimeoutString">The requested timeout as a sting.</param>
/// <param name="requestedTimeoutString">The requested timeout as a string.</param>
/// <param name="callbackUrl">The callback url.</param>
/// <returns>The response.</returns>
EventSubscriptionResponse RenewEventSubscription(string subscriptionId, string notificationType, string requestedTimeoutString, string callbackUrl);
@ -25,7 +25,7 @@ namespace Emby.Dlna
/// Creates the event subscription.
/// </summary>
/// <param name="notificationType">The notification type.</param>
/// <param name="requestedTimeoutString">The requested timeout as a sting.</param>
/// <param name="requestedTimeoutString">The requested timeout as a string.</param>
/// <param name="callbackUrl">The callback url.</param>
/// <returns>The response.</returns>
EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl);

View File

@ -235,7 +235,7 @@ namespace Emby.Dlna.PlayTo
_logger.LogDebug("Setting mute");
var value = mute ? 1 : 0;
await new SsdpHttpClient(_httpClientFactory)
await new DlnaHttpClient(_logger, _httpClientFactory)
.SendCommandAsync(
Properties.BaseUrl,
service,
@ -276,7 +276,7 @@ namespace Emby.Dlna.PlayTo
// Remote control will perform better
Volume = value;
await new SsdpHttpClient(_httpClientFactory)
await new DlnaHttpClient(_logger, _httpClientFactory)
.SendCommandAsync(
Properties.BaseUrl,
service,
@ -303,7 +303,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
await new SsdpHttpClient(_httpClientFactory)
await new DlnaHttpClient(_logger, _httpClientFactory)
.SendCommandAsync(
Properties.BaseUrl,
service,
@ -343,7 +343,7 @@ namespace Emby.Dlna.PlayTo
}
var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
await new SsdpHttpClient(_httpClientFactory)
await new DlnaHttpClient(_logger, _httpClientFactory)
.SendCommandAsync(
Properties.BaseUrl,
service,
@ -400,7 +400,8 @@ namespace Emby.Dlna.PlayTo
}
var post = avCommands.BuildPost(command, service.ServiceType, url, dictionary);
await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header: header, cancellationToken)
await new DlnaHttpClient(_logger, _httpClientFactory)
.SendCommandAsync(Properties.BaseUrl, service, command.Name, post, header, cancellationToken)
.ConfigureAwait(false);
}
@ -428,7 +429,7 @@ namespace Emby.Dlna.PlayTo
throw new InvalidOperationException("Unable to find service");
}
return new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
return new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@ -461,7 +462,7 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService();
await new SsdpHttpClient(_httpClientFactory)
await new DlnaHttpClient(_logger, _httpClientFactory)
.SendCommandAsync(
Properties.BaseUrl,
service,
@ -485,7 +486,7 @@ namespace Emby.Dlna.PlayTo
var service = GetAvTransportService();
await new SsdpHttpClient(_httpClientFactory)
await new DlnaHttpClient(_logger, _httpClientFactory)
.SendCommandAsync(
Properties.BaseUrl,
service,
@ -618,7 +619,7 @@ namespace Emby.Dlna.PlayTo
return;
}
var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@ -668,7 +669,7 @@ namespace Emby.Dlna.PlayTo
return;
}
var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@ -701,7 +702,7 @@ namespace Emby.Dlna.PlayTo
return null;
}
var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@ -747,7 +748,7 @@ namespace Emby.Dlna.PlayTo
return null;
}
var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@ -819,7 +820,7 @@ namespace Emby.Dlna.PlayTo
return (false, null);
}
var result = await new SsdpHttpClient(_httpClientFactory).SendCommandAsync(
var result = await new DlnaHttpClient(_logger, _httpClientFactory).SendCommandAsync(
Properties.BaseUrl,
service,
command.Name,
@ -930,10 +931,7 @@ namespace Emby.Dlna.PlayTo
private static UBaseObject CreateUBaseObject(XElement container, string trackUri)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
ArgumentNullException.ThrowIfNull(container);
var url = container.GetValue(UPnpNamespaces.Res);
@ -957,10 +955,7 @@ namespace Emby.Dlna.PlayTo
private static string[] GetProtocolInfo(XElement container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
ArgumentNullException.ThrowIfNull(container);
var resElement = container.Element(UPnpNamespaces.Res);
@ -997,7 +992,7 @@ namespace Emby.Dlna.PlayTo
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClientFactory);
var httpClient = new DlnaHttpClient(_logger, _httpClientFactory);
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
if (document == null)
@ -1029,7 +1024,7 @@ namespace Emby.Dlna.PlayTo
string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl);
var httpClient = new SsdpHttpClient(_httpClientFactory);
var httpClient = new DlnaHttpClient(_logger, _httpClientFactory);
_logger.LogDebug("Dlna Device.GetRenderingProtocolAsync");
var document = await httpClient.GetDataAsync(url, cancellationToken).ConfigureAwait(false);
if (document == null)
@ -1064,7 +1059,7 @@ namespace Emby.Dlna.PlayTo
public static async Task<Device> CreateuPnpDeviceAsync(Uri url, IHttpClientFactory httpClientFactory, ILogger logger, CancellationToken cancellationToken)
{
var ssdpHttpClient = new SsdpHttpClient(httpClientFactory);
var ssdpHttpClient = new DlnaHttpClient(logger, httpClientFactory);
var document = await ssdpHttpClient.GetDataAsync(url.ToString(), cancellationToken).ConfigureAwait(false);
if (document == null)
@ -1182,10 +1177,7 @@ namespace Emby.Dlna.PlayTo
#nullable enable
private static DeviceIcon CreateIcon(XElement element)
{
if (element == null)
{
throw new ArgumentNullException(nameof(element));
}
ArgumentNullException.ThrowIfNull(element);
var width = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("width"));
var height = element.GetDescendantValue(UPnpNamespaces.Ud.GetName("height"));

View File

@ -0,0 +1,108 @@
#pragma warning disable CS1591
using System;
using System.Globalization;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Linq;
using Emby.Dlna.Common;
using MediaBrowser.Common.Net;
using Microsoft.Extensions.Logging;
namespace Emby.Dlna.PlayTo
{
public class DlnaHttpClient
{
private readonly ILogger _logger;
private readonly IHttpClientFactory _httpClientFactory;
public DlnaHttpClient(ILogger logger, IHttpClientFactory httpClientFactory)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
}
private static string NormalizeServiceUrl(string baseUrl, string serviceUrl)
{
// If it's already a complete url, don't stick anything onto the front of it
if (serviceUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
return serviceUrl;
}
if (!serviceUrl.StartsWith('/'))
{
serviceUrl = "/" + serviceUrl;
}
return baseUrl + serviceUrl;
}
private async Task<XDocument?> SendRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
using var response = await _httpClientFactory.CreateClient(NamedClient.Dlna).SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
try
{
return await XDocument.LoadAsync(
stream,
LoadOptions.None,
cancellationToken).ConfigureAwait(false);
}
catch (XmlException ex)
{
_logger.LogError(ex, "Failed to parse response");
if (_logger.IsEnabled(LogLevel.Debug))
{
_logger.LogDebug("Malformed response: {Content}\n", await response.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false));
}
return null;
}
}
public async Task<XDocument?> GetDataAsync(string url, CancellationToken cancellationToken)
{
using var request = new HttpRequestMessage(HttpMethod.Get, url);
// Have to await here instead of returning the Task directly, otherwise request would be disposed too soon
return await SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
}
public async Task<XDocument?> SendCommandAsync(
string baseUrl,
DeviceService service,
string command,
string postData,
string? header = null,
CancellationToken cancellationToken = default)
{
using var request = new HttpRequestMessage(HttpMethod.Post, NormalizeServiceUrl(baseUrl, service.ControlUrl))
{
Content = new StringContent(postData, Encoding.UTF8, MediaTypeNames.Text.Xml)
};
request.Headers.TryAddWithoutValidation(
"SOAPACTION",
string.Format(
CultureInfo.InvariantCulture,
"\"{0}#{1}\"",
service.ServiceType,
command));
request.Headers.Pragma.ParseAdd("no-cache");
if (!string.IsNullOrEmpty(header))
{
request.Headers.TryAddWithoutValidation("contentFeatures.dlna.org", header);
}
// Have to await here instead of returning the Task directly, otherwise request would be disposed too soon
return await SendRequestAsync(request, cancellationToken).ConfigureAwait(false);
}
}
}

View File

@ -1,141 +0,0 @@
#nullable disable
#pragma warning disable CS1591
using System;
using System.Globalization;
using System.Net.Http;
using System.Net.Mime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Emby.Dlna.Common;
using MediaBrowser.Common.Net;
namespace Emby.Dlna.PlayTo
{
public class SsdpHttpClient
{
private const string USERAGENT = "Microsoft-Windows/6.2 UPnP/1.0 Microsoft-DLNA DLNADOC/1.50";
private const string FriendlyName = "Jellyfin";
private readonly IHttpClientFactory _httpClientFactory;
public SsdpHttpClient(IHttpClientFactory httpClientFactory)
{
_httpClientFactory = httpClientFactory;
}
public async Task<XDocument> SendCommandAsync(
string baseUrl,
DeviceService service,
string command,
string postData,
string header = null,
CancellationToken cancellationToken = default)
{
var url = NormalizeServiceUrl(baseUrl, service.ControlUrl);
using var response = await PostSoapDataAsync(
url,
$"\"{service.ServiceType}#{command}\"",
postData,
header,
cancellationToken)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
return await XDocument.LoadAsync(
stream,
LoadOptions.None,
cancellationToken).ConfigureAwait(false);
}
private static string NormalizeServiceUrl(string baseUrl, string serviceUrl)
{
// If it's already a complete url, don't stick anything onto the front of it
if (serviceUrl.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
return serviceUrl;
}
if (!serviceUrl.StartsWith('/'))
{
serviceUrl = "/" + serviceUrl;
}
return baseUrl + serviceUrl;
}
public async Task SubscribeAsync(
string url,
string ip,
int port,
string localIp,
int eventport,
int timeOut = 3600)
{
using var options = new HttpRequestMessage(new HttpMethod("SUBSCRIBE"), url);
options.Headers.UserAgent.ParseAdd(USERAGENT);
options.Headers.TryAddWithoutValidation("HOST", ip + ":" + port.ToString(CultureInfo.InvariantCulture));
options.Headers.TryAddWithoutValidation("CALLBACK", "<" + localIp + ":" + eventport.ToString(CultureInfo.InvariantCulture) + ">");
options.Headers.TryAddWithoutValidation("NT", "upnp:event");
options.Headers.TryAddWithoutValidation("TIMEOUT", "Second-" + timeOut.ToString(CultureInfo.InvariantCulture));
using var response = await _httpClientFactory.CreateClient(NamedClient.Default)
.SendAsync(options, HttpCompletionOption.ResponseHeadersRead)
.ConfigureAwait(false);
response.EnsureSuccessStatusCode();
}
public async Task<XDocument> GetDataAsync(string url, CancellationToken cancellationToken)
{
using var options = new HttpRequestMessage(HttpMethod.Get, url);
options.Headers.UserAgent.ParseAdd(USERAGENT);
options.Headers.TryAddWithoutValidation("FriendlyName.DLNA.ORG", FriendlyName);
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
try
{
return await XDocument.LoadAsync(
stream,
LoadOptions.None,
cancellationToken).ConfigureAwait(false);
}
catch
{
return null;
}
}
private async Task<HttpResponseMessage> PostSoapDataAsync(
string url,
string soapAction,
string postData,
string header,
CancellationToken cancellationToken)
{
if (soapAction[0] != '\"')
{
soapAction = $"\"{soapAction}\"";
}
using var options = new HttpRequestMessage(HttpMethod.Post, url);
options.Headers.UserAgent.ParseAdd(USERAGENT);
options.Headers.TryAddWithoutValidation("SOAPACTION", soapAction);
options.Headers.TryAddWithoutValidation("Pragma", "no-cache");
options.Headers.TryAddWithoutValidation("FriendlyName.DLNA.ORG", FriendlyName);
if (!string.IsNullOrEmpty(header))
{
options.Headers.TryAddWithoutValidation("contentFeatures.dlna.org", header);
}
options.Content = new StringContent(postData, Encoding.UTF8, MediaTypeNames.Text.Xml);
return await _httpClientFactory.CreateClient(NamedClient.Default).SendAsync(options, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
}
}
}

View File

@ -61,10 +61,7 @@ namespace Emby.Dlna.PlayTo
private static Argument ArgumentFromXml(XElement container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
ArgumentNullException.ThrowIfNull(container);
return new Argument
{

View File

@ -10,10 +10,7 @@ namespace Emby.Dlna.PlayTo
{
public static UBaseObject Create(XElement container)
{
if (container == null)
{
throw new ArgumentNullException(nameof(container));
}
ArgumentNullException.ThrowIfNull(container);
return new UBaseObject
{

View File

@ -54,10 +54,7 @@ namespace Emby.Dlna.PlayTo
public bool Equals(UBaseObject obj)
{
if (obj == null)
{
throw new ArgumentNullException(nameof(obj));
}
ArgumentNullException.ThrowIfNull(obj);
return string.Equals(Id, obj.Id, StringComparison.Ordinal);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,7 @@ namespace Emby.Naming.AudioBook
/// <summary>
/// Data object for passing result of audiobook part/chapter extraction.
/// </summary>
public struct AudioBookFilePathParserResult
public record struct AudioBookFilePathParserResult
{
/// <summary>
/// Gets or sets optional number of path extracted from audiobook filename.

View File

@ -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<AudioBookFileInfo>()
.ToList();
.OfType<AudioBookFileInfo>();
var stackResult = StackResolver.ResolveAudioBooks(audiobookFileInfos);

View File

@ -153,7 +153,7 @@ namespace Emby.Naming.Common
CleanStrings = new[]
{
@"^\s*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](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*(?<cleaned>.+?)[ _\,\.\(\)\[\]\-](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|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
@"^(?<cleaned>.+?)(\[.*\])",
@"^\s*(?<cleaned>.+?)\WE[0-9]+(-|~)E?[0-9]+(\W|$)",
@"^\s*\[[^\]]+\](?!\.\w+$)\s*(?<cleaned>.+)",
@ -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
/// </summary>
public string[] MediaDefaultFlags { get; set; }
/// <summary>
/// Gets or sets list of external media hearing impaired flags.
/// </summary>
public string[] MediaHearingImpairedFlags { get; set; }
/// <summary>
/// Gets or sets list of album stacking prefixes.
/// </summary>
public string[] AlbumStackingPrefixes { get; set; }
/// <summary>
/// Gets or sets list of artist subfolders.
/// </summary>
public string[] ArtistSubfolders { get; set; }
/// <summary>
/// Gets or sets list of subtitle file extensions.
/// </summary>

View File

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

View File

@ -11,11 +11,13 @@ namespace Emby.Naming.ExternalFiles
/// <param name="path">Path to file.</param>
/// <param name="isDefault">Is default.</param>
/// <param name="isForced">Is forced.</param>
public ExternalPathParserResult(string path, bool isDefault = false, bool isForced = false)
/// <param name="isHearingImpaired">For the hearing impaired.</param>
public ExternalPathParserResult(string path, bool isDefault = false, bool isForced = false, bool isHearingImpaired = false)
{
Path = path;
IsDefault = isDefault;
IsForced = isForced;
IsHearingImpaired = isHearingImpaired;
}
/// <summary>
@ -47,5 +49,11 @@ namespace Emby.Naming.ExternalFiles
/// </summary>
/// <value><c>true</c> if this instance is forced; otherwise, <c>false</c>.</value>
public bool IsForced { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is for the hearing impaired.
/// </summary>
/// <value><c>true</c> if this instance is for the hearing impaired; otherwise, <c>false</c>.</value>
public bool IsHearingImpaired { get; set; }
}
}

View File

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

View File

@ -210,10 +210,7 @@ namespace Emby.Server.Implementations.AppBase
/// <exception cref="ArgumentNullException"><c>newConfiguration</c> is <c>null</c>.</exception>
public virtual void ReplaceConfiguration(BaseApplicationConfiguration newConfiguration)
{
if (newConfiguration == null)
{
throw new ArgumentNullException(nameof(newConfiguration));
}
ArgumentNullException.ThrowIfNull(newConfiguration);
ValidateCachePath(newConfiguration);
@ -365,11 +362,7 @@ namespace Emby.Server.Implementations.AppBase
validatingStore.Validate(currentConfiguration, configuration);
}
NamedConfigurationUpdating?.Invoke(this, new ConfigurationUpdateEventArgs
{
Key = key,
NewConfiguration = configuration
});
NamedConfigurationUpdating?.Invoke(this, new ConfigurationUpdateEventArgs(key, configuration));
_configurations.AddOrUpdate(key, configuration, (_, _) => configuration);
@ -391,11 +384,7 @@ namespace Emby.Server.Implementations.AppBase
/// <param name="configuration">The old configuration.</param>
protected virtual void OnNamedConfigurationUpdated(string key, object configuration)
{
NamedConfigurationUpdated?.Invoke(this, new ConfigurationUpdateEventArgs
{
Key = key,
NewConfiguration = configuration
});
NamedConfigurationUpdated?.Invoke(this, new ConfigurationUpdateEventArgs(key, configuration));
}
/// <inheritdoc />

View File

@ -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;
@ -67,6 +66,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,6 +94,7 @@ 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;
@ -559,8 +560,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IInstallationManager, InstallationManager>();
serviceCollection.AddSingleton<IZipClient, ZipClient>();
serviceCollection.AddSingleton<IServerApplicationHost>(this);
serviceCollection.AddSingleton(ApplicationPaths);
@ -598,6 +597,7 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
serviceCollection.AddSingleton<ILyricManager, LyricManager>();
serviceCollection.AddSingleton<IProviderManager, ProviderManager>();
@ -630,8 +630,6 @@ namespace Emby.Server.Implementations
serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
serviceCollection.AddScoped<ISessionContext, SessionContext>();
serviceCollection.AddSingleton<IAuthService, AuthService>();
serviceCollection.AddSingleton<IQuickConnect, QuickConnectManager>();
@ -1090,15 +1088,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);
}
/// <inheritdoc/>

View File

@ -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
{
/// <summary>
/// Class DotNetZipClient.
/// </summary>
public class ZipClient : IZipClient
{
/// <inheritdoc />
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);
}
/// <inheritdoc />
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));
}
}
}
}

View File

@ -1188,10 +1188,7 @@ namespace Emby.Server.Implementations.Channels
internal IChannel GetChannelProvider(Channel channel)
{
if (channel == null)
{
throw new ArgumentNullException(nameof(channel));
}
ArgumentNullException.ThrowIfNull(channel);
var result = GetAllChannels()
.FirstOrDefault(i => GetInternalChannelId(i.Name).Equals(channel.ChannelId) || string.Equals(i.Name, channel.Name, StringComparison.OrdinalIgnoreCase));

View File

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

View File

@ -54,10 +54,7 @@ namespace Emby.Server.Implementations.Data
public static void RunQueries(this SQLiteDatabaseConnection connection, string[] queries)
{
if (queries == null)
{
throw new ArgumentNullException(nameof(queries));
}
ArgumentNullException.ThrowIfNull(queries);
connection.RunInTransaction(conn =>
{

View File

@ -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);
@ -583,10 +587,7 @@ namespace Emby.Server.Implementations.Data
public void SaveImages(BaseItem item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
ArgumentNullException.ThrowIfNull(item);
CheckDisposed();
@ -617,10 +618,7 @@ namespace Emby.Server.Implementations.Data
/// </exception>
public void SaveItems(IEnumerable<BaseItem> items, CancellationToken cancellationToken)
{
if (items == null)
{
throw new ArgumentNullException(nameof(items));
}
ArgumentNullException.ThrowIfNull(items);
cancellationToken.ThrowIfCancellationRequested();
@ -2085,10 +2083,7 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(id));
}
if (chapters == null)
{
throw new ArgumentNullException(nameof(chapters));
}
ArgumentNullException.ThrowIfNull(chapters);
var idBlob = id.ToByteArray();
@ -2557,10 +2552,7 @@ namespace Emby.Server.Implementations.Data
public int GetCount(InternalItemsQuery query)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
ArgumentNullException.ThrowIfNull(query);
CheckDisposed();
@ -2613,10 +2605,7 @@ namespace Emby.Server.Implementations.Data
public List<BaseItem> GetItemList(InternalItemsQuery query)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
ArgumentNullException.ThrowIfNull(query);
CheckDisposed();
@ -2794,10 +2783,7 @@ namespace Emby.Server.Implementations.Data
public QueryResult<BaseItem> GetItems(InternalItemsQuery query)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
ArgumentNullException.ThrowIfNull(query);
CheckDisposed();
@ -3174,10 +3160,7 @@ namespace Emby.Server.Implementations.Data
public List<Guid> GetItemIdsList(InternalItemsQuery query)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
ArgumentNullException.ThrowIfNull(query);
CheckDisposed();
@ -3541,6 +3524,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");
@ -4837,10 +4827,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
public List<string> GetPeopleNames(InternalPeopleQuery query)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
ArgumentNullException.ThrowIfNull(query);
CheckDisposed();
@ -4880,10 +4867,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
public List<PersonInfo> GetPeople(InternalPeopleQuery query)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
ArgumentNullException.ThrowIfNull(query);
CheckDisposed();
@ -4999,10 +4983,7 @@ AND Type = @InternalPersonType)");
throw new ArgumentNullException(nameof(itemId));
}
if (ancestorIds == null)
{
throw new ArgumentNullException(nameof(ancestorIds));
}
ArgumentNullException.ThrowIfNull(ancestorIds);
CheckDisposed();
@ -5175,10 +5156,7 @@ AND Type = @InternalPersonType)");
private QueryResult<(BaseItem Item, ItemCounts ItemCounts)> GetItemValues(InternalItemsQuery query, int[] itemValueTypes, string returnType)
{
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
ArgumentNullException.ThrowIfNull(query);
if (!query.Limit.HasValue)
{
@ -5531,10 +5509,7 @@ AND Type = @InternalPersonType)");
throw new ArgumentNullException(nameof(itemId));
}
if (values == null)
{
throw new ArgumentNullException(nameof(values));
}
ArgumentNullException.ThrowIfNull(values);
CheckDisposed();
@ -5607,10 +5582,7 @@ AND Type = @InternalPersonType)");
throw new ArgumentNullException(nameof(itemId));
}
if (people == null)
{
throw new ArgumentNullException(nameof(people));
}
ArgumentNullException.ThrowIfNull(people);
CheckDisposed();
@ -5710,10 +5682,7 @@ AND Type = @InternalPersonType)");
{
CheckDisposed();
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
ArgumentNullException.ThrowIfNull(query);
var cmdText = _mediaStreamSaveColumnsSelectQuery;
@ -5766,10 +5735,7 @@ AND Type = @InternalPersonType)");
throw new ArgumentNullException(nameof(id));
}
if (streams == null)
{
throw new ArgumentNullException(nameof(streams));
}
ArgumentNullException.ThrowIfNull(streams);
cancellationToken.ThrowIfCancellationRequested();
@ -5881,6 +5847,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();
@ -6092,12 +6060,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;
@ -6107,10 +6078,7 @@ AND Type = @InternalPersonType)");
{
CheckDisposed();
if (query == null)
{
throw new ArgumentNullException(nameof(query));
}
ArgumentNullException.ThrowIfNull(query);
var cmdText = _mediaAttachmentSaveColumnsSelectQuery;
@ -6152,10 +6120,7 @@ AND Type = @InternalPersonType)");
throw new ArgumentException("Guid can't be empty.", nameof(id));
}
if (attachments == null)
{
throw new ArgumentNullException(nameof(attachments));
}
ArgumentNullException.ThrowIfNull(attachments);
cancellationToken.ThrowIfCancellationRequested();

View File

@ -133,10 +133,7 @@ namespace Emby.Server.Implementations.Data
/// <inheritdoc />
public void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken)
{
if (userData == null)
{
throw new ArgumentNullException(nameof(userData));
}
ArgumentNullException.ThrowIfNull(userData);
if (userId <= 0)
{
@ -154,10 +151,7 @@ namespace Emby.Server.Implementations.Data
/// <inheritdoc />
public void SaveAllUserData(long userId, UserItemData[] userData, CancellationToken cancellationToken)
{
if (userData == null)
{
throw new ArgumentNullException(nameof(userData));
}
ArgumentNullException.ThrowIfNull(userData);
if (userId <= 0)
{
@ -304,10 +298,7 @@ namespace Emby.Server.Implementations.Data
public UserItemData GetUserData(long userId, List<string> keys)
{
if (keys == null)
{
throw new ArgumentNullException(nameof(keys));
}
ArgumentNullException.ThrowIfNull(keys);
if (keys.Count == 0)
{

View File

@ -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<ILiveTvManager> _livetvManagerFactory;
private readonly ILyricManager _lyricManager;
public DtoService(
ILogger<DtoService> logger,
ILibraryManager libraryManager,
@ -59,7 +63,8 @@ namespace Emby.Server.Implementations.Dto
IProviderManager providerManager,
IApplicationHost appHost,
IMediaSourceManager mediaSourceManager,
Lazy<ILiveTvManager> livetvManagerFactory)
Lazy<ILiveTvManager> 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))
@ -182,7 +192,7 @@ namespace Emby.Server.Implementations.Dto
if (options.ContainsField(ItemFields.People))
{
AttachPeople(dto, item);
AttachPeople(dto, item, user);
}
if (options.ContainsField(ItemFields.PrimaryImageAspectRatio))
@ -503,7 +513,8 @@ namespace Emby.Server.Implementations.Dto
/// </summary>
/// <param name="dto">The dto.</param>
/// <param name="item">The item.</param>
private void AttachPeople(BaseItemDto dto, BaseItem item)
/// <param name="user">The requesting user.</param>
private void AttachPeople(BaseItemDto dto, BaseItem item, User user = null)
{
// Ordering by person type to ensure actors and artists are at the front.
// This is taking advantage of the fact that they both begin with A
@ -560,6 +571,9 @@ namespace Emby.Server.Implementations.Dto
return null;
}
}).Where(i => i != null)
.Where(i => user == null ?
true :
i.IsVisible(user))
.GroupBy(i => i.Name, StringComparer.OrdinalIgnoreCase)
.Select(x => x.First())
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);

View File

@ -25,14 +25,13 @@
<ItemGroup>
<PackageReference Include="DiscUtils.Udf" Version="0.16.13" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.8.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="6.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="6.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.8" />
<PackageReference Include="Mono.Nat" Version="3.0.3" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.2.4" />
<PackageReference Include="sharpcompress" Version="0.32.2" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.11" />
<PackageReference Include="Mono.Nat" Version="3.0.4" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.3.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="3.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
</ItemGroup>

View File

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

View File

@ -1,60 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using Microsoft.AspNetCore.Http;
namespace Emby.Server.Implementations.HttpServer.Security
{
public class SessionContext : ISessionContext
{
private readonly IUserManager _userManager;
private readonly ISessionManager _sessionManager;
private readonly IAuthorizationContext _authContext;
public SessionContext(IUserManager userManager, IAuthorizationContext authContext, ISessionManager sessionManager)
{
_userManager = userManager;
_authContext = authContext;
_sessionManager = sessionManager;
}
public async Task<SessionInfo> GetSession(HttpContext requestContext)
{
var authorization = await _authContext.GetAuthorizationInfo(requestContext).ConfigureAwait(false);
var user = authorization.User;
return await _sessionManager.LogSessionActivity(
authorization.Client,
authorization.Version,
authorization.DeviceId,
authorization.Device,
requestContext.GetNormalizedRemoteIp().ToString(),
user).ConfigureAwait(false);
}
public Task<SessionInfo> GetSession(object requestContext)
{
return GetSession((HttpContext)requestContext);
}
public async Task<User?> GetUser(HttpContext requestContext)
{
var session = await GetSession(requestContext).ConfigureAwait(false);
return session.UserId.Equals(default)
? null
: _userManager.GetUserById(session.UserId);
}
public Task<User?> GetUser(object requestContext)
{
return GetUser(((HttpRequest)requestContext).HttpContext);
}
}
}

View File

@ -11,7 +11,6 @@ using Jellyfin.Extensions.Json;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.HttpServer

View File

@ -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<Folder>()
.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();

View File

@ -93,6 +93,7 @@ namespace Emby.Server.Implementations.Images
returnItems.Shuffle();
return returnItems;
}
returnItems = items
.Where(i => i.HasImage(ImageType.Primary))
.ToList();

View File

@ -46,7 +46,6 @@ using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Episode = MediaBrowser.Controller.Entities.TV.Episode;
using EpisodeInfo = Emby.Naming.TV.EpisodeInfo;
@ -282,10 +281,7 @@ namespace Emby.Server.Implementations.Library
public void RegisterItem(BaseItem item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
ArgumentNullException.ThrowIfNull(item);
if (item is IItemByName)
{
@ -312,10 +308,7 @@ namespace Emby.Server.Implementations.Library
public void DeleteItem(BaseItem item, DeleteOptions options, bool notifyParentItem)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
ArgumentNullException.ThrowIfNull(item);
var parent = item.GetOwner() ?? item.GetParent();
@ -324,10 +317,7 @@ namespace Emby.Server.Implementations.Library
public void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
ArgumentNullException.ThrowIfNull(item);
if (item.SourceType == SourceType.Channel)
{
@ -510,10 +500,7 @@ namespace Emby.Server.Implementations.Library
throw new ArgumentNullException(nameof(key));
}
if (type == null)
{
throw new ArgumentNullException(nameof(type));
}
ArgumentNullException.ThrowIfNull(type);
string programDataPath = _configurationManager.ApplicationPaths.ProgramDataPath;
if (key.StartsWith(programDataPath, StringComparison.Ordinal))
@ -545,10 +532,7 @@ namespace Emby.Server.Implementations.Library
string collectionType = null,
LibraryOptions libraryOptions = null)
{
if (fileInfo == null)
{
throw new ArgumentNullException(nameof(fileInfo));
}
ArgumentNullException.ThrowIfNull(fileInfo);
var fullPath = fileInfo.FullName;
@ -681,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;
}
@ -1855,10 +1835,7 @@ namespace Emby.Server.Implementations.Library
/// <inheritdoc />
public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
ArgumentNullException.ThrowIfNull(item);
var outdated = forceUpdate
? item.ImageInfos.Where(i => i.Path != null).ToArray()
@ -2297,10 +2274,7 @@ namespace Emby.Server.Implementations.Library
string viewType,
string sortName)
{
if (parent == null)
{
throw new ArgumentNullException(nameof(parent));
}
ArgumentNullException.ThrowIfNull(parent);
var name = parent.Name;
var parentId = parent.Id;
@ -2529,7 +2503,7 @@ namespace Emby.Server.Implementations.Library
}
catch (Exception ex)
{
_logger.LogError(ex, "Error reading the episode informations with ffprobe. Episode: {EpisodeInfo}", episodeInfo.Path);
_logger.LogError(ex, "Error reading the episode information with ffprobe. Episode: {EpisodeInfo}", episodeInfo.Path);
}
var changed = false;
@ -2766,7 +2740,8 @@ namespace Emby.Server.Implementations.Library
public List<Person> GetPeopleItems(InternalPeopleQuery query)
{
return _itemRepository.GetPeopleNames(query).Select(i =>
return _itemRepository.GetPeopleNames(query)
.Select(i =>
{
try
{
@ -2777,7 +2752,12 @@ namespace Emby.Server.Implementations.Library
_logger.LogError(ex, "Error getting person");
return null;
}
}).Where(i => i != null).ToList();
})
.Where(i => i != null)
.Where(i => query.User == null ?
true :
i.IsVisible(query.User))
.ToList();
}
public List<string> GetPeopleNames(InternalPeopleQuery query)
@ -2978,10 +2958,7 @@ namespace Emby.Server.Implementations.Library
private void AddMediaPathInternal(string virtualFolderName, MediaPathInfo pathInfo, bool saveLibraryOptions)
{
if (pathInfo == null)
{
throw new ArgumentNullException(nameof(pathInfo));
}
ArgumentNullException.ThrowIfNull(pathInfo);
var path = pathInfo.Path;
@ -3028,10 +3005,7 @@ namespace Emby.Server.Implementations.Library
public void UpdateMediaPath(string virtualFolderName, MediaPathInfo mediaPath)
{
if (mediaPath == null)
{
throw new ArgumentNullException(nameof(mediaPath));
}
ArgumentNullException.ThrowIfNull(mediaPath);
var rootFolderPath = _configurationManager.ApplicationPaths.DefaultUserViewsPath;
var virtualFolderPath = Path.Combine(rootFolderPath, virtualFolderName);

View File

@ -322,10 +322,7 @@ namespace Emby.Server.Implementations.Library
public List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
ArgumentNullException.ThrowIfNull(item);
var hasMediaSources = (IHasMediaSources)item;

View File

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

View File

@ -20,8 +20,9 @@ namespace Emby.Server.Implementations.Library
/// <param name="parent">The parent.</param>
/// <param name="libraryManager">The library manager.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns>True if initializing was successful.</returns>
/// <exception cref="ArgumentException">Item must have a path.</exception>
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;
}
/// <summary>

View File

@ -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
{
/// <summary>
/// Class MusicAlbumResolver.
/// The music album resolver.
/// </summary>
public class MusicAlbumResolver : ItemResolver<MusicAlbum>
{
@ -82,7 +83,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// </summary>
/// <param name="path">The path to check.</param>
/// <param name="directoryService">The directory service.</param>
/// <returns><c>true</c> if the provided path points to a music album, <c>false</c> otherwise.</returns>
/// <returns><c>true</c> if the provided path points to a music album; otherwise, <c>false</c>.</returns>
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
/// <returns><c>true</c> if [is music album] [the specified args]; otherwise, <c>false</c>.</returns>
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
/// <summary>
/// Determine if the supplied list contains what we should consider music.
/// </summary>
/// <returns><c>true</c> if the provided path list contains music; otherwise, <c>false</c>.</returns>
private bool ContainsMusic(
ICollection<FileSystemMetadata> 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;
}

View File

@ -13,7 +13,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Resolvers.Audio
{
/// <summary>
/// Class MusicArtistResolver.
/// The music artist resolver.
/// </summary>
public class MusicArtistResolver : ItemResolver<MusicArtist>
{
@ -23,8 +23,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
/// <summary>
/// Initializes a new instance of the <see cref="MusicArtistResolver"/> class.
/// </summary>
/// <param name="logger">The logger for the created <see cref="MusicAlbumResolver"/> instances.</param>
/// <param name="namingOptions">The naming options.</param>
/// <param name="logger">Instance of the <see cref="MusicAlbumResolver"/> interface.</param>
/// <param name="namingOptions">The <see cref="NamingOptions"/>.</param>
public MusicArtistResolver(
ILogger<MusicAlbumResolver> logger,
NamingOptions namingOptions)
@ -40,10 +40,10 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
public override ResolverPriority Priority => ResolverPriority.Second;
/// <summary>
/// Resolves the specified args.
/// Resolves the specified resolver arguments.
/// </summary>
/// <param name="args">The args.</param>
/// <returns>MusicArtist.</returns>
/// <param name="args">The resolver arguments.</param>
/// <returns>A <see cref="MusicArtist"/>.</returns>
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();
}
});

View File

@ -38,7 +38,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
/// </summary>
/// <param name="args">The args.</param>
/// <returns>`0.</returns>
public override T Resolve(ItemResolveArgs args)
protected override T Resolve(ItemResolveArgs args)
{
return ResolveVideo<T>(args, false);
}

View File

@ -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<Book>
public class BookResolver : ItemResolver<Book>
{
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();

View File

@ -2,6 +2,7 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
namespace Emby.Server.Implementations.Library.Resolvers
{

View File

@ -1,58 +0,0 @@
#nullable disable
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Resolvers;
namespace Emby.Server.Implementations.Library.Resolvers
{
/// <summary>
/// Class ItemResolver.
/// </summary>
/// <typeparam name="T">The type of BaseItem.</typeparam>
public abstract class ItemResolver<T> : IItemResolver
where T : BaseItem, new()
{
/// <summary>
/// Gets the priority.
/// </summary>
/// <value>The priority.</value>
public virtual ResolverPriority Priority => ResolverPriority.First;
/// <summary>
/// Resolves the specified args.
/// </summary>
/// <param name="args">The args.</param>
/// <returns>`0.</returns>
protected virtual T Resolve(ItemResolveArgs args)
{
return null;
}
/// <summary>
/// Sets initial values on the newly resolved item.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="args">The args.</param>
protected virtual void SetInitialItemValues(T item, ItemResolveArgs args)
{
}
/// <summary>
/// Resolves the path.
/// </summary>
/// <param name="args">The args.</param>
/// <returns>BaseItem.</returns>
BaseItem IItemResolver.ResolvePath(ItemResolveArgs args)
{
var item = Resolve(args);
if (item != null)
{
SetInitialItemValues(item, args);
}
return item;
}
}
}

View File

@ -80,7 +80,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
/// </summary>
/// <param name="args">The args.</param>
/// <returns>Video.</returns>
public override Video Resolve(ItemResolveArgs args)
protected override Video Resolve(ItemResolveArgs args)
{
var collectionType = args.GetCollectionType();
@ -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 (wither 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 (either id in parent dir or in file name)
var imdbid = item.Path.AsSpan().GetAttributeValue("imdbid");
if (!string.IsNullOrWhiteSpace(imdbid))

View File

@ -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
@ -91,10 +92,7 @@ namespace Emby.Server.Implementations.Library.Resolvers
internal static bool IsImageFile(string path, IImageProcessor imageProcessor)
{
if (path == null)
{
throw new ArgumentNullException(nameof(path));
}
ArgumentNullException.ThrowIfNull(path);
var filename = Path.GetFileNameWithoutExtension(path);

View File

@ -30,7 +30,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
/// </summary>
/// <param name="args">The args.</param>
/// <returns>Episode.</returns>
public override Episode Resolve(ItemResolveArgs args)
protected override Episode Resolve(ItemResolveArgs args)
{
var parent = args.Parent;

View File

@ -53,15 +53,9 @@ namespace Emby.Server.Implementations.Library
public void SaveUserData(User user, BaseItem item, UserItemData userData, UserDataSaveReason reason, CancellationToken cancellationToken)
{
if (userData == null)
{
throw new ArgumentNullException(nameof(userData));
}
ArgumentNullException.ThrowIfNull(userData);
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
ArgumentNullException.ThrowIfNull(item);
cancellationToken.ThrowIfCancellationRequested();
@ -194,10 +188,7 @@ namespace Emby.Server.Implementations.Library
/// <exception cref="ArgumentNullException"><paramref name="data"/> is <c>null</c>.</exception>
private UserItemDataDto GetUserItemDataDto(UserItemData data)
{
if (data == null)
{
throw new ArgumentNullException(nameof(data));
}
ArgumentNullException.ThrowIfNull(data);
return new UserItemDataDto
{

View File

@ -995,7 +995,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
}
throw new Exception("Tuner not found.");
throw new ResourceNotFoundException("Tuner not found.");
}
public async Task<List<MediaSourceInfo>> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken)
@ -1223,10 +1223,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private async Task RecordStream(TimerInfo timer, DateTime recordingEndDate, ActiveRecordingInfo activeRecordingInfo)
{
if (timer == null)
{
throw new ArgumentNullException(nameof(timer));
}
ArgumentNullException.ThrowIfNull(timer);
LiveTvProgram programInfo = null;
@ -2222,6 +2219,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);
}
}
@ -2347,10 +2350,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
private IEnumerable<TimerInfo> GetTimersForSeries(SeriesTimerInfo seriesTimer)
{
if (seriesTimer == null)
{
throw new ArgumentNullException(nameof(seriesTimer));
}
ArgumentNullException.ThrowIfNull(seriesTimer);
var query = new InternalItemsQuery
{

View File

@ -13,6 +13,7 @@ using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
@ -297,7 +298,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
else
{
_taskCompletionSource.TrySetException(
new Exception(
new FfmpegException(
string.Format(
CultureInfo.InvariantCulture,
"Recording for {0} failed. Exit code {1}",

View File

@ -84,10 +84,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public virtual void Update(T item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
ArgumentNullException.ThrowIfNull(item);
lock (_fileDataLock)
{
@ -107,10 +104,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
public virtual void Add(T item)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
ArgumentNullException.ThrowIfNull(item);
lock (_fileDataLock)
{

View File

@ -2,8 +2,8 @@
using System;
using System.Globalization;
using MediaBrowser.Controller.LiveTv;
using System.Text;
using MediaBrowser.Controller.LiveTv;
namespace Emby.Server.Implementations.LiveTv.EmbyTV
{
@ -56,7 +56,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
}
tmpName += " " + info.EpisodeTitle;
// Since the filename will be used with file ext. (.mp4, .ts, etc)
// Since the filename will be used with file ext. (.mp4, .ts, etc)
if (Encoding.UTF8.GetByteCount(tmpName) < 250)
{
name = tmpName;

View File

@ -20,6 +20,7 @@ using Emby.Server.Implementations.LiveTv.Listings.SchedulesDirectDtos;
using Jellyfin.Extensions;
using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
@ -165,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))
@ -178,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) ??
@ -399,7 +400,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return info;
}
private static string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, double desiredAspect)
private static string GetProgramImage(string apiUrl, IEnumerable<ImageDataDto> images, double desiredAspect, string token)
{
var match = images
.OrderBy(i => Math.Abs(desiredAspect - GetAspectRatio(i)))
@ -423,7 +424,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
else
{
return apiUrl + "/image/" + uri;
return apiUrl + "/image/" + uri + "?token=" + token;
}
}
@ -457,6 +458,8 @@ namespace Emby.Server.Implementations.LiveTv.Listings
IReadOnlyList<string> programIds,
CancellationToken cancellationToken)
{
var token = await GetToken(info, cancellationToken).ConfigureAwait(false);
if (programIds.Count == 0)
{
return Array.Empty<ShowImagesDto>();
@ -478,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
{
@ -591,13 +595,10 @@ namespace Emby.Server.Implementations.LiveTv.Listings
}
catch (HttpRequestException ex)
{
if (ex.StatusCode.HasValue)
if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.BadRequest)
{
if ((int)ex.StatusCode.Value == 400)
{
_tokens.Clear();
_lastErrorResponse = DateTime.UtcNow;
}
_tokens.Clear();
_lastErrorResponse = DateTime.UtcNow;
}
throw;
@ -662,7 +663,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
return root.Token;
}
throw new Exception("Could not authenticate with Schedules Direct Error: " + root.Message);
throw new AuthenticationException("Could not authenticate with Schedules Direct Error: " + root.Message);
}
private async Task AddLineupToAccount(ListingsProviderInfo info, CancellationToken cancellationToken)
@ -697,7 +698,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
if (string.IsNullOrEmpty(token))
{
throw new Exception("token required");
throw new ArgumentException("token required");
}
_logger.LogInformation("Headends on account ");
@ -768,14 +769,14 @@ namespace Emby.Server.Implementations.LiveTv.Listings
var listingsId = info.ListingsId;
if (string.IsNullOrEmpty(listingsId))
{
throw new Exception("ListingsId required");
throw new ArgumentException("ListingsId required");
}
var token = await GetToken(info, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(token))
{
throw new Exception("token required");
throw new ArgumentException("token required");
}
using var options = new HttpRequestMessage(HttpMethod.Get, ApiUrl + "/lineups/" + listingsId);

View File

@ -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<XmlTvListingsProvider> _logger;
private readonly IFileSystem _fileSystem;
private readonly IZipClient _zipClient;
public XmlTvListingsProvider(
IServerConfigurationManager config,
IHttpClientFactory httpClientFactory,
ILogger<XmlTvListingsProvider> logger,
IFileSystem fileSystem,
IZipClient zipClient)
ILogger<XmlTvListingsProvider> 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<string> 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<char> originalUrl, string file)
{
ReadOnlySpan<char> 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<IEnumerable<ProgramInfo>> 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)
{

View File

@ -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
{

View File

@ -196,7 +196,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
IsInfiniteStream = true,
IsRemote = isRemote,
IgnoreDts = true,
IgnoreDts = info.IgnoreDts,
SupportsDirectPlay = supportsDirectPlay,
SupportsDirectStream = supportsDirectStream,

View File

@ -44,10 +44,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
{
if (info == null)
{
throw new ArgumentNullException(nameof(info));
}
ArgumentNullException.ThrowIfNull(info);
if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{
@ -199,7 +196,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
if (string.IsNullOrWhiteSpace(numberString))
{
// Using this as a fallback now as this leads to Problems with channels like "5 USA"
// where 5 isn't ment to be the channel number
// where 5 isn't meant to be the channel number
// Check for channel number with the format from SatIp
// #EXTINF:0,84. VOX Schweiz
// #EXTINF:0,84.0 - VOX Schweiz

View File

@ -97,7 +97,7 @@
"TasksChannelsCategory": "قنوات الإنترنت",
"TasksLibraryCategory": "مكتبة",
"TasksMaintenanceCategory": "صيانة",
"TaskRefreshLibraryDescription": "يفصح مكتبة الوسائط الخاصة بك بحثًا عن ملفات جديدة، ومن ثم يتحدث البيانات الوصفية.",
"TaskRefreshLibraryDescription": "يفحص مكتبة الوسائط الخاصة بك باحثا عن ملفات جديدة، ومن ثم يتحدث البيانات الوصفية.",
"TaskRefreshLibrary": "افحص مكتبة الوسائط",
"TaskRefreshChapterImagesDescription": "يُنشئ صور مصغرة لمقاطع الفيديو التي تحتوي على فصول.",
"TaskRefreshChapterImages": "استخراج صور الفصل",

View File

@ -123,5 +123,6 @@
"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"
}

View File

@ -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í"
}

View File

@ -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"
}

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