Merge branch 'master' into tonemap-overlay

This commit is contained in:
Nyanmisaka 2021-08-13 15:01:06 +08:00 committed by GitHub
commit a84dc794c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
172 changed files with 1758 additions and 1237 deletions

View File

@ -7,7 +7,7 @@ parameters:
default: "ubuntu-latest" default: "ubuntu-latest"
- name: DotNetSdkVersion - name: DotNetSdkVersion
type: string type: string
default: 5.0.103 default: 5.0.302
jobs: jobs:
- job: CompatibilityCheck - job: CompatibilityCheck

View File

@ -1,7 +1,7 @@
parameters: parameters:
LinuxImage: 'ubuntu-latest' LinuxImage: 'ubuntu-latest'
RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj' RestoreBuildProjects: 'Jellyfin.Server/Jellyfin.Server.csproj'
DotNetSdkVersion: 5.0.103 DotNetSdkVersion: 5.0.302
jobs: jobs:
- job: Build - job: Build

View File

@ -10,7 +10,7 @@ parameters:
default: "tests/**/*Tests.csproj" default: "tests/**/*Tests.csproj"
- name: DotNetSdkVersion - name: DotNetSdkVersion
type: string type: string
default: 5.0.103 default: 5.0.302
jobs: jobs:
- job: Test - job: Test

View File

@ -6,7 +6,7 @@ variables:
- name: RestoreBuildProjects - name: RestoreBuildProjects
value: 'Jellyfin.Server/Jellyfin.Server.csproj' value: 'Jellyfin.Server/Jellyfin.Server.csproj'
- name: DotNetSdkVersion - name: DotNetSdkVersion
value: 5.0.103 value: 5.0.302
pr: pr:
autoCancel: true autoCancel: true

View File

@ -212,3 +212,4 @@
- [Tim Hobbs](https://github.com/timhobbs) - [Tim Hobbs](https://github.com/timhobbs)
- [SvenVandenbrande](https://github.com/SvenVandenbrande) - [SvenVandenbrande](https://github.com/SvenVandenbrande)
- [olsh](https://github.com/olsh) - [olsh](https://github.com/olsh)
- [gnuyent](https://github.com/gnuyent)

14
Directory.Build.props Normal file
View File

@ -0,0 +1,14 @@
<Project>
<!-- Sets defaults for all projects in the repo -->
<PropertyGroup>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<CodeAnalysisRuleSet>$(MSBuildThisFileDirectory)/jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
</PropertyGroup>
</Project>

View File

@ -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", \

View File

@ -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", \

View File

@ -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", \

View File

@ -13,7 +13,8 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <AnalysisMode>AllDisabledByDefault</AnalysisMode>
<Nullable>disable</Nullable>
</PropertyGroup> </PropertyGroup>
</Project> </Project>

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
namespace Emby.Dlna.Configuration namespace Emby.Dlna.Configuration
@ -74,7 +72,7 @@ namespace Emby.Dlna.Configuration
/// <summary> /// <summary>
/// Gets or sets the default user account that the dlna server uses. /// Gets or sets the default user account that the dlna server uses.
/// </summary> /// </summary>
public string DefaultUserId { get; set; } public string? DefaultUserId { get; set; }
/// <summary> /// <summary>
/// Gets or sets a value indicating whether playTo device profiles should be created. /// Gets or sets a value indicating whether playTo device profiles should be created.

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
@ -140,7 +138,7 @@ namespace Emby.Dlna.ContentDirectory
/// </summary> /// </summary>
/// <param name="profile">The <see cref="DeviceProfile"/>.</param> /// <param name="profile">The <see cref="DeviceProfile"/>.</param>
/// <returns>The <see cref="User"/>.</returns> /// <returns>The <see cref="User"/>.</returns>
private User GetUser(DeviceProfile profile) private User? GetUser(DeviceProfile profile)
{ {
if (!string.IsNullOrEmpty(profile.UserId)) if (!string.IsNullOrEmpty(profile.UserId))
{ {

View File

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

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
@ -8,9 +6,11 @@ namespace Emby.Dlna
{ {
public class ControlResponse public class ControlResponse
{ {
public ControlResponse() public ControlResponse(string xml, bool isSuccessful)
{ {
Headers = new Dictionary<string, string>(); Headers = new Dictionary<string, string>();
Xml = xml;
IsSuccessful = isSuccessful;
} }
public IDictionary<string, string> Headers { get; } public IDictionary<string, string> Headers { get; }

View File

@ -20,8 +20,7 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <AnalysisMode>AllDisabledByDefault</AnalysisMode>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<!-- Code Analyzers--> <!-- Code Analyzers-->
@ -31,10 +30,6 @@
<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' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Images\logo120.jpg" /> <EmbeddedResource Include="Images\logo120.jpg" />
<EmbeddedResource Include="Images\logo120.png" /> <EmbeddedResource Include="Images\logo120.png" />

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System.Collections.Generic; using System.Collections.Generic;
@ -8,8 +6,10 @@ namespace Emby.Dlna
{ {
public class EventSubscriptionResponse public class EventSubscriptionResponse
{ {
public EventSubscriptionResponse() public EventSubscriptionResponse(string content, string contentType)
{ {
Content = content;
ContentType = contentType;
Headers = new Dictionary<string, string>(); Headers = new Dictionary<string, string>();
} }

View File

@ -51,11 +51,7 @@ namespace Emby.Dlna.Eventing
return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds); return GetEventSubscriptionResponse(subscriptionId, requestedTimeoutString, timeoutSeconds);
} }
return new EventSubscriptionResponse return new EventSubscriptionResponse(string.Empty, "text/plain");
{
Content = string.Empty,
ContentType = "text/plain"
};
} }
public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl) public EventSubscriptionResponse CreateEventSubscription(string notificationType, string requestedTimeoutString, string callbackUrl)
@ -103,20 +99,12 @@ namespace Emby.Dlna.Eventing
_subscriptions.TryRemove(subscriptionId, out _); _subscriptions.TryRemove(subscriptionId, out _);
return new EventSubscriptionResponse return new EventSubscriptionResponse(string.Empty, "text/plain");
{
Content = string.Empty,
ContentType = "text/plain"
};
} }
private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds) private EventSubscriptionResponse GetEventSubscriptionResponse(string subscriptionId, string requestedTimeoutString, int timeoutSeconds)
{ {
var response = new EventSubscriptionResponse var response = new EventSubscriptionResponse(string.Empty, "text/plain");
{
Content = string.Empty,
ContentType = "text/plain"
};
response.Headers["SID"] = subscriptionId; response.Headers["SID"] = subscriptionId;
response.Headers["TIMEOUT"] = string.IsNullOrEmpty(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString; response.Headers["TIMEOUT"] = string.IsNullOrEmpty(requestedTimeoutString) ? ("SECOND-" + timeoutSeconds.ToString(_usCulture)) : requestedTimeoutString;

View File

@ -1260,10 +1260,7 @@ namespace Emby.Dlna.PlayTo
return; return;
} }
PlaybackStart?.Invoke(this, new PlaybackStartEventArgs PlaybackStart?.Invoke(this, new PlaybackStartEventArgs(mediaInfo));
{
MediaInfo = mediaInfo
});
} }
private void OnPlaybackProgress(UBaseObject mediaInfo) private void OnPlaybackProgress(UBaseObject mediaInfo)
@ -1273,27 +1270,17 @@ namespace Emby.Dlna.PlayTo
return; return;
} }
PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs PlaybackProgress?.Invoke(this, new PlaybackProgressEventArgs(mediaInfo));
{
MediaInfo = mediaInfo
});
} }
private void OnPlaybackStop(UBaseObject mediaInfo) private void OnPlaybackStop(UBaseObject mediaInfo)
{ {
PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs PlaybackStopped?.Invoke(this, new PlaybackStoppedEventArgs(mediaInfo));
{
MediaInfo = mediaInfo
});
} }
private void OnMediaChanged(UBaseObject old, UBaseObject newMedia) private void OnMediaChanged(UBaseObject old, UBaseObject newMedia)
{ {
MediaChanged?.Invoke(this, new MediaChangedEventArgs MediaChanged?.Invoke(this, new MediaChangedEventArgs(old, newMedia));
{
OldMediaInfo = old,
NewMediaInfo = newMedia
});
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -1,6 +1,4 @@
#nullable disable #pragma warning disable CS1591
#pragma warning disable CS1591
using System; using System;
@ -8,6 +6,12 @@ namespace Emby.Dlna.PlayTo
{ {
public class MediaChangedEventArgs : EventArgs public class MediaChangedEventArgs : EventArgs
{ {
public MediaChangedEventArgs(UBaseObject oldMediaInfo, UBaseObject newMediaInfo)
{
OldMediaInfo = oldMediaInfo;
NewMediaInfo = newMediaInfo;
}
public UBaseObject OldMediaInfo { get; set; } public UBaseObject OldMediaInfo { get; set; }
public UBaseObject NewMediaInfo { get; set; } public UBaseObject NewMediaInfo { get; set; }

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
@ -8,6 +6,11 @@ namespace Emby.Dlna.PlayTo
{ {
public class PlaybackProgressEventArgs : EventArgs public class PlaybackProgressEventArgs : EventArgs
{ {
public PlaybackProgressEventArgs(UBaseObject mediaInfo)
{
MediaInfo = mediaInfo;
}
public UBaseObject MediaInfo { get; set; } public UBaseObject MediaInfo { get; set; }
} }
} }

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
@ -8,6 +6,11 @@ namespace Emby.Dlna.PlayTo
{ {
public class PlaybackStartEventArgs : EventArgs public class PlaybackStartEventArgs : EventArgs
{ {
public PlaybackStartEventArgs(UBaseObject mediaInfo)
{
MediaInfo = mediaInfo;
}
public UBaseObject MediaInfo { get; set; } public UBaseObject MediaInfo { get; set; }
} }
} }

View File

@ -1,5 +1,3 @@
#nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591
using System; using System;
@ -8,6 +6,11 @@ namespace Emby.Dlna.PlayTo
{ {
public class PlaybackStoppedEventArgs : EventArgs public class PlaybackStoppedEventArgs : EventArgs
{ {
public PlaybackStoppedEventArgs(UBaseObject mediaInfo)
{
MediaInfo = mediaInfo;
}
public UBaseObject MediaInfo { get; set; } public UBaseObject MediaInfo { get; set; }
} }
} }

View File

@ -95,11 +95,7 @@ namespace Emby.Dlna.Service
var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal); var xml = builder.ToString().Replace("xmlns:m=", "xmlns:u=", StringComparison.Ordinal);
var controlResponse = new ControlResponse var controlResponse = new ControlResponse(xml, true);
{
Xml = xml,
IsSuccessful = true
};
controlResponse.Headers.Add("EXT", string.Empty); controlResponse.Headers.Add("EXT", string.Empty);

View File

@ -46,11 +46,7 @@ namespace Emby.Dlna.Service
writer.WriteEndDocument(); writer.WriteEndDocument();
} }
return new ControlResponse return new ControlResponse(builder.ToString(), false);
{
Xml = builder.ToString(),
IsSuccessful = false
};
} }
} }
} }

View File

@ -9,8 +9,7 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <AnalysisMode>AllDisabledByDefault</AnalysisMode>
<Nullable>enable</Nullable>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@ -30,8 +29,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' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project> </Project>

View File

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

View File

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

View File

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

View File

@ -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>
@ -9,12 +9,11 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<Nullable>enable</Nullable> <AnalysisMode>AllDisabledByDefault</AnalysisMode>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Stability)'=='Unstable'"> <PropertyGroup Condition=" '$(Stability)'=='Unstable'">
@ -50,8 +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' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
</Project> </Project>

View File

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

View File

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

View File

@ -9,10 +9,6 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -22,10 +22,6 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<!-- Code Analyzers--> <!-- Code Analyzers-->

View File

@ -82,12 +82,10 @@ namespace Emby.Server.Implementations.Collections
internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded) internal async Task<Folder> EnsureLibraryFolder(string path, bool createIfNeeded)
{ {
var existingFolders = FindFolders(path) var existingFolder = FindFolders(path).FirstOrDefault();
.ToList(); if (existingFolder != null)
if (existingFolders.Count > 0)
{ {
return existingFolders[0]; return existingFolder;
} }
if (!createIfNeeded) if (!createIfNeeded)

View File

@ -44,12 +44,13 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release'">true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 --> <!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
<NoWarn>AD0001</NoWarn> <NoWarn>AD0001</NoWarn>
<AnalysisMode Condition=" '$(Configuration)' == 'Debug' ">AllEnabledByDefault</AnalysisMode> <TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet> </PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<!-- Code Analyzers--> <!-- Code Analyzers-->

View File

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

View File

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "حذف سجل الأنشطة", "TaskCleanActivityLog": "حذف سجل الأنشطة",
"Default": "الإعدادات الافتراضية", "Default": "الإعدادات الافتراضية",
"Undefined": "غير معرف", "Undefined": "غير معرف",
"Forced": "ملحقة" "Forced": "ملحقة",
"TaskOptimizeDatabaseDescription": "يضغط قاعدة البيانات ويقتطع المساحة الحرة. تشغيل هذه المهمة بعد فحص المكتبة أو إجراء تغييرات أخرى تشير ضمنًا إلى أن تعديلات قاعدة البيانات قد تؤدي إلى تحسين الأداء.",
"TaskOptimizeDatabase": "تحسين قاعدة البيانات"
} }

View File

@ -8,7 +8,7 @@
"CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}", "CameraImageUploadedFrom": "Нова снимка от камера беше качена от {0}",
"Channels": "Канали", "Channels": "Канали",
"ChapterNameValue": "Глава {0}", "ChapterNameValue": "Глава {0}",
"Collections": "Поредици", "Collections": "Колекции",
"DeviceOfflineWithName": "{0} се разкачи", "DeviceOfflineWithName": "{0} се разкачи",
"DeviceOnlineWithName": "{0} е свързан", "DeviceOnlineWithName": "{0} е свързан",
"FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}", "FailedLoginAttemptWithUserName": "Неуспешен опит за влизане от {0}",
@ -29,13 +29,13 @@
"Inherit": "Наследяване", "Inherit": "Наследяване",
"ItemAddedWithName": "{0} е добавено към библиотеката", "ItemAddedWithName": "{0} е добавено към библиотеката",
"ItemRemovedWithName": "{0} е премахнато от библиотеката", "ItemRemovedWithName": "{0} е премахнато от библиотеката",
"LabelIpAddressValue": "ИП адрес: {0}", "LabelIpAddressValue": "IP адрес: {0}",
"LabelRunningTimeValue": "Стартирано от: {0}", "LabelRunningTimeValue": "Продължителност: {0}",
"Latest": "Последни", "Latest": "Последни",
"MessageApplicationUpdated": "Сървърът е обновен", "MessageApplicationUpdated": "Сървърът беше обновен",
"MessageApplicationUpdatedTo": "Сървърът е обновен до {0}", "MessageApplicationUpdatedTo": "Сървърът беше обновен до {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация се актуализира", "MessageNamedServerConfigurationUpdatedWithValue": "Секцията {0} от сървърната конфигурация беше актуализирана",
"MessageServerConfigurationUpdated": "Конфигурацията на сървъра се актуализира", "MessageServerConfigurationUpdated": "Конфигурацията на сървъра беше актуализирана",
"MixedContent": "Смесено съдържание", "MixedContent": "Смесено съдържание",
"Movies": "Филми", "Movies": "Филми",
"Music": "Музика", "Music": "Музика",
@ -118,5 +118,7 @@
"Forced": "Принудително", "Forced": "Принудително",
"Default": "По подразбиране", "Default": "По подразбиране",
"TaskCleanActivityLogDescription": "Изтрива записите в дневника с активност по стари от конфигурираната възраст.", "TaskCleanActivityLogDescription": "Изтрива записите в дневника с активност по стари от конфигурираната възраст.",
"TaskCleanActivityLog": "Изчисти дневника с активност" "TaskCleanActivityLog": "Изчисти дневника с активност",
"TaskOptimizeDatabaseDescription": "Прави базата данни по-компактна и освобождава място. Пускането на тази задача след сканиране на библиотеката или правене на други промени, свързани с модификации на базата данни, може да подобри производителността.",
"TaskOptimizeDatabase": "Оптимизирай базата данни"
} }

View File

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Ryd Aktivitetslog", "TaskCleanActivityLog": "Ryd Aktivitetslog",
"Undefined": "Udefineret", "Undefined": "Udefineret",
"Forced": "Tvunget", "Forced": "Tvunget",
"Default": "Standard" "Default": "Standard",
"TaskOptimizeDatabaseDescription": "Kompakter database og forkorter fri plads. Ved at køre denne proces efter at scanne biblioteket eller efter at ændre noget som kunne have indflydelse på databasen, kan forbedre ydeevne.",
"TaskOptimizeDatabase": "Optimér database"
} }

View File

@ -70,7 +70,7 @@
"ScheduledTaskFailedWithName": "{0} falló", "ScheduledTaskFailedWithName": "{0} falló",
"ScheduledTaskStartedWithName": "{0} iniciada", "ScheduledTaskStartedWithName": "{0} iniciada",
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado", "ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
"Shows": "Series de Televisión", "Shows": "Series",
"Songs": "Canciones", "Songs": "Canciones",
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.", "StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}", "SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",

View File

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

View File

@ -117,5 +117,7 @@
"TaskCleanActivityLog": "アクティビティの履歴を消去", "TaskCleanActivityLog": "アクティビティの履歴を消去",
"Undefined": "未定義", "Undefined": "未定義",
"Forced": "強制", "Forced": "強制",
"Default": "デフォルト" "Default": "デフォルト",
"TaskOptimizeDatabaseDescription": "データベースをコンパクトにして、空き領域を切り詰めます。メディアライブラリのスキャン後でこのタスクを実行するとパフォーマンスが向上する可能性があります。",
"TaskOptimizeDatabase": "データベースの最適化"
} }

View File

@ -117,5 +117,7 @@
"TaskCleanActivityLogDescription": "Nodzēš darbību žurnāla ierakstus, kuri ir vecāki par doto vecumu.", "TaskCleanActivityLogDescription": "Nodzēš darbību žurnāla ierakstus, kuri ir vecāki par doto vecumu.",
"TaskCleanActivityLog": "Notīrīt Darbību Žurnālu", "TaskCleanActivityLog": "Notīrīt Darbību Žurnālu",
"Undefined": "Nenoteikts", "Undefined": "Nenoteikts",
"Default": "Noklusējums" "Default": "Noklusējums",
"TaskOptimizeDatabaseDescription": "Saspiež datubāzi un atbrīvo atmiņu. Uzdevum palaišana pēc bibliotēku skenēšanas vai citām, ar datubāzi saistītām, izmaiņām iespējams uzlabos ātrdarbību.",
"TaskOptimizeDatabase": "Optimizēt datubāzi"
} }

View File

@ -118,5 +118,6 @@
"Undefined": "Udefinert", "Undefined": "Udefinert",
"Forced": "Tvunget", "Forced": "Tvunget",
"Default": "Standard", "Default": "Standard",
"TaskCleanActivityLogDescription": "Sletter oppføringer i aktivitetsloggen som er eldre enn den konfigurerte alderen." "TaskCleanActivityLogDescription": "Sletter oppføringer i aktivitetsloggen som er eldre enn den konfigurerte alderen.",
"TaskOptimizeDatabase": "Optimiser database"
} }

View File

@ -16,7 +16,7 @@
"Folders": "Mape", "Folders": "Mape",
"Genres": "Zvrsti", "Genres": "Zvrsti",
"HeaderAlbumArtists": "Izvajalci albuma", "HeaderAlbumArtists": "Izvajalci albuma",
"HeaderContinueWatching": "Nadaljuj z ogledom", "HeaderContinueWatching": "Nadaljuj ogled",
"HeaderFavoriteAlbums": "Priljubljeni albumi", "HeaderFavoriteAlbums": "Priljubljeni albumi",
"HeaderFavoriteArtists": "Priljubljeni izvajalci", "HeaderFavoriteArtists": "Priljubljeni izvajalci",
"HeaderFavoriteEpisodes": "Priljubljene epizode", "HeaderFavoriteEpisodes": "Priljubljene epizode",
@ -90,7 +90,7 @@
"UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}", "UserStartedPlayingItemWithValues": "{0} predvaja {1} na {2}",
"UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}", "UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}",
"ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici", "ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici",
"ValueSpecialEpisodeName": "Posebna - {0}", "ValueSpecialEpisodeName": "Bonus - {0}",
"VersionNumber": "Različica {0}", "VersionNumber": "Različica {0}",
"TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise", "TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise",
"TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.", "TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.",

View File

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

View File

@ -23,6 +23,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" };
@ -58,43 +61,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 str = _assembly.GetManifestResourceStream(resource);
using (var reader = new StreamReader(str)) using var reader = new StreamReader(str);
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 +113,48 @@ 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);
using var reader = new StreamReader(stream);
using (var stream = _assembly.GetManifestResourceStream(ResourcePath)) await foreach (var line in reader.ReadAllLinesAsync().ConfigureAwait(false))
using (var reader = new StreamReader(stream))
{ {
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
});
} }
} }
@ -188,7 +183,7 @@ 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));
return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions); return JsonSerializer.Deserialize<IEnumerable<CountryInfo>>(reader.ReadToEnd(), _jsonOptions);
} }
@ -350,23 +345,21 @@ 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 var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
if (stream != null)
{
var dict = await JsonSerializer.DeserializeAsync<Dictionary<string, string>>(stream, _jsonOptions).ConfigureAwait(false);
foreach (var key in dict.Keys) foreach (var key in dict.Keys)
{
dictionary[key] = dict[key];
}
}
else
{ {
_logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath); dictionary[key] = dict[key];
} }
} }
else
{
_logger.LogError("Missing translation/culture resource: {ResourcePath}", resourcePath);
}
} }
private static string GetResourceFilename(string culture) private static string GetResourceFilename(string culture)

View File

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

View File

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

View File

@ -8,17 +8,16 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 --> <!-- https://github.com/microsoft/ApplicationInsights-dotnet/issues/2047 -->
<NoWarn>AD0001</NoWarn> <NoWarn>AD0001</NoWarn>
<AnalysisMode>AllDisabledByDefault</AnalysisMode>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.8" /> <PackageReference Include="Microsoft.AspNetCore.Authorization" Version="5.0.8" />
<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.4" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="6.1.5" />
<PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.1.4" /> <PackageReference Include="Swashbuckle.AspNetCore.ReDoc" Version="6.1.5" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@ -33,10 +32,6 @@
<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' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo"> <AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleTo">
<_Parameter1>Jellyfin.Api.Tests</_Parameter1> <_Parameter1>Jellyfin.Api.Tests</_Parameter1>

View File

@ -4,10 +4,6 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
<Nullable>enable</Nullable>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>

View File

@ -9,10 +9,6 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -3,10 +3,6 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -86,15 +86,12 @@ namespace Jellyfin.Server.Implementations.Activity
private static ActivityLogEntry ConvertToOldModel(ActivityLog entry) private static ActivityLogEntry ConvertToOldModel(ActivityLog entry)
{ {
return new ActivityLogEntry return new ActivityLogEntry(entry.Name, entry.Type, entry.UserId)
{ {
Id = entry.Id, Id = entry.Id,
Name = entry.Name,
Overview = entry.Overview, Overview = entry.Overview,
ShortOverview = entry.ShortOverview, ShortOverview = entry.ShortOverview,
Type = entry.Type,
ItemId = entry.ItemId, ItemId = entry.ItemId,
UserId = entry.UserId,
Date = entry.DateCreated, Date = entry.DateCreated,
Severity = entry.LogSeverity Severity = entry.LogSeverity
}; };

View File

@ -4,14 +4,6 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<!-- Code analysers--> <!-- Code analysers-->

View File

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

View File

@ -12,11 +12,6 @@
<ServerGarbageCollection>false</ServerGarbageCollection> <ServerGarbageCollection>false</ServerGarbageCollection>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
<!-- <DisableImplicitAspNetCoreAnalyzers>true</DisableImplicitAspNetCoreAnalyzers> -->
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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())

View File

@ -32,10 +32,6 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>

View File

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

View File

@ -17,6 +17,12 @@ namespace MediaBrowser.Controller.Channels
{ {
public class Channel : Folder public class Channel : Folder
{ {
[JsonIgnore]
public override bool SupportsInheritedParentImages => false;
[JsonIgnore]
public override SourceType SourceType => SourceType.Channel;
public override bool IsVisible(User user) public override bool IsVisible(User user)
{ {
var blockedChannelsPreference = user.GetPreferenceValues<Guid>(PreferenceKind.BlockedChannels); var blockedChannelsPreference = user.GetPreferenceValues<Guid>(PreferenceKind.BlockedChannels);
@ -39,12 +45,6 @@ namespace MediaBrowser.Controller.Channels
return base.IsVisible(user); return base.IsVisible(user);
} }
[JsonIgnore]
public override bool SupportsInheritedParentImages => false;
[JsonIgnore]
public override SourceType SourceType => SourceType.Channel;
protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query) protected override QueryResult<BaseItem> GetItemsInternal(InternalItemsQuery query)
{ {
try try

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CA2227, CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CA2227, CS1591
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -1,4 +1,4 @@
#pragma warning disable CS1591 #pragma warning disable CA1819, CS1591
namespace MediaBrowser.Controller.Channels namespace MediaBrowser.Controller.Channels
{ {

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CA2227, CS1591
using System.Collections.Generic; using System.Collections.Generic;
using MediaBrowser.Model.Channels; using MediaBrowser.Model.Channels;

View File

@ -12,6 +12,8 @@ namespace MediaBrowser.Controller.Chapters
/// <summary> /// <summary>
/// Saves the chapters. /// Saves the chapters.
/// </summary> /// </summary>
/// <param name="itemId">The item.</param>
/// <param name="chapters">The set of chapters.</param>
void SaveChapters(Guid itemId, IReadOnlyList<ChapterInfo> chapters); void SaveChapters(Guid itemId, IReadOnlyList<ChapterInfo> chapters);
} }
} }

View File

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

View File

@ -32,6 +32,7 @@ namespace MediaBrowser.Controller.Collections
/// Creates the collection. /// Creates the collection.
/// </summary> /// </summary>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>BoxSet wrapped in an awaitable task.</returns>
Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options); Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options);
/// <summary> /// <summary>

View File

@ -57,6 +57,15 @@ namespace MediaBrowser.Controller.Drawing
/// <summary> /// <summary>
/// Encode an image. /// Encode an image.
/// </summary> /// </summary>
/// <param name="inputPath">Input path of image.</param>
/// <param name="dateModified">Date modified.</param>
/// <param name="outputPath">Output path of image.</param>
/// <param name="autoOrient">Auto-orient image.</param>
/// <param name="orientation">Desired orientation of image.</param>
/// <param name="quality">Quality of encoded image.</param>
/// <param name="options">Image processing options.</param>
/// <param name="outputFormat">Image format of output.</param>
/// <returns>Path of encoded image.</returns>
string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat); string EncodeImage(string inputPath, DateTime dateModified, string outputPath, bool autoOrient, ImageOrientation? orientation, int quality, ImageProcessingOptions options, ImageFormat outputFormat);
/// <summary> /// <summary>

View File

@ -1,4 +1,4 @@
#pragma warning disable CS1591 #pragma warning disable CA1711, CS1591
using System; using System;
using System.IO; using System.IO;

View File

@ -1,4 +1,5 @@
#nullable disable #nullable disable
#pragma warning disable CA1002
using System.Collections.Generic; using System.Collections.Generic;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -31,6 +31,29 @@ namespace MediaBrowser.Controller.Library
/// </summary> /// </summary>
public interface ILibraryManager public interface ILibraryManager
{ {
/// <summary>
/// Occurs when [item added].
/// </summary>
event EventHandler<ItemChangeEventArgs> ItemAdded;
/// <summary>
/// Occurs when [item updated].
/// </summary>
event EventHandler<ItemChangeEventArgs> ItemUpdated;
/// <summary>
/// Occurs when [item removed].
/// </summary>
event EventHandler<ItemChangeEventArgs> ItemRemoved;
/// <summary>
/// Gets the root folder.
/// </summary>
/// <value>The root folder.</value>
AggregateFolder RootFolder { get; }
bool IsScanRunning { get; }
/// <summary> /// <summary>
/// Resolves the path. /// Resolves the path.
/// </summary> /// </summary>
@ -57,16 +80,10 @@ namespace MediaBrowser.Controller.Library
LibraryOptions libraryOptions, LibraryOptions libraryOptions,
string collectionType = null); string collectionType = null);
/// <summary>
/// Gets the root folder.
/// </summary>
/// <value>The root folder.</value>
AggregateFolder RootFolder { get; }
/// <summary> /// <summary>
/// Gets a Person. /// Gets a Person.
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name of the person.</param>
/// <returns>Task{Person}.</returns> /// <returns>Task{Person}.</returns>
Person GetPerson(string name); Person GetPerson(string name);
@ -81,7 +98,7 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Gets the artist. /// Gets the artist.
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name of the artist.</param>
/// <returns>Task{Artist}.</returns> /// <returns>Task{Artist}.</returns>
MusicArtist GetArtist(string name); MusicArtist GetArtist(string name);
@ -90,21 +107,21 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Gets a Studio. /// Gets a Studio.
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name of the studio.</param>
/// <returns>Task{Studio}.</returns> /// <returns>Task{Studio}.</returns>
Studio GetStudio(string name); Studio GetStudio(string name);
/// <summary> /// <summary>
/// Gets a Genre. /// Gets a Genre.
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name of the genre.</param>
/// <returns>Task{Genre}.</returns> /// <returns>Task{Genre}.</returns>
Genre GetGenre(string name); Genre GetGenre(string name);
/// <summary> /// <summary>
/// Gets the genre. /// Gets the genre.
/// </summary> /// </summary>
/// <param name="name">The name.</param> /// <param name="name">The name of the music genre.</param>
/// <returns>Task{MusicGenre}.</returns> /// <returns>Task{MusicGenre}.</returns>
MusicGenre GetMusicGenre(string name); MusicGenre GetMusicGenre(string name);
@ -113,7 +130,7 @@ namespace MediaBrowser.Controller.Library
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>Task{Year}.</returns> /// <returns>Task{Year}.</returns>
/// <exception cref="ArgumentOutOfRangeException"></exception> /// <exception cref="ArgumentOutOfRangeException">Throws if year is invalid.</exception>
Year GetYear(int value); Year GetYear(int value);
/// <summary> /// <summary>
@ -205,16 +222,26 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Creates the item. /// Creates the item.
/// </summary> /// </summary>
/// <param name="item">Item to create.</param>
/// <param name="parent">Parent of new item.</param>
void CreateItem(BaseItem item, BaseItem parent); void CreateItem(BaseItem item, BaseItem parent);
/// <summary> /// <summary>
/// Creates the items. /// Creates the items.
/// </summary> /// </summary>
/// <param name="items">Items to create.</param>
/// <param name="parent">Parent of new items.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
void CreateItems(IReadOnlyList<BaseItem> items, BaseItem parent, CancellationToken cancellationToken); void CreateItems(IReadOnlyList<BaseItem> items, BaseItem parent, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Updates the item. /// Updates the item.
/// </summary> /// </summary>
/// <param name="items">Items to update.</param>
/// <param name="parent">Parent of updated items.</param>
/// <param name="updateReason">Reason for update.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns>Returns a Task that can be awaited.</returns>
Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
/// <summary> /// <summary>
@ -224,6 +251,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="parent">The parent item.</param> /// <param name="parent">The parent item.</param>
/// <param name="updateReason">The update reason.</param> /// <param name="updateReason">The update reason.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Returns a Task that can be awaited.</returns>
Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken); Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken);
/// <summary> /// <summary>
@ -233,23 +261,6 @@ namespace MediaBrowser.Controller.Library
/// <returns>BaseItem.</returns> /// <returns>BaseItem.</returns>
BaseItem RetrieveItem(Guid id); BaseItem RetrieveItem(Guid id);
bool IsScanRunning { get; }
/// <summary>
/// Occurs when [item added].
/// </summary>
event EventHandler<ItemChangeEventArgs> ItemAdded;
/// <summary>
/// Occurs when [item updated].
/// </summary>
event EventHandler<ItemChangeEventArgs> ItemUpdated;
/// <summary>
/// Occurs when [item removed].
/// </summary>
event EventHandler<ItemChangeEventArgs> ItemRemoved;
/// <summary> /// <summary>
/// Finds the type of the collection. /// Finds the type of the collection.
/// </summary> /// </summary>
@ -294,16 +305,25 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Deletes the item. /// Deletes the item.
/// </summary> /// </summary>
/// <param name="item">Item to delete.</param>
/// <param name="options">Options to use for deletion.</param>
void DeleteItem(BaseItem item, DeleteOptions options); void DeleteItem(BaseItem item, DeleteOptions options);
/// <summary> /// <summary>
/// Deletes the item. /// Deletes the item.
/// </summary> /// </summary>
/// <param name="item">Item to delete.</param>
/// <param name="options">Options to use for deletion.</param>
/// <param name="notifyParentItem">Notify parent of deletion.</param>
void DeleteItem(BaseItem item, DeleteOptions options, bool notifyParentItem); void DeleteItem(BaseItem item, DeleteOptions options, bool notifyParentItem);
/// <summary> /// <summary>
/// Deletes the item. /// Deletes the item.
/// </summary> /// </summary>
/// <param name="item">Item to delete.</param>
/// <param name="options">Options to use for deletion.</param>
/// <param name="parent">Parent of item.</param>
/// <param name="notifyParentItem">Notify parent of deletion.</param>
void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem); void DeleteItem(BaseItem item, DeleteOptions options, BaseItem parent, bool notifyParentItem);
/// <summary> /// <summary>
@ -314,6 +334,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="parentId">The parent identifier.</param> /// <param name="parentId">The parent identifier.</param>
/// <param name="viewType">Type of the view.</param> /// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param> /// <param name="sortName">Name of the sort.</param>
/// <returns>The named view.</returns>
UserView GetNamedView( UserView GetNamedView(
User user, User user,
string name, string name,
@ -328,6 +349,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="viewType">Type of the view.</param> /// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param> /// <param name="sortName">Name of the sort.</param>
/// <returns>The named view.</returns>
UserView GetNamedView( UserView GetNamedView(
User user, User user,
string name, string name,
@ -340,6 +362,7 @@ namespace MediaBrowser.Controller.Library
/// <param name="name">The name.</param> /// <param name="name">The name.</param>
/// <param name="viewType">Type of the view.</param> /// <param name="viewType">Type of the view.</param>
/// <param name="sortName">Name of the sort.</param> /// <param name="sortName">Name of the sort.</param>
/// <returns>The named view.</returns>
UserView GetNamedView( UserView GetNamedView(
string name, string name,
string viewType, string viewType,
@ -397,6 +420,9 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Fills the missing episode numbers from path. /// Fills the missing episode numbers from path.
/// </summary> /// </summary>
/// <param name="episode">Episode to use.</param>
/// <param name="forceRefresh">Option to force refresh of episode numbers.</param>
/// <returns>True if successful.</returns>
bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh); bool FillMissingEpisodeNumbersFromPath(Episode episode, bool forceRefresh);
/// <summary> /// <summary>
@ -539,6 +565,9 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Gets the items. /// Gets the items.
/// </summary> /// </summary>
/// <param name="query">The query to use.</param>
/// <param name="parents">Items to use for query.</param>
/// <returns>List of items.</returns>
List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents); List<BaseItem> GetItemList(InternalItemsQuery query, List<BaseItem> parents);
/// <summary> /// <summary>

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1711, CS1591
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -62,16 +62,32 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Gets the playack media sources. /// Gets the playack media sources.
/// </summary> /// </summary>
/// <param name="item">Item to use.</param>
/// <param name="user">User to use for operation.</param>
/// <param name="allowMediaProbe">Option to allow media probe.</param>
/// <param name="enablePathSubstitution">Option to enable path substitution.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns>List of media sources wrapped in an awaitable task.</returns>
Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken); Task<List<MediaSourceInfo>> GetPlaybackMediaSources(BaseItem item, User user, bool allowMediaProbe, bool enablePathSubstitution, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the static media sources. /// Gets the static media sources.
/// </summary> /// </summary>
/// <param name="item">Item to use.</param>
/// <param name="enablePathSubstitution">Option to enable path substitution.</param>
/// <param name="user">User to use for operation.</param>
/// <returns>List of media sources.</returns>
List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null); List<MediaSourceInfo> GetStaticMediaSources(BaseItem item, bool enablePathSubstitution, User user = null);
/// <summary> /// <summary>
/// Gets the static media source. /// Gets the static media source.
/// </summary> /// </summary>
/// <param name="item">Item to use.</param>
/// <param name="mediaSourceId">Media source to get.</param>
/// <param name="liveStreamId">Live stream to use.</param>
/// <param name="enablePathSubstitution">Option to enable path substitution.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns>The static media source wrapped in an awaitable task.</returns>
Task<MediaSourceInfo> GetMediaSource(BaseItem item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken); Task<MediaSourceInfo> GetMediaSource(BaseItem item, string mediaSourceId, string liveStreamId, bool enablePathSubstitution, CancellationToken cancellationToken);
/// <summary> /// <summary>

View File

@ -1,4 +1,4 @@
#pragma warning disable CS1591 #pragma warning disable CA1002, CS1591
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading; using System.Threading;
@ -21,6 +21,10 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Opens the media source. /// Opens the media source.
/// </summary> /// </summary>
/// <param name="openToken">Token to use.</param>
/// <param name="currentLiveStreams">List of live streams.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns>The media source wrapped as an awaitable task.</returns>
Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken); Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken);
} }
} }

View File

@ -29,7 +29,6 @@ namespace MediaBrowser.Controller.Library
/// </summary> /// </summary>
/// <param name="item">The item.</param> /// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
void Save(BaseItem item, CancellationToken cancellationToken); void Save(BaseItem item, CancellationToken cancellationToken);
} }
} }

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CS1591
using System.Collections.Generic; using System.Collections.Generic;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
@ -15,16 +15,28 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Gets the instant mix from song. /// Gets the instant mix from song.
/// </summary> /// </summary>
/// <param name="item">The item to use.</param>
/// <param name="user">The user to use.</param>
/// <param name="dtoOptions">The options to use.</param>
/// <returns>List of items.</returns>
List<BaseItem> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions); List<BaseItem> GetInstantMixFromItem(BaseItem item, User user, DtoOptions dtoOptions);
/// <summary> /// <summary>
/// Gets the instant mix from artist. /// Gets the instant mix from artist.
/// </summary> /// </summary>
/// <param name="artist">The artist to use.</param>
/// <param name="user">The user to use.</param>
/// <param name="dtoOptions">The options to use.</param>
/// <returns>List of items.</returns>
List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User user, DtoOptions dtoOptions); List<BaseItem> GetInstantMixFromArtist(MusicArtist artist, User user, DtoOptions dtoOptions);
/// <summary> /// <summary>
/// Gets the instant mix from genre. /// Gets the instant mix from genre.
/// </summary> /// </summary>
/// <param name="genres">The genres to use.</param>
/// <param name="user">The user to use.</param>
/// <param name="dtoOptions">The options to use.</param>
/// <returns>List of items.</returns>
List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions); List<BaseItem> GetInstantMixFromGenres(IEnumerable<string> genres, User user, DtoOptions dtoOptions);
} }
} }

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CA1707, CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -42,6 +42,9 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Gets the user data dto. /// Gets the user data dto.
/// </summary> /// </summary>
/// <param name="item">Item to use.</param>
/// <param name="user">User to use.</param>
/// <returns>User data dto.</returns>
UserItemDataDto GetUserDataDto(BaseItem item, User user); UserItemDataDto GetUserDataDto(BaseItem item, User user);
UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions dto_options); UserItemDataDto GetUserDataDto(BaseItem item, BaseItemDto itemDto, User user, DtoOptions dto_options);
@ -64,6 +67,10 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Updates playstate for an item and returns true or false indicating if it was played to completion. /// Updates playstate for an item and returns true or false indicating if it was played to completion.
/// </summary> /// </summary>
/// <param name="item">Item to update.</param>
/// <param name="data">Data to update.</param>
/// <param name="positionTicks">New playstate.</param>
/// <returns>True if playstate was updated.</returns>
bool UpdatePlayState(BaseItem item, UserItemData data, long? positionTicks); bool UpdatePlayState(BaseItem item, UserItemData data, long? positionTicks);
} }
} }

View File

@ -38,6 +38,7 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Initializes the user manager and ensures that a user exists. /// Initializes the user manager and ensures that a user exists.
/// </summary> /// </summary>
/// <returns>Awaitable task.</returns>
Task InitializeAsync(); Task InitializeAsync();
/// <summary> /// <summary>
@ -109,17 +110,22 @@ namespace MediaBrowser.Controller.Library
/// Resets the easy password. /// Resets the easy password.
/// </summary> /// </summary>
/// <param name="user">The user.</param> /// <param name="user">The user.</param>
/// <returns>Task.</returns>
void ResetEasyPassword(User user); void ResetEasyPassword(User user);
/// <summary> /// <summary>
/// Changes the password. /// Changes the password.
/// </summary> /// </summary>
/// <param name="user">The user.</param>
/// <param name="newPassword">New password to use.</param>
/// <returns>Awaitable task.</returns>
Task ChangePassword(User user, string newPassword); Task ChangePassword(User user, string newPassword);
/// <summary> /// <summary>
/// Changes the easy password. /// Changes the easy password.
/// </summary> /// </summary>
/// <param name="user">The user.</param>
/// <param name="newPassword">New password to use.</param>
/// <param name="newPasswordSha1">Hash of new password.</param>
void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1); void ChangeEasyPassword(User user, string newPassword, string newPasswordSha1);
/// <summary> /// <summary>
@ -133,6 +139,12 @@ namespace MediaBrowser.Controller.Library
/// <summary> /// <summary>
/// Authenticates the user. /// Authenticates the user.
/// </summary> /// </summary>
/// <param name="username">The user.</param>
/// <param name="password">The password to use.</param>
/// <param name="passwordSha1">Hash of password.</param>
/// <param name="remoteEndPoint">Remove endpoint to use.</param>
/// <param name="isUserSession">Specifies if a user session.</param>
/// <returns>User wrapped in awaitable task.</returns>
Task<User> AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession); Task<User> AuthenticateUser(string username, string password, string passwordSha1, string remoteEndPoint, bool isUserSession);
/// <summary> /// <summary>

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -13,10 +13,29 @@ namespace MediaBrowser.Controller.Library
{ {
public interface IUserViewManager public interface IUserViewManager
{ {
/// <summary>
/// Gets user views.
/// </summary>
/// <param name="query">Query to use.</param>
/// <returns>Set of folders.</returns>
Folder[] GetUserViews(UserViewQuery query); Folder[] GetUserViews(UserViewQuery query);
/// <summary>
/// Gets user sub views.
/// </summary>
/// <param name="parentId">Parent to use.</param>
/// <param name="type">Type to use.</param>
/// <param name="localizationKey">Localization key to use.</param>
/// <param name="sortName">Sort to use.</param>
/// <returns>User view.</returns>
UserView GetUserSubView(Guid parentId, string type, string localizationKey, string sortName); UserView GetUserSubView(Guid parentId, string type, string localizationKey, string sortName);
/// <summary>
/// Gets latest items.
/// </summary>
/// <param name="request">Query to use.</param>
/// <param name="options">Options to use.</param>
/// <returns>Set of items.</returns>
List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request, DtoOptions options); List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request, DtoOptions options);
} }
} }

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1711, CS1591
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;

View File

@ -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;
@ -109,6 +109,21 @@ namespace MediaBrowser.Controller.Library
/// <value>The additional locations.</value> /// <value>The additional locations.</value>
private List<string> AdditionalLocations { get; set; } private List<string> AdditionalLocations { get; set; }
/// <summary>
/// Gets the physical locations.
/// </summary>
/// <value>The physical locations.</value>
public string[] PhysicalLocations
{
get
{
var paths = string.IsNullOrEmpty(Path) ? Array.Empty<string>() : new[] { Path };
return AdditionalLocations == null ? paths : paths.Concat(AdditionalLocations).ToArray();
}
}
public string CollectionType { get; set; }
public bool HasParent<T>() public bool HasParent<T>()
where T : Folder where T : Folder
{ {
@ -138,6 +153,16 @@ namespace MediaBrowser.Controller.Library
return false; return false;
} }
/// <summary>
/// Determines whether the specified <see cref="object" /> is equal to this instance.
/// </summary>
/// <param name="obj">The object to compare with the current object.</param>
/// <returns><c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return Equals(obj as ItemResolveArgs);
}
/// <summary> /// <summary>
/// Adds the additional location. /// Adds the additional location.
/// </summary> /// </summary>
@ -156,19 +181,6 @@ namespace MediaBrowser.Controller.Library
// REVIEW: @bond // REVIEW: @bond
/// <summary>
/// Gets the physical locations.
/// </summary>
/// <value>The physical locations.</value>
public string[] PhysicalLocations
{
get
{
var paths = string.IsNullOrEmpty(Path) ? Array.Empty<string>() : new[] { Path };
return AdditionalLocations == null ? paths : paths.Concat(AdditionalLocations).ToArray();
}
}
/// <summary> /// <summary>
/// Gets the name of the file system entry by. /// Gets the name of the file system entry by.
/// </summary> /// </summary>
@ -190,7 +202,7 @@ namespace MediaBrowser.Controller.Library
/// </summary> /// </summary>
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <returns>FileSystemInfo.</returns> /// <returns>FileSystemInfo.</returns>
/// <exception cref="ArgumentNullException"></exception> /// <exception cref="ArgumentNullException">Throws if path is invalid.</exception>
public FileSystemMetadata GetFileSystemEntryByPath(string path) public FileSystemMetadata GetFileSystemEntryByPath(string path)
{ {
if (string.IsNullOrEmpty(path)) if (string.IsNullOrEmpty(path))
@ -224,18 +236,6 @@ namespace MediaBrowser.Controller.Library
return CollectionType; return CollectionType;
} }
public string CollectionType { get; set; }
/// <summary>
/// Determines whether the specified <see cref="object" /> is equal to this instance.
/// </summary>
/// <param name="obj">The object to compare with the current object.</param>
/// <returns><c>true</c> if the specified <see cref="object" /> is equal to this instance; otherwise, <c>false</c>.</returns>
public override bool Equals(object obj)
{
return Equals(obj as ItemResolveArgs);
}
/// <summary> /// <summary>
/// Returns a hash code for this instance. /// Returns a hash code for this instance.
/// </summary> /// </summary>

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CA2227, CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CA1002, CA2227, CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@ -22,12 +22,22 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary> /// </summary>
public interface ILiveTvManager public interface ILiveTvManager
{ {
event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
/// <summary> /// <summary>
/// Gets the services. /// Gets the services.
/// </summary> /// </summary>
/// <value>The services.</value> /// <value>The services.</value>
IReadOnlyList<ILiveTvService> Services { get; } IReadOnlyList<ILiveTvService> Services { get; }
IListingsProvider[] ListingProviders { get; }
/// <summary> /// <summary>
/// Gets the new timer defaults asynchronous. /// Gets the new timer defaults asynchronous.
/// </summary> /// </summary>
@ -86,6 +96,7 @@ namespace MediaBrowser.Controller.LiveTv
/// </summary> /// </summary>
/// <param name="query">The query.</param> /// <param name="query">The query.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <returns>A recording.</returns>
QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options); QueryResult<BaseItemDto> GetRecordings(RecordingQuery query, DtoOptions options);
/// <summary> /// <summary>
@ -176,11 +187,16 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="query">The query.</param> /// <param name="query">The query.</param>
/// <param name="options">The options.</param> /// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Recommended programs.</returns>
QueryResult<BaseItemDto> GetRecommendedPrograms(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken); QueryResult<BaseItemDto> GetRecommendedPrograms(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the recommended programs internal. /// Gets the recommended programs internal.
/// </summary> /// </summary>
/// <param name="query">The query.</param>
/// <param name="options">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Recommended programs.</returns>
QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken); QueryResult<BaseItem> GetRecommendedProgramsInternal(InternalItemsQuery query, DtoOptions options, CancellationToken cancellationToken);
/// <summary> /// <summary>
@ -202,6 +218,7 @@ namespace MediaBrowser.Controller.LiveTv
/// Gets the live tv folder. /// Gets the live tv folder.
/// </summary> /// </summary>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Live TV folder.</returns>
Folder GetInternalLiveTvFolder(CancellationToken cancellationToken); Folder GetInternalLiveTvFolder(CancellationToken cancellationToken);
/// <summary> /// <summary>
@ -213,11 +230,18 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary> /// <summary>
/// Gets the internal channels. /// Gets the internal channels.
/// </summary> /// </summary>
/// <param name="query">The query.</param>
/// <param name="dtoOptions">The options.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Internal channels.</returns>
QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken); QueryResult<BaseItem> GetInternalChannels(LiveTvChannelQuery query, DtoOptions dtoOptions, CancellationToken cancellationToken);
/// <summary> /// <summary>
/// Gets the channel media sources. /// Gets the channel media sources.
/// </summary> /// </summary>
/// <param name="item">Item to search for.</param>
/// <param name="cancellationToken">CancellationToken to use for operation.</param>
/// <returns>Channel media sources wrapped in a task.</returns>
Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(BaseItem item, CancellationToken cancellationToken); Task<IEnumerable<MediaSourceInfo>> GetChannelMediaSources(BaseItem item, CancellationToken cancellationToken);
/// <summary> /// <summary>
@ -232,6 +256,9 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary> /// <summary>
/// Saves the tuner host. /// Saves the tuner host.
/// </summary> /// </summary>
/// <param name="info">Turner host to save.</param>
/// <param name="dataSourceChanged">Option to specify that data source has changed.</param>
/// <returns>Tuner host information wrapped in a task.</returns>
Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true); Task<TunerHostInfo> SaveTunerHost(TunerHostInfo info, bool dataSourceChanged = true);
/// <summary> /// <summary>
@ -271,20 +298,10 @@ namespace MediaBrowser.Controller.LiveTv
Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken); Task<List<ChannelInfo>> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken);
IListingsProvider[] ListingProviders { get; }
List<NameIdPair> GetTunerHostTypes(); List<NameIdPair> GetTunerHostTypes();
Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken); Task<List<TunerHostInfo>> DiscoverTuners(bool newDevicesOnly, CancellationToken cancellationToken);
event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCancelled;
event EventHandler<GenericEventArgs<TimerEventInfo>> TimerCreated;
event EventHandler<GenericEventArgs<TimerEventInfo>> SeriesTimerCreated;
string GetEmbyTvActiveRecordingPath(string id); string GetEmbyTvActiveRecordingPath(string id);
ActiveRecordingInfo GetActiveRecordingInfo(string path); ActiveRecordingInfo GetActiveRecordingInfo(string path);

View File

@ -30,6 +30,8 @@ namespace MediaBrowser.Controller.LiveTv
/// <summary> /// <summary>
/// Gets the channels. /// Gets the channels.
/// </summary> /// </summary>
/// <param name="enableCache">Option to enable using cache.</param>
/// <param name="cancellationToken">The CancellationToken for this operation.</param>
/// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns> /// <returns>Task&lt;IEnumerable&lt;ChannelInfo&gt;&gt;.</returns>
Task<List<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken); Task<List<ChannelInfo>> GetChannels(bool enableCache, CancellationToken cancellationToken);
@ -47,6 +49,7 @@ namespace MediaBrowser.Controller.LiveTv
/// <param name="streamId">The stream identifier.</param> /// <param name="streamId">The stream identifier.</param>
/// <param name="currentLiveStreams">The current live streams.</param> /// <param name="currentLiveStreams">The current live streams.</param>
/// <param name="cancellationToken">The cancellation token to cancel operation.</param> /// <param name="cancellationToken">The cancellation token to cancel operation.</param>
/// <returns>Live stream wrapped in a task.</returns>
Task<ILiveStream> GetChannelStream(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken); Task<ILiveStream> GetChannelStream(string channelId, string streamId, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken);
/// <summary> /// <summary>

View File

@ -18,23 +18,6 @@ namespace MediaBrowser.Controller.LiveTv
{ {
public class LiveTvChannel : BaseItem, IHasMediaSources, IHasProgramAttributes public class LiveTvChannel : BaseItem, IHasMediaSources, IHasProgramAttributes
{ {
public override List<string> GetUserDataKeys()
{
var list = base.GetUserDataKeys();
if (!ConfigurationManager.Configuration.DisableLiveTvChannelUserDataName)
{
list.Insert(0, GetClientTypeName() + "-" + Name);
}
return list;
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.LiveTvChannel;
}
[JsonIgnore] [JsonIgnore]
public override bool SupportsPositionTicksResume => false; public override bool SupportsPositionTicksResume => false;
@ -59,6 +42,67 @@ namespace MediaBrowser.Controller.LiveTv
[JsonIgnore] [JsonIgnore]
public override LocationType LocationType => LocationType.Remote; public override LocationType LocationType => LocationType.Remote;
[JsonIgnore]
public override string MediaType => ChannelType == ChannelType.Radio ? Model.Entities.MediaType.Audio : Model.Entities.MediaType.Video;
[JsonIgnore]
public bool IsMovie { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is sports.
/// </summary>
/// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
[JsonIgnore]
public bool IsSports { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is series.
/// </summary>
/// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
[JsonIgnore]
public bool IsSeries { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is news.
/// </summary>
/// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
[JsonIgnore]
public bool IsNews { get; set; }
/// <summary>
/// Gets a value indicating whether this instance is kids.
/// </summary>
/// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
[JsonIgnore]
public bool IsKids => Tags.Contains("Kids", StringComparer.OrdinalIgnoreCase);
[JsonIgnore]
public bool IsRepeat { get; set; }
/// <summary>
/// Gets or sets the episode title.
/// </summary>
/// <value>The episode title.</value>
[JsonIgnore]
public string EpisodeTitle { get; set; }
public override List<string> GetUserDataKeys()
{
var list = base.GetUserDataKeys();
if (!ConfigurationManager.Configuration.DisableLiveTvChannelUserDataName)
{
list.Insert(0, GetClientTypeName() + "-" + Name);
}
return list;
}
public override UnratedItem GetBlockUnratedType()
{
return UnratedItem.LiveTvChannel;
}
protected override string CreateSortName() protected override string CreateSortName()
{ {
if (!string.IsNullOrEmpty(Number)) if (!string.IsNullOrEmpty(Number))
@ -74,9 +118,6 @@ namespace MediaBrowser.Controller.LiveTv
return (Number ?? string.Empty) + "-" + (Name ?? string.Empty); return (Number ?? string.Empty) + "-" + (Name ?? string.Empty);
} }
[JsonIgnore]
public override string MediaType => ChannelType == ChannelType.Radio ? Model.Entities.MediaType.Audio : Model.Entities.MediaType.Video;
public override string GetClientTypeName() public override string GetClientTypeName()
{ {
return "TvChannel"; return "TvChannel";
@ -122,46 +163,5 @@ namespace MediaBrowser.Controller.LiveTv
{ {
return false; return false;
} }
[JsonIgnore]
public bool IsMovie { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is sports.
/// </summary>
/// <value><c>true</c> if this instance is sports; otherwise, <c>false</c>.</value>
[JsonIgnore]
public bool IsSports { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is series.
/// </summary>
/// <value><c>true</c> if this instance is series; otherwise, <c>false</c>.</value>
[JsonIgnore]
public bool IsSeries { get; set; }
/// <summary>
/// Gets or sets a value indicating whether this instance is news.
/// </summary>
/// <value><c>true</c> if this instance is news; otherwise, <c>false</c>.</value>
[JsonIgnore]
public bool IsNews { get; set; }
/// <summary>
/// Gets a value indicating whether this instance is kids.
/// </summary>
/// <value><c>true</c> if this instance is kids; otherwise, <c>false</c>.</value>
[JsonIgnore]
public bool IsKids => Tags.Contains("Kids", StringComparer.OrdinalIgnoreCase);
[JsonIgnore]
public bool IsRepeat { get; set; }
/// <summary>
/// Gets or sets the episode title.
/// </summary>
/// <value>The episode title.</value>
[JsonIgnore]
public string EpisodeTitle { get; set; }
} }
} }

View File

@ -1,6 +1,6 @@
#nullable disable #nullable disable
#pragma warning disable CS1591 #pragma warning disable CS1591, SA1306
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -19,54 +19,14 @@ namespace MediaBrowser.Controller.LiveTv
{ {
public class LiveTvProgram : BaseItem, IHasLookupInfo<ItemLookupInfo>, IHasStartDate, IHasProgramAttributes public class LiveTvProgram : BaseItem, IHasLookupInfo<ItemLookupInfo>, IHasStartDate, IHasProgramAttributes
{ {
private static string EmbyServiceName = "Emby";
public LiveTvProgram() public LiveTvProgram()
{ {
IsVirtualItem = true; IsVirtualItem = true;
} }
public override List<string> GetUserDataKeys() public string SeriesName { get; set; }
{
var list = base.GetUserDataKeys();
if (!IsSeries)
{
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);
}
}
else if (!string.IsNullOrEmpty(EpisodeTitle))
{
var name = GetClientTypeName();
list.Insert(0, name + "-" + Name + (EpisodeTitle ?? string.Empty));
}
return list;
}
private static string EmbyServiceName = "Emby";
public override double GetDefaultPrimaryImageAspectRatio()
{
var serviceName = ServiceName;
if (string.Equals(serviceName, EmbyServiceName, StringComparison.OrdinalIgnoreCase) || string.Equals(serviceName, "Next Pvr", StringComparison.OrdinalIgnoreCase))
{
return 2.0 / 3;
}
else
{
return 16.0 / 9;
}
}
[JsonIgnore] [JsonIgnore]
public override SourceType SourceType => SourceType.LiveTV; public override SourceType SourceType => SourceType.LiveTV;
@ -182,6 +142,66 @@ namespace MediaBrowser.Controller.LiveTv
} }
} }
[JsonIgnore]
public override bool SupportsPeople
{
get
{
// Optimization
if (IsNews || IsSports)
{
return false;
}
return base.SupportsPeople;
}
}
[JsonIgnore]
public override bool SupportsAncestors => false;
public override List<string> GetUserDataKeys()
{
var list = base.GetUserDataKeys();
if (!IsSeries)
{
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);
}
}
else if (!string.IsNullOrEmpty(EpisodeTitle))
{
var name = GetClientTypeName();
list.Insert(0, name + "-" + Name + (EpisodeTitle ?? string.Empty));
}
return list;
}
public override double GetDefaultPrimaryImageAspectRatio()
{
var serviceName = ServiceName;
if (string.Equals(serviceName, EmbyServiceName, StringComparison.OrdinalIgnoreCase) || string.Equals(serviceName, "Next Pvr", StringComparison.OrdinalIgnoreCase))
{
return 2.0 / 3;
}
else
{
return 16.0 / 9;
}
}
public override string GetClientTypeName() public override string GetClientTypeName()
{ {
return "Program"; return "Program";
@ -202,24 +222,6 @@ namespace MediaBrowser.Controller.LiveTv
return false; return false;
} }
[JsonIgnore]
public override bool SupportsPeople
{
get
{
// Optimization
if (IsNews || IsSports)
{
return false;
}
return base.SupportsPeople;
}
}
[JsonIgnore]
public override bool SupportsAncestors => false;
private LiveTvOptions GetConfiguration() private LiveTvOptions GetConfiguration()
{ {
return ConfigurationManager.GetConfiguration<LiveTvOptions>("livetv"); return ConfigurationManager.GetConfiguration<LiveTvOptions>("livetv");
@ -273,7 +275,5 @@ namespace MediaBrowser.Controller.LiveTv
return list; return list;
} }
public string SeriesName { get; set; }
} }
} }

View File

@ -35,14 +35,15 @@
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net5.0</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors Condition=" '$(Configuration)' == 'Release' ">true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisMode Condition=" '$(Configuration)' == 'Debug' ">AllEnabledByDefault</AnalysisMode>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols> <IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat> <SymbolPackageFormat>snupkg</SymbolPackageFormat>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release'">
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Stability)'=='Unstable'"> <PropertyGroup Condition=" '$(Stability)'=='Unstable'">

View File

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

View File

@ -189,6 +189,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;
@ -343,6 +346,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
@ -372,6 +380,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);
@ -517,6 +527,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="options">Encoding options.</param>
/// <returns>Input arguments.</returns>
public string GetInputArgument(EncodingJobInfo state, EncodingOptions options) public string GetInputArgument(EncodingJobInfo state, EncodingOptions options)
{ {
var arg = new StringBuilder(); var arg = new StringBuilder();
@ -1002,6 +1015,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;
@ -2003,8 +2021,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,
@ -2552,6 +2574,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,
@ -2562,8 +2591,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,
@ -3111,6 +3145,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)
{ {
@ -3759,6 +3797,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);
@ -3777,6 +3820,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();

View File

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

View File

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

View File

@ -83,13 +83,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,
@ -134,10 +163,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);
} }
} }

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