Merge branch 'master' into minor15

pull/6267/head
Bond-009 3 years ago committed by GitHub
commit e5a29824e5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

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

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

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

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

@ -17,6 +17,7 @@ assignees: ''
- Browser: [e.g. Firefox 72, Chrome 80, Safari 13] - Browser: [e.g. Firefox 72, Chrome 80, Safari 13]
- Jellyfin Version: [e.g. 10.4.3, nightly 20191231] - Jellyfin Version: [e.g. 10.4.3, nightly 20191231]
- Playback: [Direct Play, Remux, Direct Stream, Transcode] - Playback: [Direct Play, Remux, Direct Stream, Transcode]
- Hardware Acceleration: [e.g. none, VAAPI, NVENC, etc.]
- Installed Plugins: [e.g. none, Fanart, Anime, etc.] - Installed Plugins: [e.g. none, Fanart, Anime, etc.]
- Reverse Proxy: [e.g. none, nginx, apache, etc.] - Reverse Proxy: [e.g. none, nginx, apache, etc.]
- Base URL: [e.g. none, yes: /example] - Base URL: [e.g. none, yes: /example]

6
.github/stale.yml vendored

@ -17,9 +17,13 @@ staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable # Comment to post when marking an issue as stale. Set to `false` to disable
markComment: > markComment: >
This issue has gone 120 days without comment. To avoid abandoned issues, it will be closed in 21 days if there are no new comments. This issue has gone 120 days without comment. To avoid abandoned issues, it will be closed in 21 days if there are no new comments.
If you're the original submitter of this issue, please comment confirming if this issue still affects you in the latest release or nightlies, or close the issue if it has been fixed. If you're another user also affected by this bug, please comment confirming so. Either action will remove the stale label. If you're the original submitter of this issue, please comment confirming if this issue still affects you in the latest release or nightlies, or close the issue if it has been fixed. If you're another user also affected by this bug, please comment confirming so. Either action will remove the stale label.
This bot exists to prevent issues from becoming stale and forgotten. Jellyfin is always moving forward, and bugs are often fixed as side effects of other changes. We therefore ask that bug report authors remain vigilant about their issues to ensure they are closed if fixed, or re-confirmed - perhaps with fresh logs or reproduction examples - regularly. If you have any questions you can reach us on [Matrix or Social Media](https://docs.jellyfin.org/general/getting-help.html). This bot exists to prevent issues from becoming stale and forgotten. Jellyfin is always moving forward, and bugs are often fixed as side effects of other changes. We therefore ask that bug report authors remain vigilant about their issues to ensure they are closed if fixed, or re-confirmed - perhaps with fresh logs or reproduction examples - regularly. If you have any questions you can reach us on [Matrix or Social Media](https://docs.jellyfin.org/general/getting-help.html).
# Comment to post when closing a stale issue. Set to `false` to disable # Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false closeComment: false
# Disable automatic closing of pull requests
pulls:
daysUntilClose: false

@ -26,7 +26,7 @@ jobs:
if: ${{ github.repository == 'jellyfin/jellyfin' }} if: ${{ github.repository == 'jellyfin/jellyfin' }}
steps: steps:
- name: Remove from 'Current Release' project - name: Remove from 'Current Release' project
uses: alex-page/github-project-automation-plus@v0.7.1 uses: alex-page/github-project-automation-plus@v0.8.1
if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport') if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
continue-on-error: true continue-on-error: true
with: with:
@ -35,7 +35,7 @@ jobs:
repo-token: ${{ secrets.JF_BOT_TOKEN }} repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add to 'Release Next' project - name: Add to 'Release Next' project
uses: alex-page/github-project-automation-plus@v0.7.1 uses: alex-page/github-project-automation-plus@v0.8.1
if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened' if: (github.event.pull_request || github.event.issue.pull_request) && github.event.action == 'opened'
continue-on-error: true continue-on-error: true
with: with:
@ -44,7 +44,7 @@ jobs:
repo-token: ${{ secrets.JF_BOT_TOKEN }} repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add to 'Current Release' project - name: Add to 'Current Release' project
uses: alex-page/github-project-automation-plus@v0.7.1 uses: alex-page/github-project-automation-plus@v0.8.1
if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport') if: (github.event.pull_request || github.event.issue.pull_request) && !contains(github.event.*.labels.*.name, 'stable backport')
continue-on-error: true continue-on-error: true
with: with:
@ -58,7 +58,7 @@ jobs:
run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)" run: echo "::set-output name=number::$(curl -s ${{ github.event.issue.comments_url }} | jq '.[] | select(.author_association == "MEMBER") | .author_association' | wc -l)"
- name: Move issue to needs triage - name: Move issue to needs triage
uses: alex-page/github-project-automation-plus@v0.7.1 uses: alex-page/github-project-automation-plus@v0.8.1
if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1 if: github.event.issue.pull_request == '' && github.event.comment.author_association == 'MEMBER' && steps.member_comments.outputs.number <= 1
continue-on-error: true continue-on-error: true
with: with:
@ -67,7 +67,7 @@ jobs:
repo-token: ${{ secrets.JF_BOT_TOKEN }} repo-token: ${{ secrets.JF_BOT_TOKEN }}
- name: Add issue to triage project - name: Add issue to triage project
uses: alex-page/github-project-automation-plus@v0.7.1 uses: alex-page/github-project-automation-plus@v0.8.1
if: github.event.issue.pull_request == '' && github.event.action == 'opened' if: github.event.issue.pull_request == '' && github.event.action == 'opened'
continue-on-error: true continue-on-error: true
with: with:

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

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

@ -8,15 +8,7 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
&& npm ci --no-audit --unsafe-perm \ && npm ci --no-audit --unsafe-perm \
&& mv dist /dist && mv dist /dist
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder FROM debian:buster-slim as app
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# because of changes in docker and systemd we need to not build in parallel at the moment
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none"
FROM debian:buster-slim
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable # https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive" ARG DEBIAN_FRONTEND="noninteractive"
@ -25,9 +17,6 @@ ARG APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn
# https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support) # https://github.com/NVIDIA/nvidia-docker/wiki/Installation-(Native-GPU-Support)
ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility" ENV NVIDIA_DRIVER_CAPABILITIES="compute,video,utility"
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
# https://github.com/intel/compute-runtime/releases # https://github.com/intel/compute-runtime/releases
ARG GMMLIB_VERSION=20.3.2 ARG GMMLIB_VERSION=20.3.2
ARG IGC_VERSION=1.0.5435 ARG IGC_VERSION=1.0.5435
@ -73,6 +62,19 @@ ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8 ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en ENV LANGUAGE en_US:en
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# because of changes in docker and systemd we need to not build in parallel at the moment
# see https://success.docker.com/article/how-to-reserve-resource-temporarily-unavailable-errors-due-to-tasksmax-setting
RUN dotnet publish Jellyfin.Server --disable-parallel --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:DebugSymbols=false;DebugType=none"
FROM app
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096 EXPOSE 8096
VOLUME /cache /config /media VOLUME /cache /config /media
ENTRYPOINT ["./jellyfin/jellyfin", \ ENTRYPOINT ["./jellyfin/jellyfin", \

@ -13,19 +13,8 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
&& npm ci --no-audit --unsafe-perm \ && npm ci --no-audit --unsafe-perm \
&& mv dist /dist && mv dist /dist
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r
# Build
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none"
FROM multiarch/qemu-user-static:x86_64-arm as qemu FROM multiarch/qemu-user-static:x86_64-arm as qemu
FROM arm32v7/debian:buster-slim FROM arm32v7/debian:buster-slim as app
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable # https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive" ARG DEBIAN_FRONTEND="noninteractive"
@ -61,14 +50,25 @@ RUN apt-get update \
&& chmod 777 /cache /config /media \ && chmod 777 /cache /config /media \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
ENV LC_ALL en_US.UTF-8 ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8 ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en ENV LANGUAGE en_US:en
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r
# Build
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm "-p:DebugSymbols=false;DebugType=none"
FROM app
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096 EXPOSE 8096
VOLUME /cache /config /media VOLUME /cache /config /media
ENTRYPOINT ["./jellyfin/jellyfin", \ ENTRYPOINT ["./jellyfin/jellyfin", \

@ -13,18 +13,8 @@ RUN apk add curl git zlib zlib-dev autoconf g++ make libpng-dev gifsicle alpine-
&& npm ci --no-audit --unsafe-perm \ && npm ci --no-audit --unsafe-perm \
&& mv dist /dist && mv dist /dist
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r
# Build
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none"
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
FROM arm64v8/debian:buster-slim FROM arm64v8/debian:buster-slim as app
# https://askubuntu.com/questions/972516/debian-frontend-environment-variable # https://askubuntu.com/questions/972516/debian-frontend-environment-variable
ARG DEBIAN_FRONTEND="noninteractive" ARG DEBIAN_FRONTEND="noninteractive"
@ -50,14 +40,25 @@ RUN apt-get update && apt-get install --no-install-recommends --no-install-sugge
&& chmod 777 /cache /config /media \ && chmod 777 /cache /config /media \
&& sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen && sed -i -e 's/# en_US.UTF-8 UTF-8/en_US.UTF-8 UTF-8/' /etc/locale.gen && locale-gen
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
ENV LC_ALL en_US.UTF-8 ENV LC_ALL en_US.UTF-8
ENV LANG en_US.UTF-8 ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en ENV LANGUAGE en_US:en
FROM mcr.microsoft.com/dotnet/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo
COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r
# Build
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-arm64 "-p:DebugSymbols=false;DebugType=none"
FROM app
COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web
EXPOSE 8096 EXPOSE 8096
VOLUME /cache /config /media VOLUME /cache /config /media
ENTRYPOINT ["./jellyfin/jellyfin", \ ENTRYPOINT ["./jellyfin/jellyfin", \

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

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

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

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

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

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

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

@ -27,11 +27,9 @@ using MediaBrowser.Controller.TV;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Rssdp; using Rssdp;
using Rssdp.Infrastructure; using Rssdp.Infrastructure;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Dlna.Main namespace Emby.Dlna.Main
{ {
@ -204,8 +202,8 @@ namespace Emby.Dlna.Main
{ {
if (_communicationsServer == null) if (_communicationsServer == null)
{ {
var enableMultiSocketBinding = OperatingSystem.Id == OperatingSystemId.Windows || var enableMultiSocketBinding = OperatingSystem.IsWindows() ||
OperatingSystem.Id == OperatingSystemId.Linux; OperatingSystem.IsLinux();
_communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding) _communicationsServer = new SsdpCommunicationsServer(_socketFactory, _networkManager, _logger, enableMultiSocketBinding)
{ {
@ -268,7 +266,12 @@ namespace Emby.Dlna.Main
try try
{ {
_publisher = new SsdpDevicePublisher(_communicationsServer, _networkManager, OperatingSystem.Name, Environment.OSVersion.VersionString, _config.GetDlnaConfiguration().SendOnlyMatchedHost) _publisher = new SsdpDevicePublisher(
_communicationsServer,
_networkManager,
MediaBrowser.Common.System.OperatingSystem.Name,
Environment.OSVersion.VersionString,
_config.GetDlnaConfiguration().SendOnlyMatchedHost)
{ {
LogFunction = LogMessage, LogFunction = LogMessage,
SupportPnpRootDevice = false SupportPnpRootDevice = false

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

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

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

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

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

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

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

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

@ -137,7 +137,7 @@ namespace Emby.Naming.Common
CleanStrings = new[] CleanStrings = new[]
{ {
@"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)", @"[ _\,\.\(\)\[\]\-](3d|sbs|tab|hsbs|htab|mvc|HDR|HDC|UHD|UltraHD|4k|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip|hdtvrip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|cd[1-9]|r3|r5|bd5|bd|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|480p|480i|576p|576i|720p|720i|1080p|1080i|2160p|hrhd|hrhdtv|hddvd|bluray|blu-ray|x264|x265|h264|h265|xvid|xvidvd|xxx|www.www|AAC|DTS|\[.*\])([ _\,\.\(\)\[\]\-]|$)",
@"(\[.*\])" @"(\[.*\])"
}; };
@ -277,14 +277,14 @@ namespace Emby.Naming.Common
IsNamed = true IsNamed = true
}, },
new EpisodeExpression("[\\\\/\\._ \\[\\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\\.[1-9])(?![0-9]))?)([^\\\\/]*)$") new EpisodeExpression(@"[\\\/\._ \[\(-]([0-9]+)x([0-9]+(?:(?:[a-i]|\.[1-9])(?![0-9]))?)([^\\\/]*)$")
{ {
SupportsAbsoluteEpisodeNumbers = true SupportsAbsoluteEpisodeNumbers = true
}, },
// 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
}, },
@ -305,6 +305,12 @@ namespace Emby.Naming.Common
// *** End Kodi Standard Naming // *** End Kodi Standard Naming
// "Episode 16", "Episode 16 - Title"
new EpisodeExpression(@"[Ee]pisode (?<epnumber>[0-9]+)(-(?<endingepnumber>[0-9]+))?[^\\\/]*$")
{
IsNamed = true
},
new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$") new EpisodeExpression(@".*(\\|\/)[sS]?(?<seasonnumber>[0-9]+)[xX](?<epnumber>[0-9]+)[^\\\/]*$")
{ {
IsNamed = true IsNamed = true
@ -362,12 +368,6 @@ namespace Emby.Naming.Common
IsOptimistic = true, IsOptimistic = true,
IsNamed = true IsNamed = true
}, },
// "Episode 16", "Episode 16 - Title"
new EpisodeExpression(@".*[\\\/][^\\\/]* (?<epnumber>[0-9]{1,3})(-(?<endingepnumber>[0-9]{2,3}))*[^\\\/]*$")
{
IsOptimistic = true,
IsNamed = true
}
}; };
EpisodeWithoutSeasonExpressions = new[] EpisodeWithoutSeasonExpressions = new[]

@ -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,13 +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>
<CodeAnalysisRuleSet>../jellyfin.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Stability)'=='Unstable'"> <PropertyGroup Condition=" '$(Stability)'=='Unstable'">

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

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

@ -77,7 +77,6 @@ namespace Emby.Notifications
{ {
_libraryManager.ItemAdded += OnLibraryManagerItemAdded; _libraryManager.ItemAdded += OnLibraryManagerItemAdded;
_appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged; _appHost.HasPendingRestartChanged += OnAppHostHasPendingRestartChanged;
_appHost.HasUpdateAvailableChanged += OnAppHostHasUpdateAvailableChanged;
_activityManager.EntryCreated += OnActivityManagerEntryCreated; _activityManager.EntryCreated += OnActivityManagerEntryCreated;
return Task.CompletedTask; return Task.CompletedTask;
@ -132,25 +131,6 @@ namespace Emby.Notifications
return _config.GetConfiguration<NotificationOptions>("notifications"); return _config.GetConfiguration<NotificationOptions>("notifications");
} }
private async void OnAppHostHasUpdateAvailableChanged(object? sender, EventArgs e)
{
if (!_appHost.HasUpdateAvailable)
{
return;
}
var type = NotificationType.ApplicationUpdateAvailable.ToString();
var notification = new NotificationRequest
{
Description = "Please see jellyfin.org for details.",
NotificationType = type,
Name = _localization.GetLocalizedString("NewVersionIsAvailable")
};
await SendNotification(notification, null).ConfigureAwait(false);
}
private void OnLibraryManagerItemAdded(object? sender, ItemChangeEventArgs e) private void OnLibraryManagerItemAdded(object? sender, ItemChangeEventArgs e)
{ {
if (!FilterItem(e.Item)) if (!FilterItem(e.Item))
@ -325,7 +305,6 @@ namespace Emby.Notifications
_libraryManager.ItemAdded -= OnLibraryManagerItemAdded; _libraryManager.ItemAdded -= OnLibraryManagerItemAdded;
_appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged; _appHost.HasPendingRestartChanged -= OnAppHostHasPendingRestartChanged;
_appHost.HasUpdateAvailableChanged -= OnAppHostHasUpdateAvailableChanged;
_activityManager.EntryCreated -= OnActivityManagerEntryCreated; _activityManager.EntryCreated -= OnActivityManagerEntryCreated;
_disposed = true; _disposed = true;

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

@ -103,7 +103,6 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Prometheus.DotNetRuntime; using Prometheus.DotNetRuntime;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager; using WebSocketManager = Emby.Server.Implementations.HttpServer.WebSocketManager;
namespace Emby.Server.Implementations namespace Emby.Server.Implementations
@ -150,13 +149,7 @@ namespace Emby.Server.Implementations
return false; return false;
} }
if (OperatingSystem.Id == OperatingSystemId.Windows return OperatingSystem.IsWindows() || OperatingSystem.IsMacOS();
|| OperatingSystem.Id == OperatingSystemId.Darwin)
{
return true;
}
return false;
} }
} }
@ -721,7 +714,7 @@ namespace Emby.Server.Implementations
logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars); logger.LogInformation("Environment Variables: {EnvVars}", relevantEnvVars);
logger.LogInformation("Arguments: {Args}", commandLineArgs); logger.LogInformation("Arguments: {Args}", commandLineArgs);
logger.LogInformation("Operating system: {OS}", OperatingSystem.Name); logger.LogInformation("Operating system: {OS}", MediaBrowser.Common.System.OperatingSystem.Name);
logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture); logger.LogInformation("Architecture: {Architecture}", RuntimeInformation.OSArchitecture);
logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess); logger.LogInformation("64-Bit Process: {Is64Bit}", Environment.Is64BitProcess);
logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive); logger.LogInformation("User Interactive: {IsUserInteractive}", Environment.UserInteractive);
@ -1098,11 +1091,10 @@ namespace Emby.Server.Implementations
ItemsByNamePath = ApplicationPaths.InternalMetadataPath, ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
InternalMetadataPath = ApplicationPaths.InternalMetadataPath, InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
CachePath = ApplicationPaths.CachePath, CachePath = ApplicationPaths.CachePath,
OperatingSystem = OperatingSystem.Id.ToString(), OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(),
OperatingSystemDisplayName = OperatingSystem.Name, OperatingSystemDisplayName = MediaBrowser.Common.System.OperatingSystem.Name,
CanSelfRestart = CanSelfRestart, CanSelfRestart = CanSelfRestart,
CanLaunchWebBrowser = CanLaunchWebBrowser, CanLaunchWebBrowser = CanLaunchWebBrowser,
HasUpdateAvailable = HasUpdateAvailable,
TranscodingTempPath = ConfigurationManager.GetTranscodePath(), TranscodingTempPath = ConfigurationManager.GetTranscodePath(),
ServerName = FriendlyName, ServerName = FriendlyName,
LocalAddress = GetSmartApiUrl(source), LocalAddress = GetSmartApiUrl(source),
@ -1118,16 +1110,16 @@ namespace Emby.Server.Implementations
.Select(i => new WakeOnLanInfo(i)) .Select(i => new WakeOnLanInfo(i))
.ToList(); .ToList();
public PublicSystemInfo GetPublicSystemInfo(IPAddress source) public PublicSystemInfo GetPublicSystemInfo(IPAddress address)
{ {
return new PublicSystemInfo return new PublicSystemInfo
{ {
Version = ApplicationVersionString, Version = ApplicationVersionString,
ProductName = ApplicationProductName, ProductName = ApplicationProductName,
Id = SystemId, Id = SystemId,
OperatingSystem = OperatingSystem.Id.ToString(), OperatingSystem = MediaBrowser.Common.System.OperatingSystem.Id.ToString(),
ServerName = FriendlyName, ServerName = FriendlyName,
LocalAddress = GetSmartApiUrl(source), LocalAddress = GetSmartApiUrl(address),
StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted StartupWizardCompleted = ConfigurationManager.CommonConfiguration.IsStartupWizardCompleted
}; };
} }
@ -1136,7 +1128,7 @@ namespace Emby.Server.Implementations
public bool ListenWithHttps => Certificate != null && ConfigurationManager.GetNetworkConfiguration().EnableHttps; public bool ListenWithHttps => Certificate != null && ConfigurationManager.GetNetworkConfiguration().EnableHttps;
/// <inheritdoc/> /// <inheritdoc/>
public string GetSmartApiUrl(IPAddress ipAddress, int? port = null) public string GetSmartApiUrl(IPAddress remoteAddr, int? port = null)
{ {
// Published server ends with a / // Published server ends with a /
if (!string.IsNullOrEmpty(PublishedServerUrl)) if (!string.IsNullOrEmpty(PublishedServerUrl))
@ -1145,7 +1137,7 @@ namespace Emby.Server.Implementations
return PublishedServerUrl.Trim('/'); return PublishedServerUrl.Trim('/');
} }
string smart = NetManager.GetBindInterface(ipAddress, out port); string smart = NetManager.GetBindInterface(remoteAddr, out port);
// If the smartAPI doesn't start with http then treat it as a host or ip. // If the smartAPI doesn't start with http then treat it as a host or ip.
if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase)) if (smart.StartsWith("http", StringComparison.OrdinalIgnoreCase))
{ {
@ -1208,14 +1200,14 @@ namespace Emby.Server.Implementations
} }
/// <inheritdoc/> /// <inheritdoc/>
public string GetLocalApiUrl(string host, string scheme = null, int? port = null) public string GetLocalApiUrl(string hostname, string scheme = null, int? port = null)
{ {
// NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does // NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
// not. For consistency, always trim the trailing slash. // not. For consistency, always trim the trailing slash.
return new UriBuilder return new UriBuilder
{ {
Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp), Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp),
Host = host, Host = hostname,
Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort), Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort),
Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl Path = ConfigurationManager.GetNetworkConfiguration().BaseUrl
}.ToString().TrimEnd('/'); }.ToString().TrimEnd('/');
@ -1252,26 +1244,6 @@ namespace Emby.Server.Implementations
protected abstract void ShutdownInternal(); protected abstract void ShutdownInternal();
public event EventHandler HasUpdateAvailableChanged;
private bool _hasUpdateAvailable;
public bool HasUpdateAvailable
{
get => _hasUpdateAvailable;
set
{
var fireEvent = value && !_hasUpdateAvailable;
_hasUpdateAvailable = value;
if (fireEvent)
{
HasUpdateAvailableChanged?.Invoke(this, EventArgs.Empty);
}
}
}
public IEnumerable<Assembly> GetApiPluginAssemblies() public IEnumerable<Assembly> GetApiPluginAssemblies()
{ {
var assemblies = _allConcreteTypes var assemblies = _allConcreteTypes

@ -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)
@ -164,7 +162,7 @@ namespace Emby.Server.Implementations.Collections
DateCreated = DateTime.UtcNow DateCreated = DateTime.UtcNow
}; };
parentFolder.AddChild(collection, CancellationToken.None); parentFolder.AddChild(collection);
if (options.ItemIdList.Count > 0) if (options.ItemIdList.Count > 0)
{ {

@ -24,11 +24,11 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" /> <PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.2" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.7" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.8" />
<PackageReference Include="Mono.Nat" Version="3.0.1" /> <PackageReference Include="Mono.Nat" Version="3.0.1" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" /> <PackageReference Include="prometheus-net.DotNetRuntime" Version="4.1.0" />
<PackageReference Include="sharpcompress" Version="0.28.3" /> <PackageReference Include="sharpcompress" Version="0.28.3" />
@ -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-->

@ -11,7 +11,6 @@ using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.System; using MediaBrowser.Model.System;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
namespace Emby.Server.Implementations.IO namespace Emby.Server.Implementations.IO
{ {
@ -24,7 +23,7 @@ namespace Emby.Server.Implementations.IO
private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>(); private readonly List<IShortcutHandler> _shortcutHandlers = new List<IShortcutHandler>();
private readonly string _tempPath; private readonly string _tempPath;
private static readonly bool _isEnvironmentCaseInsensitive = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); private static readonly bool _isEnvironmentCaseInsensitive = OperatingSystem.IsWindows();
public ManagedFileSystem( public ManagedFileSystem(
ILogger<ManagedFileSystem> logger, ILogger<ManagedFileSystem> logger,
@ -402,7 +401,7 @@ namespace Emby.Server.Implementations.IO
public virtual void SetHidden(string path, bool isHidden) public virtual void SetHidden(string path, bool isHidden)
{ {
if (OperatingSystem.Id != OperatingSystemId.Windows) if (!OperatingSystem.IsWindows())
{ {
return; return;
} }
@ -426,7 +425,7 @@ namespace Emby.Server.Implementations.IO
public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly) public virtual void SetAttributes(string path, bool isHidden, bool isReadOnly)
{ {
if (OperatingSystem.Id != OperatingSystemId.Windows) if (!OperatingSystem.IsWindows())
{ {
return; return;
} }

@ -2540,9 +2540,10 @@ namespace Emby.Server.Implementations.Library
{ {
episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming); episodeInfo = resolver.Resolve(episode.Path, isFolder, null, null, isAbsoluteNaming);
// Resolve from parent folder if it's not the Season folder // Resolve from parent folder if it's not the Season folder
if (episodeInfo == null && episode.Parent.GetType() == typeof(Folder)) var parent = episode.GetParent();
if (episodeInfo == null && parent.GetType() == typeof(Folder))
{ {
episodeInfo = resolver.Resolve(episode.Parent.Path, true, null, null, isAbsoluteNaming); episodeInfo = resolver.Resolve(parent.Path, true, null, null, isAbsoluteNaming);
if (episodeInfo != null) if (episodeInfo != null)
{ {
// add the container // add the container

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

@ -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": "Оптимизирай базата данни"
} }

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Buidar Registre d'Activitat", "TaskCleanActivityLog": "Buidar Registre d'Activitat",
"Undefined": "Indefinit", "Undefined": "Indefinit",
"Forced": "Forçat", "Forced": "Forçat",
"Default": "Defecto" "Default": "Defecto",
"TaskOptimizeDatabaseDescription": "Compacta la base de dades i trunca l'espai lliure. Executar aquesta tasca després descanejar la biblioteca o fer altres canvis que impliquin modificacions a la base de dades pot millorar el rendiment.",
"TaskOptimizeDatabase": "Optimitzar la base de dades"
} }

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Smazat záznam aktivity", "TaskCleanActivityLog": "Smazat záznam aktivity",
"Undefined": "Nedefinované", "Undefined": "Nedefinované",
"Forced": "Vynucené", "Forced": "Vynucené",
"Default": "Výchozí" "Default": "Výchozí",
"TaskOptimizeDatabaseDescription": "Zmenší databázi a odstraní prázdné místo. Spuštění této úlohy po skenování knihovny či jiných změnách databáze může zlepšit výkon.",
"TaskOptimizeDatabase": "Optimalizovat databázi"
} }

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

@ -3,7 +3,7 @@
"AppDeviceValues": "App: {0}, Gerät: {1}", "AppDeviceValues": "App: {0}, Gerät: {1}",
"Application": "Anwendung", "Application": "Anwendung",
"Artists": "Interpreten", "Artists": "Interpreten",
"AuthenticationSucceededWithUserName": "{0} wurde angemeldet", "AuthenticationSucceededWithUserName": "{0} erfolgreich authentifiziert",
"Books": "Bücher", "Books": "Bücher",
"CameraImageUploadedFrom": "Ein neues Kamerafoto wurde von {0} hochgeladen", "CameraImageUploadedFrom": "Ein neues Kamerafoto wurde von {0} hochgeladen",
"Channels": "Kanäle", "Channels": "Kanäle",
@ -16,7 +16,7 @@
"Folders": "Verzeichnisse", "Folders": "Verzeichnisse",
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Album-Interpreten", "HeaderAlbumArtists": "Album-Interpreten",
"HeaderContinueWatching": "Fortsetzen", "HeaderContinueWatching": "Weiterschauen",
"HeaderFavoriteAlbums": "Lieblingsalben", "HeaderFavoriteAlbums": "Lieblingsalben",
"HeaderFavoriteArtists": "Lieblings-Interpreten", "HeaderFavoriteArtists": "Lieblings-Interpreten",
"HeaderFavoriteEpisodes": "Lieblingsepisoden", "HeaderFavoriteEpisodes": "Lieblingsepisoden",
@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Aktivitätsprotokoll aufräumen", "TaskCleanActivityLog": "Aktivitätsprotokoll aufräumen",
"Undefined": "Undefiniert", "Undefined": "Undefiniert",
"Forced": "Erzwungen", "Forced": "Erzwungen",
"Default": "Standard" "Default": "Standard",
"TaskOptimizeDatabaseDescription": "Komprimiert die Datenbank und trimmt den freien Speicherplatz. Die Ausführung dieser Aufgabe nach dem Scannen der Bibliothek oder nach anderen Änderungen, die Datenbankänderungen implizieren, kann die Leistung verbessern.",
"TaskOptimizeDatabase": "Datenbank optimieren"
} }

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Clean Activity Log", "TaskCleanActivityLog": "Clean Activity Log",
"Undefined": "Undefined", "Undefined": "Undefined",
"Forced": "Forced", "Forced": "Forced",
"Default": "Default" "Default": "Default",
"TaskOptimizeDatabaseDescription": "Compacts database and truncates free space. Running this task after scanning the library or doing other changes that imply database modifications might improve performance.",
"TaskOptimizeDatabase": "Optimise database"
} }

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Borrar log de actividades", "TaskCleanActivityLog": "Borrar log de actividades",
"Undefined": "Indefinido", "Undefined": "Indefinido",
"Forced": "Forzado", "Forced": "Forzado",
"Default": "Por Defecto" "Default": "Por Defecto",
"TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.",
"TaskOptimizeDatabase": "Optimización de base de datos"
} }

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

@ -117,5 +117,7 @@
"TaskCleanActivityLog": "Limpiar Registro de Actividades", "TaskCleanActivityLog": "Limpiar Registro de Actividades",
"Undefined": "Sin definir", "Undefined": "Sin definir",
"Forced": "Forzado", "Forced": "Forzado",
"Default": "Por Defecto" "Default": "Por Defecto",
"TaskOptimizeDatabaseDescription": "Compacta la base de datos y restaura el espacio libre. Ejecutar esta tarea después de actualizar las librerías o realizar otros cambios que impliquen modificar las bases de datos puede mejorar la performance.",
"TaskOptimizeDatabase": "Optimización de base de datos"
} }

@ -117,5 +117,7 @@
"Default": "Oletus", "Default": "Oletus",
"TaskCleanActivityLogDescription": "Poistaa määritettyä ikää vanhemmat tapahtumat toimintahistoriasta.", "TaskCleanActivityLogDescription": "Poistaa määritettyä ikää vanhemmat tapahtumat toimintahistoriasta.",
"TaskCleanActivityLog": "Tyhjennä toimintahistoria", "TaskCleanActivityLog": "Tyhjennä toimintahistoria",
"Undefined": "Määrittelemätön" "Undefined": "Määrittelemätön",
"TaskOptimizeDatabaseDescription": "Tiivistää ja puhdistaa tietokannan. Tämän toiminnon suorittaminen kirjastojen skannauksen tai muiden tietokantaan liittyvien muutoksien jälkeen voi parantaa suorituskykyä.",
"TaskOptimizeDatabase": "Optimoi tietokanta"
} }

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Nettoyer le journal d'activité", "TaskCleanActivityLog": "Nettoyer le journal d'activité",
"Undefined": "Non défini", "Undefined": "Non défini",
"Forced": "Forcé", "Forced": "Forcé",
"Default": "Par défaut" "Default": "Par défaut",
"TaskOptimizeDatabaseDescription": "Réduit les espaces vides/inutiles et compacte la base de données. Utiliser cette fonction après une mise à jour de la bibliothèque ou toute autre modification de la base de données peut améliorer les performances du serveur.",
"TaskOptimizeDatabase": "Optimiser la base de données"
} }

@ -88,5 +88,34 @@
"NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo parada", "NotificationOptionVideoPlaybackStopped": "Reproducción de vídeo parada",
"NotificationOptionVideoPlayback": "Reproducción de vídeo iniciada", "NotificationOptionVideoPlayback": "Reproducción de vídeo iniciada",
"NotificationOptionUserLockedOut": "Usuario bloqueado", "NotificationOptionUserLockedOut": "Usuario bloqueado",
"NotificationOptionTaskFailed": "Falla na tarefa axendada" "NotificationOptionTaskFailed": "Falla na tarefa axendada",
"TaskCleanTranscodeDescription": "Borra os arquivos de transcode anteriores a un día.",
"TaskCleanTranscode": "Limpar Directorio de Transcode",
"UserStoppedPlayingItemWithValues": "{0} rematou de reproducir {1} en {2}",
"UserStartedPlayingItemWithValues": "{0} está reproducindo {1} en {2}",
"TaskDownloadMissingSubtitlesDescription": "Busca en internet por subtítulos que faltan baseado na configuración de metadatos.",
"TaskDownloadMissingSubtitles": "Descargar subtítulos que faltan",
"TaskRefreshChannelsDescription": "Refresca a información do canle de internet.",
"TaskRefreshChannels": "Refrescar Canles",
"TaskUpdatePluginsDescription": "Descarga e instala actualizacións para plugins que están configurados para actualizarse automáticamente.",
"TaskRefreshPeopleDescription": "Actualiza os metadatos dos actores e directores na túa libraría multimedia.",
"TaskRefreshPeople": "Refrescar Persoas",
"TaskCleanLogsDescription": "Borra arquivos de rexistro que son mais antigos que {0} días.",
"TaskRefreshLibraryDescription": "Escanea a tua libraría multimedia buscando novos arquivos e refrescando os metadatos.",
"TaskRefreshLibrary": "Escanear Libraría Multimedia",
"TaskRefreshChapterImagesDescription": "Crea previsualizacións para videos que teñen capítulos.",
"TaskRefreshChapterImages": "Extraer Imaxes dos Capítulos",
"TaskCleanCacheDescription": "Borra ficheiros da caché que xa non son necesarios para o sistema.",
"TaskCleanCache": "Limpa Directorio de Caché",
"TaskCleanActivityLogDescription": "Borra as entradas no rexistro de actividade anteriores á data configurada.",
"TasksApplicationCategory": "Aplicación",
"ValueSpecialEpisodeName": "Especial - {0}",
"ValueHasBeenAddedToLibrary": "{0} foi engadido a túa libraría multimedia",
"TasksLibraryCategory": "Libraría",
"TasksMaintenanceCategory": "Mantemento",
"VersionNumber": "Versión {0}",
"UserPolicyUpdatedWithName": "A política de usuario foi actualizada para {0}",
"UserPasswordChangedWithName": "Cambiouse o contrasinal para o usuario {0}",
"UserOnlineFromDevice": "{0} está en liña desde {1}",
"UserOfflineFromDevice": "{0} desconectouse desde {1}"
} }

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Tevékenységnapló törlése", "TaskCleanActivityLog": "Tevékenységnapló törlése",
"Undefined": "Meghatározatlan", "Undefined": "Meghatározatlan",
"Forced": "Kényszerített", "Forced": "Kényszerített",
"Default": "Alapértelmezett" "Default": "Alapértelmezett",
"TaskOptimizeDatabaseDescription": "Tömöríti az adatbázist és csonkolja a szabad helyet. A feladat futtatása a könyvtár beolvasása után, vagy egyéb, adatbázis-módosítást igénylő változtatások végrehajtása javíthatja a teljesítményt.",
"TaskOptimizeDatabase": "Adatbázis optimalizálása"
} }

@ -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}",
@ -118,5 +118,7 @@
"TaskCleanActivityLogDescription": "Elimina gli inserimenti nel registro delle attività più vecchie delletà configurata.", "TaskCleanActivityLogDescription": "Elimina gli inserimenti nel registro delle attività più vecchie delletà configurata.",
"Undefined": "Non Definito", "Undefined": "Non Definito",
"Forced": "Forzato", "Forced": "Forzato",
"Default": "Predefinito" "Default": "Predefinito",
"TaskOptimizeDatabaseDescription": "Compatta Database e tronca spazi liberi. Eseguire questa azione dopo la scansione o dopo aver fatto altri cambiamenti inerenti il database potrebbe aumentarne la performance.",
"TaskOptimizeDatabase": "Ottimizza Database"
} }

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

@ -118,5 +118,7 @@
"TaskRefreshLibraryDescription": "Tasyğyşhanadağy jaña faildardy skanerleidі jäne metaderekterdı jañğyrtady.", "TaskRefreshLibraryDescription": "Tasyğyşhanadağy jaña faildardy skanerleidі jäne metaderekterdı jañğyrtady.",
"TaskRefreshChapterImagesDescription": "Sahnalary bar beineler üşın nobailar jasaidy.", "TaskRefreshChapterImagesDescription": "Sahnalary bar beineler üşın nobailar jasaidy.",
"TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.", "TaskCleanCacheDescription": "Jüiede qajet emes keştelgen faildardy joiady.",
"TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teñşelgen jasynan asqan jazbalary joiady." "TaskCleanActivityLogDescription": "Äreket jūrnalyndağy teñşelgen jasynan asqan jazbalary joiady.",
"TaskOptimizeDatabaseDescription": "Derekqordy qysyp, bos oryndy qysqartady. Būl tapsyrmany tasyğyşhanany skanerlegennen keiın nemese derekqorğa meñzeitın basqa özgertuler ıstelgennen keiın oryndau önımdılıktı damytuy mümkın.",
"TaskOptimizeDatabase": "Derekqordy oñtailandyru"
} }

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "활동내역청소", "TaskCleanActivityLog": "활동내역청소",
"Undefined": "일치하지 않음", "Undefined": "일치하지 않음",
"Forced": "강제하기", "Forced": "강제하기",
"Default": "기본 설정" "Default": "기본 설정",
"TaskOptimizeDatabaseDescription": "데이터베이스를 압축하고 사용 가능한 공간을 늘립니다. 라이브러리를 검색한 후 이 작업을 실행하거나 데이터베이스 수정같은 비슷한 작업을 수행하면 성능이 향상될 수 있습니다.",
"TaskOptimizeDatabase": "데이터베이스 최적화"
} }

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

@ -117,5 +117,7 @@
"Favorites": "പ്രിയങ്കരങ്ങൾ", "Favorites": "പ്രിയങ്കരങ്ങൾ",
"Books": "പുസ്തകങ്ങൾ", "Books": "പുസ്തകങ്ങൾ",
"Genres": "വിഭാഗങ്ങൾ", "Genres": "വിഭാഗങ്ങൾ",
"Channels": "ചാനലുകൾ" "Channels": "ചാനലുകൾ",
"TaskOptimizeDatabaseDescription": "ഡാറ്റാബേസ് ചുരുക്കുകയും സ്വതന്ത്ര ഇടം വെട്ടിച്ചുരുക്കുകയും ചെയ്യുന്നു. ലൈബ്രറി സ്‌കാൻ ചെയ്‌തതിനുശേഷം അല്ലെങ്കിൽ ഡാറ്റാബേസ് പരിഷ്‌ക്കരണങ്ങളെ സൂചിപ്പിക്കുന്ന മറ്റ് മാറ്റങ്ങൾ ചെയ്‌തതിന് ശേഷം ഈ ടാസ്‌ക് പ്രവർത്തിപ്പിക്കുന്നത് പ്രകടനം മെച്ചപ്പെടുത്തും.",
"TaskOptimizeDatabase": "ഡാറ്റാബേസ് ഒപ്റ്റിമൈസ് ചെയ്യുക"
} }

@ -5,7 +5,7 @@
"Artists": "Artis", "Artists": "Artis",
"AuthenticationSucceededWithUserName": "{0} berjaya disahkan", "AuthenticationSucceededWithUserName": "{0} berjaya disahkan",
"Books": "Buku-buku", "Books": "Buku-buku",
"CameraImageUploadedFrom": "Ada gambar dari kamera yang baru dimuat naik melalui {0}", "CameraImageUploadedFrom": "Gambar baharu telah dimuat naik melalui {0}",
"Channels": "Saluran", "Channels": "Saluran",
"ChapterNameValue": "Bab {0}", "ChapterNameValue": "Bab {0}",
"Collections": "Koleksi", "Collections": "Koleksi",
@ -101,5 +101,13 @@
"Forced": "Paksa", "Forced": "Paksa",
"Default": "Asal", "Default": "Asal",
"TaskCleanCache": "Bersihkan Direktori Cache", "TaskCleanCache": "Bersihkan Direktori Cache",
"TaskCleanActivityLogDescription": "Padamkan entri log aktiviti yang lebih tua daripada usia yang dikonfigurasi." "TaskCleanActivityLogDescription": "Padamkan entri log aktiviti yang lebih tua daripada usia yang dikonfigurasi.",
"TaskRefreshPeople": "Segarkan Orang",
"TaskCleanLogsDescription": "Padamkan fail log yang berumur lebih dari {0} hari.",
"TaskCleanLogs": "Bersihkan Direktotri Log",
"TaskRefreshLibraryDescription": "Imbas perpustakaan media untuk mencari fail-fail baru dan menyegarkan metadata.",
"TaskRefreshLibrary": "Imbas Perpustakaan Media",
"TaskRefreshChapterImagesDescription": "Membuat gambaran kecil untuk video yang mempunyai bab.",
"TaskRefreshChapterImages": "Ekstrak Gambar-gambar Bab",
"TaskCleanCacheDescription": "Menghapuskan fail cache yang tidak lagi diperlukan oleh sistem."
} }

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

@ -1,7 +1,7 @@
{ {
"Albums": "Albums", "Albums": "Albums",
"AppDeviceValues": "App: {0}, Apparaat: {1}", "AppDeviceValues": "App: {0}, Apparaat: {1}",
"Application": "Applicatie", "Application": "Toepassing",
"Artists": "Artiesten", "Artists": "Artiesten",
"AuthenticationSucceededWithUserName": "{0} is succesvol geauthenticeerd", "AuthenticationSucceededWithUserName": "{0} is succesvol geauthenticeerd",
"Books": "Boeken", "Books": "Boeken",
@ -25,7 +25,7 @@
"HeaderLiveTV": "Live TV", "HeaderLiveTV": "Live TV",
"HeaderNextUp": "Volgende", "HeaderNextUp": "Volgende",
"HeaderRecordingGroups": "Opnamegroepen", "HeaderRecordingGroups": "Opnamegroepen",
"HomeVideos": "Home video's", "HomeVideos": "Thuis video's",
"Inherit": "Erven", "Inherit": "Erven",
"ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek", "ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
"ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek", "ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
@ -92,11 +92,11 @@
"ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek", "ValueHasBeenAddedToLibrary": "{0} is toegevoegd aan je mediabibliotheek",
"ValueSpecialEpisodeName": "Speciaal - {0}", "ValueSpecialEpisodeName": "Speciaal - {0}",
"VersionNumber": "Versie {0}", "VersionNumber": "Versie {0}",
"TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertitels gebaseerd op metadata configuratie.", "TaskDownloadMissingSubtitlesDescription": "Zoekt op het internet naar missende ondertiteling gebaseerd op metadata configuratie.",
"TaskDownloadMissingSubtitles": "Download missende ondertitels", "TaskDownloadMissingSubtitles": "Download missende ondertiteling",
"TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.", "TaskRefreshChannelsDescription": "Vernieuwt informatie van internet kanalen.",
"TaskRefreshChannels": "Vernieuw Kanalen", "TaskRefreshChannels": "Vernieuw Kanalen",
"TaskCleanTranscodeDescription": "Verwijder transcode bestanden ouder dan 1 dag.", "TaskCleanTranscodeDescription": "Verwijdert transcode bestanden ouder dan 1 dag.",
"TaskCleanLogs": "Log Folder Opschonen", "TaskCleanLogs": "Log Folder Opschonen",
"TaskCleanTranscode": "Transcode Folder Opschonen", "TaskCleanTranscode": "Transcode Folder Opschonen",
"TaskUpdatePluginsDescription": "Download en installeert updates voor plugins waar automatisch updaten aan staat.", "TaskUpdatePluginsDescription": "Download en installeert updates voor plugins waar automatisch updaten aan staat.",
@ -108,15 +108,17 @@
"TaskRefreshLibrary": "Scan Media Bibliotheek", "TaskRefreshLibrary": "Scan Media Bibliotheek",
"TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.", "TaskRefreshChapterImagesDescription": "Maakt thumbnails aan voor videos met hoofdstukken.",
"TaskRefreshChapterImages": "Hoofdstukafbeeldingen Uitpakken", "TaskRefreshChapterImages": "Hoofdstukafbeeldingen Uitpakken",
"TaskCleanCacheDescription": "Verwijder gecachte bestanden die het systeem niet langer nodig heeft.", "TaskCleanCacheDescription": "Verwijdert gecachte bestanden die het systeem niet langer nodig heeft.",
"TaskCleanCache": "Cache Folder Opschonen", "TaskCleanCache": "Cache Folder Opschonen",
"TasksChannelsCategory": "Internet Kanalen", "TasksChannelsCategory": "Internet Kanalen",
"TasksApplicationCategory": "Applicatie", "TasksApplicationCategory": "Applicatie",
"TasksLibraryCategory": "Bibliotheek", "TasksLibraryCategory": "Bibliotheek",
"TasksMaintenanceCategory": "Onderhoud", "TasksMaintenanceCategory": "Onderhoud",
"TaskCleanActivityLogDescription": "Verwijder activiteiten logs ouder dan de ingestelde tijd.", "TaskCleanActivityLogDescription": "Verwijdert activiteiten logs ouder dan de ingestelde tijd.",
"TaskCleanActivityLog": "Leeg activiteiten logboek", "TaskCleanActivityLog": "Leeg activiteiten logboek",
"Undefined": "Niet gedefinieerd", "Undefined": "Niet gedefinieerd",
"Forced": "Geforceerd", "Forced": "Geforceerd",
"Default": "Standaard" "Default": "Standaard",
"TaskOptimizeDatabaseDescription": "Comprimeert de database en trimt vrije ruimte. Het uitvoeren van deze taak kan de prestaties verbeteren, na het scannen van de bibliotheek of andere aanpassingen die invloed hebben op de database.",
"TaskOptimizeDatabase": "Database optimaliseren"
} }

@ -118,5 +118,6 @@
"TaskCleanActivityLog": "Czyść dziennik aktywności", "TaskCleanActivityLog": "Czyść dziennik aktywności",
"Undefined": "Nieustalony", "Undefined": "Nieustalony",
"Forced": "Wymuszony", "Forced": "Wymuszony",
"Default": "Domyślne" "Default": "Domyślne",
"TaskOptimizeDatabase": "Optymalizuj bazę danych"
} }

@ -61,7 +61,7 @@
"NameSeasonUnknown": "Temporada Desconhecida", "NameSeasonUnknown": "Temporada Desconhecida",
"NameSeasonNumber": "Temporada {0}", "NameSeasonNumber": "Temporada {0}",
"NameInstallFailed": "Falha na instalação de {0}", "NameInstallFailed": "Falha na instalação de {0}",
"MusicVideos": "Videoclips", "MusicVideos": "Videoclipes",
"Music": "Música", "Music": "Música",
"MixedContent": "Conteúdo Misto", "MixedContent": "Conteúdo Misto",
"MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada", "MessageServerConfigurationUpdated": "A configuração do servidor foi actualizada",

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Очистить журнал активности", "TaskCleanActivityLog": "Очистить журнал активности",
"Undefined": "Не определено", "Undefined": "Не определено",
"Forced": "Форсир-ые", "Forced": "Форсир-ые",
"Default": "По умолчанию" "Default": "По умолчанию",
"TaskOptimizeDatabaseDescription": "Сжимает базу данных и обрезает свободное место. Выполнение этой задачи после сканирования библиотеки или внесения других изменений, предполагающих модификации базы данных, может повысить производительность.",
"TaskOptimizeDatabase": "Оптимизировать базу данных"
} }

@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Vyčistiť log aktivít", "TaskCleanActivityLog": "Vyčistiť log aktivít",
"Undefined": "Nedefinované", "Undefined": "Nedefinované",
"Forced": "Vynútené", "Forced": "Vynútené",
"Default": "Predvolené" "Default": "Predvolené",
"TaskOptimizeDatabaseDescription": "Zmenší databázu a odstráni prázdne miesto. Spustenie tejto úlohy po skenovaní knižnice alebo po iných zmenách zahŕňajúcich úpravy databáze môže zlepšiť výkon.",
"TaskOptimizeDatabase": "Optimalizovať databázu"
} }

@ -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.",
@ -118,5 +118,7 @@
"TaskCleanActivityLog": "Počisti dnevnik aktivnosti", "TaskCleanActivityLog": "Počisti dnevnik aktivnosti",
"Undefined": "Nedoločen", "Undefined": "Nedoločen",
"Forced": "Prisilno", "Forced": "Prisilno",
"Default": "Privzeto" "Default": "Privzeto",
"TaskOptimizeDatabaseDescription": "Stisne bazo podatkov in uredi prazen prostor. Zagon tega opravila po iskanju predstavnosti ali drugih spremembah ki vplivajo na bazo podatkov lahko izboljša hitrost delovanja.",
"TaskOptimizeDatabase": "Optimiziraj bazo podatkov"
} }

@ -42,8 +42,8 @@
"Sync": "Sinkronizo", "Sync": "Sinkronizo",
"SubtitleDownloadFailureFromForItem": "Titrat deshtuan të shkarkohen nga {0} për {1}", "SubtitleDownloadFailureFromForItem": "Titrat deshtuan të shkarkohen nga {0} për {1}",
"StartupEmbyServerIsLoading": "Serveri Jellyfin po ngarkohet. Ju lutemi provoni përseri pas pak.", "StartupEmbyServerIsLoading": "Serveri Jellyfin po ngarkohet. Ju lutemi provoni përseri pas pak.",
"Songs": "Këngë", "Songs": "Këngët",
"Shows": "Seriale", "Shows": "Serialet",
"ServerNameNeedsToBeRestarted": "{0} duhet të ristartoj", "ServerNameNeedsToBeRestarted": "{0} duhet të ristartoj",
"ScheduledTaskStartedWithName": "{0} filloi", "ScheduledTaskStartedWithName": "{0} filloi",
"ScheduledTaskFailedWithName": "{0} dështoi", "ScheduledTaskFailedWithName": "{0} dështoi",
@ -74,9 +74,9 @@
"NameSeasonUnknown": "Sezon i panjohur", "NameSeasonUnknown": "Sezon i panjohur",
"NameSeasonNumber": "Sezoni {0}", "NameSeasonNumber": "Sezoni {0}",
"NameInstallFailed": "Instalimi i {0} dështoi", "NameInstallFailed": "Instalimi i {0} dështoi",
"MusicVideos": "Video muzikore", "MusicVideos": "Videot muzikore",
"Music": "Muzikë", "Music": "Muzikë",
"Movies": "Filma", "Movies": "Filmat",
"MixedContent": "Përmbajtje e përzier", "MixedContent": "Përmbajtje e përzier",
"MessageServerConfigurationUpdated": "Konfigurimet e serverit u përditësuan", "MessageServerConfigurationUpdated": "Konfigurimet e serverit u përditësuan",
"MessageNamedServerConfigurationUpdatedWithValue": "Seksioni i konfigurimit të serverit {0} u përditësua", "MessageNamedServerConfigurationUpdatedWithValue": "Seksioni i konfigurimit të serverit {0} u përditësua",
@ -97,20 +97,27 @@
"HeaderFavoriteAlbums": "Albumet e preferuar", "HeaderFavoriteAlbums": "Albumet e preferuar",
"HeaderContinueWatching": "Vazhdo të shikosh", "HeaderContinueWatching": "Vazhdo të shikosh",
"HeaderAlbumArtists": "Artistët e albumeve", "HeaderAlbumArtists": "Artistët e albumeve",
"Genres": "Zhanre", "Genres": "Zhanret",
"Folders": "Dosje", "Folders": "Skedarët",
"Favorites": "Të preferuara", "Favorites": "Të preferuarat",
"FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}", "FailedLoginAttemptWithUserName": "Përpjekja për hyrje dështoi nga {0}",
"DeviceOnlineWithName": "{0} u lidh", "DeviceOnlineWithName": "{0} u lidh",
"DeviceOfflineWithName": "{0} u shkëput", "DeviceOfflineWithName": "{0} u shkëput",
"Collections": "Koleksione", "Collections": "Koleksionet",
"ChapterNameValue": "Kapituj", "ChapterNameValue": "Kapituj",
"Channels": "Kanale", "Channels": "Kanalet",
"CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}", "CameraImageUploadedFrom": "Një foto e re nga kamera u ngarkua nga {0}",
"Books": "Libra", "Books": "Librat",
"AuthenticationSucceededWithUserName": "{0} u identifikua me sukses", "AuthenticationSucceededWithUserName": "{0} u identifikua me sukses",
"Artists": "Artistë", "Artists": "Artistët",
"Application": "Aplikacioni", "Application": "Aplikacioni",
"AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}", "AppDeviceValues": "Aplikacioni: {0}, Pajisja: {1}",
"Albums": "Albume" "Albums": "Albumet",
"TaskCleanActivityLogDescription": "Pastro të dhënat mbi aktivitetin më të vjetra sesa koha e përcaktuar.",
"TaskCleanActivityLog": "Pastro të dhënat mbi aktivitetin",
"Undefined": "I papërcaktuar",
"Forced": "I detyruar",
"Default": "Parazgjedhur",
"TaskOptimizeDatabaseDescription": "Kompakton bazën e të dhënave dhe shkurton hapësirën e lirë. Drejtimi i kësaj detyre pasi skanoni bibliotekën ose bëni ndryshime të tjera që nënkuptojnë modifikime të bazës së të dhënave mund të përmirësojë performancën.",
"TaskOptimizeDatabase": "Optimizo databazën"
} }

@ -117,5 +117,7 @@
"TaskCleanActivityLog": "செயல்பாட்டு பதிவை அழி", "TaskCleanActivityLog": "செயல்பாட்டு பதிவை அழி",
"Undefined": "வரையறுக்கப்படாத", "Undefined": "வரையறுக்கப்படாத",
"Forced": "கட்டாயப்படுத்தப்பட்டது", "Forced": "கட்டாயப்படுத்தப்பட்டது",
"Default": "இயல்புநிலை" "Default": "இயல்புநிலை",
"TaskOptimizeDatabaseDescription": "தரவுத்தளத்தை சுருக்கி, இலவச இடத்தை குறைக்கிறது. நூலகத்தை ஸ்கேன் செய்தபின் அல்லது தரவுத்தள மாற்றங்களைக் குறிக்கும் பிற மாற்றங்களைச் செய்தபின் இந்த பணியை இயக்குவது செயல்திறனை மேம்படுத்தக்கூடும்.",
"TaskOptimizeDatabase": "தரவுத்தளத்தை மேம்படுத்தவும்"
} }

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

@ -117,5 +117,7 @@
"TaskCleanActivityLog": "Xóa Nhật Ký Hoạt Động", "TaskCleanActivityLog": "Xóa Nhật Ký Hoạt Động",
"Undefined": "Không Xác Định", "Undefined": "Không Xác Định",
"Forced": "Bắt Buộc", "Forced": "Bắt Buộc",
"Default": "Mặc Định" "Default": "Mặc Định",
"TaskOptimizeDatabaseDescription": "Thu gọn cơ sở dữ liệu và cắt bớt dung lượng trống. Chạy tác vụ này sau khi quét thư viện hoặc thực hiện các thay đổi khác ngụ ý sửa đổi cơ sở dữ liệu có thể cải thiện hiệu suất.",
"TaskOptimizeDatabase": "Tối ưu hóa cơ sở dữ liệu"
} }

@ -118,5 +118,7 @@
"TaskCleanActivityLogDescription": "删除早于设置时间的活动日志条目。", "TaskCleanActivityLogDescription": "删除早于设置时间的活动日志条目。",
"Undefined": "未定义", "Undefined": "未定义",
"Forced": "强制的", "Forced": "强制的",
"Default": "默认" "Default": "默认",
"TaskOptimizeDatabaseDescription": "压缩数据库并优化可用空间,在扫描库或执行其他数据库修改后运行此任务可能会提高性能。",
"TaskOptimizeDatabase": "优化数据库"
} }

@ -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
string[] parts = line.Split(','); && int.TryParse(parts[1], NumberStyles.Integer, CultureInfo.InvariantCulture, out var value))
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));
var name = parts[0]; }
dict.Add(name, new ParentalRating(name, value));
}
#if DEBUG #if DEBUG
else else
{ {
_logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode); _logger.LogWarning("Malformed line in ratings file for country {CountryCode}", countryCode);
}
#endif
} }
#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))
{
continue;
}
if (parts.Length == 5) string[] threeletterNames;
if (string.IsNullOrWhiteSpace(parts[1]))
{
threeletterNames = new[] { parts[0] };
}
else
{ {
string name = parts[3]; threeletterNames = new[] { parts[0], parts[1] };
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
});
} }
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)

@ -147,7 +147,7 @@ namespace Emby.Server.Implementations.Playlists
playlist.SetMediaType(options.MediaType); playlist.SetMediaType(options.MediaType);
parentFolder.AddChild(playlist, CancellationToken.None); parentFolder.AddChild(playlist);
await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, CancellationToken.None) await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, CancellationToken.None)
.ConfigureAwait(false); .ConfigureAwait(false);

@ -65,7 +65,7 @@ namespace Emby.Server.Implementations.ScheduledTasks.Tasks
throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}"); throw new Exception($"Activity Log Retention days must be at least 0. Currently: {retentionDays}");
} }
var startDate = DateTime.UtcNow.AddDays(retentionDays.Value * -1); var startDate = DateTime.UtcNow.AddDays(-retentionDays.Value);
return _activityManager.CleanAsync(startDate); return _activityManager.CleanAsync(startDate);
} }

@ -1380,7 +1380,7 @@ namespace Jellyfin.Api.Controllers
} }
else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase))
{ {
var outputFmp4HeaderArg = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) switch var outputFmp4HeaderArg = OperatingSystem.IsWindows() switch
{ {
// on Windows, the path of fmp4 header file needs to be configured // on Windows, the path of fmp4 header file needs to be configured
true => " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"", true => " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"",
@ -1496,7 +1496,7 @@ namespace Jellyfin.Api.Controllers
args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
} }
args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions, true); args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions);
return args; return args;
} }

@ -228,42 +228,6 @@ namespace Jellyfin.Api.Controllers
return GetResult(items, user, limit, dtoOptions); return GetResult(items, user, limit, dtoOptions);
} }
/// <summary>
/// Creates an instant playlist based on a given genre.
/// </summary>
/// <param name="id">The item id.</param>
/// <param name="userId">Optional. Filter by user id, and attach user data.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
/// <param name="enableImages">Optional. Include image information in output.</param>
/// <param name="enableUserData">Optional. Include user data.</param>
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <response code="200">Instant playlist returned.</response>
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
[HttpGet("MusicGenres/{id}/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenreById(
[FromRoute, Required] Guid id,
[FromQuery] Guid? userId,
[FromQuery] int? limit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool? enableImages,
[FromQuery] bool? enableUserData,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{
var item = _libraryManager.GetItemById(id);
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes!);
var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
return GetResult(items, user, limit, dtoOptions);
}
/// <summary> /// <summary>
/// Creates an instant playlist based on a given item. /// Creates an instant playlist based on a given item.
/// </summary> /// </summary>
@ -352,8 +316,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns> /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the playlist items.</returns>
[HttpGet("MusicGenres/InstantMix")] [HttpGet("MusicGenres/InstantMix")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[Obsolete("Use GetInstantMixFromMusicGenres instead")] public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenreById(
public ActionResult<QueryResult<BaseItemDto>> GetInstantMixFromMusicGenreById2(
[FromQuery, Required] Guid id, [FromQuery, Required] Guid id,
[FromQuery] Guid? userId, [FromQuery] Guid? userId,
[FromQuery] int? limit, [FromQuery] int? limit,
@ -363,15 +326,15 @@ namespace Jellyfin.Api.Controllers
[FromQuery] int? imageTypeLimit, [FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes) [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes)
{ {
return GetInstantMixFromMusicGenreById( var item = _libraryManager.GetItemById(id);
id, var user = userId.HasValue && !userId.Equals(Guid.Empty)
userId, ? _userManager.GetUserById(userId.Value)
limit, : null;
fields, var dtoOptions = new DtoOptions { Fields = fields }
enableImages, .AddClientFields(Request)
enableUserData, .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes!);
imageTypeLimit, var items = _musicManager.GetInstantMixFromItem(item, user, dtoOptions);
enableImageTypes); return GetResult(items, user, limit, dtoOptions);
} }
private QueryResult<BaseItemDto> GetResult(List<BaseItem> items, User? user, int? limit, DtoOptions dtoOptions) private QueryResult<BaseItemDto> GetResult(List<BaseItem> items, User? user, int? limit, DtoOptions dtoOptions)

@ -161,6 +161,11 @@ namespace Jellyfin.Api.Controllers
liveStreamId) liveStreamId)
.ConfigureAwait(false); .ConfigureAwait(false);
if (info.ErrorCode != null)
{
return info;
}
if (profile != null) if (profile != null)
{ {
// set device specific data // set device specific data
@ -302,27 +307,12 @@ namespace Jellyfin.Api.Controllers
/// </summary> /// </summary>
/// <param name="size">The bitrate. Defaults to 102400.</param> /// <param name="size">The bitrate. Defaults to 102400.</param>
/// <response code="200">Test buffer returned.</response> /// <response code="200">Test buffer returned.</response>
/// <response code="400">Size has to be a numer between 0 and 10,000,000.</response>
/// <returns>A <see cref="FileResult"/> with specified bitrate.</returns> /// <returns>A <see cref="FileResult"/> with specified bitrate.</returns>
[HttpGet("Playback/BitrateTest")] [HttpGet("Playback/BitrateTest")]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[Produces(MediaTypeNames.Application.Octet)]
[ProducesFile(MediaTypeNames.Application.Octet)] [ProducesFile(MediaTypeNames.Application.Octet)]
public ActionResult GetBitrateTestBytes([FromQuery] int size = 102400) public ActionResult GetBitrateTestBytes([FromQuery][Range(1, 100_000_000, ErrorMessage = "The requested size must be greater than or equal to {1} and less than or equal to {2}")] int size = 102400)
{ {
const int MaxSize = 10_000_000;
if (size <= 0)
{
return BadRequest($"The requested size ({size}) is equal to or smaller than 0.");
}
if (size > MaxSize)
{
return BadRequest($"The requested size ({size}) is larger than the max allowed value ({MaxSize}).");
}
byte[] buffer = ArrayPool<byte>.Shared.Rent(size); byte[] buffer = ArrayPool<byte>.Shared.Rent(size);
try try
{ {

@ -366,8 +366,7 @@ namespace Jellyfin.Api.Controllers
else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase)) else if (string.Equals(segmentFormat, "mp4", StringComparison.OrdinalIgnoreCase))
{ {
var outputFmp4HeaderArg = string.Empty; var outputFmp4HeaderArg = string.Empty;
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); if (OperatingSystem.IsWindows())
if (isWindows)
{ {
// on Windows, the path of fmp4 header file needs to be configured // on Windows, the path of fmp4 header file needs to be configured
outputFmp4HeaderArg = " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\""; outputFmp4HeaderArg = " -hls_fmp4_init_filename \"" + outputPrefix + "-1" + outputExtension + "\"";
@ -485,7 +484,7 @@ namespace Jellyfin.Api.Controllers
args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture); args += " -ar " + state.OutputAudioSampleRate.Value.ToString(CultureInfo.InvariantCulture);
} }
args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions, true); args += _encodingHelper.GetAudioFilterParam(state, _encodingOptions);
return args; return args;
} }

@ -296,6 +296,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param> /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
/// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param> /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
/// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param> /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
/// <param name="maxWidth">Optional. The maximum horizontal resolution of the encoded video.</param>
/// <param name="maxHeight">Optional. The maximum vertical resolution of the encoded video.</param>
/// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param> /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
/// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param> /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
/// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param> /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
@ -352,6 +354,8 @@ namespace Jellyfin.Api.Controllers
[FromQuery] long? startTimeTicks, [FromQuery] long? startTimeTicks,
[FromQuery] int? width, [FromQuery] int? width,
[FromQuery] int? height, [FromQuery] int? height,
[FromQuery] int? maxWidth,
[FromQuery] int? maxHeight,
[FromQuery] int? videoBitRate, [FromQuery] int? videoBitRate,
[FromQuery] int? subtitleStreamIndex, [FromQuery] int? subtitleStreamIndex,
[FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] SubtitleDeliveryMethod? subtitleMethod,
@ -407,6 +411,8 @@ namespace Jellyfin.Api.Controllers
StartTimeTicks = startTimeTicks, StartTimeTicks = startTimeTicks,
Width = width, Width = width,
Height = height, Height = height,
MaxWidth = maxWidth,
MaxHeight = maxHeight,
VideoBitRate = videoBitRate, VideoBitRate = videoBitRate,
SubtitleStreamIndex = subtitleStreamIndex, SubtitleStreamIndex = subtitleStreamIndex,
SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode, SubtitleMethod = subtitleMethod ?? SubtitleDeliveryMethod.Encode,
@ -550,6 +556,8 @@ namespace Jellyfin.Api.Controllers
/// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param> /// <param name="startTimeTicks">Optional. Specify a starting offset, in ticks. 1 tick = 10000 ms.</param>
/// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param> /// <param name="width">Optional. The fixed horizontal resolution of the encoded video.</param>
/// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param> /// <param name="height">Optional. The fixed vertical resolution of the encoded video.</param>
/// <param name="maxWidth">Optional. The maximum horizontal resolution of the encoded video.</param>
/// <param name="maxHeight">Optional. The maximum vertical resolution of the encoded video.</param>
/// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param> /// <param name="videoBitRate">Optional. Specify a video bitrate to encode to, e.g. 500000. If omitted this will be left to encoder defaults.</param>
/// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param> /// <param name="subtitleStreamIndex">Optional. The index of the subtitle stream to use. If omitted no subtitles will be used.</param>
/// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param> /// <param name="subtitleMethod">Optional. Specify the subtitle delivery method.</param>
@ -606,6 +614,8 @@ namespace Jellyfin.Api.Controllers
[FromQuery] long? startTimeTicks, [FromQuery] long? startTimeTicks,
[FromQuery] int? width, [FromQuery] int? width,
[FromQuery] int? height, [FromQuery] int? height,
[FromQuery] int? maxWidth,
[FromQuery] int? maxHeight,
[FromQuery] int? videoBitRate, [FromQuery] int? videoBitRate,
[FromQuery] int? subtitleStreamIndex, [FromQuery] int? subtitleStreamIndex,
[FromQuery] SubtitleDeliveryMethod? subtitleMethod, [FromQuery] SubtitleDeliveryMethod? subtitleMethod,
@ -657,6 +667,8 @@ namespace Jellyfin.Api.Controllers
startTimeTicks, startTimeTicks,
width, width,
height, height,
maxWidth,
maxHeight,
videoBitRate, videoBitRate,
subtitleStreamIndex, subtitleStreamIndex,
subtitleMethod, subtitleMethod,

@ -99,8 +99,7 @@ namespace Jellyfin.Api.Helpers
return fmp4InitFileName; return fmp4InitFileName;
} }
var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); if (OperatingSystem.IsWindows())
if (isWindows)
{ {
// on Windows // on Windows
// #EXT-X-MAP:URI="X:\transcodes\prefix-1.mp4" // #EXT-X-MAP:URI="X:\transcodes\prefix-1.mp4"

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

@ -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.7" /> <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>

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

@ -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>
@ -22,8 +18,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BlurHashSharp" Version="1.2.0" /> <PackageReference Include="BlurHashSharp" Version="1.2.0" />
<PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.2.0" /> <PackageReference Include="BlurHashSharp.SkiaSharp" Version="1.2.0" />
<PackageReference Include="SkiaSharp" Version="2.80.2" /> <PackageReference Include="SkiaSharp" Version="2.80.3" />
<PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.80.2" /> <PackageReference Include="SkiaSharp.NativeAssets.Linux" Version="2.80.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

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

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

@ -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-->
@ -27,13 +19,13 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Linq.Async" Version="5.0.0" /> <PackageReference Include="System.Linq.Async" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.7" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="5.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.7" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="5.0.8" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.7"> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.8">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.7"> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.8">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

@ -303,7 +303,7 @@ namespace Jellyfin.Server.Extensions
{ {
description.TryGetMethodInfo(out MethodInfo methodInfo); description.TryGetMethodInfo(out MethodInfo methodInfo);
// Attribute name, method name, none. // Attribute name, method name, none.
return description?.ActionDescriptor?.AttributeRouteInfo?.Name return description?.ActionDescriptor.AttributeRouteInfo?.Name
?? methodInfo?.Name ?? methodInfo?.Name
?? null; ?? null;
}); });
@ -341,7 +341,7 @@ namespace Jellyfin.Server.Extensions
{ {
foreach (var address in host.GetAddresses()) foreach (var address in host.GetAddresses())
{ {
AddIpAddress(config, options, addr.Address, addr.PrefixLength); AddIpAddress(config, options, address, address.AddressFamily == AddressFamily.InterNetwork ? 32 : 128);
} }
} }
} }
@ -397,7 +397,7 @@ namespace Jellyfin.Server.Extensions
Type = "object", Type = "object",
Properties = typeof(ImageType).GetEnumNames().ToDictionary( Properties = typeof(ImageType).GetEnumNames().ToDictionary(
name => name, name => name,
name => new OpenApiSchema _ => new OpenApiSchema
{ {
Type = "object", Type = "object",
AdditionalProperties = new OpenApiSchema AdditionalProperties = new OpenApiSchema

@ -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>
@ -38,15 +33,15 @@
<PackageReference Include="CommandLineParser" Version="2.8.0" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.7" /> <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="5.0.8" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.7" /> <PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="5.0.8" />
<PackageReference Include="prometheus-net" Version="4.1.1" /> <PackageReference Include="prometheus-net" Version="4.2.0" />
<PackageReference Include="prometheus-net.AspNetCore" Version="4.1.1" /> <PackageReference Include="prometheus-net.AspNetCore" Version="4.2.0" />
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" /> <PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />
<PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" /> <PackageReference Include="Serilog.Enrichers.Thread" Version="3.1.0" />
<PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" /> <PackageReference Include="Serilog.Settings.Configuration" Version="3.1.0" />
<PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" /> <PackageReference Include="Serilog.Sinks.Async" Version="1.5.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" /> <PackageReference Include="Serilog.Sinks.Console" Version="4.0.0" />
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" /> <PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageReference Include="Serilog.Sinks.Graylog" Version="2.2.2" /> <PackageReference Include="Serilog.Sinks.Graylog" Version="2.2.2" />
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.4" /> <PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.0.4" />

@ -58,9 +58,12 @@ namespace Jellyfin.Server.Middleware
return; return;
} }
if (!startsWithBaseUrl) if (!startsWithBaseUrl
|| localPath.Length == baseUrlPrefix.Length
// Local path is /baseUrl/
|| (localPath.Length == baseUrlPrefix.Length + 1 && localPath[^1] == '/'))
{ {
// Always redirect back to the default path if the base prefix is invalid or missing // Always redirect back to the default path if the base prefix is invalid, missing, or is the full path.
_logger.LogDebug("Normalizing an URL at {LocalPath}", localPath); _logger.LogDebug("Normalizing an URL at {LocalPath}", localPath);
httpContext.Response.Redirect(baseUrlPrefix + "/" + _configuration[ConfigurationExtensions.DefaultRedirectKey]); httpContext.Response.Redirect(baseUrlPrefix + "/" + _configuration[ConfigurationExtensions.DefaultRedirectKey]);
return; return;

@ -137,11 +137,6 @@ namespace Jellyfin.Server.Middleware
private string NormalizeExceptionMessage(string msg) private string NormalizeExceptionMessage(string msg)
{ {
if (msg == null)
{
return string.Empty;
}
// Strip any information we don't want to reveal // Strip any information we don't want to reveal
return msg.Replace( return msg.Replace(
_configuration.ApplicationPaths.ProgramSystemPath, _configuration.ApplicationPaths.ProgramSystemPath,

@ -40,7 +40,7 @@ namespace Jellyfin.Server.Migrations
.Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m)) .Select(m => ActivatorUtilities.CreateInstance(host.ServiceProvider, m))
.OfType<IMigrationRoutine>() .OfType<IMigrationRoutine>()
.ToArray(); .ToArray();
var migrationOptions = ((IConfigurationManager)host.ConfigurationManager).GetConfiguration<MigrationOptions>(MigrationsListStore.StoreKey); var migrationOptions = host.ConfigurationManager.GetConfiguration<MigrationOptions>(MigrationsListStore.StoreKey);
if (!host.ConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0) if (!host.ConfigurationManager.Configuration.IsStartupWizardCompleted && migrationOptions.Applied.Count == 0)
{ {

@ -92,7 +92,7 @@ namespace Jellyfin.Server.Migrations.Routines
if (entry[6].SQLiteType != SQLiteType.Null && !Guid.TryParse(entry[6].ToString(), out guid)) if (entry[6].SQLiteType != SQLiteType.Null && !Guid.TryParse(entry[6].ToString(), out guid))
{ {
// This is not a valid Guid, see if it is an internal ID from an old Emby schema // This is not a valid Guid, see if it is an internal ID from an old Emby schema
_logger.LogWarning("Invalid Guid in UserId column: ", entry[6].ToString()); _logger.LogWarning("Invalid Guid in UserId column: {Guid}", entry[6].ToString());
using var statement = userDbConnection.PrepareStatement("SELECT guid FROM LocalUsersv2 WHERE Id=@Id"); using var statement = userDbConnection.PrepareStatement("SELECT guid FROM LocalUsersv2 WHERE Id=@Id");
statement.TryBind("@Id", entry[6].ToString()); statement.TryBind("@Id", entry[6].ToString());

@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text.Json; using System.Text.Json;

@ -5,7 +5,6 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -121,11 +120,11 @@ namespace Jellyfin.Server
// Log uncaught exceptions to the logging instead of std error // Log uncaught exceptions to the logging instead of std error
AppDomain.CurrentDomain.UnhandledException -= UnhandledExceptionToConsole; AppDomain.CurrentDomain.UnhandledException -= UnhandledExceptionToConsole;
AppDomain.CurrentDomain.UnhandledException += (sender, e) AppDomain.CurrentDomain.UnhandledException += (_, e)
=> _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception"); => _logger.LogCritical((Exception)e.ExceptionObject, "Unhandled Exception");
// Intercept Ctrl+C and Ctrl+Break // Intercept Ctrl+C and Ctrl+Break
Console.CancelKeyPress += (sender, e) => Console.CancelKeyPress += (_, e) =>
{ {
if (_tokenSource.IsCancellationRequested) if (_tokenSource.IsCancellationRequested)
{ {
@ -139,7 +138,7 @@ namespace Jellyfin.Server
}; };
// Register a SIGTERM handler // Register a SIGTERM handler
AppDomain.CurrentDomain.ProcessExit += (sender, e) => AppDomain.CurrentDomain.ProcessExit += (_, _) =>
{ {
if (_tokenSource.IsCancellationRequested) if (_tokenSource.IsCancellationRequested)
{ {
@ -180,7 +179,7 @@ namespace Jellyfin.Server
"The server is expected to host the web client, but the provided content directory is either " + "The server is expected to host the web client, but the provided content directory is either " +
$"invalid or empty: {webContentPath}. If you do not want to host the web client with the " + $"invalid or empty: {webContentPath}. If you do not want to host the web client with the " +
"server, you may set the '--nowebclient' command line flag, or set" + "server, you may set the '--nowebclient' command line flag, or set" +
$"'{MediaBrowser.Controller.Extensions.ConfigurationExtensions.HostWebClientKey}=false' in your config settings."); $"'{ConfigurationExtensions.HostWebClientKey}=false' in your config settings.");
} }
} }
@ -318,8 +317,8 @@ namespace Jellyfin.Server
} }
} }
// Bind to unix socket (only on macOS and Linux) // Bind to unix socket (only on unix systems)
if (startupConfig.UseUnixSocket() && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (startupConfig.UseUnixSocket() && Environment.OSVersion.Platform == PlatformID.Unix)
{ {
var socketPath = startupConfig.GetUnixSocketPath(); var socketPath = startupConfig.GetUnixSocketPath();
if (string.IsNullOrEmpty(socketPath)) if (string.IsNullOrEmpty(socketPath))
@ -404,7 +403,7 @@ namespace Jellyfin.Server
{ {
if (options.DataDir != null if (options.DataDir != null
|| Directory.Exists(Path.Combine(dataDir, "config")) || Directory.Exists(Path.Combine(dataDir, "config"))
|| RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) || OperatingSystem.IsWindows())
{ {
// Hang config folder off already set dataDir // Hang config folder off already set dataDir
configDir = Path.Combine(dataDir, "config"); configDir = Path.Combine(dataDir, "config");
@ -442,7 +441,7 @@ namespace Jellyfin.Server
if (string.IsNullOrEmpty(cacheDir)) if (string.IsNullOrEmpty(cacheDir))
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (OperatingSystem.IsWindows())
{ {
// Hang cache folder off already set dataDir // Hang cache folder off already set dataDir
cacheDir = Path.Combine(dataDir, "cache"); cacheDir = Path.Combine(dataDir, "cache");
@ -543,7 +542,7 @@ namespace Jellyfin.Server
// Get a stream of the resource contents // Get a stream of the resource contents
// NOTE: The .csproj name is used instead of the assembly name in the resource path // NOTE: The .csproj name is used instead of the assembly name in the resource path
const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json"; const string ResourcePath = "Jellyfin.Server.Resources.Configuration.logging.json";
await using Stream? resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath) await using Stream resource = typeof(Program).Assembly.GetManifestResourceStream(ResourcePath)
?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'"); ?? throw new InvalidOperationException($"Invalid resource path: '{ResourcePath}'");
// Copy the resource contents to the expected file path for the config file // Copy the resource contents to the expected file path for the config file

@ -40,7 +40,7 @@ namespace MediaBrowser.Common.Extensions
// Add an event handler for the process exit event // Add an event handler for the process exit event
var tcs = new TaskCompletionSource<bool>(); var tcs = new TaskCompletionSource<bool>();
process.Exited += (sender, args) => tcs.TrySetResult(true); process.Exited += (_, _) => tcs.TrySetResult(true);
// Return immediately if the process has already exited // Return immediately if the process has already exited
if (process.HasExitedSafe()) if (process.HasExitedSafe())

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

@ -79,16 +79,11 @@ namespace MediaBrowser.Common.Net
/// </summary> /// </summary>
public override byte PrefixLength public override byte PrefixLength
{ {
get get => (byte)(ResolveHost() ? 128 : 32);
{
return (byte)(ResolveHost() ? 128 : 32);
}
set // Not implemented, as a host object can only have a prefix length of 128 (IPv6) or 32 (IPv4) prefix length,
{ // which is automatically determined by it's IP type. Anything else is meaningless.
// Not implemented, as a host object can only have a prefix length of 128 (IPv6) or 32 (IPv4) prefix length, set => throw new NotImplementedException();
// which is automatically determined by it's IP type. Anything else is meaningless.
}
} }
/// <summary> /// <summary>

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

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

Loading…
Cancel
Save