mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-07 18:24:19 -04:00
Merge branch 'master' into baseitemkind-fixes
This commit is contained in:
commit
663c79cba8
@ -213,3 +213,4 @@
|
|||||||
- [SvenVandenbrande](https://github.com/SvenVandenbrande)
|
- [SvenVandenbrande](https://github.com/SvenVandenbrande)
|
||||||
- [olsh](https://github.com/olsh)
|
- [olsh](https://github.com/olsh)
|
||||||
- [lbenini](https://github.com/lbenini)
|
- [lbenini](https://github.com/lbenini)
|
||||||
|
- [gnuyent](https://github.com/gnuyent)
|
||||||
|
26
Dockerfile
26
Dockerfile
@ -8,15 +8,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
|
|||||||
&& npm ci --no-audit --unsafe-perm \
|
&& npm ci --no-audit --unsafe-perm \
|
||||||
&& mv dist /dist
|
&& mv dist /dist
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
|
FROM debian:buster-slim as app
|
||||||
WORKDIR /repo
|
|
||||||
COPY . .
|
|
||||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|
||||||
# because of changes in docker and systemd we need to not build in parallel at the moment
|
|
||||||
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
|
||||||
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none"
|
|
||||||
|
|
||||||
FROM debian:buster-slim
|
|
||||||
|
|
||||||
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
@ -25,9 +17,6 @@ ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
|
|||||||
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
|
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
|
||||||
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
|
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
|
||||||
|
|
||||||
COPY --from=builder /jellyfin /jellyfin
|
|
||||||
COPY --from=web-builder /dist /jellyfin/jellyfin-web
|
|
||||||
|
|
||||||
# https://github.com/intel/compute-runtime/releases
|
# https://github.com/intel/compute-runtime/releases
|
||||||
ARG GMMLIB_VERSION=20.3.2
|
ARG GMMLIB_VERSION=20.3.2
|
||||||
ARG IGC_VERSION=1.0.5435
|
ARG IGC_VERSION=1.0.5435
|
||||||
@ -73,6 +62,19 @@ ENV LC_ALL en_US.UTF-8
|
|||||||
ENV LANG en_US.UTF-8
|
ENV LANG en_US.UTF-8
|
||||||
ENV LANGUAGE en_US:en
|
ENV LANGUAGE en_US:en
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
|
||||||
|
WORKDIR /repo
|
||||||
|
COPY . .
|
||||||
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
# because of changes in docker and systemd we need to not build in parallel at the moment
|
||||||
|
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
|
||||||
|
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none"
|
||||||
|
|
||||||
|
FROM app
|
||||||
|
|
||||||
|
COPY --from=builder /jellyfin /jellyfin
|
||||||
|
COPY --from=web-builder /dist /jellyfin/jellyfin-web
|
||||||
|
|
||||||
EXPOSE 8096
|
EXPOSE 8096
|
||||||
VOLUME /cache /config /media
|
VOLUME /cache /config /media
|
||||||
ENTRYPOINT ["./jellyfin/jellyfin", \
|
ENTRYPOINT ["./jellyfin/jellyfin", \
|
||||||
|
@ -13,19 +13,8 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
|
|||||||
&& npm ci --no-audit --unsafe-perm \
|
&& npm ci --no-audit --unsafe-perm \
|
||||||
&& mv dist /dist
|
&& mv dist /dist
|
||||||
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
|
|
||||||
WORKDIR /repo
|
|
||||||
COPY . .
|
|
||||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|
||||||
# Discard objs - may cause failures if exists
|
|
||||||
RUN find . -type d -name obj | xargs -r rm -r
|
|
||||||
# Build
|
|
||||||
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none"
|
|
||||||
|
|
||||||
|
|
||||||
FROM multiarch/qemu-user-static:x86_64-arm as qemu
|
FROM multiarch/qemu-user-static:x86_64-arm as qemu
|
||||||
FROM arm32v7/debian:buster-slim
|
FROM arm32v7/debian:buster-slim as app
|
||||||
|
|
||||||
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
@ -61,14 +50,25 @@ RUN apt-get update \
|
|||||||
&& chmod 777 /cache /config /media \
|
&& chmod 777 /cache /config /media \
|
||||||
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
||||||
|
|
||||||
COPY --from=builder /jellyfin /jellyfin
|
|
||||||
COPY --from=web-builder /dist /jellyfin/jellyfin-web
|
|
||||||
|
|
||||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||||
ENV LC_ALL en_US.UTF-8
|
ENV LC_ALL en_US.UTF-8
|
||||||
ENV LANG en_US.UTF-8
|
ENV LANG en_US.UTF-8
|
||||||
ENV LANGUAGE en_US:en
|
ENV LANGUAGE en_US:en
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
|
||||||
|
WORKDIR /repo
|
||||||
|
COPY . .
|
||||||
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
# Discard objs - may cause failures if exists
|
||||||
|
RUN find . -type d -name obj | xargs -r rm -r
|
||||||
|
# Build
|
||||||
|
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none"
|
||||||
|
|
||||||
|
FROM app
|
||||||
|
|
||||||
|
COPY --from=builder /jellyfin /jellyfin
|
||||||
|
COPY --from=web-builder /dist /jellyfin/jellyfin-web
|
||||||
|
|
||||||
EXPOSE 8096
|
EXPOSE 8096
|
||||||
VOLUME /cache /config /media
|
VOLUME /cache /config /media
|
||||||
ENTRYPOINT ["./jellyfin/jellyfin", \
|
ENTRYPOINT ["./jellyfin/jellyfin", \
|
||||||
|
@ -13,18 +13,8 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
|
|||||||
&& npm ci --no-audit --unsafe-perm \
|
&& npm ci --no-audit --unsafe-perm \
|
||||||
&& mv dist /dist
|
&& mv dist /dist
|
||||||
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
|
|
||||||
WORKDIR /repo
|
|
||||||
COPY . .
|
|
||||||
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|
||||||
# Discard objs - may cause failures if exists
|
|
||||||
RUN find . -type d -name obj | xargs -r rm -r
|
|
||||||
# Build
|
|
||||||
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none"
|
|
||||||
|
|
||||||
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
|
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
|
||||||
FROM arm64v8/debian:buster-slim
|
FROM arm64v8/debian:buster-slim as app
|
||||||
|
|
||||||
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable
|
||||||
ARG DEBIAN_FRONTEND="noninteractive"
|
ARG DEBIAN_FRONTEND="noninteractive"
|
||||||
@ -50,14 +40,25 @@ RUN apt-get update && apt-get install --no-install-recommends --no-install-sugge
|
|||||||
&& chmod 777 /cache /config /media \
|
&& chmod 777 /cache /config /media \
|
||||||
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
|
||||||
|
|
||||||
COPY --from=builder /jellyfin /jellyfin
|
|
||||||
COPY --from=web-builder /dist /jellyfin/jellyfin-web
|
|
||||||
|
|
||||||
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
|
||||||
ENV LC_ALL en_US.UTF-8
|
ENV LC_ALL en_US.UTF-8
|
||||||
ENV LANG en_US.UTF-8
|
ENV LANG en_US.UTF-8
|
||||||
ENV LANGUAGE en_US:en
|
ENV LANGUAGE en_US:en
|
||||||
|
|
||||||
|
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
|
||||||
|
WORKDIR /repo
|
||||||
|
COPY . .
|
||||||
|
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
|
||||||
|
# Discard objs - may cause failures if exists
|
||||||
|
RUN find . -type d -name obj | xargs -r rm -r
|
||||||
|
# Build
|
||||||
|
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none"
|
||||||
|
|
||||||
|
FROM app
|
||||||
|
|
||||||
|
COPY --from=builder /jellyfin /jellyfin
|
||||||
|
COPY --from=web-builder /dist /jellyfin/jellyfin-web
|
||||||
|
|
||||||
EXPOSE 8096
|
EXPOSE 8096
|
||||||
VOLUME /cache /config /media
|
VOLUME /cache /config /media
|
||||||
ENTRYPOINT ["./jellyfin/jellyfin", \
|
ENTRYPOINT ["./jellyfin/jellyfin", \
|
||||||
|
@ -288,21 +288,14 @@ namespace Emby.Dlna.ContentDirectory
|
|||||||
/// <returns>The xml feature list.</returns>
|
/// <returns>The xml feature list.</returns>
|
||||||
private static string WriteFeatureListXml()
|
private static string WriteFeatureListXml()
|
||||||
{
|
{
|
||||||
// TODO: clean this up
|
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||||
var builder = new StringBuilder();
|
+ "<Features xmlns=\"urn:schemas-upnp-org:av:avs\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:schemas-upnp-org:av:avs http://www.upnp.org/schemas/av/avs.xsd\">"
|
||||||
|
+ "<Feature name=\"samsung.com_BASICVIEW\" version=\"1\">"
|
||||||
builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
|
+ "<container id=\"I\" type=\"object.item.imageItem\"/>"
|
||||||
builder.Append("<Features xmlns=\"urn:schemas-upnp-org:av:avs\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"urn:schemas-upnp-org:av:avs http://www.upnp.org/schemas/av/avs.xsd\">");
|
+ "<container id=\"A\" type=\"object.item.audioItem\"/>"
|
||||||
|
+ "<container id=\"V\" type=\"object.item.videoItem\"/>"
|
||||||
builder.Append("<Feature name=\"samsung.com_BASICVIEW\" version=\"1\">");
|
+ "</Feature>"
|
||||||
builder.Append("<container id=\"I\" type=\"object.item.imageItem\"/>");
|
+ "</Features>";
|
||||||
builder.Append("<container id=\"A\" type=\"object.item.audioItem\"/>");
|
|
||||||
builder.Append("<container id=\"V\" type=\"object.item.videoItem\"/>");
|
|
||||||
builder.Append("</Feature>");
|
|
||||||
|
|
||||||
builder.Append("</Features>");
|
|
||||||
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
@ -96,12 +93,14 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public DeviceProfile GetDefaultProfile()
|
public DeviceProfile GetDefaultProfile()
|
||||||
{
|
{
|
||||||
return new DefaultProfile();
|
return new DefaultProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceProfile GetProfile(DeviceIdentification deviceInfo)
|
/// <inheritdoc />
|
||||||
|
public DeviceProfile? GetProfile(DeviceIdentification deviceInfo)
|
||||||
{
|
{
|
||||||
if (deviceInfo == null)
|
if (deviceInfo == null)
|
||||||
{
|
{
|
||||||
@ -111,13 +110,13 @@ namespace Emby.Dlna
|
|||||||
var profile = GetProfiles()
|
var profile = GetProfiles()
|
||||||
.FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification));
|
.FirstOrDefault(i => i.Identification != null && IsMatch(deviceInfo, i.Identification));
|
||||||
|
|
||||||
if (profile != null)
|
if (profile == null)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name);
|
LogUnmatchedProfile(deviceInfo);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
LogUnmatchedProfile(deviceInfo);
|
_logger.LogDebug("Found matching device profile: {ProfileName}", profile.Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return profile;
|
return profile;
|
||||||
@ -187,7 +186,8 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceProfile GetProfile(IHeaderDictionary headers)
|
/// <inheritdoc />
|
||||||
|
public DeviceProfile? GetProfile(IHeaderDictionary headers)
|
||||||
{
|
{
|
||||||
if (headers == null)
|
if (headers == null)
|
||||||
{
|
{
|
||||||
@ -195,15 +195,13 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
|
|
||||||
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
|
var profile = GetProfiles().FirstOrDefault(i => i.Identification != null && IsMatch(headers, i.Identification));
|
||||||
|
if (profile == null)
|
||||||
if (profile != null)
|
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Found matching device profile: {0}", profile.Name);
|
_logger.LogDebug("No matching device profile found. {@Headers}", headers);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var headerString = string.Join(", ", headers.Select(i => string.Format(CultureInfo.InvariantCulture, "{0}={1}", i.Key, i.Value)));
|
_logger.LogDebug("Found matching device profile: {0}", profile.Name);
|
||||||
_logger.LogDebug("No matching device profile found. {0}", headerString);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return profile;
|
return profile;
|
||||||
@ -253,19 +251,19 @@ namespace Emby.Dlna
|
|||||||
return xmlFies
|
return xmlFies
|
||||||
.Select(i => ParseProfileFile(i, type))
|
.Select(i => ParseProfileFile(i, type))
|
||||||
.Where(i => i != null)
|
.Where(i => i != null)
|
||||||
.ToList();
|
.ToList()!; // We just filtered out all the nulls
|
||||||
}
|
}
|
||||||
catch (IOException)
|
catch (IOException)
|
||||||
{
|
{
|
||||||
return new List<DeviceProfile>();
|
return Array.Empty<DeviceProfile>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DeviceProfile ParseProfileFile(string path, DeviceProfileType type)
|
private DeviceProfile? ParseProfileFile(string path, DeviceProfileType type)
|
||||||
{
|
{
|
||||||
lock (_profiles)
|
lock (_profiles)
|
||||||
{
|
{
|
||||||
if (_profiles.TryGetValue(path, out Tuple<InternalProfileInfo, DeviceProfile> profileTuple))
|
if (_profiles.TryGetValue(path, out Tuple<InternalProfileInfo, DeviceProfile>? profileTuple))
|
||||||
{
|
{
|
||||||
return profileTuple.Item2;
|
return profileTuple.Item2;
|
||||||
}
|
}
|
||||||
@ -293,7 +291,8 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeviceProfile GetProfile(string id)
|
/// <inheritdoc />
|
||||||
|
public DeviceProfile? GetProfile(string id)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(id))
|
if (string.IsNullOrEmpty(id))
|
||||||
{
|
{
|
||||||
@ -322,6 +321,7 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public IEnumerable<DeviceProfileInfo> GetProfileInfos()
|
public IEnumerable<DeviceProfileInfo> GetProfileInfos()
|
||||||
{
|
{
|
||||||
return GetProfileInfosInternal().Select(i => i.Info);
|
return GetProfileInfosInternal().Select(i => i.Info);
|
||||||
@ -329,17 +329,14 @@ namespace Emby.Dlna
|
|||||||
|
|
||||||
private InternalProfileInfo GetInternalProfileInfo(FileSystemMetadata file, DeviceProfileType type)
|
private InternalProfileInfo GetInternalProfileInfo(FileSystemMetadata file, DeviceProfileType type)
|
||||||
{
|
{
|
||||||
return new InternalProfileInfo
|
return new InternalProfileInfo(
|
||||||
{
|
new DeviceProfileInfo
|
||||||
Path = file.FullName,
|
|
||||||
|
|
||||||
Info = new DeviceProfileInfo
|
|
||||||
{
|
{
|
||||||
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture),
|
Id = file.FullName.ToLowerInvariant().GetMD5().ToString("N", CultureInfo.InvariantCulture),
|
||||||
Name = _fileSystem.GetFileNameWithoutExtension(file),
|
Name = _fileSystem.GetFileNameWithoutExtension(file),
|
||||||
Type = type
|
Type = type
|
||||||
}
|
},
|
||||||
};
|
file.FullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExtractSystemProfilesAsync()
|
private async Task ExtractSystemProfilesAsync()
|
||||||
@ -359,7 +356,8 @@ namespace Emby.Dlna
|
|||||||
systemProfilesPath,
|
systemProfilesPath,
|
||||||
Path.GetFileName(name.AsSpan()).Slice(namespaceName.Length));
|
Path.GetFileName(name.AsSpan()).Slice(namespaceName.Length));
|
||||||
|
|
||||||
using (var stream = _assembly.GetManifestResourceStream(name))
|
// The stream should exist as we just got its name from GetManifestResourceNames
|
||||||
|
using (var stream = _assembly.GetManifestResourceStream(name)!)
|
||||||
{
|
{
|
||||||
var fileInfo = _fileSystem.GetFileInfo(path);
|
var fileInfo = _fileSystem.GetFileInfo(path);
|
||||||
|
|
||||||
@ -380,6 +378,7 @@ namespace Emby.Dlna
|
|||||||
Directory.CreateDirectory(UserProfilesPath);
|
Directory.CreateDirectory(UserProfilesPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void DeleteProfile(string id)
|
public void DeleteProfile(string id)
|
||||||
{
|
{
|
||||||
var info = GetProfileInfosInternal().First(i => string.Equals(id, i.Info.Id, StringComparison.OrdinalIgnoreCase));
|
var info = GetProfileInfosInternal().First(i => string.Equals(id, i.Info.Id, StringComparison.OrdinalIgnoreCase));
|
||||||
@ -397,6 +396,7 @@ namespace Emby.Dlna
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void CreateProfile(DeviceProfile profile)
|
public void CreateProfile(DeviceProfile profile)
|
||||||
{
|
{
|
||||||
profile = ReserializeProfile(profile);
|
profile = ReserializeProfile(profile);
|
||||||
@ -412,6 +412,7 @@ namespace Emby.Dlna
|
|||||||
SaveProfile(profile, path, DeviceProfileType.User);
|
SaveProfile(profile, path, DeviceProfileType.User);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void UpdateProfile(DeviceProfile profile)
|
public void UpdateProfile(DeviceProfile profile)
|
||||||
{
|
{
|
||||||
profile = ReserializeProfile(profile);
|
profile = ReserializeProfile(profile);
|
||||||
@ -470,9 +471,11 @@ namespace Emby.Dlna
|
|||||||
|
|
||||||
var json = JsonSerializer.Serialize(profile, _jsonOptions);
|
var json = JsonSerializer.Serialize(profile, _jsonOptions);
|
||||||
|
|
||||||
return JsonSerializer.Deserialize<DeviceProfile>(json, _jsonOptions);
|
// Output can't be null if the input isn't null
|
||||||
|
return JsonSerializer.Deserialize<DeviceProfile>(json, _jsonOptions)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
|
public string GetServerDescriptionXml(IHeaderDictionary headers, string serverUuId, string serverAddress)
|
||||||
{
|
{
|
||||||
var profile = GetDefaultProfile();
|
var profile = GetDefaultProfile();
|
||||||
@ -482,6 +485,7 @@ namespace Emby.Dlna
|
|||||||
return new DescriptionXmlBuilder(profile, serverUuId, serverAddress, _appHost.FriendlyName, serverId).GetXml();
|
return new DescriptionXmlBuilder(profile, serverUuId, serverAddress, _appHost.FriendlyName, serverId).GetXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public ImageStream GetIcon(string filename)
|
public ImageStream GetIcon(string filename)
|
||||||
{
|
{
|
||||||
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
|
||||||
@ -499,9 +503,15 @@ namespace Emby.Dlna
|
|||||||
|
|
||||||
private class InternalProfileInfo
|
private class InternalProfileInfo
|
||||||
{
|
{
|
||||||
internal DeviceProfileInfo Info { get; set; }
|
internal InternalProfileInfo(DeviceProfileInfo info, string path)
|
||||||
|
{
|
||||||
|
Info = info;
|
||||||
|
Path = path;
|
||||||
|
}
|
||||||
|
|
||||||
internal string Path { get; set; }
|
internal DeviceProfileInfo Info { get; }
|
||||||
|
|
||||||
|
internal string Path { get; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ namespace Emby.Naming.AudioBook
|
|||||||
/// <param name="files">List of files composing the actual audiobook.</param>
|
/// <param name="files">List of files composing the actual audiobook.</param>
|
||||||
/// <param name="extras">List of extra files.</param>
|
/// <param name="extras">List of extra files.</param>
|
||||||
/// <param name="alternateVersions">Alternative version of files.</param>
|
/// <param name="alternateVersions">Alternative version of files.</param>
|
||||||
public AudioBookInfo(string name, int? year, List<AudioBookFileInfo> files, List<AudioBookFileInfo> extras, List<AudioBookFileInfo> alternateVersions)
|
public AudioBookInfo(string name, int? year, IReadOnlyList<AudioBookFileInfo> files, IReadOnlyList<AudioBookFileInfo> extras, IReadOnlyList<AudioBookFileInfo> alternateVersions)
|
||||||
{
|
{
|
||||||
Name = name;
|
Name = name;
|
||||||
Year = year;
|
Year = year;
|
||||||
@ -39,18 +39,18 @@ namespace Emby.Naming.AudioBook
|
|||||||
/// Gets or sets the files.
|
/// Gets or sets the files.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The files.</value>
|
/// <value>The files.</value>
|
||||||
public List<AudioBookFileInfo> Files { get; set; }
|
public IReadOnlyList<AudioBookFileInfo> Files { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the extras.
|
/// Gets or sets the extras.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The extras.</value>
|
/// <value>The extras.</value>
|
||||||
public List<AudioBookFileInfo> Extras { get; set; }
|
public IReadOnlyList<AudioBookFileInfo> Extras { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the alternate versions.
|
/// Gets or sets the alternate versions.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The alternate versions.</value>
|
/// <value>The alternate versions.</value>
|
||||||
public List<AudioBookFileInfo> AlternateVersions { get; set; }
|
public IReadOnlyList<AudioBookFileInfo> AlternateVersions { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,7 +87,7 @@ namespace Emby.Naming.AudioBook
|
|||||||
foreach (var audioFile in group)
|
foreach (var audioFile in group)
|
||||||
{
|
{
|
||||||
var name = Path.GetFileNameWithoutExtension(audioFile.Path);
|
var name = Path.GetFileNameWithoutExtension(audioFile.Path);
|
||||||
if (name.Equals("audiobook") ||
|
if (name.Equals("audiobook", StringComparison.OrdinalIgnoreCase) ||
|
||||||
name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) ||
|
name.Contains(nameParserResult.Name, StringComparison.OrdinalIgnoreCase) ||
|
||||||
name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase))
|
name.Contains(nameWithReplacedDots, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
|
@ -284,7 +284,7 @@ namespace Emby.Naming.Common
|
|||||||
|
|
||||||
// Not a Kodi rule as well, but below rule also causes false positives for triple-digit episode names
|
// Not a Kodi rule as well, but below rule also causes false positives for triple-digit episode names
|
||||||
// [bar] Foo - 1 [baz] special case of below expression to prevent false positives with digits in the series name
|
// [bar] Foo - 1 [baz] special case of below expression to prevent false positives with digits in the series name
|
||||||
new EpisodeExpression(@".*?(\[.*?\])+.*?(?<seriesname>[\w\s]+?)[\s_]*-[\s_]*(?<epnumber>[0-9]+).*$")
|
new EpisodeExpression(@".*[\\\/]?.*?(\[.*?\])+.*?(?<seriesname>[-\w\s]+?)[\s_]*-[\s_]*(?<epnumber>[0-9]+).*$")
|
||||||
{
|
{
|
||||||
IsNamed = true
|
IsNamed = true
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
|
<!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
@ -49,7 +49,4 @@
|
|||||||
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" PrivateAssets="All" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Emby.Naming.Audio;
|
using Emby.Naming.Audio;
|
||||||
using Emby.Naming.Common;
|
using Emby.Naming.Common;
|
||||||
|
@ -21,7 +21,7 @@ namespace Emby.Naming.Video
|
|||||||
/// <param name="namingOptions">The naming options.</param>
|
/// <param name="namingOptions">The naming options.</param>
|
||||||
/// <param name="supportMultiVersion">Indication we should consider multi-versions of content.</param>
|
/// <param name="supportMultiVersion">Indication we should consider multi-versions of content.</param>
|
||||||
/// <returns>Returns enumerable of <see cref="VideoInfo"/> which groups files together when related.</returns>
|
/// <returns>Returns enumerable of <see cref="VideoInfo"/> which groups files together when related.</returns>
|
||||||
public static IEnumerable<VideoInfo> Resolve(List<FileSystemMetadata> files, NamingOptions namingOptions, bool supportMultiVersion = true)
|
public static IEnumerable<VideoInfo> Resolve(IEnumerable<FileSystemMetadata> files, NamingOptions namingOptions, bool supportMultiVersion = true)
|
||||||
{
|
{
|
||||||
var videoInfos = files
|
var videoInfos = files
|
||||||
.Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, namingOptions))
|
.Select(i => VideoResolver.Resolve(i.FullName, i.IsDirectory, namingOptions))
|
||||||
|
@ -880,7 +880,7 @@ namespace Emby.Server.Implementations.Channels
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task CacheResponse(object result, string path)
|
private async Task CacheResponse(ChannelItemResult result, string path)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
@ -63,13 +61,13 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
|
public event EventHandler<CollectionCreatedEventArgs>? CollectionCreated;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
|
public event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection;
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
|
public event EventHandler<CollectionModifiedEventArgs>? ItemsRemovedFromCollection;
|
||||||
|
|
||||||
private IEnumerable<Folder> FindFolders(string path)
|
private IEnumerable<Folder> FindFolders(string path)
|
||||||
{
|
{
|
||||||
@ -80,7 +78,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
.Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path));
|
.Where(i => _fileSystem.AreEqual(path, i.Path) || _fileSystem.ContainsSubPath(i.Path, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded)
|
internal async Task<Folder?> EnsureLibraryFolder(string path, bool createIfNeeded)
|
||||||
{
|
{
|
||||||
var existingFolder = FindFolders(path).FirstOrDefault();
|
var existingFolder = FindFolders(path).FirstOrDefault();
|
||||||
if (existingFolder != null)
|
if (existingFolder != null)
|
||||||
@ -114,7 +112,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
return Path.Combine(_appPaths.DataPath, "collections");
|
return Path.Combine(_appPaths.DataPath, "collections");
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task<Folder> GetCollectionsFolder(bool createIfNeeded)
|
private Task<Folder?> GetCollectionsFolder(bool createIfNeeded)
|
||||||
{
|
{
|
||||||
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
|
return EnsureLibraryFolder(GetCollectionsFolderPath(), createIfNeeded);
|
||||||
}
|
}
|
||||||
@ -203,8 +201,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
|
|
||||||
private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
|
private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
|
||||||
{
|
{
|
||||||
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
|
if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
|
||||||
if (collection == null)
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException("No collection exists with the supplied Id");
|
throw new ArgumentException("No collection exists with the supplied Id");
|
||||||
}
|
}
|
||||||
@ -256,9 +253,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
|
public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
|
||||||
{
|
{
|
||||||
var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
|
if (_libraryManager.GetItemById(collectionId) is not BoxSet collection)
|
||||||
|
|
||||||
if (collection == null)
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException("No collection exists with the supplied Id");
|
throw new ArgumentException("No collection exists with the supplied Id");
|
||||||
}
|
}
|
||||||
@ -312,11 +307,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
|
|
||||||
foreach (var item in items)
|
foreach (var item in items)
|
||||||
{
|
{
|
||||||
if (item is not ISupportsBoxSetGrouping)
|
if (item is ISupportsBoxSetGrouping)
|
||||||
{
|
|
||||||
results[item.Id] = item;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
var itemId = item.Id;
|
var itemId = item.Id;
|
||||||
|
|
||||||
@ -340,6 +331,7 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
}
|
}
|
||||||
|
|
||||||
var alreadyInResults = false;
|
var alreadyInResults = false;
|
||||||
|
|
||||||
// this is kind of a performance hack because only Video has alternate versions that should be in a box set?
|
// this is kind of a performance hack because only Video has alternate versions that should be in a box set?
|
||||||
if (item is Video video)
|
if (item is Video video)
|
||||||
{
|
{
|
||||||
@ -355,11 +347,13 @@ namespace Emby.Server.Implementations.Collections
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!alreadyInResults)
|
if (alreadyInResults)
|
||||||
{
|
{
|
||||||
results[itemId] = item;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
results[item.Id] = item;
|
||||||
}
|
}
|
||||||
|
|
||||||
return results.Values;
|
return results.Values;
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.9" />
|
||||||
<PackageReference Include="Mono.Nat" Version="3.0.1" />
|
<PackageReference Include="Mono.Nat" Version="3.0.1" />
|
||||||
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
|
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
|
||||||
<PackageReference Include="sharpcompress" Version="0.28.3" />
|
<PackageReference Include="sharpcompress" Version="0.28.3" />
|
||||||
|
@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.HttpServer.Security
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Temporary. TODO - allow clients to specify that the token has been shared with a casting device
|
// Temporary. TODO - allow clients to specify that the token has been shared with a casting device
|
||||||
var allowTokenInfoUpdate = authInfo.Client == null || authInfo.Client.IndexOf("chromecast", StringComparison.OrdinalIgnoreCase) == -1;
|
var allowTokenInfoUpdate = authInfo.Client == null || !authInfo.Client.Contains("chromecast", StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(authInfo.Device))
|
if (string.IsNullOrWhiteSpace(authInfo.Device))
|
||||||
{
|
{
|
||||||
|
@ -13,7 +13,6 @@ using System.Threading.Tasks;
|
|||||||
using Jellyfin.Extensions;
|
using Jellyfin.Extensions;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller;
|
|
||||||
using MediaBrowser.Controller.LiveTv;
|
using MediaBrowser.Controller.LiveTv;
|
||||||
using MediaBrowser.Model.LiveTv;
|
using MediaBrowser.Model.LiveTv;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -44,22 +43,29 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
|
|
||||||
public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
|
public async Task<Stream> GetListingsStream(TunerHostInfo info, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
if (info == null)
|
||||||
{
|
{
|
||||||
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
|
throw new ArgumentNullException(nameof(info));
|
||||||
if (!string.IsNullOrEmpty(info.UserAgent))
|
|
||||||
{
|
|
||||||
requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
var response = await _httpClientFactory.CreateClient(NamedClient.Default)
|
|
||||||
.SendAsync(requestMessage, cancellationToken)
|
|
||||||
.ConfigureAwait(false);
|
|
||||||
response.EnsureSuccessStatusCode();
|
|
||||||
return await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return File.OpenRead(info.Url);
|
if (!info.Url.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return File.OpenRead(info.Url);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var requestMessage = new HttpRequestMessage(HttpMethod.Get, info.Url);
|
||||||
|
if (!string.IsNullOrEmpty(info.UserAgent))
|
||||||
|
{
|
||||||
|
requestMessage.Headers.UserAgent.TryParseAdd(info.UserAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set HttpCompletionOption.ResponseHeadersRead to prevent timeouts on larger files
|
||||||
|
var response = await _httpClientFactory.CreateClient(NamedClient.Default)
|
||||||
|
.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
|
response.EnsureSuccessStatusCode();
|
||||||
|
|
||||||
|
return await response.Content.ReadAsStreamAsync(cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<List<ChannelInfo>> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId)
|
private async Task<List<ChannelInfo>> GetChannelsAsync(TextReader reader, string channelIdPrefix, string tunerHostId)
|
||||||
@ -83,7 +89,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
if (trimmedLine.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase))
|
if (trimmedLine.StartsWith(ExtInfPrefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
extInf = trimmedLine.Substring(ExtInfPrefix.Length).Trim();
|
extInf = trimmedLine.Substring(ExtInfPrefix.Length).Trim();
|
||||||
_logger.LogInformation("Found m3u channel: {0}", extInf);
|
|
||||||
}
|
}
|
||||||
else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#'))
|
else if (!string.IsNullOrWhiteSpace(extInf) && !trimmedLine.StartsWith('#'))
|
||||||
{
|
{
|
||||||
@ -99,6 +104,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
|
|
||||||
channel.Path = trimmedLine;
|
channel.Path = trimmedLine;
|
||||||
channels.Add(channel);
|
channels.Add(channel);
|
||||||
|
_logger.LogInformation("Parsed channel: {ChannelName}", channel.Name);
|
||||||
extInf = string.Empty;
|
extInf = string.Empty;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,24 +2,24 @@
|
|||||||
"Artists": "Kunstenare",
|
"Artists": "Kunstenare",
|
||||||
"Channels": "Kanale",
|
"Channels": "Kanale",
|
||||||
"Folders": "Lêergidse",
|
"Folders": "Lêergidse",
|
||||||
"Favorites": "Gunstellinge",
|
"Favorites": "Gunstelinge",
|
||||||
"HeaderFavoriteShows": "Gunsteling Vertonings",
|
"HeaderFavoriteShows": "Gunsteling Vertonings",
|
||||||
"ValueSpecialEpisodeName": "Spesiale - {0}",
|
"ValueSpecialEpisodeName": "Spesiale - {0}",
|
||||||
"HeaderAlbumArtists": "Album Kunstenaars",
|
"HeaderAlbumArtists": "Kunstenaars se Album",
|
||||||
"Books": "Boeke",
|
"Books": "Boeke",
|
||||||
"HeaderNextUp": "Volgende",
|
"HeaderNextUp": "Volgende",
|
||||||
"Movies": "Flieks",
|
"Movies": "Flieks",
|
||||||
"Shows": "Televisie Reekse",
|
"Shows": "Televisie Reekse",
|
||||||
"HeaderContinueWatching": "Kyk Verder",
|
"HeaderContinueWatching": "Kyk Verder",
|
||||||
"HeaderFavoriteEpisodes": "Gunsteling Episodes",
|
"HeaderFavoriteEpisodes": "Gunsteling Episodes",
|
||||||
"Photos": "Fotos",
|
"Photos": "Foto's",
|
||||||
"Playlists": "Snitlyste",
|
"Playlists": "Snitlyste",
|
||||||
"HeaderFavoriteArtists": "Gunsteling Kunstenaars",
|
"HeaderFavoriteArtists": "Gunsteling Kunstenaars",
|
||||||
"HeaderFavoriteAlbums": "Gunsteling Albums",
|
"HeaderFavoriteAlbums": "Gunsteling Albums",
|
||||||
"Sync": "Sinkroniseer",
|
"Sync": "Sinkroniseer",
|
||||||
"HeaderFavoriteSongs": "Gunsteling Liedjies",
|
"HeaderFavoriteSongs": "Gunsteling Liedjies",
|
||||||
"Songs": "Liedjies",
|
"Songs": "Liedjies",
|
||||||
"DeviceOnlineWithName": "{0} gekoppel is",
|
"DeviceOnlineWithName": "{0} is gekoppel",
|
||||||
"DeviceOfflineWithName": "{0} is ontkoppel",
|
"DeviceOfflineWithName": "{0} is ontkoppel",
|
||||||
"Collections": "Versamelings",
|
"Collections": "Versamelings",
|
||||||
"Inherit": "Ontvang",
|
"Inherit": "Ontvang",
|
||||||
@ -71,7 +71,7 @@
|
|||||||
"NameSeasonUnknown": "Seisoen Onbekend",
|
"NameSeasonUnknown": "Seisoen Onbekend",
|
||||||
"NameSeasonNumber": "Seisoen {0}",
|
"NameSeasonNumber": "Seisoen {0}",
|
||||||
"NameInstallFailed": "{0} installering het misluk",
|
"NameInstallFailed": "{0} installering het misluk",
|
||||||
"MusicVideos": "Musiek videos",
|
"MusicVideos": "Musiek Videos",
|
||||||
"Music": "Musiek",
|
"Music": "Musiek",
|
||||||
"MixedContent": "Gemengde inhoud",
|
"MixedContent": "Gemengde inhoud",
|
||||||
"MessageServerConfigurationUpdated": "Bediener konfigurasie is opgedateer",
|
"MessageServerConfigurationUpdated": "Bediener konfigurasie is opgedateer",
|
||||||
@ -79,15 +79,15 @@
|
|||||||
"MessageApplicationUpdatedTo": "Jellyfin Bediener is opgedateer na {0}",
|
"MessageApplicationUpdatedTo": "Jellyfin Bediener is opgedateer na {0}",
|
||||||
"MessageApplicationUpdated": "Jellyfin Bediener is opgedateer",
|
"MessageApplicationUpdated": "Jellyfin Bediener is opgedateer",
|
||||||
"Latest": "Nuutste",
|
"Latest": "Nuutste",
|
||||||
"LabelRunningTimeValue": "Lopende tyd: {0}",
|
"LabelRunningTimeValue": "Werktyd: {0}",
|
||||||
"LabelIpAddressValue": "IP adres: {0}",
|
"LabelIpAddressValue": "IP adres: {0}",
|
||||||
"ItemRemovedWithName": "{0} is uit versameling verwyder",
|
"ItemRemovedWithName": "{0} is uit versameling verwyder",
|
||||||
"ItemAddedWithName": "{0} is in die versameling",
|
"ItemAddedWithName": "{0} is by die versameling gevoeg",
|
||||||
"HomeVideos": "Tuis opnames",
|
"HomeVideos": "Tuis Videos",
|
||||||
"HeaderRecordingGroups": "Groep Opnames",
|
"HeaderRecordingGroups": "Groep Opnames",
|
||||||
"Genres": "Genres",
|
"Genres": "Genres",
|
||||||
"FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}",
|
"FailedLoginAttemptWithUserName": "Mislukte aansluiting van {0}",
|
||||||
"ChapterNameValue": "Hoofstuk",
|
"ChapterNameValue": "Hoofstuk {0}",
|
||||||
"CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}",
|
"CameraImageUploadedFrom": "'n Nuwe kamera photo opgelaai van {0}",
|
||||||
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
|
"AuthenticationSucceededWithUserName": "{0} suksesvol geverifieer",
|
||||||
"Albums": "Albums",
|
"Albums": "Albums",
|
||||||
@ -117,5 +117,7 @@
|
|||||||
"Forced": "Geforseer",
|
"Forced": "Geforseer",
|
||||||
"Default": "Oorspronklik",
|
"Default": "Oorspronklik",
|
||||||
"TaskCleanActivityLogDescription": "Verwyder aktiwiteitsaantekeninge ouer as die opgestelde ouderdom.",
|
"TaskCleanActivityLogDescription": "Verwyder aktiwiteitsaantekeninge ouer as die opgestelde ouderdom.",
|
||||||
"TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon"
|
"TaskCleanActivityLog": "Maak Aktiwiteitsaantekeninge Skoon",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Komprimeer databasis en verkort vrye ruimte. As hierdie taak uitgevoer word nadat die media versameling geskandeer is of ander veranderings aangebring is wat databasisaanpassings impliseer, kan dit die prestasie verbeter.",
|
||||||
|
"TaskOptimizeDatabase": "Optimaliseer databasis"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"Artists": "Artistes",
|
"Artists": "Artistes",
|
||||||
"AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
|
"AuthenticationSucceededWithUserName": "{0} s'ha autenticat correctament",
|
||||||
"Books": "Llibres",
|
"Books": "Llibres",
|
||||||
"CameraImageUploadedFrom": "Una nova imatge de la càmera ha estat pujada des de {0}",
|
"CameraImageUploadedFrom": "S'ha pujat una nova imatge des de la camera desde {0}",
|
||||||
"Channels": "Canals",
|
"Channels": "Canals",
|
||||||
"ChapterNameValue": "Capítol {0}",
|
"ChapterNameValue": "Capítol {0}",
|
||||||
"Collections": "Col·leccions",
|
"Collections": "Col·leccions",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"Albums": "Άλμπουμς",
|
"Albums": "Άλμπουμ",
|
||||||
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
|
"AppDeviceValues": "Εφαρμογή: {0}, Συσκευή: {1}",
|
||||||
"Application": "Εφαρμογή",
|
"Application": "Εφαρμογή",
|
||||||
"Artists": "Καλλιτέχνες",
|
"Artists": "Καλλιτέχνες",
|
||||||
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Αγαπημένα",
|
"Favorites": "Αγαπημένα",
|
||||||
"Folders": "Φάκελοι",
|
"Folders": "Φάκελοι",
|
||||||
"Genres": "Είδη",
|
"Genres": "Είδη",
|
||||||
"HeaderAlbumArtists": "Καλλιτέχνες του Άλμπουμ",
|
"HeaderAlbumArtists": "Άλμπουμ Καλλιτέχνη",
|
||||||
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
|
"HeaderContinueWatching": "Συνεχίστε την παρακολούθηση",
|
||||||
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
|
"HeaderFavoriteAlbums": "Αγαπημένα Άλμπουμ",
|
||||||
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
|
"HeaderFavoriteArtists": "Αγαπημένοι Καλλιτέχνες",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "Ανάμεικτο Περιεχόμενο",
|
"MixedContent": "Ανάμεικτο Περιεχόμενο",
|
||||||
"Movies": "Ταινίες",
|
"Movies": "Ταινίες",
|
||||||
"Music": "Μουσική",
|
"Music": "Μουσική",
|
||||||
"MusicVideos": "Μουσικά βίντεο",
|
"MusicVideos": "Μουσικά Βίντεο",
|
||||||
"NameInstallFailed": "{0} η εγκατάσταση απέτυχε",
|
"NameInstallFailed": "{0} η εγκατάσταση απέτυχε",
|
||||||
"NameSeasonNumber": "Κύκλος {0}",
|
"NameSeasonNumber": "Κύκλος {0}",
|
||||||
"NameSeasonUnknown": "Άγνωστος Κύκλος",
|
"NameSeasonUnknown": "Άγνωστος Κύκλος",
|
||||||
@ -62,7 +62,7 @@
|
|||||||
"NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε",
|
"NotificationOptionVideoPlaybackStopped": "Η αναπαραγωγή βίντεο σταμάτησε",
|
||||||
"Photos": "Φωτογραφίες",
|
"Photos": "Φωτογραφίες",
|
||||||
"Playlists": "Λίστες αναπαραγωγής",
|
"Playlists": "Λίστες αναπαραγωγής",
|
||||||
"Plugin": "Plugin",
|
"Plugin": "Πρόσθετο",
|
||||||
"PluginInstalledWithName": "{0} εγκαταστήθηκε",
|
"PluginInstalledWithName": "{0} εγκαταστήθηκε",
|
||||||
"PluginUninstalledWithName": "{0} έχει απεγκατασταθεί",
|
"PluginUninstalledWithName": "{0} έχει απεγκατασταθεί",
|
||||||
"PluginUpdatedWithName": "{0} έχει αναβαθμιστεί",
|
"PluginUpdatedWithName": "{0} έχει αναβαθμιστεί",
|
||||||
@ -118,5 +118,7 @@
|
|||||||
"TaskCleanActivityLog": "Καθαρό Αρχείο Καταγραφής Δραστηριοτήτων",
|
"TaskCleanActivityLog": "Καθαρό Αρχείο Καταγραφής Δραστηριοτήτων",
|
||||||
"Undefined": "Απροσδιόριστο",
|
"Undefined": "Απροσδιόριστο",
|
||||||
"Forced": "Εξαναγκασμένο",
|
"Forced": "Εξαναγκασμένο",
|
||||||
"Default": "Προεπιλογή"
|
"Default": "Προεπιλογή",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Συμπιέζει τη βάση δεδομένων και δημιουργεί ελεύθερο χώρο. Η εκτέλεση αυτής της εργασίας μετά τη σάρωση της βιβλιοθήκης ή την πραγματοποίηση άλλων αλλαγών που συνεπάγονται τροποποιήσεις της βάσης δεδομένων μπορεί να βελτιώσει την απόδοση.",
|
||||||
|
"TaskOptimizeDatabase": "Βελτιστοποίηση βάσης δεδομένων"
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
"Folders": "Folders",
|
"Folders": "Folders",
|
||||||
"Forced": "Forced",
|
"Forced": "Forced",
|
||||||
"Genres": "Genres",
|
"Genres": "Genres",
|
||||||
"HeaderAlbumArtists": "Album Artists",
|
"HeaderAlbumArtists": "Artist's Album",
|
||||||
"HeaderContinueWatching": "Continue Watching",
|
"HeaderContinueWatching": "Continue Watching",
|
||||||
"HeaderFavoriteAlbums": "Favorite Albums",
|
"HeaderFavoriteAlbums": "Favorite Albums",
|
||||||
"HeaderFavoriteArtists": "Favorite Artists",
|
"HeaderFavoriteArtists": "Favorite Artists",
|
||||||
@ -27,7 +27,7 @@
|
|||||||
"HeaderLiveTV": "Live TV",
|
"HeaderLiveTV": "Live TV",
|
||||||
"HeaderNextUp": "Next Up",
|
"HeaderNextUp": "Next Up",
|
||||||
"HeaderRecordingGroups": "Recording Groups",
|
"HeaderRecordingGroups": "Recording Groups",
|
||||||
"HomeVideos": "Home videos",
|
"HomeVideos": "Home Videos",
|
||||||
"Inherit": "Inherit",
|
"Inherit": "Inherit",
|
||||||
"ItemAddedWithName": "{0} was added to the library",
|
"ItemAddedWithName": "{0} was added to the library",
|
||||||
"ItemRemovedWithName": "{0} was removed from the library",
|
"ItemRemovedWithName": "{0} was removed from the library",
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"MixedContent": "Mixed content",
|
"MixedContent": "Mixed content",
|
||||||
"Movies": "Movies",
|
"Movies": "Movies",
|
||||||
"Music": "Music",
|
"Music": "Music",
|
||||||
"MusicVideos": "Music videos",
|
"MusicVideos": "Music Videos",
|
||||||
"NameInstallFailed": "{0} installation failed",
|
"NameInstallFailed": "{0} installation failed",
|
||||||
"NameSeasonNumber": "Season {0}",
|
"NameSeasonNumber": "Season {0}",
|
||||||
"NameSeasonUnknown": "Season Unknown",
|
"NameSeasonUnknown": "Season Unknown",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Favoritos",
|
"Favorites": "Favoritos",
|
||||||
"Folders": "Carpetas",
|
"Folders": "Carpetas",
|
||||||
"Genres": "Géneros",
|
"Genres": "Géneros",
|
||||||
"HeaderAlbumArtists": "Artistas del álbum",
|
"HeaderAlbumArtists": "Artistas del Álbum",
|
||||||
"HeaderContinueWatching": "Continuar viendo",
|
"HeaderContinueWatching": "Continuar viendo",
|
||||||
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
||||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "TV en vivo",
|
"HeaderLiveTV": "TV en vivo",
|
||||||
"HeaderNextUp": "A continuación",
|
"HeaderNextUp": "A continuación",
|
||||||
"HeaderRecordingGroups": "Grupos de grabación",
|
"HeaderRecordingGroups": "Grupos de grabación",
|
||||||
"HomeVideos": "Videos caseros",
|
"HomeVideos": "Videos Caseros",
|
||||||
"Inherit": "Heredar",
|
"Inherit": "Heredar",
|
||||||
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
|
"ItemAddedWithName": "{0} fue agregado a la biblioteca",
|
||||||
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
|
"ItemRemovedWithName": "{0} fue removido de la biblioteca",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "Contenido mezclado",
|
"MixedContent": "Contenido mezclado",
|
||||||
"Movies": "Películas",
|
"Movies": "Películas",
|
||||||
"Music": "Música",
|
"Music": "Música",
|
||||||
"MusicVideos": "Videos musicales",
|
"MusicVideos": "Videos Musicales",
|
||||||
"NameInstallFailed": "Falló la instalación de {0}",
|
"NameInstallFailed": "Falló la instalación de {0}",
|
||||||
"NameSeasonNumber": "Temporada {0}",
|
"NameSeasonNumber": "Temporada {0}",
|
||||||
"NameSeasonUnknown": "Temporada desconocida",
|
"NameSeasonUnknown": "Temporada desconocida",
|
||||||
@ -49,7 +49,7 @@
|
|||||||
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
|
"NotificationOptionAudioPlayback": "Reproducción de audio iniciada",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
|
"NotificationOptionAudioPlaybackStopped": "Reproducción de audio detenida",
|
||||||
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
|
"NotificationOptionCameraImageUploaded": "Imagen de la cámara subida",
|
||||||
"NotificationOptionInstallationFailed": "Falla de instalación",
|
"NotificationOptionInstallationFailed": "Fallo en la instalación",
|
||||||
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
|
"NotificationOptionNewLibraryContent": "Nuevo contenido agregado",
|
||||||
"NotificationOptionPluginError": "Falla de complemento",
|
"NotificationOptionPluginError": "Falla de complemento",
|
||||||
"NotificationOptionPluginInstalled": "Complemento instalado",
|
"NotificationOptionPluginInstalled": "Complemento instalado",
|
||||||
@ -69,7 +69,7 @@
|
|||||||
"ProviderValue": "Proveedor: {0}",
|
"ProviderValue": "Proveedor: {0}",
|
||||||
"ScheduledTaskFailedWithName": "{0} falló",
|
"ScheduledTaskFailedWithName": "{0} falló",
|
||||||
"ScheduledTaskStartedWithName": "{0} iniciado",
|
"ScheduledTaskStartedWithName": "{0} iniciado",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} debe ser reiniciado",
|
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
|
||||||
"Shows": "Programas",
|
"Shows": "Programas",
|
||||||
"Songs": "Canciones",
|
"Songs": "Canciones",
|
||||||
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
|
"StartupEmbyServerIsLoading": "El servidor Jellyfin está cargando. Por favor, intente de nuevo pronto.",
|
||||||
@ -94,9 +94,9 @@
|
|||||||
"VersionNumber": "Versión {0}",
|
"VersionNumber": "Versión {0}",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
|
"TaskDownloadMissingSubtitlesDescription": "Busca subtítulos faltantes en Internet basándose en la configuración de metadatos.",
|
||||||
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
|
"TaskDownloadMissingSubtitles": "Descargar subtítulos faltantes",
|
||||||
"TaskRefreshChannelsDescription": "Actualiza la información de canales de Internet.",
|
"TaskRefreshChannelsDescription": "Actualiza la información de los canales de Internet.",
|
||||||
"TaskRefreshChannels": "Actualizar canales",
|
"TaskRefreshChannels": "Actualizar canales",
|
||||||
"TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día.",
|
"TaskCleanTranscodeDescription": "Elimina archivos transcodificados que tengan más de un día de antigüedad.",
|
||||||
"TaskCleanTranscode": "Limpiar directorio de transcodificado",
|
"TaskCleanTranscode": "Limpiar directorio de transcodificado",
|
||||||
"TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.",
|
"TaskUpdatePluginsDescription": "Descarga e instala actualizaciones para complementos que están configurados para actualizarse automáticamente.",
|
||||||
"TaskUpdatePlugins": "Actualizar complementos",
|
"TaskUpdatePlugins": "Actualizar complementos",
|
||||||
@ -118,5 +118,7 @@
|
|||||||
"TaskCleanActivityLog": "Limpiar registro de actividades",
|
"TaskCleanActivityLog": "Limpiar registro de actividades",
|
||||||
"Undefined": "Sin definir",
|
"Undefined": "Sin definir",
|
||||||
"Forced": "Forzado",
|
"Forced": "Forzado",
|
||||||
"Default": "Predeterminado"
|
"Default": "Predeterminado",
|
||||||
|
"TaskOptimizeDatabase": "Optimizar base de datos",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Compacta la base de datos y trunca el espacio libre. Puede mejorar el rendimiento si se realiza esta tarea después de escanear la biblioteca o después de realizar otros cambios que impliquen modificar la base de datos."
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Favoritos",
|
"Favorites": "Favoritos",
|
||||||
"Folders": "Carpetas",
|
"Folders": "Carpetas",
|
||||||
"Genres": "Géneros",
|
"Genres": "Géneros",
|
||||||
"HeaderAlbumArtists": "Artistas del álbum",
|
"HeaderAlbumArtists": "Artista del álbum",
|
||||||
"HeaderContinueWatching": "Continuar viendo",
|
"HeaderContinueWatching": "Continuar viendo",
|
||||||
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
"HeaderFavoriteAlbums": "Álbumes favoritos",
|
||||||
"HeaderFavoriteArtists": "Artistas favoritos",
|
"HeaderFavoriteArtists": "Artistas favoritos",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Kedvencek",
|
"Favorites": "Kedvencek",
|
||||||
"Folders": "Könyvtárak",
|
"Folders": "Könyvtárak",
|
||||||
"Genres": "Műfajok",
|
"Genres": "Műfajok",
|
||||||
"HeaderAlbumArtists": "Album előadók",
|
"HeaderAlbumArtists": "Előadó albumai",
|
||||||
"HeaderContinueWatching": "Megtekintés folytatása",
|
"HeaderContinueWatching": "Megtekintés folytatása",
|
||||||
"HeaderFavoriteAlbums": "Kedvenc albumok",
|
"HeaderFavoriteAlbums": "Kedvenc albumok",
|
||||||
"HeaderFavoriteArtists": "Kedvenc előadók",
|
"HeaderFavoriteArtists": "Kedvenc előadók",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Preferiti",
|
"Favorites": "Preferiti",
|
||||||
"Folders": "Cartelle",
|
"Folders": "Cartelle",
|
||||||
"Genres": "Generi",
|
"Genres": "Generi",
|
||||||
"HeaderAlbumArtists": "Artisti degli Album",
|
"HeaderAlbumArtists": "Artisti dell'Album",
|
||||||
"HeaderContinueWatching": "Continua a guardare",
|
"HeaderContinueWatching": "Continua a guardare",
|
||||||
"HeaderFavoriteAlbums": "Album Preferiti",
|
"HeaderFavoriteAlbums": "Album Preferiti",
|
||||||
"HeaderFavoriteArtists": "Artisti Preferiti",
|
"HeaderFavoriteArtists": "Artisti Preferiti",
|
||||||
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "Diretta TV",
|
"HeaderLiveTV": "Diretta TV",
|
||||||
"HeaderNextUp": "Prossimo",
|
"HeaderNextUp": "Prossimo",
|
||||||
"HeaderRecordingGroups": "Gruppi di Registrazione",
|
"HeaderRecordingGroups": "Gruppi di Registrazione",
|
||||||
"HomeVideos": "Video personali",
|
"HomeVideos": "Video Personali",
|
||||||
"Inherit": "Eredita",
|
"Inherit": "Eredita",
|
||||||
"ItemAddedWithName": "{0} è stato aggiunto alla libreria",
|
"ItemAddedWithName": "{0} è stato aggiunto alla libreria",
|
||||||
"ItemRemovedWithName": "{0} è stato rimosso dalla libreria",
|
"ItemRemovedWithName": "{0} è stato rimosso dalla libreria",
|
||||||
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "Contenuto misto",
|
"MixedContent": "Contenuto misto",
|
||||||
"Movies": "Film",
|
"Movies": "Film",
|
||||||
"Music": "Musica",
|
"Music": "Musica",
|
||||||
"MusicVideos": "Video musicali",
|
"MusicVideos": "Video Musicali",
|
||||||
"NameInstallFailed": "{0} installazione fallita",
|
"NameInstallFailed": "{0} installazione fallita",
|
||||||
"NameSeasonNumber": "Stagione {0}",
|
"NameSeasonNumber": "Stagione {0}",
|
||||||
"NameSeasonUnknown": "Stagione sconosciuta",
|
"NameSeasonUnknown": "Stagione sconosciuta",
|
||||||
@ -70,7 +70,7 @@
|
|||||||
"ScheduledTaskFailedWithName": "{0} fallito",
|
"ScheduledTaskFailedWithName": "{0} fallito",
|
||||||
"ScheduledTaskStartedWithName": "{0} avviati",
|
"ScheduledTaskStartedWithName": "{0} avviati",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} deve essere riavviato",
|
"ServerNameNeedsToBeRestarted": "{0} deve essere riavviato",
|
||||||
"Shows": "Programmi",
|
"Shows": "Serie TV",
|
||||||
"Songs": "Canzoni",
|
"Songs": "Canzoni",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin server si sta avviando. Per favore riprova più tardi.",
|
"StartupEmbyServerIsLoading": "Jellyfin server si sta avviando. Per favore riprova più tardi.",
|
||||||
"SubtitleDownloadFailureForItem": "Impossibile scaricare i sottotitoli per {0}",
|
"SubtitleDownloadFailureForItem": "Impossibile scaricare i sottotitoli per {0}",
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
"Favorites": "Tañdaulylar",
|
"Favorites": "Tañdaulylar",
|
||||||
"Folders": "Qaltalar",
|
"Folders": "Qaltalar",
|
||||||
"Genres": "Janrlar",
|
"Genres": "Janrlar",
|
||||||
"HeaderAlbumArtists": "Älbom oryndauşylary",
|
"HeaderAlbumArtists": "Oryndauşynyñ älbomy",
|
||||||
"HeaderContinueWatching": "Qaraudy jalğastyru",
|
"HeaderContinueWatching": "Qaraudy jalğastyru",
|
||||||
"HeaderFavoriteAlbums": "Tañdauly älbomdar",
|
"HeaderFavoriteAlbums": "Tañdauly älbomdar",
|
||||||
"HeaderFavoriteArtists": "Tañdauly oryndauşylar",
|
"HeaderFavoriteArtists": "Tañdauly oryndauşylar",
|
||||||
|
@ -103,7 +103,7 @@
|
|||||||
"ValueSpecialEpisodeName": "പ്രത്യേക - {0}",
|
"ValueSpecialEpisodeName": "പ്രത്യേക - {0}",
|
||||||
"Collections": "ശേഖരങ്ങൾ",
|
"Collections": "ശേഖരങ്ങൾ",
|
||||||
"Folders": "ഫോൾഡറുകൾ",
|
"Folders": "ഫോൾഡറുകൾ",
|
||||||
"HeaderAlbumArtists": "ആൽബം ആർട്ടിസ്റ്റുകൾ",
|
"HeaderAlbumArtists": "കലാകാരന്റെ ആൽബം",
|
||||||
"Sync": "സമന്വയിപ്പിക്കുക",
|
"Sync": "സമന്വയിപ്പിക്കുക",
|
||||||
"Movies": "സിനിമകൾ",
|
"Movies": "സിനിമകൾ",
|
||||||
"Photos": "ഫോട്ടോകൾ",
|
"Photos": "ഫോട്ടോകൾ",
|
||||||
|
1
Emby.Server.Implementations/Localization/Core/pr.json
Normal file
1
Emby.Server.Implementations/Localization/Core/pr.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
{}
|
@ -25,7 +25,7 @@
|
|||||||
"HeaderLiveTV": "Эфир",
|
"HeaderLiveTV": "Эфир",
|
||||||
"HeaderNextUp": "Очередное",
|
"HeaderNextUp": "Очередное",
|
||||||
"HeaderRecordingGroups": "Группы записей",
|
"HeaderRecordingGroups": "Группы записей",
|
||||||
"HomeVideos": "Домашнее видео",
|
"HomeVideos": "Домашние видео",
|
||||||
"Inherit": "Наследуемое",
|
"Inherit": "Наследуемое",
|
||||||
"ItemAddedWithName": "{0} - добавлено в медиатеку",
|
"ItemAddedWithName": "{0} - добавлено в медиатеку",
|
||||||
"ItemRemovedWithName": "{0} - изъято из медиатеки",
|
"ItemRemovedWithName": "{0} - изъято из медиатеки",
|
||||||
|
@ -39,7 +39,7 @@
|
|||||||
"MixedContent": "Zmiešaný obsah",
|
"MixedContent": "Zmiešaný obsah",
|
||||||
"Movies": "Filmy",
|
"Movies": "Filmy",
|
||||||
"Music": "Hudba",
|
"Music": "Hudba",
|
||||||
"MusicVideos": "Hudobné videoklipy",
|
"MusicVideos": "Hudobné videá",
|
||||||
"NameInstallFailed": "Inštalácia {0} zlyhala",
|
"NameInstallFailed": "Inštalácia {0} zlyhala",
|
||||||
"NameSeasonNumber": "Séria {0}",
|
"NameSeasonNumber": "Séria {0}",
|
||||||
"NameSeasonUnknown": "Neznáma séria",
|
"NameSeasonUnknown": "Neznáma séria",
|
||||||
|
@ -118,5 +118,6 @@
|
|||||||
"TaskCleanActivityLog": "Rensa Aktivitets Logg",
|
"TaskCleanActivityLog": "Rensa Aktivitets Logg",
|
||||||
"Undefined": "odefinierad",
|
"Undefined": "odefinierad",
|
||||||
"Forced": "Tvingad",
|
"Forced": "Tvingad",
|
||||||
"Default": "Standard"
|
"Default": "Standard",
|
||||||
|
"TaskOptimizeDatabase": "Optimera databasen"
|
||||||
}
|
}
|
||||||
|
@ -43,7 +43,7 @@
|
|||||||
"NameInstallFailed": "{0} kurulumu başarısız",
|
"NameInstallFailed": "{0} kurulumu başarısız",
|
||||||
"NameSeasonNumber": "Sezon {0}",
|
"NameSeasonNumber": "Sezon {0}",
|
||||||
"NameSeasonUnknown": "Bilinmeyen Sezon",
|
"NameSeasonUnknown": "Bilinmeyen Sezon",
|
||||||
"NewVersionIsAvailable": "Jellyfin Sunucusunun yeni bir versiyonu indirmek için hazır.",
|
"NewVersionIsAvailable": "Jellyfin Sunucusunun yeni bir sürümü indirmek için hazır.",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "Uygulama güncellemesi mevcut",
|
"NotificationOptionApplicationUpdateAvailable": "Uygulama güncellemesi mevcut",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "Uygulama güncellemesi yüklendi",
|
"NotificationOptionApplicationUpdateInstalled": "Uygulama güncellemesi yüklendi",
|
||||||
"NotificationOptionAudioPlayback": "Ses çalma başladı",
|
"NotificationOptionAudioPlayback": "Ses çalma başladı",
|
||||||
@ -75,7 +75,7 @@
|
|||||||
"StartupEmbyServerIsLoading": "Jellyfin Sunucusu yükleniyor. Lütfen kısa süre sonra tekrar deneyin.",
|
"StartupEmbyServerIsLoading": "Jellyfin Sunucusu yükleniyor. Lütfen kısa süre sonra tekrar deneyin.",
|
||||||
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
|
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
|
||||||
"SubtitleDownloadFailureFromForItem": "{1} için alt yazılar {0} 'dan indirilemedi",
|
"SubtitleDownloadFailureFromForItem": "{1} için alt yazılar {0} 'dan indirilemedi",
|
||||||
"Sync": "Eşitle",
|
"Sync": "Eşzamanlama",
|
||||||
"System": "Sistem",
|
"System": "Sistem",
|
||||||
"TvShows": "Diziler",
|
"TvShows": "Diziler",
|
||||||
"User": "Kullanıcı",
|
"User": "Kullanıcı",
|
||||||
@ -89,34 +89,36 @@
|
|||||||
"UserPolicyUpdatedWithName": "Kullanıcı politikası {0} için güncellendi",
|
"UserPolicyUpdatedWithName": "Kullanıcı politikası {0} için güncellendi",
|
||||||
"UserStartedPlayingItemWithValues": "{0}, {2} cihazında {1} izliyor",
|
"UserStartedPlayingItemWithValues": "{0}, {2} cihazında {1} izliyor",
|
||||||
"UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi",
|
"UserStoppedPlayingItemWithValues": "{0}, {2} cihazında {1} izlemeyi bitirdi",
|
||||||
"ValueHasBeenAddedToLibrary": "Medya kitaplığınıza {0} eklendi",
|
"ValueHasBeenAddedToLibrary": "Medya kütüphanenize {0} eklendi",
|
||||||
"ValueSpecialEpisodeName": "Özel - {0}",
|
"ValueSpecialEpisodeName": "Özel - {0}",
|
||||||
"VersionNumber": "Versiyon {0}",
|
"VersionNumber": "Sürüm {0}",
|
||||||
"TaskCleanCache": "Geçici dosya klasörünü temizle",
|
"TaskCleanCache": "Geçici dosya klasörünü temizle",
|
||||||
"TasksChannelsCategory": "İnternet kanalları",
|
"TasksChannelsCategory": "İnternet kanalları",
|
||||||
"TasksApplicationCategory": "Uygulama",
|
"TasksApplicationCategory": "Uygulama",
|
||||||
"TasksLibraryCategory": "Kütüphane",
|
"TasksLibraryCategory": "Kütüphane",
|
||||||
"TasksMaintenanceCategory": "Onarım",
|
"TasksMaintenanceCategory": "Bakım",
|
||||||
"TaskRefreshPeopleDescription": "Medya kütüphanenizdeki videoların oyuncu ve yönetmen bilgilerini günceller.",
|
"TaskRefreshPeopleDescription": "Medya kütüphanenizdeki videoların oyuncu ve yönetmen bilgilerini günceller.",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Metadata ayarlarını baz alarak eksik altyazıları internette arar.",
|
"TaskDownloadMissingSubtitlesDescription": "Metadata ayarlarını baz alarak eksik altyazıları internette arar.",
|
||||||
"TaskDownloadMissingSubtitles": "Eksik altyazıları indir",
|
"TaskDownloadMissingSubtitles": "Eksik altyazıları indir",
|
||||||
"TaskRefreshChannelsDescription": "Internet kanal bilgilerini yenile.",
|
"TaskRefreshChannelsDescription": "Internet kanal bilgilerini yenile.",
|
||||||
"TaskRefreshChannels": "Kanalları Yenile",
|
"TaskRefreshChannels": "Kanalları Yenile",
|
||||||
"TaskCleanTranscodeDescription": "Bir günü dolmuş dönüştürme bilgisi içeren dosyaları siler.",
|
"TaskCleanTranscodeDescription": "Bir günden daha eski dönüştürme dosyalarını siler.",
|
||||||
"TaskCleanTranscode": "Dönüşüm Dizinini Temizle",
|
"TaskCleanTranscode": "Dönüşüm Dizinini Temizle",
|
||||||
"TaskUpdatePluginsDescription": "Otomatik güncellenmeye ayarlanmış eklentilerin güncellemelerini indirir ve kurar.",
|
"TaskUpdatePluginsDescription": "Otomatik güncellenmeye ayarlanmış eklentilerin güncellemelerini indirir ve kurar.",
|
||||||
"TaskUpdatePlugins": "Eklentileri Güncelle",
|
"TaskUpdatePlugins": "Eklentileri Güncelle",
|
||||||
"TaskRefreshPeople": "Kullanıcıları Yenile",
|
"TaskRefreshPeople": "Kullanıcıları Yenile",
|
||||||
"TaskCleanLogsDescription": "{0} günden eski log dosyalarını siler.",
|
"TaskCleanLogsDescription": "{0} günden eski günlük dosyalarını siler.",
|
||||||
"TaskCleanLogs": "Log Dizinini Temizle",
|
"TaskCleanLogs": "Günlük Dizinini Temizle",
|
||||||
"TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve bilgileri yeniler.",
|
"TaskRefreshLibraryDescription": "Medya kütüphanenize eklenen yeni dosyaları arar ve ortam bilgilerini yeniler.",
|
||||||
"TaskRefreshLibrary": "Medya Kütüphanesini Tara",
|
"TaskRefreshLibrary": "Medya Kütüphanesini Tara",
|
||||||
"TaskRefreshChapterImagesDescription": "Sahnelere ayrılmış videolar için küçük resimler oluştur.",
|
"TaskRefreshChapterImagesDescription": "Sahnelere ayrılmış videolar için küçük resimler oluştur.",
|
||||||
"TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar",
|
"TaskRefreshChapterImages": "Bölüm Resimlerini Çıkar",
|
||||||
"TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler.",
|
"TaskCleanCacheDescription": "Sistem tarafından artık ihtiyaç duyulmayan önbellek dosyalarını siler.",
|
||||||
"TaskCleanActivityLog": "İşlem Günlüğünü Temizle",
|
"TaskCleanActivityLog": "Etkinlik Günlüğünü Temizle",
|
||||||
"TaskCleanActivityLogDescription": "Belirtilen sureden daha eski etkinlik log kayıtları silindi.",
|
"TaskCleanActivityLogDescription": "Yapılandırılan tarihten daha eski olan etkinlik günlüğü girişlerini siler.",
|
||||||
"Undefined": "Bilinmeyen",
|
"Undefined": "Bilinmeyen",
|
||||||
"Default": "Varsayılan",
|
"Default": "Varsayılan",
|
||||||
"Forced": "Zorla"
|
"Forced": "Zorla",
|
||||||
|
"TaskOptimizeDatabaseDescription": "Veritabanını sıkıştırır ve boş alanı keser. Kitaplığı taradıktan sonra veya veritabanında değişiklik anlamına gelen diğer işlemleri yaptıktan sonra bu görevi çalıştırmak performansı artırabilir.",
|
||||||
|
"TaskOptimizeDatabase": "Veritabanını optimize et"
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"Favorites": "Yêu Thích",
|
"Favorites": "Yêu Thích",
|
||||||
"Folders": "Thư Mục",
|
"Folders": "Thư Mục",
|
||||||
"Genres": "Thể Loại",
|
"Genres": "Thể Loại",
|
||||||
"HeaderAlbumArtists": "Tuyển Tập Nghệ sĩ",
|
"HeaderAlbumArtists": "Album Nghệ sĩ",
|
||||||
"HeaderContinueWatching": "Xem Tiếp",
|
"HeaderContinueWatching": "Xem Tiếp",
|
||||||
"HeaderLiveTV": "TV Trực Tiếp",
|
"HeaderLiveTV": "TV Trực Tiếp",
|
||||||
"Movies": "Phim",
|
"Movies": "Phim",
|
||||||
@ -82,7 +82,7 @@
|
|||||||
"NameSeasonUnknown": "Không Rõ Mùa",
|
"NameSeasonUnknown": "Không Rõ Mùa",
|
||||||
"NameSeasonNumber": "Phần {0}",
|
"NameSeasonNumber": "Phần {0}",
|
||||||
"NameInstallFailed": "{0} cài đặt thất bại",
|
"NameInstallFailed": "{0} cài đặt thất bại",
|
||||||
"MusicVideos": "Video Nhạc",
|
"MusicVideos": "Videos Nhạc",
|
||||||
"Music": "Nhạc",
|
"Music": "Nhạc",
|
||||||
"MixedContent": "Nội dung hỗn hợp",
|
"MixedContent": "Nội dung hỗn hợp",
|
||||||
"MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật",
|
"MessageServerConfigurationUpdated": "Cấu hình máy chủ đã được cập nhật",
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -23,6 +21,9 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
public class LocalizationManager : ILocalizationManager
|
public class LocalizationManager : ILocalizationManager
|
||||||
{
|
{
|
||||||
private const string DefaultCulture = "en-US";
|
private const string DefaultCulture = "en-US";
|
||||||
|
private const string RatingsPath = "Emby.Server.Implementations.Localization.Ratings.";
|
||||||
|
private const string CulturesPath = "Emby.Server.Implementations.Localization.iso6392.txt";
|
||||||
|
private const string CountriesPath = "Emby.Server.Implementations.Localization.countries.json";
|
||||||
private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
|
private static readonly Assembly _assembly = typeof(LocalizationManager).Assembly;
|
||||||
private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
|
private static readonly string[] _unratedValues = { "n/a", "unrated", "not rated" };
|
||||||
|
|
||||||
@ -35,10 +36,10 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
|
private readonly ConcurrentDictionary<string, Dictionary<string, string>> _dictionaries =
|
||||||
new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
new ConcurrentDictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
private List<CultureDto> _cultures;
|
|
||||||
|
|
||||||
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
|
||||||
|
|
||||||
|
private List<CultureDto> _cultures = new List<CultureDto>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="LocalizationManager" /> class.
|
/// Initializes a new instance of the <see cref="LocalizationManager" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -58,43 +59,39 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
/// <returns><see cref="Task" />.</returns>
|
/// <returns><see cref="Task" />.</returns>
|
||||||
public async Task LoadAll()
|
public async Task LoadAll()
|
||||||
{
|
{
|
||||||
const string RatingsResource = "Emby.Server.Implementations.Localization.Ratings.";
|
|
||||||
|
|
||||||
// Extract from the assembly
|
// Extract from the assembly
|
||||||
foreach (var resource in _assembly.GetManifestResourceNames())
|
foreach (var resource in _assembly.GetManifestResourceNames())
|
||||||
{
|
{
|
||||||
if (!resource.StartsWith(RatingsResource, StringComparison.Ordinal))
|
if (!resource.StartsWith(RatingsPath, StringComparison.Ordinal))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
string countryCode = resource.Substring(RatingsResource.Length, 2);
|
string countryCode = resource.Substring(RatingsPath.Length, 2);
|
||||||
var dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase);
|
var dict = new Dictionary<string, ParentalRating>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
using (var str = _assembly.GetManifestResourceStream(resource))
|
await using var stream = _assembly.GetManifestResourceStream(resource);
|
||||||
using (var reader = new StreamReader(str))
|
using var reader = new StreamReader(stream!); // shouldn't be null here, we just got the resource path from Assembly.GetManifestResourceNames()
|
||||||
|
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(line))
|
continue;
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] parts = line.Split(',');
|
|
||||||
if (parts.Length == 2
|
|
||||||
&& int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
|
|
||||||
{
|
|
||||||
var name = parts[0];
|
|
||||||
dict.Add(name, new ParentalRating(name, value));
|
|
||||||
}
|
|
||||||
#if DEBUG
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string[] parts = line.Split(',');
|
||||||
|
if (parts.Length == 2
|
||||||
|
&& int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
|
||||||
|
{
|
||||||
|
var name = parts[0];
|
||||||
|
dict.Add(name, new ParentalRating(name, value));
|
||||||
|
}
|
||||||
|
#if DEBUG
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
_allParentalRatings[countryCode] = dict;
|
_allParentalRatings[countryCode] = dict;
|
||||||
@ -114,52 +111,49 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
{
|
{
|
||||||
List<CultureDto> list = new List<CultureDto>();
|
List<CultureDto> list = new List<CultureDto>();
|
||||||
|
|
||||||
const string ResourcePath = "Emby.Server.Implementations.Localization.iso6392.txt";
|
await using var stream = _assembly.GetManifestResourceStream(CulturesPath)
|
||||||
|
?? throw new InvalidOperationException($"Invalid resource path: '{CulturesPath}'");
|
||||||
using (var stream = _assembly.GetManifestResourceStream(ResourcePath))
|
using var reader = new StreamReader(stream);
|
||||||
using (var reader = new StreamReader(stream))
|
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
||||||
{
|
{
|
||||||
await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(line))
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var parts = line.Split('|');
|
||||||
|
|
||||||
|
if (parts.Length == 5)
|
||||||
|
{
|
||||||
|
string name = parts[3];
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var parts = line.Split('|');
|
string twoCharName = parts[2];
|
||||||
|
if (string.IsNullOrWhiteSpace(twoCharName))
|
||||||
if (parts.Length == 5)
|
|
||||||
{
|
{
|
||||||
string name = parts[3];
|
continue;
|
||||||
if (string.IsNullOrWhiteSpace(name))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string twoCharName = parts[2];
|
|
||||||
if (string.IsNullOrWhiteSpace(twoCharName))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
string[] threeletterNames;
|
|
||||||
if (string.IsNullOrWhiteSpace(parts[1]))
|
|
||||||
{
|
|
||||||
threeletterNames = new[] { parts[0] };
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
threeletterNames = new[] { parts[0], parts[1] };
|
|
||||||
}
|
|
||||||
|
|
||||||
list.Add(new CultureDto
|
|
||||||
{
|
|
||||||
DisplayName = name,
|
|
||||||
Name = name,
|
|
||||||
ThreeLetterISOLanguageNames = threeletterNames,
|
|
||||||
TwoLetterISOLanguageName = twoCharName
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string[] threeletterNames;
|
||||||
|
if (string.IsNullOrWhiteSpace(parts[1]))
|
||||||
|
{
|
||||||
|
threeletterNames = new[] { parts[0] };
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
threeletterNames = new[] { parts[0], parts[1] };
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(new CultureDto
|
||||||
|
{
|
||||||
|
DisplayName = name,
|
||||||
|
Name = name,
|
||||||
|
ThreeLetterISOLanguageNames = threeletterNames,
|
||||||
|
TwoLetterISOLanguageName = twoCharName
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +161,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public CultureDto FindLanguageInfo(string language)
|
public CultureDto? FindLanguageInfo(string language)
|
||||||
{
|
{
|
||||||
// TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs
|
// TODO language should ideally be a ReadOnlySpan but moq cannot mock ref structs
|
||||||
for (var i = 0; i < _cultures.Count; i++)
|
for (var i = 0; i < _cultures.Count; i++)
|
||||||
@ -188,9 +182,10 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public IEnumerable<CountryInfo> GetCountries()
|
public IEnumerable<CountryInfo> GetCountries()
|
||||||
{
|
{
|
||||||
using StreamReader reader = new StreamReader(_assembly.GetManifestResourceStream("Emby.Server.Implementations.Localization.countries.json"));
|
using StreamReader reader = new StreamReader(
|
||||||
|
_assembly.GetManifestResourceStream(CountriesPath) ?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'"));
|
||||||
return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions);
|
return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions)
|
||||||
|
?? throw new InvalidOperationException($"Resource contains invalid data: '{CountriesPath}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -210,7 +205,9 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
countryCode = "us";
|
countryCode = "us";
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetRatings(countryCode) ?? GetRatings("us");
|
return GetRatings(countryCode)
|
||||||
|
?? GetRatings("us")
|
||||||
|
?? throw new InvalidOperationException($"Invalid resource path: '{CountriesPath}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -218,7 +215,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="countryCode">The country code.</param>
|
/// <param name="countryCode">The country code.</param>
|
||||||
/// <returns>The ratings.</returns>
|
/// <returns>The ratings.</returns>
|
||||||
private Dictionary<string, ParentalRating> GetRatings(string countryCode)
|
private Dictionary<string, ParentalRating>? GetRatings(string countryCode)
|
||||||
{
|
{
|
||||||
_allParentalRatings.TryGetValue(countryCode, out var value);
|
_allParentalRatings.TryGetValue(countryCode, out var value);
|
||||||
|
|
||||||
@ -243,7 +240,7 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
|
|
||||||
var ratingsDictionary = GetParentalRatingsDictionary();
|
var ratingsDictionary = GetParentalRatingsDictionary();
|
||||||
|
|
||||||
if (ratingsDictionary.TryGetValue(rating, out ParentalRating value))
|
if (ratingsDictionary.TryGetValue(rating, out ParentalRating? value))
|
||||||
{
|
{
|
||||||
return value.Value;
|
return value.Value;
|
||||||
}
|
}
|
||||||
@ -273,20 +270,6 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool HasUnicodeCategory(string value, UnicodeCategory category)
|
|
||||||
{
|
|
||||||
foreach (var chr in value)
|
|
||||||
{
|
|
||||||
if (char.GetUnicodeCategory(chr) == category)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string GetLocalizedString(string phrase)
|
public string GetLocalizedString(string phrase)
|
||||||
{
|
{
|
||||||
@ -350,22 +333,23 @@ namespace Emby.Server.Implementations.Localization
|
|||||||
|
|
||||||
private async Task CopyInto(IDictionary<string, string> dictionary, string resourcePath)
|
private async Task CopyInto(IDictionary<string, string> dictionary, string resourcePath)
|
||||||
{
|
{
|
||||||
using (var stream = _assembly.GetManifestResourceStream(resourcePath))
|
await using var stream = _assembly.GetManifestResourceStream(resourcePath);
|
||||||
|
// If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
|
||||||
|
if (stream == null)
|
||||||
{
|
{
|
||||||
// If a Culture doesn't have a translation the stream will be null and it defaults to en-us further up the chain
|
_logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
|
||||||
if (stream != null)
|
return;
|
||||||
{
|
}
|
||||||
var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
|
|
||||||
|
|
||||||
foreach (var key in dict.Keys)
|
var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
|
||||||
{
|
if (dict == null)
|
||||||
dictionary[key] = dict[key];
|
{
|
||||||
}
|
throw new InvalidOperationException($"Resource contains invalid data: '{stream}'");
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
foreach (var key in dict.Keys)
|
||||||
_logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
|
{
|
||||||
}
|
dictionary[key] = dict[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,9 +55,19 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
|||||||
_localization = localization;
|
_localization = localization;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Creates the triggers that define when the task will run.
|
public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages");
|
||||||
/// </summary>
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription");
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public string Key => "RefreshChapterImages";
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
|
||||||
{
|
{
|
||||||
return new[]
|
return new[]
|
||||||
@ -162,26 +172,5 @@ namespace Emby.Server.Implementations.ScheduledTasks
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Name => _localization.GetLocalizedString("TaskRefreshChapterImages");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Description => _localization.GetLocalizedString("TaskRefreshChapterImagesDescription");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Category => _localization.GetLocalizedString("TasksLibraryCategory");
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public string Key => "RefreshChapterImages";
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool IsHidden => false;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool IsEnabled => true;
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public bool IsLogged => true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -380,7 +380,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
private void DeleteHlsPartialStreamFiles(string outputFilePath)
|
private void DeleteHlsPartialStreamFiles(string outputFilePath)
|
||||||
{
|
{
|
||||||
var directory = Path.GetDirectoryName(outputFilePath)
|
var directory = Path.GetDirectoryName(outputFilePath)
|
||||||
?? throw new ArgumentException("Path can't be a root directory.", nameof(outputFilePath));
|
?? throw new ArgumentException("Path can't be a root directory.", nameof(outputFilePath));
|
||||||
|
|
||||||
var name = Path.GetFileNameWithoutExtension(outputFilePath);
|
var name = Path.GetFileNameWithoutExtension(outputFilePath);
|
||||||
|
|
||||||
@ -444,6 +444,10 @@ namespace Jellyfin.Api.Helpers
|
|||||||
{
|
{
|
||||||
var audioCodec = state.ActualOutputAudioCodec;
|
var audioCodec = state.ActualOutputAudioCodec;
|
||||||
var videoCodec = state.ActualOutputVideoCodec;
|
var videoCodec = state.ActualOutputVideoCodec;
|
||||||
|
var hardwareAccelerationTypeString = _serverConfigurationManager.GetEncodingOptions().HardwareAccelerationType;
|
||||||
|
HardwareEncodingType? hardwareAccelerationType = string.IsNullOrEmpty(hardwareAccelerationTypeString)
|
||||||
|
? null
|
||||||
|
: (HardwareEncodingType)Enum.Parse(typeof(HardwareEncodingType), hardwareAccelerationTypeString, true);
|
||||||
|
|
||||||
_sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo
|
_sessionManager.ReportTranscodingInfo(deviceId, new TranscodingInfo
|
||||||
{
|
{
|
||||||
@ -458,6 +462,7 @@ namespace Jellyfin.Api.Helpers
|
|||||||
AudioChannels = state.OutputAudioChannels,
|
AudioChannels = state.OutputAudioChannels,
|
||||||
IsAudioDirect = EncodingHelper.IsCopyCodec(state.OutputAudioCodec),
|
IsAudioDirect = EncodingHelper.IsCopyCodec(state.OutputAudioCodec),
|
||||||
IsVideoDirect = EncodingHelper.IsCopyCodec(state.OutputVideoCodec),
|
IsVideoDirect = EncodingHelper.IsCopyCodec(state.OutputVideoCodec),
|
||||||
|
HardwareAccelerationType = hardwareAccelerationType,
|
||||||
TranscodeReasons = state.TranscodeReasons
|
TranscodeReasons = state.TranscodeReasons
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -759,8 +764,8 @@ namespace Jellyfin.Api.Helpers
|
|||||||
if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Request.LiveStreamId))
|
if (state.MediaSource.RequiresOpening && string.IsNullOrWhiteSpace(state.Request.LiveStreamId))
|
||||||
{
|
{
|
||||||
var liveStreamResponse = await _mediaSourceManager.OpenLiveStream(
|
var liveStreamResponse = await _mediaSourceManager.OpenLiveStream(
|
||||||
new LiveStreamRequest { OpenToken = state.MediaSource.OpenToken },
|
new LiveStreamRequest { OpenToken = state.MediaSource.OpenToken },
|
||||||
cancellationTokenSource.Token)
|
cancellationTokenSource.Token)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
|
var encodingOptions = _serverConfigurationManager.GetEncodingOptions();
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.9" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Http" Version="5.0.0" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.5" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.5" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.1.5" />
|
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.1.5" />
|
||||||
|
@ -19,13 +19,13 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
|
<PackageReference Include="System.Linq.Async" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.9" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.9" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.9">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.8">
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.9">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
|
@ -303,7 +303,7 @@ namespace Jellyfin.Server.Extensions
|
|||||||
{
|
{
|
||||||
description.TryGetMethodInfo(out MethodInfo methodInfo);
|
description.TryGetMethodInfo(out MethodInfo methodInfo);
|
||||||
// Attribute name, method name, none.
|
// Attribute name, method name, none.
|
||||||
return description?.ActionDescriptor?.AttributeRouteInfo?.Name
|
return description?.ActionDescriptor.AttributeRouteInfo?.Name
|
||||||
?? methodInfo?.Name
|
?? methodInfo?.Name
|
||||||
?? null;
|
?? null;
|
||||||
});
|
});
|
||||||
@ -341,7 +341,7 @@ namespace Jellyfin.Server.Extensions
|
|||||||
{
|
{
|
||||||
foreach (var address in host.GetAddresses())
|
foreach (var address in host.GetAddresses())
|
||||||
{
|
{
|
||||||
AddIpAddress(config, options, addr.Address, addr.PrefixLength);
|
AddIpAddress(config, options, address, address.AddressFamily == AddressFamily.InterNetwork ? 32 : 128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -397,7 +397,7 @@ namespace Jellyfin.Server.Extensions
|
|||||||
Type = "object",
|
Type = "object",
|
||||||
Properties = typeof(ImageType).GetEnumNames().ToDictionary(
|
Properties = typeof(ImageType).GetEnumNames().ToDictionary(
|
||||||
name => name,
|
name => name,
|
||||||
name => new OpenApiSchema
|
_ => new OpenApiSchema
|
||||||
{
|
{
|
||||||
Type = "object",
|
Type = "object",
|
||||||
AdditionalProperties = new OpenApiSchema
|
AdditionalProperties = new OpenApiSchema
|
||||||
|
@ -33,13 +33,13 @@
|
|||||||
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
<PackageReference Include="CommandLineParser" Version="2.8.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.9" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.9" />
|
||||||
<PackageReference Include="prometheus-net" Version="4.2.0" />
|
<PackageReference Include="prometheus-net" Version="4.2.0" />
|
||||||
<PackageReference Include="prometheus-net.AspNetCore" Version="4.2.0" />
|
<PackageReference Include="prometheus-net.AspNetCore" Version="4.2.0" />
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
|
||||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
|
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
|
||||||
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
|
<PackageReference Include="Serilog.Settings.Configuration" Version="3.2.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
|
<PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
|
||||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||||
|
@ -58,9 +58,12 @@ namespace Jellyfin.Server.Middleware
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!startsWithBaseUrl)
|
if (!startsWithBaseUrl
|
||||||
|
|| localPath.Length == baseUrlPrefix.Length
|
||||||
|
// Local path is /baseUrl/
|
||||||
|
|| (localPath.Length == baseUrlPrefix.Length + 1 && localPath[^1] == '/'))
|
||||||
{
|
{
|
||||||
// Always redirect back to the default path if the base prefix is invalid or missing
|
// Always redirect back to the default path if the base prefix is invalid, missing, or is the full path.
|
||||||
_logger.LogDebug("Normalizing an URL at {LocalPath}", localPath);
|
_logger.LogDebug("Normalizing an URL at {LocalPath}", localPath);
|
||||||
httpContext.Response.Redirect(baseUrlPrefix + "/" + _configuration[ConfigurationExtensions.DefaultRedirectKey]);
|
httpContext.Response.Redirect(baseUrlPrefix + "/" + _configuration[ConfigurationExtensions.DefaultRedirectKey]);
|
||||||
return;
|
return;
|
||||||
|
@ -137,11 +137,6 @@ namespace Jellyfin.Server.Middleware
|
|||||||
|
|
||||||
private string NormalizeExceptionMessage(string msg)
|
private string NormalizeExceptionMessage(string msg)
|
||||||
{
|
{
|
||||||
if (msg == null)
|
|
||||||
{
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip any information we don't want to reveal
|
// Strip any information we don't want to reveal
|
||||||
return msg.Replace(
|
return msg.Replace(
|
||||||
_configuration.ApplicationPaths.ProgramSystemPath,
|
_configuration.ApplicationPaths.ProgramSystemPath,
|
||||||
|
@ -40,7 +40,7 @@ namespace Jellyfin.Server.Migrations
|
|||||||
.Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m))
|
.Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m))
|
||||||
.OfType<IMigrationRoutine>()
|
.OfType<IMigrationRoutine>()
|
||||||
.ToArray();
|
.ToArray();
|
||||||
var migrationOptions = ((IConfigurationManager)host.ConfigurationManager).GetConfiguration<MigrationOptions>(MigrationsListStore.StoreKey);
|
var migrationOptions = host.ConfigurationManager.GetConfiguration<MigrationOptions>(MigrationsListStore.StoreKey);
|
||||||
|
|
||||||
if (!host.ConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0)
|
if (!host.ConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0)
|
||||||
{
|
{
|
||||||
|
@ -92,7 +92,7 @@ namespace Jellyfin.Server.Migrations.Routines
|
|||||||
if (entry[6].SQLiteType != SQLiteType.Null && !Guid.TryParse(entry[6].ToString(), out guid))
|
if (entry[6].SQLiteType != SQLiteType.Null && !Guid.TryParse(entry[6].ToString(), out guid))
|
||||||
{
|
{
|
||||||
// This is not a valid Guid, see if it is an internal ID from an old Emby schema
|
// This is not a valid Guid, see if it is an internal ID from an old Emby schema
|
||||||
_logger.LogWarning("Invalid Guid in UserId column: ", entry[6].ToString());
|
_logger.LogWarning("Invalid Guid in UserId column: {Guid}", entry[6].ToString());
|
||||||
|
|
||||||
using var statement = userDbConnection.PrepareStatement("SELECT guid FROM LocalUsersv2 WHERE Id=@Id");
|
using var statement = userDbConnection.PrepareStatement("SELECT guid FROM LocalUsersv2 WHERE Id=@Id");
|
||||||
statement.TryBind("@Id", entry[6].ToString());
|
statement.TryBind("@Id", entry[6].ToString());
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
@ -5,7 +5,6 @@ using System.IO;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@ -121,11 +120,11 @@ namespace Jellyfin.Server
|
|||||||
|
|
||||||
// Log uncaught exceptions to the logging instead of std error
|
// Log uncaught exceptions to the logging instead of std error
|
||||||
AppDomain.CurrentDomain.UnhandledException -= UnhandledExceptionToConsole;
|
AppDomain.CurrentDomain.UnhandledException -= UnhandledExceptionToConsole;
|
||||||
AppDomain.CurrentDomain.UnhandledException += (sender, e)
|
AppDomain.CurrentDomain.UnhandledException += (_, e)
|
||||||
=> _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception");
|
=> _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception");
|
||||||
|
|
||||||
// Intercept Ctrl+C and Ctrl+Break
|
// Intercept Ctrl+C and Ctrl+Break
|
||||||
Console.CancelKeyPress += (sender, e) =>
|
Console.CancelKeyPress += (_, e) =>
|
||||||
{
|
{
|
||||||
if (_tokenSource.IsCancellationRequested)
|
if (_tokenSource.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@ -139,7 +138,7 @@ namespace Jellyfin.Server
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Register a SIGTERM handler
|
// Register a SIGTERM handler
|
||||||
AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
|
AppDomain.CurrentDomain.ProcessExit += (_, _) =>
|
||||||
{
|
{
|
||||||
if (_tokenSource.IsCancellationRequested)
|
if (_tokenSource.IsCancellationRequested)
|
||||||
{
|
{
|
||||||
@ -180,7 +179,7 @@ namespace Jellyfin.Server
|
|||||||
"The server is expected to host the web client, but the provided content directory is either " +
|
"The server is expected to host the web client, but the provided content directory is either " +
|
||||||
$"invalid or empty: {webContentPath}. If you do not want to host the web client with the " +
|
$"invalid or empty: {webContentPath}. If you do not want to host the web client with the " +
|
||||||
"server, you may set the '--nowebclient' command line flag, or set" +
|
"server, you may set the '--nowebclient' command line flag, or set" +
|
||||||
$"'{MediaBrowser.Controller.Extensions.ConfigurationExtensions.HostWebClientKey}=false' in your config settings.");
|
$"'{ConfigurationExtensions.HostWebClientKey}=false' in your config settings.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -543,7 +542,7 @@ namespace Jellyfin.Server
|
|||||||
// Get a stream of the resource contents
|
// Get a stream of the resource contents
|
||||||
// NOTE: The .csproj name is used instead of the assembly name in the resource path
|
// NOTE: The .csproj name is used instead of the assembly name in the resource path
|
||||||
const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json";
|
const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json";
|
||||||
await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath)
|
await using Stream resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath)
|
||||||
?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
|
?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
|
||||||
|
|
||||||
// Copy the resource contents to the expected file path for the config file
|
// Copy the resource contents to the expected file path for the config file
|
||||||
|
@ -40,7 +40,7 @@ namespace MediaBrowser.Common.Extensions
|
|||||||
|
|
||||||
// Add an event handler for the process exit event
|
// Add an event handler for the process exit event
|
||||||
var tcs = new TaskCompletionSource<bool>();
|
var tcs = new TaskCompletionSource<bool>();
|
||||||
process.Exited += (sender, args) => tcs.TrySetResult(true);
|
process.Exited += (_, _) => tcs.TrySetResult(true);
|
||||||
|
|
||||||
// Return immediately if the process has already exited
|
// Return immediately if the process has already exited
|
||||||
if (process.HasExitedSafe())
|
if (process.HasExitedSafe())
|
||||||
|
@ -4,7 +4,6 @@ using System.Linq;
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Common.Net
|
namespace MediaBrowser.Common.Net
|
||||||
{
|
{
|
||||||
@ -196,7 +195,7 @@ namespace MediaBrowser.Common.Net
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidCastException("Host does not contain a valid value. {host}");
|
throw new InvalidCastException($"Host does not contain a valid value. {host}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -221,7 +220,7 @@ namespace MediaBrowser.Common.Net
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new InvalidCastException("Host does not contain a valid value. {host}");
|
throw new InvalidCastException($"Host does not contain a valid value. {host}");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -349,7 +348,7 @@ namespace MediaBrowser.Common.Net
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
output = output[0..^1];
|
output = output[..^1];
|
||||||
|
|
||||||
if (moreThanOne)
|
if (moreThanOne)
|
||||||
{
|
{
|
||||||
@ -400,7 +399,7 @@ namespace MediaBrowser.Common.Net
|
|||||||
if ((_addresses.Length == 0 && !Resolved) || (DateTime.UtcNow > _lastResolved.Value.AddMinutes(Timeout)))
|
if ((_addresses.Length == 0 && !Resolved) || (DateTime.UtcNow > _lastResolved.Value.AddMinutes(Timeout)))
|
||||||
{
|
{
|
||||||
_lastResolved = DateTime.UtcNow;
|
_lastResolved = DateTime.UtcNow;
|
||||||
ResolveHostInternal().GetAwaiter().GetResult();
|
ResolveHostInternal();
|
||||||
Resolved = true;
|
Resolved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -410,30 +409,31 @@ namespace MediaBrowser.Common.Net
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Task that looks up a Host name and returns its IP addresses.
|
/// Task that looks up a Host name and returns its IP addresses.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
|
private void ResolveHostInternal()
|
||||||
private async Task ResolveHostInternal()
|
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrEmpty(HostName))
|
var hostName = HostName;
|
||||||
|
if (string.IsNullOrEmpty(hostName))
|
||||||
{
|
{
|
||||||
// Resolves the host name - so save a DNS lookup.
|
return;
|
||||||
if (string.Equals(HostName, "localhost", StringComparison.OrdinalIgnoreCase))
|
}
|
||||||
{
|
|
||||||
_addresses = new IPAddress[] { IPAddress.Loopback, IPAddress.IPv6Loopback };
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Uri.CheckHostName(HostName).Equals(UriHostNameType.Dns))
|
// Resolves the host name - so save a DNS lookup.
|
||||||
|
if (string.Equals(hostName, "localhost", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
_addresses = new IPAddress[] { IPAddress.Loopback, IPAddress.IPv6Loopback };
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Uri.CheckHostName(hostName) == UriHostNameType.Dns)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
try
|
_addresses = Dns.GetHostEntry(hostName).AddressList;
|
||||||
{
|
}
|
||||||
IPHostEntry ip = await Dns.GetHostEntryAsync(HostName).ConfigureAwait(false);
|
catch (SocketException ex)
|
||||||
_addresses = ip.AddressList;
|
{
|
||||||
}
|
// Log and then ignore socket errors, as the result value will just be an empty array.
|
||||||
catch (SocketException ex)
|
Debug.WriteLine("GetHostAddresses failed with {Message}.", ex.Message);
|
||||||
{
|
|
||||||
// Log and then ignore socket errors, as the result value will just be an empty array.
|
|
||||||
Debug.WriteLine("GetHostEntryAsync failed with {Message}.", ex.Message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,10 +47,10 @@ namespace MediaBrowser.Common.Plugins
|
|||||||
var assemblyFilePath = assembly.Location;
|
var assemblyFilePath = assembly.Location;
|
||||||
|
|
||||||
var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
|
var dataFolderPath = Path.Combine(ApplicationPaths.PluginsPath, Path.GetFileNameWithoutExtension(assemblyFilePath));
|
||||||
if (!Directory.Exists(dataFolderPath) && Version != null)
|
if (Version != null && !Directory.Exists(dataFolderPath))
|
||||||
{
|
{
|
||||||
// Try again with the version number appended to the folder name.
|
// Try again with the version number appended to the folder name.
|
||||||
dataFolderPath = dataFolderPath + "_" + Version.ToString();
|
dataFolderPath += "_" + Version.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);
|
SetAttributes(assemblyFilePath, dataFolderPath, assemblyName.Version);
|
||||||
|
@ -18,7 +18,7 @@ namespace MediaBrowser.Common.Providers
|
|||||||
/// <param name="text">The text to parse.</param>
|
/// <param name="text">The text to parse.</param>
|
||||||
/// <param name="imdbId">The parsed IMDb id.</param>
|
/// <param name="imdbId">The parsed IMDb id.</param>
|
||||||
/// <returns>True if parsing was successful, false otherwise.</returns>
|
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||||
public static bool TryFindImdbId(ReadOnlySpan<char> text, [NotNullWhen(true)] out ReadOnlySpan<char> imdbId)
|
public static bool TryFindImdbId(ReadOnlySpan<char> text, out ReadOnlySpan<char> imdbId)
|
||||||
{
|
{
|
||||||
// imdb id is at least 9 chars (tt + 7 numbers)
|
// imdb id is at least 9 chars (tt + 7 numbers)
|
||||||
while (text.Length >= 2 + ImdbMinNumbers)
|
while (text.Length >= 2 + ImdbMinNumbers)
|
||||||
@ -62,7 +62,7 @@ namespace MediaBrowser.Common.Providers
|
|||||||
/// <param name="text">The text with the url to parse.</param>
|
/// <param name="text">The text with the url to parse.</param>
|
||||||
/// <param name="tmdbId">The parsed TMDb id.</param>
|
/// <param name="tmdbId">The parsed TMDb id.</param>
|
||||||
/// <returns>True if parsing was successful, false otherwise.</returns>
|
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||||
public static bool TryFindTmdbMovieId(ReadOnlySpan<char> text, [NotNullWhen(true)] out ReadOnlySpan<char> tmdbId)
|
public static bool TryFindTmdbMovieId(ReadOnlySpan<char> text, out ReadOnlySpan<char> tmdbId)
|
||||||
=> TryFindProviderId(text, "themoviedb.org/movie/", out tmdbId);
|
=> TryFindProviderId(text, "themoviedb.org/movie/", out tmdbId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -71,7 +71,7 @@ namespace MediaBrowser.Common.Providers
|
|||||||
/// <param name="text">The text with the url to parse.</param>
|
/// <param name="text">The text with the url to parse.</param>
|
||||||
/// <param name="tmdbId">The parsed TMDb id.</param>
|
/// <param name="tmdbId">The parsed TMDb id.</param>
|
||||||
/// <returns>True if parsing was successful, false otherwise.</returns>
|
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||||
public static bool TryFindTmdbSeriesId(ReadOnlySpan<char> text, [NotNullWhen(true)] out ReadOnlySpan<char> tmdbId)
|
public static bool TryFindTmdbSeriesId(ReadOnlySpan<char> text, out ReadOnlySpan<char> tmdbId)
|
||||||
=> TryFindProviderId(text, "themoviedb.org/tv/", out tmdbId);
|
=> TryFindProviderId(text, "themoviedb.org/tv/", out tmdbId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -80,7 +80,7 @@ namespace MediaBrowser.Common.Providers
|
|||||||
/// <param name="text">The text with the url to parse.</param>
|
/// <param name="text">The text with the url to parse.</param>
|
||||||
/// <param name="tvdbId">The parsed TVDb id.</param>
|
/// <param name="tvdbId">The parsed TVDb id.</param>
|
||||||
/// <returns>True if parsing was successful, false otherwise.</returns>
|
/// <returns>True if parsing was successful, false otherwise.</returns>
|
||||||
public static bool TryFindTvdbId(ReadOnlySpan<char> text, [NotNullWhen(true)] out ReadOnlySpan<char> tvdbId)
|
public static bool TryFindTvdbId(ReadOnlySpan<char> text, out ReadOnlySpan<char> tvdbId)
|
||||||
=> TryFindProviderId(text, "thetvdb.com/?tab=series&id=", out tvdbId);
|
=> TryFindProviderId(text, "thetvdb.com/?tab=series&id=", out tvdbId);
|
||||||
|
|
||||||
private static bool TryFindProviderId(ReadOnlySpan<char> text, ReadOnlySpan<char> searchString, [NotNullWhen(true)] out ReadOnlySpan<char> providerId)
|
private static bool TryFindProviderId(ReadOnlySpan<char> text, ReadOnlySpan<char> searchString, [NotNullWhen(true)] out ReadOnlySpan<char> providerId)
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using Jellyfin.Extensions;
|
using Jellyfin.Extensions;
|
||||||
@ -16,7 +15,7 @@ namespace MediaBrowser.Controller.BaseItemManager
|
|||||||
{
|
{
|
||||||
private readonly IServerConfigurationManager _serverConfigurationManager;
|
private readonly IServerConfigurationManager _serverConfigurationManager;
|
||||||
|
|
||||||
private int _metadataRefreshConcurrency = 0;
|
private int _metadataRefreshConcurrency;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BaseItemManager"/> class.
|
/// Initializes a new instance of the <see cref="BaseItemManager"/> class.
|
||||||
@ -101,7 +100,7 @@ namespace MediaBrowser.Controller.BaseItemManager
|
|||||||
/// Called when the configuration is updated.
|
/// Called when the configuration is updated.
|
||||||
/// It will refresh the metadata throttler if the relevant config changed.
|
/// It will refresh the metadata throttler if the relevant config changed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnConfigurationUpdated(object sender, EventArgs e)
|
private void OnConfigurationUpdated(object? sender, EventArgs e)
|
||||||
{
|
{
|
||||||
int newMetadataRefreshConcurrency = GetMetadataRefreshConcurrency();
|
int newMetadataRefreshConcurrency = GetMetadataRefreshConcurrency();
|
||||||
if (_metadataRefreshConcurrency != newMetadataRefreshConcurrency)
|
if (_metadataRefreshConcurrency != newMetadataRefreshConcurrency)
|
||||||
@ -114,6 +113,7 @@ namespace MediaBrowser.Controller.BaseItemManager
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the metadata refresh throttler.
|
/// Creates the metadata refresh throttler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[MemberNotNull(nameof(MetadataRefreshThrottler))]
|
||||||
private void SetupMetadataThrottler()
|
private void SetupMetadataThrottler()
|
||||||
{
|
{
|
||||||
MetadataRefreshThrottler = new SemaphoreSlim(_metadataRefreshConcurrency);
|
MetadataRefreshThrottler = new SemaphoreSlim(_metadataRefreshConcurrency);
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using MediaBrowser.Controller.Entities;
|
using MediaBrowser.Controller.Entities;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
@ -34,4 +32,4 @@ namespace MediaBrowser.Controller.BaseItemManager
|
|||||||
/// <returns><c>true</c> if image fetcher is enabled, else false.</returns>
|
/// <returns><c>true</c> if image fetcher is enabled, else false.</returns>
|
||||||
bool IsImageFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name);
|
bool IsImageFetcherEnabled(BaseItem baseItem, LibraryOptions libraryOptions, string name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#nullable disable
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
#pragma warning disable CA1002, CA2227, CS1591
|
|
||||||
|
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Channels
|
namespace MediaBrowser.Controller.Channels
|
||||||
@ -10,10 +9,10 @@ namespace MediaBrowser.Controller.Channels
|
|||||||
{
|
{
|
||||||
public ChannelItemResult()
|
public ChannelItemResult()
|
||||||
{
|
{
|
||||||
Items = new List<ChannelItemInfo>();
|
Items = Array.Empty<ChannelItemInfo>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ChannelItemInfo> Items { get; set; }
|
public IReadOnlyList<ChannelItemInfo> Items { get; set; }
|
||||||
|
|
||||||
public int? TotalRecordCount { get; set; }
|
public int? TotalRecordCount { get; set; }
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CA2227, CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -16,17 +14,17 @@ namespace MediaBrowser.Controller.Collections
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when [collection created].
|
/// Occurs when [collection created].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EventHandler<CollectionCreatedEventArgs> CollectionCreated;
|
event EventHandler<CollectionCreatedEventArgs>? CollectionCreated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when [items added to collection].
|
/// Occurs when [items added to collection].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EventHandler<CollectionModifiedEventArgs> ItemsAddedToCollection;
|
event EventHandler<CollectionModifiedEventArgs>? ItemsAddedToCollection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Occurs when [items removed from collection].
|
/// Occurs when [items removed from collection].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EventHandler<CollectionModifiedEventArgs> ItemsRemovedFromCollection;
|
event EventHandler<CollectionModifiedEventArgs>? ItemsRemovedFromCollection;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the collection.
|
/// Creates the collection.
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
#nullable disable
|
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -22,7 +20,7 @@ namespace MediaBrowser.Controller.Dlna
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="headers">The headers.</param>
|
/// <param name="headers">The headers.</param>
|
||||||
/// <returns>DeviceProfile.</returns>
|
/// <returns>DeviceProfile.</returns>
|
||||||
DeviceProfile GetProfile(IHeaderDictionary headers);
|
DeviceProfile? GetProfile(IHeaderDictionary headers);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the default profile.
|
/// Gets the default profile.
|
||||||
@ -53,14 +51,14 @@ namespace MediaBrowser.Controller.Dlna
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="id">The identifier.</param>
|
/// <param name="id">The identifier.</param>
|
||||||
/// <returns>DeviceProfile.</returns>
|
/// <returns>DeviceProfile.</returns>
|
||||||
DeviceProfile GetProfile(string id);
|
DeviceProfile? GetProfile(string id);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the profile.
|
/// Gets the profile.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="deviceInfo">The device information.</param>
|
/// <param name="deviceInfo">The device information.</param>
|
||||||
/// <returns>DeviceProfile.</returns>
|
/// <returns>DeviceProfile.</returns>
|
||||||
DeviceProfile GetProfile(DeviceIdentification deviceInfo);
|
DeviceProfile? GetProfile(DeviceIdentification deviceInfo);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the server description XML.
|
/// Gets the server description XML.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1819, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
@ -18,32 +18,23 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Specialized folder that can have items added to it's children by external entities.
|
/// Specialized folder that can have items added to it's children by external entities.
|
||||||
/// Used for our RootFolder so plug-ins can add items.
|
/// Used for our RootFolder so plugins can add items.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class AggregateFolder : Folder
|
public class AggregateFolder : Folder
|
||||||
{
|
{
|
||||||
private bool _requiresRefresh;
|
private readonly object _childIdsLock = new object();
|
||||||
|
|
||||||
public AggregateFolder()
|
|
||||||
{
|
|
||||||
PhysicalLocationsList = Array.Empty<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool IsPhysicalRoot => true;
|
|
||||||
|
|
||||||
public override bool CanDelete()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPlayedStatus => false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _virtual children.
|
/// The _virtual children.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentBag<BaseItem> _virtualChildren = new ConcurrentBag<BaseItem>();
|
private readonly ConcurrentBag<BaseItem> _virtualChildren = new ConcurrentBag<BaseItem>();
|
||||||
|
private bool _requiresRefresh;
|
||||||
|
private Guid[] _childrenIds = null;
|
||||||
|
|
||||||
|
public AggregateFolder()
|
||||||
|
{
|
||||||
|
PhysicalLocationsList = Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the virtual children.
|
/// Gets the virtual children.
|
||||||
@ -51,19 +42,27 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <value>The virtual children.</value>
|
/// <value>The virtual children.</value>
|
||||||
public ConcurrentBag<BaseItem> VirtualChildren => _virtualChildren;
|
public ConcurrentBag<BaseItem> VirtualChildren => _virtualChildren;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool IsPhysicalRoot => true;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPlayedStatus => false;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override string[] PhysicalLocations => PhysicalLocationsList;
|
public override string[] PhysicalLocations => PhysicalLocationsList;
|
||||||
|
|
||||||
public string[] PhysicalLocationsList { get; set; }
|
public string[] PhysicalLocationsList { get; set; }
|
||||||
|
|
||||||
|
public override bool CanDelete()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
|
protected override FileSystemMetadata[] GetFileSystemChildren(IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
return CreateResolveArgs(directoryService, true).FileSystemChildren;
|
return CreateResolveArgs(directoryService, true).FileSystemChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Guid[] _childrenIds = null;
|
|
||||||
private readonly object _childIdsLock = new object();
|
|
||||||
|
|
||||||
protected override List<BaseItem> LoadChildren()
|
protected override List<BaseItem> LoadChildren()
|
||||||
{
|
{
|
||||||
lock (_childIdsLock)
|
lock (_childIdsLock)
|
||||||
@ -169,7 +168,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Adds the virtual child.
|
/// Adds the virtual child.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="child">The child.</param>
|
/// <param name="child">The child.</param>
|
||||||
/// <exception cref="ArgumentNullException"></exception>
|
/// <exception cref="ArgumentNullException">Throws if child is null.</exception>
|
||||||
public void AddVirtualChild(BaseItem child)
|
public void AddVirtualChild(BaseItem child)
|
||||||
{
|
{
|
||||||
if (child == null)
|
if (child == null)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1002, CA1724, CA1826, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -25,6 +25,12 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
IHasLookupInfo<SongInfo>,
|
IHasLookupInfo<SongInfo>,
|
||||||
IHasMediaSources
|
IHasMediaSources
|
||||||
{
|
{
|
||||||
|
public Audio()
|
||||||
|
{
|
||||||
|
Artists = Array.Empty<string>();
|
||||||
|
AlbumArtists = Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IReadOnlyList<string> Artists { get; set; }
|
public IReadOnlyList<string> Artists { get; set; }
|
||||||
@ -33,17 +39,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public IReadOnlyList<string> AlbumArtists { get; set; }
|
public IReadOnlyList<string> AlbumArtists { get; set; }
|
||||||
|
|
||||||
public Audio()
|
|
||||||
{
|
|
||||||
Artists = Array.Empty<string>();
|
|
||||||
AlbumArtists = Array.Empty<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsPlayedStatus => true;
|
public override bool SupportsPlayedStatus => true;
|
||||||
|
|
||||||
@ -62,11 +57,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override Folder LatestItemsIndexContainer => AlbumEntity;
|
public override Folder LatestItemsIndexContainer => AlbumEntity;
|
||||||
|
|
||||||
public override bool CanDownload()
|
|
||||||
{
|
|
||||||
return IsFileProtocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public MusicAlbum AlbumEntity => FindParent<MusicAlbum>();
|
public MusicAlbum AlbumEntity => FindParent<MusicAlbum>();
|
||||||
|
|
||||||
@ -77,6 +67,16 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override string MediaType => Model.Entities.MediaType.Audio;
|
public override string MediaType => Model.Entities.MediaType.Audio;
|
||||||
|
|
||||||
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanDownload()
|
||||||
|
{
|
||||||
|
return IsFileProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the name of the sort.
|
/// Creates the name of the sort.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1819, CS1591
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities.Audio
|
namespace MediaBrowser.Controller.Entities.Audio
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1721, CA1826, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -23,18 +23,18 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer
|
public class MusicAlbum : Folder, IHasAlbumArtist, IHasArtist, IHasMusicGenres, IHasLookupInfo<AlbumInfo>, IMetadataContainer
|
||||||
{
|
{
|
||||||
/// <inheritdoc />
|
|
||||||
public IReadOnlyList<string> AlbumArtists { get; set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public IReadOnlyList<string> Artists { get; set; }
|
|
||||||
|
|
||||||
public MusicAlbum()
|
public MusicAlbum()
|
||||||
{
|
{
|
||||||
Artists = Array.Empty<string>();
|
Artists = Array.Empty<string>();
|
||||||
AlbumArtists = Array.Empty<string>();
|
AlbumArtists = Array.Empty<string>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IReadOnlyList<string> AlbumArtists { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public IReadOnlyList<string> Artists { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsAddingToPlaylist => true;
|
public override bool SupportsAddingToPlaylist => true;
|
||||||
|
|
||||||
@ -44,6 +44,25 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public MusicArtist MusicArtist => GetMusicArtist(new DtoOptions(true));
|
public MusicArtist MusicArtist => GetMusicArtist(new DtoOptions(true));
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPlayedStatus => false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsCumulativeRunTimeTicks => true;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string AlbumArtist => AlbumArtists.FirstOrDefault();
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPeople => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the tracks.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The tracks.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public IEnumerable<Audio> Tracks => GetRecursiveChildren(i => i is Audio).Cast<Audio>();
|
||||||
|
|
||||||
public MusicArtist GetMusicArtist(DtoOptions options)
|
public MusicArtist GetMusicArtist(DtoOptions options)
|
||||||
{
|
{
|
||||||
var parents = GetParents();
|
var parents = GetParents();
|
||||||
@ -64,25 +83,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPlayedStatus => false;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsCumulativeRunTimeTicks => true;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public string AlbumArtist => AlbumArtists.FirstOrDefault();
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPeople => false;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the tracks.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The tracks.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public IEnumerable<Audio> Tracks => GetRecursiveChildren(i => i is Audio).Cast<Audio>();
|
|
||||||
|
|
||||||
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
|
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
|
||||||
{
|
{
|
||||||
return Tracks;
|
return Tracks;
|
||||||
|
@ -44,6 +44,36 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsPlayedStatus => false;
|
public override bool SupportsPlayedStatus => false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the folder containing the item.
|
||||||
|
/// If the item is a folder, it returns the folder itself.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The containing folder path.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string ContainingFolderPath => Path;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override IEnumerable<BaseItem> Children
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsAccessedByName)
|
||||||
|
{
|
||||||
|
return new List<BaseItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.Children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPeople => false;
|
||||||
|
|
||||||
|
public static string GetPath(string name)
|
||||||
|
{
|
||||||
|
return GetPath(name, true);
|
||||||
|
}
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
@ -65,20 +95,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
return LibraryManager.GetItemList(query);
|
return LibraryManager.GetItemList(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override IEnumerable<BaseItem> Children
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsAccessedByName)
|
|
||||||
{
|
|
||||||
return new List<BaseItem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.Children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override int GetChildCount(User user)
|
public override int GetChildCount(User user)
|
||||||
{
|
{
|
||||||
return IsAccessedByName ? 0 : base.GetChildCount(user);
|
return IsAccessedByName ? 0 : base.GetChildCount(user);
|
||||||
@ -113,14 +129,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the folder containing the item.
|
|
||||||
/// If the item is a folder, it returns the folder itself.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The containing folder path.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public override string ContainingFolderPath => Path;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user data key.
|
/// Gets the user data key.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -167,14 +175,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPeople => false;
|
|
||||||
|
|
||||||
public static string GetPath(string name)
|
|
||||||
{
|
|
||||||
return GetPath(name, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetPath(string name, bool normalizeName)
|
public static string GetPath(string name, bool normalizeName)
|
||||||
{
|
{
|
||||||
// Trim the period at the end because windows will have a hard time with that
|
// Trim the period at the end because windows will have a hard time with that
|
||||||
@ -208,6 +208,8 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="replaceAllMetadata">Option to replace metadata.</param>
|
||||||
|
/// <returns>True if metadata changed.</returns>
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||||
|
@ -15,19 +15,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class MusicGenre : BaseItem, IItemByName
|
public class MusicGenre : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
public override List<string> GetUserDataKeys()
|
|
||||||
{
|
|
||||||
var list = base.GetUserDataKeys();
|
|
||||||
|
|
||||||
list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string CreatePresentationUniqueKey()
|
|
||||||
{
|
|
||||||
return GetUserDataKeys()[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsAddingToPlaylist => true;
|
public override bool SupportsAddingToPlaylist => true;
|
||||||
|
|
||||||
@ -45,6 +32,22 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override string ContainingFolderPath => Path;
|
public override string ContainingFolderPath => Path;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPeople => false;
|
||||||
|
|
||||||
|
public override List<string> GetUserDataKeys()
|
||||||
|
{
|
||||||
|
var list = base.GetUserDataKeys();
|
||||||
|
|
||||||
|
list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string CreatePresentationUniqueKey()
|
||||||
|
{
|
||||||
|
return GetUserDataKeys()[0];
|
||||||
|
}
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
@ -60,9 +63,6 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPeople => false;
|
|
||||||
|
|
||||||
public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
|
public IList<BaseItem> GetTaggedItems(InternalItemsQuery query)
|
||||||
{
|
{
|
||||||
query.GenreIds = new[] { Id };
|
query.GenreIds = new[] { Id };
|
||||||
@ -106,6 +106,8 @@ namespace MediaBrowser.Controller.Entities.Audio
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="replaceAllMetadata">Option to replace metadata.</param>
|
||||||
|
/// <returns>True if metadata changed.</returns>
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1724, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -64,6 +64,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The source object.</param>
|
/// <param name="source">The source object.</param>
|
||||||
/// <param name="dest">The destination object.</param>
|
/// <param name="dest">The destination object.</param>
|
||||||
|
/// <typeparam name="T">Source type.</typeparam>
|
||||||
|
/// <typeparam name="TU">Destination type.</typeparam>
|
||||||
public static void DeepCopy<T, TU>(this T source, TU dest)
|
public static void DeepCopy<T, TU>(this T source, TU dest)
|
||||||
where T : BaseItem
|
where T : BaseItem
|
||||||
where TU : BaseItem
|
where TU : BaseItem
|
||||||
@ -109,6 +111,9 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Copies all properties on newly created object. Skips properties that do not exist.
|
/// Copies all properties on newly created object. Skips properties that do not exist.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="source">The source object.</param>
|
/// <param name="source">The source object.</param>
|
||||||
|
/// <typeparam name="T">Source type.</typeparam>
|
||||||
|
/// <typeparam name="TU">Destination type.</typeparam>
|
||||||
|
/// <returns>Destination object.</returns>
|
||||||
public static TU DeepCopy<T, TU>(this T source)
|
public static TU DeepCopy<T, TU>(this T source)
|
||||||
where T : BaseItem
|
where T : BaseItem
|
||||||
where TU : BaseItem, new()
|
where TU : BaseItem, new()
|
||||||
|
@ -15,6 +15,12 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public virtual string CollectionType => null;
|
public virtual string CollectionType => null;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsInheritedParentImages => false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPeople => false;
|
||||||
|
|
||||||
public override bool CanDelete()
|
public override bool CanDelete()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -24,11 +30,5 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsInheritedParentImages => false;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPeople => false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,23 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
PhysicalFolderIds = Array.Empty<Guid>();
|
PhysicalFolderIds = Array.Empty<Guid>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the display preferences id.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Allow different display preferences for each collection folder.
|
||||||
|
/// </remarks>
|
||||||
|
/// <value>The display prefs id.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public override Guid DisplayPreferencesId => Id;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string[] PhysicalLocations => PhysicalLocationsList;
|
||||||
|
|
||||||
|
public string[] PhysicalLocationsList { get; set; }
|
||||||
|
|
||||||
|
public Guid[] PhysicalFolderIds { get; set; }
|
||||||
|
|
||||||
public static IXmlSerializer XmlSerializer { get; set; }
|
public static IXmlSerializer XmlSerializer { get; set; }
|
||||||
|
|
||||||
public static IServerApplicationHost ApplicationHost { get; set; }
|
public static IServerApplicationHost ApplicationHost { get; set; }
|
||||||
@ -63,6 +80,9 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override IEnumerable<BaseItem> Children => GetActualChildren();
|
public override IEnumerable<BaseItem> Children => GetActualChildren();
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPeople => false;
|
||||||
|
|
||||||
public override bool CanDelete()
|
public override bool CanDelete()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -160,23 +180,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the display preferences id.
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// Allow different display preferences for each collection folder.
|
|
||||||
/// </remarks>
|
|
||||||
/// <value>The display prefs id.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public override Guid DisplayPreferencesId => Id;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override string[] PhysicalLocations => PhysicalLocationsList;
|
|
||||||
|
|
||||||
public string[] PhysicalLocationsList { get; set; }
|
|
||||||
|
|
||||||
public Guid[] PhysicalFolderIds { get; set; }
|
|
||||||
|
|
||||||
public override bool IsSaveLocalMetadataEnabled()
|
public override bool IsSaveLocalMetadataEnabled()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -373,8 +376,5 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPeople => false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the trailer URL.
|
/// Adds the trailer URL.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="item">Media item.</param>
|
||||||
|
/// <param name="url">Trailer URL.</param>
|
||||||
public static void AddTrailerUrl(this BaseItem item, string url)
|
public static void AddTrailerUrl(this BaseItem item, string url)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(url))
|
if (string.IsNullOrEmpty(url))
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1002, CA1721, CA1819, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -165,6 +165,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ICollectionManager CollectionManager { get; set; }
|
||||||
|
|
||||||
public override bool CanDelete()
|
public override bool CanDelete()
|
||||||
{
|
{
|
||||||
if (IsRoot)
|
if (IsRoot)
|
||||||
@ -258,6 +260,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Loads our children. Validation will occur externally.
|
/// Loads our children. Validation will occur externally.
|
||||||
/// We want this synchronous.
|
/// We want this synchronous.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <returns>Returns children.</returns>
|
||||||
protected virtual List<BaseItem> LoadChildren()
|
protected virtual List<BaseItem> LoadChildren()
|
||||||
{
|
{
|
||||||
// logger.LogDebug("Loading children from {0} {1} {2}", GetType().Name, Id, Path);
|
// logger.LogDebug("Loading children from {0} {1} {2}", GetType().Name, Id, Path);
|
||||||
@ -642,6 +645,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Get the children of this folder from the actual file system.
|
/// Get the children of this folder from the actual file system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>IEnumerable{BaseItem}.</returns>
|
/// <returns>IEnumerable{BaseItem}.</returns>
|
||||||
|
/// <param name="directoryService">The directory service to use for operation.</param>
|
||||||
|
/// <returns>Returns set of base items.</returns>
|
||||||
protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
|
protected virtual IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
|
||||||
{
|
{
|
||||||
var collectionType = LibraryManager.GetContentType(this);
|
var collectionType = LibraryManager.GetContentType(this);
|
||||||
@ -998,8 +1003,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return PostFilterAndSort(items, query, true);
|
return PostFilterAndSort(items, query, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ICollectionManager CollectionManager { get; set; }
|
|
||||||
|
|
||||||
protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query, bool enableSorting)
|
protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query, bool enableSorting)
|
||||||
{
|
{
|
||||||
var user = query.User;
|
var user = query.User;
|
||||||
@ -1666,7 +1669,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <param name="datePlayed">The date played.</param>
|
/// <param name="datePlayed">The date played.</param>
|
||||||
/// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
|
/// <param name="resetPosition">if set to <c>true</c> [reset position].</param>
|
||||||
/// <returns>Task.</returns>
|
|
||||||
public override void MarkPlayed(
|
public override void MarkPlayed(
|
||||||
User user,
|
User user,
|
||||||
DateTime? datePlayed,
|
DateTime? datePlayed,
|
||||||
@ -1708,7 +1710,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// Marks the unplayed.
|
/// Marks the unplayed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
/// <returns>Task.</returns>
|
|
||||||
public override void MarkUnplayed(User user)
|
public override void MarkUnplayed(User user)
|
||||||
{
|
{
|
||||||
var itemsResult = GetItemList(new InternalItemsQuery
|
var itemsResult = GetItemList(new InternalItemsQuery
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1819, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
@ -20,6 +20,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the media sources.
|
/// Gets the media sources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="enablePathSubstitution"><c>true</c> to enable path substitution, <c>false</c> to not.</param>
|
||||||
|
/// <returns>A list of media sources.</returns>
|
||||||
List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
|
List<MediaSourceInfo> GetMediaSources(bool enablePathSubstitution);
|
||||||
|
|
||||||
List<MediaStream> GetMediaStreams();
|
List<MediaStream> GetMediaStreams();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1819, CS1591
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.Entities
|
namespace MediaBrowser.Controller.Entities
|
||||||
{
|
{
|
||||||
|
@ -39,6 +39,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the trailer count.
|
/// Gets the trailer count.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="item">Media item.</param>
|
||||||
/// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
|
/// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
|
||||||
public static int GetTrailerCount(this IHasTrailers item)
|
public static int GetTrailerCount(this IHasTrailers item)
|
||||||
=> item.LocalTrailerIds.Count + item.RemoteTrailerIds.Count;
|
=> item.LocalTrailerIds.Count + item.RemoteTrailerIds.Count;
|
||||||
@ -46,6 +47,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the trailer ids.
|
/// Gets the trailer ids.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="item">Media item.</param>
|
||||||
/// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
|
/// <returns><see cref="IReadOnlyList{Guid}" />.</returns>
|
||||||
public static IReadOnlyList<Guid> GetTrailerIds(this IHasTrailers item)
|
public static IReadOnlyList<Guid> GetTrailerIds(this IHasTrailers item)
|
||||||
{
|
{
|
||||||
@ -70,6 +72,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the trailers.
|
/// Gets the trailers.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="item">Media item.</param>
|
||||||
/// <returns><see cref="IReadOnlyList{BaseItem}" />.</returns>
|
/// <returns><see cref="IReadOnlyList{BaseItem}" />.</returns>
|
||||||
public static IReadOnlyList<BaseItem> GetTrailers(this IHasTrailers item)
|
public static IReadOnlyList<BaseItem> GetTrailers(this IHasTrailers item)
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1044, CA1819, CA2227, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -12,6 +12,55 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
{
|
{
|
||||||
public class InternalItemsQuery
|
public class InternalItemsQuery
|
||||||
{
|
{
|
||||||
|
public InternalItemsQuery()
|
||||||
|
{
|
||||||
|
AlbumArtistIds = Array.Empty<Guid>();
|
||||||
|
AlbumIds = Array.Empty<Guid>();
|
||||||
|
AncestorIds = Array.Empty<Guid>();
|
||||||
|
ArtistIds = Array.Empty<Guid>();
|
||||||
|
BlockUnratedItems = Array.Empty<UnratedItem>();
|
||||||
|
BoxSetLibraryFolders = Array.Empty<Guid>();
|
||||||
|
ChannelIds = Array.Empty<Guid>();
|
||||||
|
ContributingArtistIds = Array.Empty<Guid>();
|
||||||
|
DtoOptions = new DtoOptions();
|
||||||
|
EnableTotalRecordCount = true;
|
||||||
|
ExcludeArtistIds = Array.Empty<Guid>();
|
||||||
|
ExcludeInheritedTags = Array.Empty<string>();
|
||||||
|
ExcludeItemIds = Array.Empty<Guid>();
|
||||||
|
ExcludeItemTypes = Array.Empty<string>();
|
||||||
|
ExcludeTags = Array.Empty<string>();
|
||||||
|
GenreIds = Array.Empty<Guid>();
|
||||||
|
Genres = Array.Empty<string>();
|
||||||
|
GroupByPresentationUniqueKey = true;
|
||||||
|
ImageTypes = Array.Empty<ImageType>();
|
||||||
|
IncludeItemTypes = Array.Empty<string>();
|
||||||
|
ItemIds = Array.Empty<Guid>();
|
||||||
|
MediaTypes = Array.Empty<string>();
|
||||||
|
MinSimilarityScore = 20;
|
||||||
|
OfficialRatings = Array.Empty<string>();
|
||||||
|
OrderBy = Array.Empty<ValueTuple<string, SortOrder>>();
|
||||||
|
PersonIds = Array.Empty<Guid>();
|
||||||
|
PersonTypes = Array.Empty<string>();
|
||||||
|
PresetViews = Array.Empty<string>();
|
||||||
|
SeriesStatuses = Array.Empty<SeriesStatus>();
|
||||||
|
SourceTypes = Array.Empty<SourceType>();
|
||||||
|
StudioIds = Array.Empty<Guid>();
|
||||||
|
Tags = Array.Empty<string>();
|
||||||
|
TopParentIds = Array.Empty<Guid>();
|
||||||
|
TrailerTypes = Array.Empty<TrailerType>();
|
||||||
|
VideoTypes = Array.Empty<VideoType>();
|
||||||
|
Years = Array.Empty<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public InternalItemsQuery(User? user)
|
||||||
|
: this()
|
||||||
|
{
|
||||||
|
if (user != null)
|
||||||
|
{
|
||||||
|
SetUser(user);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public bool Recursive { get; set; }
|
public bool Recursive { get; set; }
|
||||||
|
|
||||||
public int? StartIndex { get; set; }
|
public int? StartIndex { get; set; }
|
||||||
@ -186,23 +235,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
public Guid[] TopParentIds { get; set; }
|
public Guid[] TopParentIds { get; set; }
|
||||||
|
|
||||||
public BaseItem? Parent
|
|
||||||
{
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (value == null)
|
|
||||||
{
|
|
||||||
ParentId = Guid.Empty;
|
|
||||||
ParentType = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ParentId = value.Id;
|
|
||||||
ParentType = value.GetType().Name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public string[] PresetViews { get; set; }
|
public string[] PresetViews { get; set; }
|
||||||
|
|
||||||
public TrailerType[] TrailerTypes { get; set; }
|
public TrailerType[] TrailerTypes { get; set; }
|
||||||
@ -270,72 +302,23 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool? DisplayAlbumFolders { get; set; }
|
public bool? DisplayAlbumFolders { get; set; }
|
||||||
|
|
||||||
public InternalItemsQuery()
|
public BaseItem? Parent
|
||||||
{
|
{
|
||||||
AlbumArtistIds = Array.Empty<Guid>();
|
set
|
||||||
AlbumIds = Array.Empty<Guid>();
|
|
||||||
AncestorIds = Array.Empty<Guid>();
|
|
||||||
ArtistIds = Array.Empty<Guid>();
|
|
||||||
BlockUnratedItems = Array.Empty<UnratedItem>();
|
|
||||||
BoxSetLibraryFolders = Array.Empty<Guid>();
|
|
||||||
ChannelIds = Array.Empty<Guid>();
|
|
||||||
ContributingArtistIds = Array.Empty<Guid>();
|
|
||||||
DtoOptions = new DtoOptions();
|
|
||||||
EnableTotalRecordCount = true;
|
|
||||||
ExcludeArtistIds = Array.Empty<Guid>();
|
|
||||||
ExcludeInheritedTags = Array.Empty<string>();
|
|
||||||
ExcludeItemIds = Array.Empty<Guid>();
|
|
||||||
ExcludeItemTypes = Array.Empty<string>();
|
|
||||||
ExcludeTags = Array.Empty<string>();
|
|
||||||
GenreIds = Array.Empty<Guid>();
|
|
||||||
Genres = Array.Empty<string>();
|
|
||||||
GroupByPresentationUniqueKey = true;
|
|
||||||
ImageTypes = Array.Empty<ImageType>();
|
|
||||||
IncludeItemTypes = Array.Empty<string>();
|
|
||||||
ItemIds = Array.Empty<Guid>();
|
|
||||||
MediaTypes = Array.Empty<string>();
|
|
||||||
MinSimilarityScore = 20;
|
|
||||||
OfficialRatings = Array.Empty<string>();
|
|
||||||
OrderBy = Array.Empty<ValueTuple<string, SortOrder>>();
|
|
||||||
PersonIds = Array.Empty<Guid>();
|
|
||||||
PersonTypes = Array.Empty<string>();
|
|
||||||
PresetViews = Array.Empty<string>();
|
|
||||||
SeriesStatuses = Array.Empty<SeriesStatus>();
|
|
||||||
SourceTypes = Array.Empty<SourceType>();
|
|
||||||
StudioIds = Array.Empty<Guid>();
|
|
||||||
Tags = Array.Empty<string>();
|
|
||||||
TopParentIds = Array.Empty<Guid>();
|
|
||||||
TrailerTypes = Array.Empty<TrailerType>();
|
|
||||||
VideoTypes = Array.Empty<VideoType>();
|
|
||||||
Years = Array.Empty<int>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InternalItemsQuery(User? user)
|
|
||||||
: this()
|
|
||||||
{
|
|
||||||
if (user != null)
|
|
||||||
{
|
{
|
||||||
SetUser(user);
|
if (value == null)
|
||||||
|
{
|
||||||
|
ParentId = Guid.Empty;
|
||||||
|
ParentType = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ParentId = value.Id;
|
||||||
|
ParentType = value.GetType().Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetUser(User user)
|
|
||||||
{
|
|
||||||
MaxParentalRating = user.MaxParentalAgeRating;
|
|
||||||
|
|
||||||
if (MaxParentalRating.HasValue)
|
|
||||||
{
|
|
||||||
string other = UnratedItem.Other.ToString();
|
|
||||||
BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems)
|
|
||||||
.Where(i => i != other)
|
|
||||||
.Select(e => Enum.Parse<UnratedItem>(e, true)).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags);
|
|
||||||
|
|
||||||
User = user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Dictionary<string, string>? HasAnyProviderId { get; set; }
|
public Dictionary<string, string>? HasAnyProviderId { get; set; }
|
||||||
|
|
||||||
public Guid[] AlbumArtistIds { get; set; }
|
public Guid[] AlbumArtistIds { get; set; }
|
||||||
@ -361,5 +344,22 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
public string? SearchTerm { get; set; }
|
public string? SearchTerm { get; set; }
|
||||||
|
|
||||||
public string? SeriesTimerId { get; set; }
|
public string? SeriesTimerId { get; set; }
|
||||||
|
|
||||||
|
public void SetUser(User user)
|
||||||
|
{
|
||||||
|
MaxParentalRating = user.MaxParentalAgeRating;
|
||||||
|
|
||||||
|
if (MaxParentalRating.HasValue)
|
||||||
|
{
|
||||||
|
string other = UnratedItem.Other.ToString();
|
||||||
|
BlockUnratedItems = user.GetPreference(PreferenceKind.BlockUnratedItems)
|
||||||
|
.Where(i => i != other)
|
||||||
|
.Select(e => Enum.Parse<UnratedItem>(e, true)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExcludeInheritedTags = user.GetPreference(PreferenceKind.BlockedTags);
|
||||||
|
|
||||||
|
User = user;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1721, CA1819, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -49,6 +49,30 @@ namespace MediaBrowser.Controller.Entities.Movies
|
|||||||
/// <value>The display order.</value>
|
/// <value>The display order.</value>
|
||||||
public string DisplayOrder { get; set; }
|
public string DisplayOrder { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
private bool IsLegacyBoxSet
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(Path))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LinkedChildren.Length > 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool IsPreSorted => true;
|
||||||
|
|
||||||
|
public Guid[] LibraryFolderIds { get; set; }
|
||||||
|
|
||||||
protected override bool GetBlockUnratedValue(User user)
|
protected override bool GetBlockUnratedValue(User user)
|
||||||
{
|
{
|
||||||
return user.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Movie);
|
return user.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems).Contains(UnratedItem.Movie);
|
||||||
@ -83,28 +107,6 @@ namespace MediaBrowser.Controller.Entities.Movies
|
|||||||
return new List<BaseItem>();
|
return new List<BaseItem>();
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
private bool IsLegacyBoxSet
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(Path))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LinkedChildren.Length > 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return !FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, Path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool IsPreSorted => true;
|
|
||||||
|
|
||||||
public override bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
|
public override bool IsAuthorizedToDelete(User user, List<Folder> allCollectionFolders)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -191,8 +193,6 @@ namespace MediaBrowser.Controller.Entities.Movies
|
|||||||
return IsVisible(user);
|
return IsVisible(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid[] LibraryFolderIds { get; set; }
|
|
||||||
|
|
||||||
private Guid[] GetLibraryFolderIds(User user)
|
private Guid[] GetLibraryFolderIds(User user)
|
||||||
{
|
{
|
||||||
return LibraryManager.GetUserRootFolder().GetChildren(user, true)
|
return LibraryManager.GetUserRootFolder().GetChildren(user, true)
|
||||||
|
@ -16,6 +16,26 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Person : BaseItem, IItemByName, IHasLookupInfo<PersonLookupInfo>
|
public class Person : BaseItem, IItemByName, IHasLookupInfo<PersonLookupInfo>
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the folder containing the item.
|
||||||
|
/// If the item is a folder, it returns the folder itself.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The containing folder path.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string ContainingFolderPath => Path;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether to enable alpha numeric sorting.
|
||||||
|
/// </summary>
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool EnableAlphaNumericSorting => false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPeople => false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsAncestors => false;
|
||||||
|
|
||||||
public override List<string> GetUserDataKeys()
|
public override List<string> GetUserDataKeys()
|
||||||
{
|
{
|
||||||
var list = base.GetUserDataKeys();
|
var list = base.GetUserDataKeys();
|
||||||
@ -49,14 +69,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return LibraryManager.GetItemList(query);
|
return LibraryManager.GetItemList(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the folder containing the item.
|
|
||||||
/// If the item is a folder, it returns the folder itself.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The containing folder path.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public override string ContainingFolderPath => Path;
|
|
||||||
|
|
||||||
public override bool CanDelete()
|
public override bool CanDelete()
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
@ -67,18 +79,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether to enable alpha numeric sorting.
|
|
||||||
/// </summary>
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool EnableAlphaNumericSorting => false;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPeople => false;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsAncestors => false;
|
|
||||||
|
|
||||||
public static string GetPath(string name)
|
public static string GetPath(string name)
|
||||||
{
|
{
|
||||||
return GetPath(name, true);
|
return GetPath(name, true);
|
||||||
@ -129,6 +129,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="replaceAllMetadata"><c>true</c> to replace all metadata, <c>false</c> to not.</param>
|
||||||
|
/// <returns><c>true</c> if changes were made, <c>false</c> if not.</returns>
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA2227, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
@ -36,6 +36,30 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string CameraMake { get; set; }
|
||||||
|
|
||||||
|
public string CameraModel { get; set; }
|
||||||
|
|
||||||
|
public string Software { get; set; }
|
||||||
|
|
||||||
|
public double? ExposureTime { get; set; }
|
||||||
|
|
||||||
|
public double? FocalLength { get; set; }
|
||||||
|
|
||||||
|
public ImageOrientation? Orientation { get; set; }
|
||||||
|
|
||||||
|
public double? Aperture { get; set; }
|
||||||
|
|
||||||
|
public double? ShutterSpeed { get; set; }
|
||||||
|
|
||||||
|
public double? Latitude { get; set; }
|
||||||
|
|
||||||
|
public double? Longitude { get; set; }
|
||||||
|
|
||||||
|
public double? Altitude { get; set; }
|
||||||
|
|
||||||
|
public int? IsoSpeedRating { get; set; }
|
||||||
|
|
||||||
public override bool CanDownload()
|
public override bool CanDownload()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -69,29 +93,5 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
return base.GetDefaultPrimaryImageAspectRatio();
|
return base.GetDefaultPrimaryImageAspectRatio();
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CameraMake { get; set; }
|
|
||||||
|
|
||||||
public string CameraModel { get; set; }
|
|
||||||
|
|
||||||
public string Software { get; set; }
|
|
||||||
|
|
||||||
public double? ExposureTime { get; set; }
|
|
||||||
|
|
||||||
public double? FocalLength { get; set; }
|
|
||||||
|
|
||||||
public ImageOrientation? Orientation { get; set; }
|
|
||||||
|
|
||||||
public double? Aperture { get; set; }
|
|
||||||
|
|
||||||
public double? ShutterSpeed { get; set; }
|
|
||||||
|
|
||||||
public double? Latitude { get; set; }
|
|
||||||
|
|
||||||
public double? Longitude { get; set; }
|
|
||||||
|
|
||||||
public double? Altitude { get; set; }
|
|
||||||
|
|
||||||
public int? IsoSpeedRating { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,19 +15,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Studio : BaseItem, IItemByName
|
public class Studio : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
public override List<string> GetUserDataKeys()
|
|
||||||
{
|
|
||||||
var list = base.GetUserDataKeys();
|
|
||||||
|
|
||||||
list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string CreatePresentationUniqueKey()
|
|
||||||
{
|
|
||||||
return GetUserDataKeys()[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the folder containing the item.
|
/// Gets the folder containing the item.
|
||||||
/// If the item is a folder, it returns the folder itself.
|
/// If the item is a folder, it returns the folder itself.
|
||||||
@ -42,6 +29,22 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsAncestors => false;
|
public override bool SupportsAncestors => false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPeople => false;
|
||||||
|
|
||||||
|
public override List<string> GetUserDataKeys()
|
||||||
|
{
|
||||||
|
var list = base.GetUserDataKeys();
|
||||||
|
|
||||||
|
list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string CreatePresentationUniqueKey()
|
||||||
|
{
|
||||||
|
return GetUserDataKeys()[0];
|
||||||
|
}
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
{
|
{
|
||||||
double value = 16;
|
double value = 16;
|
||||||
@ -67,9 +70,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return LibraryManager.GetItemList(query);
|
return LibraryManager.GetItemList(query);
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPeople => false;
|
|
||||||
|
|
||||||
public static string GetPath(string name)
|
public static string GetPath(string name)
|
||||||
{
|
{
|
||||||
return GetPath(name, true);
|
return GetPath(name, true);
|
||||||
@ -105,6 +105,8 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="replaceAllMetadata"><c>true</c> to replace all metadata, <c>false</c> to not.</param>
|
||||||
|
/// <returns><c>true</c> if changes were made, <c>false</c> if not.</returns>
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
var hasChanges = base.BeforeMetadataRefresh(replaceAllMetadata);
|
||||||
|
@ -49,12 +49,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
/// <value>The index number.</value>
|
/// <value>The index number.</value>
|
||||||
public int? IndexNumberEnd { get; set; }
|
public int? IndexNumberEnd { get; set; }
|
||||||
|
|
||||||
public string FindSeriesSortName()
|
|
||||||
{
|
|
||||||
var series = Series;
|
|
||||||
return series == null ? SeriesName : series.SortName;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
protected override bool SupportsOwnedItems => IsStacked || MediaSourceCount > 1;
|
protected override bool SupportsOwnedItems => IsStacked || MediaSourceCount > 1;
|
||||||
|
|
||||||
@ -76,45 +70,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
protected override bool EnableDefaultVideoUserDataKeys => false;
|
protected override bool EnableDefaultVideoUserDataKeys => false;
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
|
||||||
{
|
|
||||||
// hack for tv plugins
|
|
||||||
if (SourceType == SourceType.Channel)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 16.0 / 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override List<string> GetUserDataKeys()
|
|
||||||
{
|
|
||||||
var list = base.GetUserDataKeys();
|
|
||||||
|
|
||||||
var series = Series;
|
|
||||||
if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
|
|
||||||
{
|
|
||||||
var seriesUserDataKeys = series.GetUserDataKeys();
|
|
||||||
var take = seriesUserDataKeys.Count;
|
|
||||||
if (seriesUserDataKeys.Count > 1)
|
|
||||||
{
|
|
||||||
take--;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newList = seriesUserDataKeys.GetRange(0, take);
|
|
||||||
var suffix = ParentIndexNumber.Value.ToString("000", CultureInfo.InvariantCulture) + IndexNumber.Value.ToString("000", CultureInfo.InvariantCulture);
|
|
||||||
for (int i = 0; i < take; i++)
|
|
||||||
{
|
|
||||||
newList[i] = newList[i] + suffix;
|
|
||||||
}
|
|
||||||
|
|
||||||
newList.AddRange(list);
|
|
||||||
list = newList;
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the Episode's Series Instance.
|
/// Gets the Episode's Series Instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -161,6 +116,74 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string SeasonName { get; set; }
|
public string SeasonName { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsRemoteImageDownloading
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (IsMissingEpisode)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool IsMissingEpisode => LocationType == LocationType.Virtual;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Guid SeasonId { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Guid SeriesId { get; set; }
|
||||||
|
|
||||||
|
public string FindSeriesSortName()
|
||||||
|
{
|
||||||
|
var series = Series;
|
||||||
|
return series == null ? SeriesName : series.SortName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
|
{
|
||||||
|
// hack for tv plugins
|
||||||
|
if (SourceType == SourceType.Channel)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 16.0 / 9;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<string> GetUserDataKeys()
|
||||||
|
{
|
||||||
|
var list = base.GetUserDataKeys();
|
||||||
|
|
||||||
|
var series = Series;
|
||||||
|
if (series != null && ParentIndexNumber.HasValue && IndexNumber.HasValue)
|
||||||
|
{
|
||||||
|
var seriesUserDataKeys = series.GetUserDataKeys();
|
||||||
|
var take = seriesUserDataKeys.Count;
|
||||||
|
if (seriesUserDataKeys.Count > 1)
|
||||||
|
{
|
||||||
|
take--;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newList = seriesUserDataKeys.GetRange(0, take);
|
||||||
|
var suffix = ParentIndexNumber.Value.ToString("000", CultureInfo.InvariantCulture) + IndexNumber.Value.ToString("000", CultureInfo.InvariantCulture);
|
||||||
|
for (int i = 0; i < take; i++)
|
||||||
|
{
|
||||||
|
newList[i] = newList[i] + suffix;
|
||||||
|
}
|
||||||
|
|
||||||
|
newList.AddRange(list);
|
||||||
|
list = newList;
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
public string FindSeriesPresentationUniqueKey()
|
public string FindSeriesPresentationUniqueKey()
|
||||||
{
|
{
|
||||||
var series = Series;
|
var series = Series;
|
||||||
@ -242,29 +265,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsRemoteImageDownloading
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (IsMissingEpisode)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public bool IsMissingEpisode => LocationType == LocationType.Virtual;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Guid SeasonId { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Guid SeriesId { get; set; }
|
|
||||||
|
|
||||||
public Guid FindSeriesId()
|
public Guid FindSeriesId()
|
||||||
{
|
{
|
||||||
var series = FindParent<Series>();
|
var series = FindParent<Series>();
|
||||||
|
@ -38,6 +38,50 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override Guid DisplayParentId => SeriesId;
|
public override Guid DisplayParentId => SeriesId;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets this Episode's Series Instance.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The series.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public Series Series
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var seriesId = SeriesId;
|
||||||
|
if (seriesId == Guid.Empty)
|
||||||
|
{
|
||||||
|
seriesId = FindSeriesId();
|
||||||
|
}
|
||||||
|
|
||||||
|
return seriesId == Guid.Empty ? null : (LibraryManager.GetItemById(seriesId) as Series);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string SeriesPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var series = Series;
|
||||||
|
|
||||||
|
if (series != null)
|
||||||
|
{
|
||||||
|
return series.Path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return System.IO.Path.GetDirectoryName(Path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string SeriesPresentationUniqueKey { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public string SeriesName { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public Guid SeriesId { get; set; }
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
{
|
{
|
||||||
double value = 2;
|
double value = 2;
|
||||||
@ -80,41 +124,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets this Episode's Series Instance.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The series.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public Series Series
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var seriesId = SeriesId;
|
|
||||||
if (seriesId == Guid.Empty)
|
|
||||||
{
|
|
||||||
seriesId = FindSeriesId();
|
|
||||||
}
|
|
||||||
|
|
||||||
return seriesId == Guid.Empty ? null : (LibraryManager.GetItemById(seriesId) as Series);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public string SeriesPath
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var series = Series;
|
|
||||||
|
|
||||||
if (series != null)
|
|
||||||
{
|
|
||||||
return series.Path;
|
|
||||||
}
|
|
||||||
|
|
||||||
return System.IO.Path.GetDirectoryName(Path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string CreatePresentationUniqueKey()
|
public override string CreatePresentationUniqueKey()
|
||||||
{
|
{
|
||||||
if (IndexNumber.HasValue)
|
if (IndexNumber.HasValue)
|
||||||
@ -157,6 +166,9 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the episodes.
|
/// Gets the episodes.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="user">The user.</param>
|
||||||
|
/// <param name="options">The options to use.</param>
|
||||||
|
/// <returns>Set of episodes.</returns>
|
||||||
public List<BaseItem> GetEpisodes(User user, DtoOptions options)
|
public List<BaseItem> GetEpisodes(User user, DtoOptions options)
|
||||||
{
|
{
|
||||||
return GetEpisodes(Series, user, options);
|
return GetEpisodes(Series, user, options);
|
||||||
@ -193,15 +205,6 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
return UnratedItem.Series;
|
return UnratedItem.Series;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public string SeriesPresentationUniqueKey { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public string SeriesName { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public Guid SeriesId { get; set; }
|
|
||||||
|
|
||||||
public string FindSeriesPresentationUniqueKey()
|
public string FindSeriesPresentationUniqueKey()
|
||||||
{
|
{
|
||||||
var series = Series;
|
var series = Series;
|
||||||
@ -241,6 +244,7 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
/// This is called before any metadata refresh and returns true or false indicating if changes were made.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="replaceAllMetadata"><c>true</c> to replace metdata, <c>false</c> to not.</param>
|
||||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
|
||||||
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
public override bool BeforeMetadataRefresh(bool replaceAllMetadata)
|
||||||
{
|
{
|
||||||
|
@ -72,6 +72,9 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
/// <value>The status.</value>
|
/// <value>The status.</value>
|
||||||
public SeriesStatus? Status { get; set; }
|
public SeriesStatus? Status { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool StopRefreshIfLocalMetadataFound => false;
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
{
|
{
|
||||||
double value = 2;
|
double value = 2;
|
||||||
@ -394,6 +397,10 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Filters the episodes by season.
|
/// Filters the episodes by season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="episodes">The episodes.</param>
|
||||||
|
/// <param name="parentSeason">The season.</param>
|
||||||
|
/// <param name="includeSpecials"><c>true</c> to include special, <c>false</c> to not.</param>
|
||||||
|
/// <returns>The set of episodes.</returns>
|
||||||
public static IEnumerable<BaseItem> FilterEpisodesBySeason(IEnumerable<BaseItem> episodes, Season parentSeason, bool includeSpecials)
|
public static IEnumerable<BaseItem> FilterEpisodesBySeason(IEnumerable<BaseItem> episodes, Season parentSeason, bool includeSpecials)
|
||||||
{
|
{
|
||||||
var seasonNumber = parentSeason.IndexNumber;
|
var seasonNumber = parentSeason.IndexNumber;
|
||||||
@ -424,6 +431,10 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Filters the episodes by season.
|
/// Filters the episodes by season.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="episodes">The episodes.</param>
|
||||||
|
/// <param name="seasonNumber">The season.</param>
|
||||||
|
/// <param name="includeSpecials"><c>true</c> to include special, <c>false</c> to not.</param>
|
||||||
|
/// <returns>The set of episodes.</returns>
|
||||||
public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials)
|
public static IEnumerable<Episode> FilterEpisodesBySeason(IEnumerable<Episode> episodes, int seasonNumber, bool includeSpecials)
|
||||||
{
|
{
|
||||||
if (!includeSpecials || seasonNumber < 1)
|
if (!includeSpecials || seasonNumber < 1)
|
||||||
@ -499,8 +510,5 @@ namespace MediaBrowser.Controller.Entities.TV
|
|||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool StopRefreshIfLocalMetadataFound => false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CA1819, CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -23,6 +23,9 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
TrailerTypes = Array.Empty<TrailerType>();
|
TrailerTypes = Array.Empty<TrailerType>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool StopRefreshIfLocalMetadataFound => false;
|
||||||
|
|
||||||
public TrailerType[] TrailerTypes { get; set; }
|
public TrailerType[] TrailerTypes { get; set; }
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
@ -97,8 +100,5 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool StopRefreshIfLocalMetadataFound => false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,13 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class UserItemData
|
public class UserItemData
|
||||||
{
|
{
|
||||||
|
public const double MinLikeValue = 6.5;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The _rating.
|
||||||
|
/// </summary>
|
||||||
|
private double? _rating;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the user id.
|
/// Gets or sets the user id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -24,11 +31,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <value>The key.</value>
|
/// <value>The key.</value>
|
||||||
public string Key { get; set; }
|
public string Key { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The _rating.
|
|
||||||
/// </summary>
|
|
||||||
private double? _rating;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the users 0-10 rating.
|
/// Gets or sets the users 0-10 rating.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -93,8 +95,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <value>The index of the subtitle stream.</value>
|
/// <value>The index of the subtitle stream.</value>
|
||||||
public int? SubtitleStreamIndex { get; set; }
|
public int? SubtitleStreamIndex { get; set; }
|
||||||
|
|
||||||
public const double MinLikeValue = 6.5;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether the item is liked or not.
|
/// Gets or sets a value indicating whether the item is liked or not.
|
||||||
/// This should never be serialized.
|
/// This should never be serialized.
|
||||||
|
@ -21,8 +21,28 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class UserRootFolder : Folder
|
public class UserRootFolder : Folder
|
||||||
{
|
{
|
||||||
private List<Guid> _childrenIds = null;
|
|
||||||
private readonly object _childIdsLock = new object();
|
private readonly object _childIdsLock = new object();
|
||||||
|
private List<Guid> _childrenIds = null;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsInheritedParentImages => false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool SupportsPlayedStatus => false;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
protected override bool SupportsShortcutChildren => true;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool IsPreSorted => true;
|
||||||
|
|
||||||
|
private void ClearCache()
|
||||||
|
{
|
||||||
|
lock (_childIdsLock)
|
||||||
|
{
|
||||||
|
_childrenIds = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected override List<BaseItem> LoadChildren()
|
protected override List<BaseItem> LoadChildren()
|
||||||
{
|
{
|
||||||
@ -39,20 +59,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsInheritedParentImages => false;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPlayedStatus => false;
|
|
||||||
|
|
||||||
private void ClearCache()
|
|
||||||
{
|
|
||||||
lock (_childIdsLock)
|
|
||||||
{
|
|
||||||
_childrenIds = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
|
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
|
||||||
{
|
{
|
||||||
if (query.Recursive)
|
if (query.Recursive)
|
||||||
@ -74,12 +80,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return GetChildren(user, true).Count;
|
return GetChildren(user, true).Count;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
protected override bool SupportsShortcutChildren => true;
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool IsPreSorted => true;
|
|
||||||
|
|
||||||
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
|
protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
|
||||||
{
|
{
|
||||||
var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
|
var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
|
||||||
|
@ -28,6 +28,14 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
ISupportsPlaceHolders,
|
ISupportsPlaceHolders,
|
||||||
IHasMediaSources
|
IHasMediaSources
|
||||||
{
|
{
|
||||||
|
public Video()
|
||||||
|
{
|
||||||
|
AdditionalParts = Array.Empty<string>();
|
||||||
|
LocalAlternateVersions = Array.Empty<string>();
|
||||||
|
SubtitleFiles = Array.Empty<string>();
|
||||||
|
LinkedAlternateVersions = Array.Empty<LinkedChild>();
|
||||||
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public string PrimaryVersionId { get; set; }
|
public string PrimaryVersionId { get; set; }
|
||||||
|
|
||||||
@ -74,30 +82,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetPrimaryVersionId(string id)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(id))
|
|
||||||
{
|
|
||||||
PrimaryVersionId = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PrimaryVersionId = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
PresentationUniqueKey = CreatePresentationUniqueKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string CreatePresentationUniqueKey()
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(PrimaryVersionId))
|
|
||||||
{
|
|
||||||
return PrimaryVersionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.CreatePresentationUniqueKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsThemeMedia => true;
|
public override bool SupportsThemeMedia => true;
|
||||||
|
|
||||||
@ -151,24 +135,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// <value>The aspect ratio.</value>
|
/// <value>The aspect ratio.</value>
|
||||||
public string AspectRatio { get; set; }
|
public string AspectRatio { get; set; }
|
||||||
|
|
||||||
public Video()
|
|
||||||
{
|
|
||||||
AdditionalParts = Array.Empty<string>();
|
|
||||||
LocalAlternateVersions = Array.Empty<string>();
|
|
||||||
SubtitleFiles = Array.Empty<string>();
|
|
||||||
LinkedAlternateVersions = Array.Empty<LinkedChild>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanDownload()
|
|
||||||
{
|
|
||||||
if (VideoType == VideoType.Dvd || VideoType == VideoType.BluRay)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return IsFileProtocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsAddingToPlaylist => true;
|
public override bool SupportsAddingToPlaylist => true;
|
||||||
|
|
||||||
@ -196,16 +162,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool HasLocalAlternateVersions => LocalAlternateVersions.Length > 0;
|
public override bool HasLocalAlternateVersions => LocalAlternateVersions.Length > 0;
|
||||||
|
|
||||||
public IEnumerable<Guid> GetAdditionalPartIds()
|
|
||||||
{
|
|
||||||
return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Guid> GetLocalAlternateVersionIds()
|
|
||||||
{
|
|
||||||
return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ILiveTvManager LiveTvManager { get; set; }
|
public static ILiveTvManager LiveTvManager { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
@ -222,21 +178,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool IsActiveRecording()
|
|
||||||
{
|
|
||||||
return LiveTvManager.GetActiveRecordingInfo(Path) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool CanDelete()
|
|
||||||
{
|
|
||||||
if (IsActiveRecording())
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.CanDelete();
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool IsCompleteMedia
|
public bool IsCompleteMedia
|
||||||
{
|
{
|
||||||
@ -254,80 +195,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
protected virtual bool EnableDefaultVideoUserDataKeys => true;
|
protected virtual bool EnableDefaultVideoUserDataKeys => true;
|
||||||
|
|
||||||
public override List<string> GetUserDataKeys()
|
|
||||||
{
|
|
||||||
var list = base.GetUserDataKeys();
|
|
||||||
|
|
||||||
if (EnableDefaultVideoUserDataKeys)
|
|
||||||
{
|
|
||||||
if (ExtraType.HasValue)
|
|
||||||
{
|
|
||||||
var key = this.GetProviderId(MetadataProvider.Tmdb);
|
|
||||||
if (!string.IsNullOrEmpty(key))
|
|
||||||
{
|
|
||||||
list.Insert(0, GetUserDataKey(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
key = this.GetProviderId(MetadataProvider.Imdb);
|
|
||||||
if (!string.IsNullOrEmpty(key))
|
|
||||||
{
|
|
||||||
list.Insert(0, GetUserDataKey(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var key = this.GetProviderId(MetadataProvider.Imdb);
|
|
||||||
if (!string.IsNullOrEmpty(key))
|
|
||||||
{
|
|
||||||
list.Insert(0, key);
|
|
||||||
}
|
|
||||||
|
|
||||||
key = this.GetProviderId(MetadataProvider.Tmdb);
|
|
||||||
if (!string.IsNullOrEmpty(key))
|
|
||||||
{
|
|
||||||
list.Insert(0, key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetUserDataKey(string providerId)
|
|
||||||
{
|
|
||||||
var key = providerId + "-" + ExtraType.ToString().ToLowerInvariant();
|
|
||||||
|
|
||||||
// Make sure different trailers have their own data.
|
|
||||||
if (RunTimeTicks.HasValue)
|
|
||||||
{
|
|
||||||
key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IEnumerable<Video> GetLinkedAlternateVersions()
|
|
||||||
{
|
|
||||||
return LinkedAlternateVersions
|
|
||||||
.Select(GetLinkedChild)
|
|
||||||
.Where(i => i != null)
|
|
||||||
.OfType<Video>()
|
|
||||||
.OrderBy(i => i.SortName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the additional parts.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>IEnumerable{Video}.</returns>
|
|
||||||
public IOrderedEnumerable<Video> GetAdditionalParts()
|
|
||||||
{
|
|
||||||
return GetAdditionalPartIds()
|
|
||||||
.Select(i => LibraryManager.GetItemById(i))
|
|
||||||
.Where(i => i != null)
|
|
||||||
.OfType<Video>()
|
|
||||||
.OrderBy(i => i.SortName);
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override string ContainingFolderPath
|
public override string ContainingFolderPath
|
||||||
{
|
{
|
||||||
@ -369,6 +236,153 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether [is3 D].
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if [is3 D]; otherwise, <c>false</c>.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public bool Is3D => Video3DFormat.HasValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the media.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The type of the media.</value>
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string MediaType => Model.Entities.MediaType.Video;
|
||||||
|
|
||||||
|
public override List<string> GetUserDataKeys()
|
||||||
|
{
|
||||||
|
var list = base.GetUserDataKeys();
|
||||||
|
|
||||||
|
if (EnableDefaultVideoUserDataKeys)
|
||||||
|
{
|
||||||
|
if (ExtraType.HasValue)
|
||||||
|
{
|
||||||
|
var key = this.GetProviderId(MetadataProvider.Tmdb);
|
||||||
|
if (!string.IsNullOrEmpty(key))
|
||||||
|
{
|
||||||
|
list.Insert(0, GetUserDataKey(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
key = this.GetProviderId(MetadataProvider.Imdb);
|
||||||
|
if (!string.IsNullOrEmpty(key))
|
||||||
|
{
|
||||||
|
list.Insert(0, GetUserDataKey(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var key = this.GetProviderId(MetadataProvider.Imdb);
|
||||||
|
if (!string.IsNullOrEmpty(key))
|
||||||
|
{
|
||||||
|
list.Insert(0, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
key = this.GetProviderId(MetadataProvider.Tmdb);
|
||||||
|
if (!string.IsNullOrEmpty(key))
|
||||||
|
{
|
||||||
|
list.Insert(0, key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPrimaryVersionId(string id)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(id))
|
||||||
|
{
|
||||||
|
PrimaryVersionId = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PrimaryVersionId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
PresentationUniqueKey = CreatePresentationUniqueKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string CreatePresentationUniqueKey()
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(PrimaryVersionId))
|
||||||
|
{
|
||||||
|
return PrimaryVersionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.CreatePresentationUniqueKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanDownload()
|
||||||
|
{
|
||||||
|
if (VideoType == VideoType.Dvd || VideoType == VideoType.BluRay)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsFileProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override bool IsActiveRecording()
|
||||||
|
{
|
||||||
|
return LiveTvManager.GetActiveRecordingInfo(Path) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool CanDelete()
|
||||||
|
{
|
||||||
|
if (IsActiveRecording())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return base.CanDelete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Guid> GetAdditionalPartIds()
|
||||||
|
{
|
||||||
|
return AdditionalParts.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Guid> GetLocalAlternateVersionIds()
|
||||||
|
{
|
||||||
|
return LocalAlternateVersions.Select(i => LibraryManager.GetNewItemId(i, typeof(Video)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetUserDataKey(string providerId)
|
||||||
|
{
|
||||||
|
var key = providerId + "-" + ExtraType.ToString().ToLowerInvariant();
|
||||||
|
|
||||||
|
// Make sure different trailers have their own data.
|
||||||
|
if (RunTimeTicks.HasValue)
|
||||||
|
{
|
||||||
|
key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<Video> GetLinkedAlternateVersions()
|
||||||
|
{
|
||||||
|
return LinkedAlternateVersions
|
||||||
|
.Select(GetLinkedChild)
|
||||||
|
.Where(i => i != null)
|
||||||
|
.OfType<Video>()
|
||||||
|
.OrderBy(i => i.SortName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the additional parts.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>IEnumerable{Video}.</returns>
|
||||||
|
public IOrderedEnumerable<Video> GetAdditionalParts()
|
||||||
|
{
|
||||||
|
return GetAdditionalPartIds()
|
||||||
|
.Select(i => LibraryManager.GetItemById(i))
|
||||||
|
.Where(i => i != null)
|
||||||
|
.OfType<Video>()
|
||||||
|
.OrderBy(i => i.SortName);
|
||||||
|
}
|
||||||
|
|
||||||
internal override ItemUpdateType UpdateFromResolvedItem(BaseItem newItem)
|
internal override ItemUpdateType UpdateFromResolvedItem(BaseItem newItem)
|
||||||
{
|
{
|
||||||
var updateType = base.UpdateFromResolvedItem(newItem);
|
var updateType = base.UpdateFromResolvedItem(newItem);
|
||||||
@ -397,20 +411,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return updateType;
|
return updateType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether [is3 D].
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if [is3 D]; otherwise, <c>false</c>.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public bool Is3D => Video3DFormat.HasValue;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the type of the media.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The type of the media.</value>
|
|
||||||
[JsonIgnore]
|
|
||||||
public override string MediaType => Model.Entities.MediaType.Video;
|
|
||||||
|
|
||||||
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
protected override async Task<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemMetadata> fileSystemChildren, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
|
var hasChanges = await base.RefreshedOwnedItems(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
|
||||||
|
@ -15,13 +15,11 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class Year : BaseItem, IItemByName
|
public class Year : BaseItem, IItemByName
|
||||||
{
|
{
|
||||||
public override List<string> GetUserDataKeys()
|
[JsonIgnore]
|
||||||
{
|
public override bool SupportsAncestors => false;
|
||||||
var list = base.GetUserDataKeys();
|
|
||||||
|
|
||||||
list.Insert(0, "Year-" + Name);
|
[JsonIgnore]
|
||||||
return list;
|
public override bool SupportsPeople => false;
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the folder containing the item.
|
/// Gets the folder containing the item.
|
||||||
@ -31,6 +29,19 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override string ContainingFolderPath => Path;
|
public override string ContainingFolderPath => Path;
|
||||||
|
|
||||||
|
public override bool CanDelete()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override List<string> GetUserDataKeys()
|
||||||
|
{
|
||||||
|
var list = base.GetUserDataKeys();
|
||||||
|
|
||||||
|
list.Insert(0, "Year-" + Name);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
{
|
{
|
||||||
double value = 2;
|
double value = 2;
|
||||||
@ -39,14 +50,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsAncestors => false;
|
|
||||||
|
|
||||||
public override bool CanDelete()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsSaveLocalMetadataEnabled()
|
public override bool IsSaveLocalMetadataEnabled()
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
@ -76,9 +79,6 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool SupportsPeople => false;
|
|
||||||
|
|
||||||
public static string GetPath(string name)
|
public static string GetPath(string name)
|
||||||
{
|
{
|
||||||
return GetPath(name, true);
|
return GetPath(name, true);
|
||||||
|
@ -10,6 +10,15 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
{
|
{
|
||||||
public class BaseEncodingJobOptions
|
public class BaseEncodingJobOptions
|
||||||
{
|
{
|
||||||
|
public BaseEncodingJobOptions()
|
||||||
|
{
|
||||||
|
EnableAutoStreamCopy = true;
|
||||||
|
AllowVideoStreamCopy = true;
|
||||||
|
AllowAudioStreamCopy = true;
|
||||||
|
Context = EncodingContext.Streaming;
|
||||||
|
StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the id.
|
/// Gets or sets the id.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -191,14 +200,5 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public BaseEncodingJobOptions()
|
|
||||||
{
|
|
||||||
EnableAutoStreamCopy = true;
|
|
||||||
AllowVideoStreamCopy = true;
|
|
||||||
AllowAudioStreamCopy = true;
|
|
||||||
Context = EncodingContext.Streaming;
|
|
||||||
StreamOptions = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,6 @@ using System.Collections.Generic;
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@ -16,9 +15,7 @@ using MediaBrowser.Model.Configuration;
|
|||||||
using MediaBrowser.Model.Dlna;
|
using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Dto;
|
using MediaBrowser.Model.Dto;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.MediaEncoding
|
namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
@ -161,6 +158,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the name of the output video codec.
|
/// Gets the name of the output video codec.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="state">Encording state.</param>
|
||||||
|
/// <param name="encodingOptions">Encoding options.</param>
|
||||||
|
/// <returns>Encoder string.</returns>
|
||||||
public string GetVideoEncoder(EncodingJobInfo state, EncodingOptions encodingOptions)
|
public string GetVideoEncoder(EncodingJobInfo state, EncodingOptions encodingOptions)
|
||||||
{
|
{
|
||||||
var codec = state.OutputVideoCodec;
|
var codec = state.OutputVideoCodec;
|
||||||
@ -315,6 +315,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets decoder from a codec.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="codec">Codec to use.</param>
|
||||||
|
/// <returns>Decoder string.</returns>
|
||||||
public string GetDecoderFromCodec(string codec)
|
public string GetDecoderFromCodec(string codec)
|
||||||
{
|
{
|
||||||
// For these need to find out the ffmpeg names
|
// For these need to find out the ffmpeg names
|
||||||
@ -344,6 +349,8 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Infers the audio codec based on the url.
|
/// Infers the audio codec based on the url.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="container">Container to use.</param>
|
||||||
|
/// <returns>Codec string.</returns>
|
||||||
public string InferAudioCodec(string container)
|
public string InferAudioCodec(string container)
|
||||||
{
|
{
|
||||||
var ext = "." + (container ?? string.Empty);
|
var ext = "." + (container ?? string.Empty);
|
||||||
@ -489,6 +496,9 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the input argument.
|
/// Gets the input argument.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="state">Encoding state.</param>
|
||||||
|
/// <param name="encodingOptions">Encoding options.</param>
|
||||||
|
/// <returns>Input arguments.</returns>
|
||||||
public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
|
public string GetInputArgument(EncodingJobInfo state, EncodingOptions encodingOptions)
|
||||||
{
|
{
|
||||||
var arg = new StringBuilder();
|
var arg = new StringBuilder();
|
||||||
@ -965,6 +975,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the video bitrate to specify on the command line.
|
/// Gets the video bitrate to specify on the command line.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="state">Encoding state.</param>
|
||||||
|
/// <param name="videoEncoder">Video encoder to use.</param>
|
||||||
|
/// <param name="encodingOptions">Encoding options.</param>
|
||||||
|
/// <param name="defaultPreset">Default present to use for encoding.</param>
|
||||||
|
/// <returns>Video bitrate.</returns>
|
||||||
public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultPreset)
|
public string GetVideoQualityParam(EncodingJobInfo state, string videoEncoder, EncodingOptions encodingOptions, string defaultPreset)
|
||||||
{
|
{
|
||||||
var param = string.Empty;
|
var param = string.Empty;
|
||||||
@ -1966,8 +1981,12 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the graphical subtitle param.
|
/// Gets the graphical subtitle parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="state">Encoding state.</param>
|
||||||
|
/// <param name="options">Encoding options.</param>
|
||||||
|
/// <param name="outputVideoCodec">Video codec to use.</param>
|
||||||
|
/// <returns>Graphical subtitle parameter.</returns>
|
||||||
public string GetGraphicalSubtitleParam(
|
public string GetGraphicalSubtitleParam(
|
||||||
EncodingJobInfo state,
|
EncodingJobInfo state,
|
||||||
EncodingOptions options,
|
EncodingOptions options,
|
||||||
@ -2485,6 +2504,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
return string.Format(CultureInfo.InvariantCulture, filter, widthParam, heightParam);
|
return string.Format(CultureInfo.InvariantCulture, filter, widthParam, heightParam);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the output size parameter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="state">Encoding state.</param>
|
||||||
|
/// <param name="options">Encoding options.</param>
|
||||||
|
/// <param name="outputVideoCodec">Video codec to use.</param>
|
||||||
|
/// <returns>The output size parameter.</returns>
|
||||||
public string GetOutputSizeParam(
|
public string GetOutputSizeParam(
|
||||||
EncodingJobInfo state,
|
EncodingJobInfo state,
|
||||||
EncodingOptions options,
|
EncodingOptions options,
|
||||||
@ -2495,8 +2521,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
/// Gets the output size parameter.
|
||||||
/// If we're going to put a fixed size on the command line, this will calculate it.
|
/// If we're going to put a fixed size on the command line, this will calculate it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="state">Encoding state.</param>
|
||||||
|
/// <param name="options">Encoding options.</param>
|
||||||
|
/// <param name="outputVideoCodec">Video codec to use.</param>
|
||||||
|
/// <returns>The output size parameter.</returns>
|
||||||
public string GetOutputSizeParamInternal(
|
public string GetOutputSizeParamInternal(
|
||||||
EncodingJobInfo state,
|
EncodingJobInfo state,
|
||||||
EncodingOptions options,
|
EncodingOptions options,
|
||||||
@ -2908,6 +2939,10 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the number of threads.
|
/// Gets the number of threads.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="state">Encoding state.</param>
|
||||||
|
/// <param name="encodingOptions">Encoding options.</param>
|
||||||
|
/// <param name="outputVideoCodec">Video codec to use.</param>
|
||||||
|
/// <returns>Number of threads.</returns>
|
||||||
#nullable enable
|
#nullable enable
|
||||||
public static int GetNumberOfThreads(EncodingJobInfo? state, EncodingOptions encodingOptions, string? outputVideoCodec)
|
public static int GetNumberOfThreads(EncodingJobInfo? state, EncodingOptions encodingOptions, string? outputVideoCodec)
|
||||||
{
|
{
|
||||||
@ -3551,6 +3586,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a hw decoder name.
|
/// Gets a hw decoder name.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="options">Encoding options.</param>
|
||||||
|
/// <param name="decoder">Decoder to use.</param>
|
||||||
|
/// <param name="videoCodec">Video codec to use.</param>
|
||||||
|
/// <param name="isColorDepth10">Specifies if color depth 10.</param>
|
||||||
|
/// <returns>Hardware decoder name.</returns>
|
||||||
public string GetHwDecoderName(EncodingOptions options, string decoder, string videoCodec, bool isColorDepth10)
|
public string GetHwDecoderName(EncodingOptions options, string decoder, string videoCodec, bool isColorDepth10)
|
||||||
{
|
{
|
||||||
var isCodecAvailable = _mediaEncoder.SupportsDecoder(decoder) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase);
|
var isCodecAvailable = _mediaEncoder.SupportsDecoder(decoder) && options.HardwareDecodingCodecs.Contains(videoCodec, StringComparer.OrdinalIgnoreCase);
|
||||||
@ -3569,6 +3609,11 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a hwaccel type to use as a hardware decoder(dxva/vaapi) depending on the system.
|
/// Gets a hwaccel type to use as a hardware decoder(dxva/vaapi) depending on the system.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="state">Encoding state.</param>
|
||||||
|
/// <param name="options">Encoding options.</param>
|
||||||
|
/// <param name="videoCodec">Video codec to use.</param>
|
||||||
|
/// <param name="isColorDepth10">Specifies if color depth 10.</param>
|
||||||
|
/// <returns>Hardware accelerator type.</returns>
|
||||||
public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec, bool isColorDepth10)
|
public string GetHwaccelType(EncodingJobInfo state, EncodingOptions options, string videoCodec, bool isColorDepth10)
|
||||||
{
|
{
|
||||||
var isWindows = OperatingSystem.IsWindows();
|
var isWindows = OperatingSystem.IsWindows();
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591, SA1401
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -20,6 +20,44 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
// For now, a common base class until the API and MediaEncoding classes are unified
|
// For now, a common base class until the API and MediaEncoding classes are unified
|
||||||
public class EncodingJobInfo
|
public class EncodingJobInfo
|
||||||
{
|
{
|
||||||
|
public int? OutputAudioBitrate;
|
||||||
|
public int? OutputAudioChannels;
|
||||||
|
|
||||||
|
private TranscodeReason[] _transcodeReasons = null;
|
||||||
|
|
||||||
|
public EncodingJobInfo(TranscodingJobType jobType)
|
||||||
|
{
|
||||||
|
TranscodingType = jobType;
|
||||||
|
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
SupportedAudioCodecs = Array.Empty<string>();
|
||||||
|
SupportedVideoCodecs = Array.Empty<string>();
|
||||||
|
SupportedSubtitleCodecs = Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TranscodeReason[] TranscodeReasons
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_transcodeReasons == null)
|
||||||
|
{
|
||||||
|
if (BaseRequest.TranscodeReasons == null)
|
||||||
|
{
|
||||||
|
return Array.Empty<TranscodeReason>();
|
||||||
|
}
|
||||||
|
|
||||||
|
_transcodeReasons = BaseRequest.TranscodeReasons
|
||||||
|
.Split(',')
|
||||||
|
.Where(i => !string.IsNullOrEmpty(i))
|
||||||
|
.Select(v => (TranscodeReason)Enum.Parse(typeof(TranscodeReason), v, true))
|
||||||
|
.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
return _transcodeReasons;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IProgress<double> Progress { get; set; }
|
||||||
|
|
||||||
public MediaStream VideoStream { get; set; }
|
public MediaStream VideoStream { get; set; }
|
||||||
|
|
||||||
public VideoType VideoType { get; set; }
|
public VideoType VideoType { get; set; }
|
||||||
@ -58,40 +96,6 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
|
|
||||||
public string MimeType { get; set; }
|
public string MimeType { get; set; }
|
||||||
|
|
||||||
public string GetMimeType(string outputPath, bool enableStreamDefault = true)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(MimeType))
|
|
||||||
{
|
|
||||||
return MimeType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
|
|
||||||
}
|
|
||||||
|
|
||||||
private TranscodeReason[] _transcodeReasons = null;
|
|
||||||
|
|
||||||
public TranscodeReason[] TranscodeReasons
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (_transcodeReasons == null)
|
|
||||||
{
|
|
||||||
if (BaseRequest.TranscodeReasons == null)
|
|
||||||
{
|
|
||||||
return Array.Empty<TranscodeReason>();
|
|
||||||
}
|
|
||||||
|
|
||||||
_transcodeReasons = BaseRequest.TranscodeReasons
|
|
||||||
.Split(',')
|
|
||||||
.Where(i => !string.IsNullOrEmpty(i))
|
|
||||||
.Select(v => (TranscodeReason)Enum.Parse(typeof(TranscodeReason), v, true))
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
return _transcodeReasons;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IgnoreInputDts => MediaSource.IgnoreDts;
|
public bool IgnoreInputDts => MediaSource.IgnoreDts;
|
||||||
|
|
||||||
public bool IgnoreInputIndex => MediaSource.IgnoreIndex;
|
public bool IgnoreInputIndex => MediaSource.IgnoreIndex;
|
||||||
@ -144,196 +148,17 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
|
|
||||||
public BaseEncodingJobOptions BaseRequest { get; set; }
|
public BaseEncodingJobOptions BaseRequest { get; set; }
|
||||||
|
|
||||||
public long? StartTimeTicks => BaseRequest.StartTimeTicks;
|
|
||||||
|
|
||||||
public bool CopyTimestamps => BaseRequest.CopyTimestamps;
|
|
||||||
|
|
||||||
public int? OutputAudioBitrate;
|
|
||||||
public int? OutputAudioChannels;
|
|
||||||
|
|
||||||
public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)
|
|
||||||
{
|
|
||||||
var videoStream = VideoStream;
|
|
||||||
var isInputInterlaced = videoStream != null && videoStream.IsInterlaced;
|
|
||||||
|
|
||||||
if (!isInputInterlaced)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Support general param
|
|
||||||
if (BaseRequest.DeInterlace)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(videoCodec))
|
|
||||||
{
|
|
||||||
if (string.Equals(BaseRequest.GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return forceDeinterlaceIfSourceIsInterlaced && isInputInterlaced;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string[] GetRequestedProfiles(string codec)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(BaseRequest.Profile))
|
|
||||||
{
|
|
||||||
return BaseRequest.Profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(codec))
|
|
||||||
{
|
|
||||||
var profile = BaseRequest.GetOption(codec, "profile");
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(profile))
|
|
||||||
{
|
|
||||||
return profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.Empty<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public string GetRequestedLevel(string codec)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(BaseRequest.Level))
|
|
||||||
{
|
|
||||||
return BaseRequest.Level;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(codec))
|
|
||||||
{
|
|
||||||
return BaseRequest.GetOption(codec, "level");
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? GetRequestedMaxRefFrames(string codec)
|
|
||||||
{
|
|
||||||
if (BaseRequest.MaxRefFrames.HasValue)
|
|
||||||
{
|
|
||||||
return BaseRequest.MaxRefFrames;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(codec))
|
|
||||||
{
|
|
||||||
var value = BaseRequest.GetOption(codec, "maxrefframes");
|
|
||||||
if (!string.IsNullOrEmpty(value)
|
|
||||||
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? GetRequestedVideoBitDepth(string codec)
|
|
||||||
{
|
|
||||||
if (BaseRequest.MaxVideoBitDepth.HasValue)
|
|
||||||
{
|
|
||||||
return BaseRequest.MaxVideoBitDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(codec))
|
|
||||||
{
|
|
||||||
var value = BaseRequest.GetOption(codec, "videobitdepth");
|
|
||||||
if (!string.IsNullOrEmpty(value)
|
|
||||||
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? GetRequestedAudioBitDepth(string codec)
|
|
||||||
{
|
|
||||||
if (BaseRequest.MaxAudioBitDepth.HasValue)
|
|
||||||
{
|
|
||||||
return BaseRequest.MaxAudioBitDepth;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(codec))
|
|
||||||
{
|
|
||||||
var value = BaseRequest.GetOption(codec, "audiobitdepth");
|
|
||||||
if (!string.IsNullOrEmpty(value)
|
|
||||||
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? GetRequestedAudioChannels(string codec)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(codec))
|
|
||||||
{
|
|
||||||
var value = BaseRequest.GetOption(codec, "audiochannels");
|
|
||||||
if (!string.IsNullOrEmpty(value)
|
|
||||||
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BaseRequest.MaxAudioChannels.HasValue)
|
|
||||||
{
|
|
||||||
return BaseRequest.MaxAudioChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BaseRequest.AudioChannels.HasValue)
|
|
||||||
{
|
|
||||||
return BaseRequest.AudioChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BaseRequest.TranscodingMaxAudioChannels.HasValue)
|
|
||||||
{
|
|
||||||
return BaseRequest.TranscodingMaxAudioChannels;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsVideoRequest { get; set; }
|
public bool IsVideoRequest { get; set; }
|
||||||
|
|
||||||
public TranscodingJobType TranscodingType { get; set; }
|
public TranscodingJobType TranscodingType { get; set; }
|
||||||
|
|
||||||
public EncodingJobInfo(TranscodingJobType jobType)
|
public long? StartTimeTicks => BaseRequest.StartTimeTicks;
|
||||||
{
|
|
||||||
TranscodingType = jobType;
|
public bool CopyTimestamps => BaseRequest.CopyTimestamps;
|
||||||
RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
|
||||||
SupportedAudioCodecs = Array.Empty<string>();
|
|
||||||
SupportedVideoCodecs = Array.Empty<string>();
|
|
||||||
SupportedSubtitleCodecs = Array.Empty<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSegmentedLiveStream
|
public bool IsSegmentedLiveStream
|
||||||
=> TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue;
|
=> TranscodingType != TranscodingJobType.Progressive && !RunTimeTicks.HasValue;
|
||||||
|
|
||||||
public bool EnableBreakOnNonKeyFrames(string videoCodec)
|
|
||||||
{
|
|
||||||
if (TranscodingType != TranscodingJobType.Progressive)
|
|
||||||
{
|
|
||||||
if (IsSegmentedLiveStream)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return BaseRequest.BreakOnNonKeyFrames && EncodingHelper.IsCopyCodec(videoCodec);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int? TotalOutputBitrate => (OutputAudioBitrate ?? 0) + (OutputVideoBitrate ?? 0);
|
public int? TotalOutputBitrate => (OutputAudioBitrate ?? 0) + (OutputVideoBitrate ?? 0);
|
||||||
|
|
||||||
public int? OutputWidth
|
public int? OutputWidth
|
||||||
@ -682,6 +507,21 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
|
|
||||||
public int HlsListSize => 0;
|
public int HlsListSize => 0;
|
||||||
|
|
||||||
|
public bool EnableBreakOnNonKeyFrames(string videoCodec)
|
||||||
|
{
|
||||||
|
if (TranscodingType != TranscodingJobType.Progressive)
|
||||||
|
{
|
||||||
|
if (IsSegmentedLiveStream)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return BaseRequest.BreakOnNonKeyFrames && EncodingHelper.IsCopyCodec(videoCodec);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private int? GetMediaStreamCount(MediaStreamType type, int limit)
|
private int? GetMediaStreamCount(MediaStreamType type, int limit)
|
||||||
{
|
{
|
||||||
var count = MediaSource.GetStreamCount(type);
|
var count = MediaSource.GetStreamCount(type);
|
||||||
@ -694,7 +534,167 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IProgress<double> Progress { get; set; }
|
public string GetMimeType(string outputPath, bool enableStreamDefault = true)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(MimeType))
|
||||||
|
{
|
||||||
|
return MimeType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MimeTypes.GetMimeType(outputPath, enableStreamDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool DeInterlace(string videoCodec, bool forceDeinterlaceIfSourceIsInterlaced)
|
||||||
|
{
|
||||||
|
var videoStream = VideoStream;
|
||||||
|
var isInputInterlaced = videoStream != null && videoStream.IsInterlaced;
|
||||||
|
|
||||||
|
if (!isInputInterlaced)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support general param
|
||||||
|
if (BaseRequest.DeInterlace)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(videoCodec))
|
||||||
|
{
|
||||||
|
if (string.Equals(BaseRequest.GetOption(videoCodec, "deinterlace"), "true", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return forceDeinterlaceIfSourceIsInterlaced && isInputInterlaced;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string[] GetRequestedProfiles(string codec)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(BaseRequest.Profile))
|
||||||
|
{
|
||||||
|
return BaseRequest.Profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(codec))
|
||||||
|
{
|
||||||
|
var profile = BaseRequest.GetOption(codec, "profile");
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(profile))
|
||||||
|
{
|
||||||
|
return profile.Split(new[] { '|', ',' }, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetRequestedLevel(string codec)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(BaseRequest.Level))
|
||||||
|
{
|
||||||
|
return BaseRequest.Level;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(codec))
|
||||||
|
{
|
||||||
|
return BaseRequest.GetOption(codec, "level");
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? GetRequestedMaxRefFrames(string codec)
|
||||||
|
{
|
||||||
|
if (BaseRequest.MaxRefFrames.HasValue)
|
||||||
|
{
|
||||||
|
return BaseRequest.MaxRefFrames;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(codec))
|
||||||
|
{
|
||||||
|
var value = BaseRequest.GetOption(codec, "maxrefframes");
|
||||||
|
if (!string.IsNullOrEmpty(value)
|
||||||
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? GetRequestedVideoBitDepth(string codec)
|
||||||
|
{
|
||||||
|
if (BaseRequest.MaxVideoBitDepth.HasValue)
|
||||||
|
{
|
||||||
|
return BaseRequest.MaxVideoBitDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(codec))
|
||||||
|
{
|
||||||
|
var value = BaseRequest.GetOption(codec, "videobitdepth");
|
||||||
|
if (!string.IsNullOrEmpty(value)
|
||||||
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? GetRequestedAudioBitDepth(string codec)
|
||||||
|
{
|
||||||
|
if (BaseRequest.MaxAudioBitDepth.HasValue)
|
||||||
|
{
|
||||||
|
return BaseRequest.MaxAudioBitDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(codec))
|
||||||
|
{
|
||||||
|
var value = BaseRequest.GetOption(codec, "audiobitdepth");
|
||||||
|
if (!string.IsNullOrEmpty(value)
|
||||||
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int? GetRequestedAudioChannels(string codec)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(codec))
|
||||||
|
{
|
||||||
|
var value = BaseRequest.GetOption(codec, "audiochannels");
|
||||||
|
if (!string.IsNullOrEmpty(value)
|
||||||
|
&& int.TryParse(value, NumberStyles.Any, CultureInfo.InvariantCulture, out var result))
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BaseRequest.MaxAudioChannels.HasValue)
|
||||||
|
{
|
||||||
|
return BaseRequest.MaxAudioChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BaseRequest.AudioChannels.HasValue)
|
||||||
|
{
|
||||||
|
return BaseRequest.AudioChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BaseRequest.TranscodingMaxAudioChannels.HasValue)
|
||||||
|
{
|
||||||
|
return BaseRequest.TranscodingMaxAudioChannels;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
|
public virtual void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded, int? bitRate)
|
||||||
{
|
{
|
||||||
|
@ -16,6 +16,13 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refreshes the chapter images.
|
/// Refreshes the chapter images.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="video">Video to use.</param>
|
||||||
|
/// <param name="directoryService">Directory service to use.</param>
|
||||||
|
/// <param name="chapters">Set of chapters to refresh.</param>
|
||||||
|
/// <param name="extractImages">Option to extract images.</param>
|
||||||
|
/// <param name="saveChapters">Option to save chapters.</param>
|
||||||
|
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
|
||||||
|
/// <returns><c>true</c> if successful, <c>false</c> if not.</returns>
|
||||||
Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
|
Task<bool> RefreshChapterImages(Video video, IDirectoryService directoryService, IReadOnlyList<ChapterInfo> chapters, bool extractImages, bool saveChapters, CancellationToken cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,13 +71,42 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts the video image.
|
/// Extracts the video image.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="inputFile">Input file.</param>
|
||||||
|
/// <param name="container">Video container type.</param>
|
||||||
|
/// <param name="mediaSource">Media source information.</param>
|
||||||
|
/// <param name="videoStream">Media stream information.</param>
|
||||||
|
/// <param name="threedFormat">Video 3D format.</param>
|
||||||
|
/// <param name="offset">Time offset.</param>
|
||||||
|
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
|
||||||
|
/// <returns>Location of video image.</returns>
|
||||||
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
|
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream videoStream, Video3DFormat? threedFormat, TimeSpan? offset, CancellationToken cancellationToken);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts the video image.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="inputFile">Input file.</param>
|
||||||
|
/// <param name="container">Video container type.</param>
|
||||||
|
/// <param name="mediaSource">Media source information.</param>
|
||||||
|
/// <param name="imageStream">Media stream information.</param>
|
||||||
|
/// <param name="imageStreamIndex">Index of the stream to extract from.</param>
|
||||||
|
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
|
||||||
|
/// <returns>Location of video image.</returns>
|
||||||
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
|
Task<string> ExtractVideoImage(string inputFile, string container, MediaSourceInfo mediaSource, MediaStream imageStream, int? imageStreamIndex, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts the video images on interval.
|
/// Extracts the video images on interval.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="inputFile">Input file.</param>
|
||||||
|
/// <param name="container">Video container type.</param>
|
||||||
|
/// <param name="videoStream">Media stream information.</param>
|
||||||
|
/// <param name="mediaSource">Media source information.</param>
|
||||||
|
/// <param name="threedFormat">Video 3D format.</param>
|
||||||
|
/// <param name="interval">Time interval.</param>
|
||||||
|
/// <param name="targetDirectory">Directory to write images.</param>
|
||||||
|
/// <param name="filenamePrefix">Filename prefix to use.</param>
|
||||||
|
/// <param name="maxWidth">Maximum width of image.</param>
|
||||||
|
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
|
||||||
|
/// <returns>A task.</returns>
|
||||||
Task ExtractVideoImagesOnInterval(
|
Task ExtractVideoImagesOnInterval(
|
||||||
string inputFile,
|
string inputFile,
|
||||||
string container,
|
string container,
|
||||||
@ -122,10 +151,24 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
string EscapeSubtitleFilterPath(string path);
|
string EscapeSubtitleFilterPath(string path);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the path to find FFmpeg.
|
||||||
|
/// </summary>
|
||||||
void SetFFmpegPath();
|
void SetFFmpegPath();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the encoder path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The path.</param>
|
||||||
|
/// <param name="pathType">The type of path.</param>
|
||||||
void UpdateEncoderPath(string path, string pathType);
|
void UpdateEncoderPath(string path, string pathType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the primary playlist of .vob files.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">The to the .vob files.</param>
|
||||||
|
/// <param name="titleNumber">The title number to start with.</param>
|
||||||
|
/// <returns>A playlist.</returns>
|
||||||
IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber);
|
IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, uint? titleNumber);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,14 @@ namespace MediaBrowser.Controller.MediaEncoding
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the subtitles.
|
/// Gets the subtitles.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="item">Item to use.</param>
|
||||||
|
/// <param name="mediaSourceId">Media source.</param>
|
||||||
|
/// <param name="subtitleStreamIndex">Subtitle stream to use.</param>
|
||||||
|
/// <param name="outputFormat">Output format to use.</param>
|
||||||
|
/// <param name="startTimeTicks">Start time.</param>
|
||||||
|
/// <param name="endTimeTicks">End time.</param>
|
||||||
|
/// <param name="preserveOriginalTimestamps">Option to preserve original timestamps.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token for the operation.</param>
|
||||||
/// <returns>Task{Stream}.</returns>
|
/// <returns>Task{Stream}.</returns>
|
||||||
Task<Stream> GetSubtitles(
|
Task<Stream> GetSubtitles(
|
||||||
BaseItem item,
|
BaseItem item,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#nullable disable
|
#nullable disable
|
||||||
|
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591, SA1306, SA1401
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@ -30,6 +30,21 @@ namespace MediaBrowser.Controller.Net
|
|||||||
private readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>> _activeConnections =
|
private readonly List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>> _activeConnections =
|
||||||
new List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>>();
|
new List<Tuple<IWebSocketConnection, CancellationTokenSource, TStateType>>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The logger.
|
||||||
|
/// </summary>
|
||||||
|
protected ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> Logger;
|
||||||
|
|
||||||
|
protected BasePeriodicWebSocketListener(ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> logger)
|
||||||
|
{
|
||||||
|
if (logger == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type used for the messages sent to the client.
|
/// Gets the type used for the messages sent to the client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -54,21 +69,6 @@ namespace MediaBrowser.Controller.Net
|
|||||||
/// <returns>Task{`1}.</returns>
|
/// <returns>Task{`1}.</returns>
|
||||||
protected abstract Task<TReturnDataType> GetDataToSend();
|
protected abstract Task<TReturnDataType> GetDataToSend();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The logger.
|
|
||||||
/// </summary>
|
|
||||||
protected ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> Logger;
|
|
||||||
|
|
||||||
protected BasePeriodicWebSocketListener(ILogger<BasePeriodicWebSocketListener<TReturnDataType, TStateType>> logger)
|
|
||||||
{
|
|
||||||
if (logger == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes the message.
|
/// Processes the message.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -18,7 +18,6 @@ namespace MediaBrowser.Controller.Persistence
|
|||||||
/// <param name="key">The key.</param>
|
/// <param name="key">The key.</param>
|
||||||
/// <param name="userData">The user data.</param>
|
/// <param name="userData">The user data.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
|
||||||
void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken);
|
void SaveUserData(long userId, string key, UserItemData userData, CancellationToken cancellationToken);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -31,24 +31,18 @@ namespace MediaBrowser.Controller.Playlists
|
|||||||
".zpl"
|
".zpl"
|
||||||
};
|
};
|
||||||
|
|
||||||
public Guid OwnerUserId { get; set; }
|
|
||||||
|
|
||||||
public Share[] Shares { get; set; }
|
|
||||||
|
|
||||||
public Playlist()
|
public Playlist()
|
||||||
{
|
{
|
||||||
Shares = Array.Empty<Share>();
|
Shares = Array.Empty<Share>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Guid OwnerUserId { get; set; }
|
||||||
|
|
||||||
|
public Share[] Shares { get; set; }
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public bool IsFile => IsPlaylistFile(Path);
|
public bool IsFile => IsPlaylistFile(Path);
|
||||||
|
|
||||||
public static bool IsPlaylistFile(string path)
|
|
||||||
{
|
|
||||||
// The path will sometimes be a directory and "Path.HasExtension" returns true if the name contains a '.' (dot).
|
|
||||||
return System.IO.Path.HasExtension(path) && !Directory.Exists(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override string ContainingFolderPath
|
public override string ContainingFolderPath
|
||||||
{
|
{
|
||||||
@ -80,6 +74,41 @@ namespace MediaBrowser.Controller.Playlists
|
|||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public override bool SupportsCumulativeRunTimeTicks => true;
|
public override bool SupportsCumulativeRunTimeTicks => true;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override bool IsPreSorted => true;
|
||||||
|
|
||||||
|
public string PlaylistMediaType { get; set; }
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public override string MediaType => PlaylistMediaType;
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
private bool IsSharedItem
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
var path = Path;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool IsPlaylistFile(string path)
|
||||||
|
{
|
||||||
|
// The path will sometimes be a directory and "Path.HasExtension" returns true if the name contains a '.' (dot).
|
||||||
|
return System.IO.Path.HasExtension(path) && !Directory.Exists(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMediaType(string value)
|
||||||
|
{
|
||||||
|
PlaylistMediaType = value;
|
||||||
|
}
|
||||||
|
|
||||||
public override double GetDefaultPrimaryImageAspectRatio()
|
public override double GetDefaultPrimaryImageAspectRatio()
|
||||||
{
|
{
|
||||||
return 1;
|
return 1;
|
||||||
@ -197,35 +226,6 @@ namespace MediaBrowser.Controller.Playlists
|
|||||||
return new[] { item };
|
return new[] { item };
|
||||||
}
|
}
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override bool IsPreSorted => true;
|
|
||||||
|
|
||||||
public string PlaylistMediaType { get; set; }
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
public override string MediaType => PlaylistMediaType;
|
|
||||||
|
|
||||||
public void SetMediaType(string value)
|
|
||||||
{
|
|
||||||
PlaylistMediaType = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[JsonIgnore]
|
|
||||||
private bool IsSharedItem
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
var path = Path;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return FileSystem.ContainsSubPath(ConfigurationManager.ApplicationPaths.DataPath, path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool IsVisible(User user)
|
public override bool IsVisible(User user)
|
||||||
{
|
{
|
||||||
if (!IsSharedItem)
|
if (!IsSharedItem)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#pragma warning disable CA1002, CS1591
|
#pragma warning disable CA1002, CA1819, CS1591
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
|
@ -13,18 +13,18 @@ namespace MediaBrowser.Controller.Resolvers
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IItemResolver
|
public interface IItemResolver
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the priority.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>The priority.</value>
|
||||||
|
ResolverPriority Priority { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves the path.
|
/// Resolves the path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="args">The args.</param>
|
/// <param name="args">The args.</param>
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
BaseItem ResolvePath(ItemResolveArgs args);
|
BaseItem ResolvePath(ItemResolveArgs args);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the priority.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The priority.</value>
|
|
||||||
ResolverPriority Priority { get; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface IMultiItemResolver
|
public interface IMultiItemResolver
|
||||||
@ -38,14 +38,14 @@ namespace MediaBrowser.Controller.Resolvers
|
|||||||
|
|
||||||
public class MultiItemResolverResult
|
public class MultiItemResolverResult
|
||||||
{
|
{
|
||||||
public List<BaseItem> Items { get; set; }
|
|
||||||
|
|
||||||
public List<FileSystemMetadata> ExtraFiles { get; set; }
|
|
||||||
|
|
||||||
public MultiItemResolverResult()
|
public MultiItemResolverResult()
|
||||||
{
|
{
|
||||||
Items = new List<BaseItem>();
|
Items = new List<BaseItem>();
|
||||||
ExtraFiles = new List<FileSystemMetadata>();
|
ExtraFiles = new List<FileSystemMetadata>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<BaseItem> Items { get; set; }
|
||||||
|
|
||||||
|
public List<FileSystemMetadata> ExtraFiles { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user