Merge remote-tracking branch 'origin/master' into media-attachments-clean

This commit is contained in:
Andrew Mahone 2019-11-04 10:03:18 -05:00
commit 1600d5b53f
143 changed files with 1776 additions and 1217 deletions

View File

@ -245,7 +245,7 @@ jobs:
inputs: inputs:
targetType: 'filePath' # Optional. Options: filePath, inline targetType: 'filePath' # Optional. Options: filePath, inline
filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath filePath: ./deployment/windows/build-jellyfin.ps1 # Required when targetType == FilePath
arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory) arguments: -InstallFFMPEG -InstallNSSM -MakeNSIS -InstallTrayApp -UXLocation $(Agent.TempDirectory)\jellyfin-ux -InstallLocation $(build.artifactstagingdirectory)
#script: '# Write your PowerShell commands here.Write-Host Hello World' # Required when targetType == Inline #script: '# Write your PowerShell commands here.Write-Host Hello World' # Required when targetType == Inline
errorActionPreference: 'stop' # Optional. Options: stop, continue, silentlyContinue errorActionPreference: 'stop' # Optional. Options: stop, continue, silentlyContinue
#failOnStderr: false # Optional #failOnStderr: false # Optional

View File

@ -1,8 +1,59 @@
srpm: VERSION := $(shell sed -ne '/^Version:/s/.* *//p' \
dnf -y install git deployment/fedora-package-x64/pkg-src/jellyfin.spec)
git submodule update --init --recursive
deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz:
curl -f -L -o deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz \
https://github.com/jellyfin/jellyfin-web/archive/v$(VERSION).tar.gz \
|| curl -f -L -o deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz \
https://github.com/jellyfin/jellyfin-web/archive/master.tar.gz \
srpm: deployment/fedora-package-x64/pkg-src/jellyfin-web-$(VERSION).tar.gz
cd deployment/fedora-package-x64; \ cd deployment/fedora-package-x64; \
./create_tarball.sh; \ SOURCE_DIR=../.. \
WORKDIR="$${PWD}"; \
package_temporary_dir="$${WORKDIR}/pkg-dist-tmp"; \
pkg_src_dir="$${WORKDIR}/pkg-src"; \
GNU_TAR=1; \
tar \
--transform "s,^\.,jellyfin-$(VERSION)," \
--exclude='.git*' \
--exclude='**/.git' \
--exclude='**/.hg' \
--exclude='**/.vs' \
--exclude='**/.vscode' \
--exclude='deployment' \
--exclude='**/bin' \
--exclude='**/obj' \
--exclude='**/.nuget' \
--exclude='*.deb' \
--exclude='*.rpm' \
-czf "pkg-src/jellyfin-$(VERSION).tar.gz" \
-C $${SOURCE_DIR} ./ || GNU_TAR=0; \
if [ $${GNU_TAR} -eq 0 ]; then \
package_temporary_dir="$$(mktemp -d)"; \
mkdir -p "$${package_temporary_dir}/jellyfin"; \
tar \
--exclude='.git*' \
--exclude='**/.git' \
--exclude='**/.hg' \
--exclude='**/.vs' \
--exclude='**/.vscode' \
--exclude='deployment' \
--exclude='**/bin' \
--exclude='**/obj' \
--exclude='**/.nuget' \
--exclude='*.deb' \
--exclude='*.rpm' \
-czf "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz" \
-C $${SOURCE_DIR} ./; \
mkdir -p "$${package_temporary_dir}/jellyfin-$(VERSION)"; \
tar -xzf "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz" \
-C "$${package_temporary_dir}/jellyfin-$(VERSION); \
rm -f "$${package_temporary_dir}/jellyfin/jellyfin-$(VERSION).tar.gz"; \
tar -czf "$${SOURCE_DIR}/SOURCES/pkg-src/jellyfin-$(VERSION).tar.gz" \
-C "$${package_temporary_dir}" "jellyfin-$(VERSION); \
rm -rf $${package_temporary_dir}; \
fi; \
rpmbuild -bs pkg-src/jellyfin.spec \ rpmbuild -bs pkg-src/jellyfin.spec \
--define "_sourcedir $$PWD/pkg-src/" \ --define "_sourcedir $$PWD/pkg-src/" \
--define "_srcrpmdir $(outdir)" --define "_srcrpmdir $(outdir)"

1
.github/stale.yml vendored
View File

@ -11,6 +11,7 @@ exemptLabels:
- future - future
- feature - feature
- enhancement - enhancement
- confirmed
# Label to use when marking an issue as stale # Label to use when marking an issue as stale
staleLabel: stale 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

3
.gitignore vendored
View File

@ -268,3 +268,6 @@ doc/
# Deployment artifacts # Deployment artifacts
dist dist
*.exe *.exe
# BenchmarkDotNet artifacts
BenchmarkDotNet.Artifacts

2
.vscode/launch.json vendored
View File

@ -10,7 +10,7 @@
"request": "launch", "request": "launch",
"preLaunchTask": "build", "preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path. // If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/netcoreapp2.1/jellyfin.dll", "program": "${workspaceFolder}/Jellyfin.Server/bin/Debug/netcoreapp3.0/jellyfin.dll",
"args": [], "args": [],
"cwd": "${workspaceFolder}/Jellyfin.Server", "cwd": "${workspaceFolder}/Jellyfin.Server",
// For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window // For more information about the 'console' field, see https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md#console-terminal-window

View File

@ -1,8 +1,8 @@
ARG DOTNET_VERSION=2.2 ARG DOTNET_VERSION=3.0
ARG FFMPEG_VERSION=latest ARG FFMPEG_VERSION=latest
FROM node:alpine as web-builder FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=v10.5.0 ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl \ RUN apk add curl \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \ && cd jellyfin-web-* \
@ -10,33 +10,39 @@ RUN apk add curl \
&& yarn build \ && yarn build \
&& mv dist /dist && mv dist /dist
FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION}-buster as builder
WORKDIR /repo WORKDIR /repo
COPY . . COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none" RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin" --self-contained --runtime linux-x64 "-p:GenerateDocumentationFile=false;DebugSymbols=false;DebugType=none"
FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg FROM jellyfin/ffmpeg:${FFMPEG_VERSION} as ffmpeg
FROM debian:buster-slim
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION} COPY --from=ffmpeg /opt/ffmpeg /opt/ffmpeg
COPY --from=ffmpeg / /
COPY --from=builder /jellyfin /jellyfin COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web COPY --from=web-builder /dist /jellyfin/jellyfin-web
# Install dependencies: # Install dependencies:
# libfontconfig1: needed for Skia # libfontconfig1: needed for Skia
# libgomp1: needed for ffmpeg
# libva-drm2: needed for ffmpeg
# mesa-va-drivers: needed for VAAPI # mesa-va-drivers: needed for VAAPI
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y \ && apt-get install --no-install-recommends --no-install-suggests -y \
libfontconfig1 mesa-va-drivers \ libfontconfig1 libgomp1 libva-drm2 mesa-va-drivers openssl \
&& apt-get clean autoclean \ && apt-get clean autoclean \
&& apt-get autoremove \ && apt-get autoremove \
&& rm -rf /var/lib/apt/lists/* \ && rm -rf /var/lib/apt/lists/* \
&& mkdir -p /cache /config /media \ && mkdir -p /cache /config /media \
&& chmod 777 /cache /config /media && chmod 777 /cache /config /media \
&& ln -s /opt/ffmpeg/bin/ffmpeg /usr/local/bin \
&& ln -s /opt/ffmpeg/bin/ffprobe /usr/local/bin
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
EXPOSE 8096 EXPOSE 8096
VOLUME /cache /config /media VOLUME /cache /config /media
ENTRYPOINT dotnet /jellyfin/jellyfin.dll \ ENTRYPOINT ./jellyfin/jellyfin \
--datadir /config \ --datadir /config \
--cachedir /cache \ --cachedir /cache \
--ffmpeg /usr/local/bin/ffmpeg --ffmpeg /usr/local/bin/ffmpeg

View File

@ -4,7 +4,7 @@ ARG DOTNET_VERSION=3.0
FROM node:alpine as web-builder FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=v10.5.0 ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl \ RUN apk add curl \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \ && cd jellyfin-web-* \
@ -17,8 +17,6 @@ FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo WORKDIR /repo
COPY . . COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# TODO Remove or update the sed line when we update dotnet version.
RUN find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \;
# Discard objs - may cause failures if exists # Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r RUN find . -type d -name obj | xargs -r rm -r
# Build # Build
@ -26,7 +24,7 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin"
FROM multiarch/qemu-user-static:x86_64-arm as qemu FROM multiarch/qemu-user-static:x86_64-arm as qemu
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm32v7 FROM debian:stretch-slim-arm32v7
COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin COPY --from=qemu /usr/bin/qemu-arm-static /usr/bin
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
@ -36,9 +34,11 @@ RUN apt-get update \
COPY --from=builder /jellyfin /jellyfin COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web COPY --from=web-builder /dist /jellyfin/jellyfin-web
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
EXPOSE 8096 EXPOSE 8096
VOLUME /cache /config /media VOLUME /cache /config /media
ENTRYPOINT dotnet /jellyfin/jellyfin.dll \ ENTRYPOINT ./jellyfin/jellyfin \
--datadir /config \ --datadir /config \
--cachedir /cache \ --cachedir /cache \
--ffmpeg /usr/bin/ffmpeg --ffmpeg /usr/bin/ffmpeg

View File

@ -4,7 +4,7 @@ ARG DOTNET_VERSION=3.0
FROM node:alpine as web-builder FROM node:alpine as web-builder
ARG JELLYFIN_WEB_VERSION=v10.5.0 ARG JELLYFIN_WEB_VERSION=master
RUN apk add curl \ RUN apk add curl \
&& curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \ && curl -L https://github.com/jellyfin/jellyfin-web/archive/${JELLYFIN_WEB_VERSION}.tar.gz | tar zxf - \
&& cd jellyfin-web-* \ && cd jellyfin-web-* \
@ -17,8 +17,6 @@ FROM mcr.microsoft.com/dotnet/core/sdk:${DOTNET_VERSION} as builder
WORKDIR /repo WORKDIR /repo
COPY . . COPY . .
ENV DOTNET_CLI_TELEMETRY_OPTOUT=1 ENV DOTNET_CLI_TELEMETRY_OPTOUT=1
# TODO Remove or update the sed line when we update dotnet version.
RUN find . -type f -exec sed -i 's/netcoreapp2.1/netcoreapp3.0/g' {} \;
# Discard objs - may cause failures if exists # Discard objs - may cause failures if exists
RUN find . -type d -name obj | xargs -r rm -r RUN find . -type d -name obj | xargs -r rm -r
# Build # Build
@ -26,7 +24,7 @@ RUN dotnet publish Jellyfin.Server --configuration Release --output="/jellyfin"
FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu FROM multiarch/qemu-user-static:x86_64-aarch64 as qemu
FROM mcr.microsoft.com/dotnet/core/runtime:${DOTNET_VERSION}-stretch-slim-arm64v8 FROM debian:stretch-slim-arm64v8
COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin COPY --from=qemu /usr/bin/qemu-aarch64-static /usr/bin
RUN apt-get update \ RUN apt-get update \
&& apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \ && apt-get install --no-install-recommends --no-install-suggests -y ffmpeg \
@ -36,9 +34,11 @@ RUN apt-get update \
COPY --from=builder /jellyfin /jellyfin COPY --from=builder /jellyfin /jellyfin
COPY --from=web-builder /dist /jellyfin/jellyfin-web COPY --from=web-builder /dist /jellyfin/jellyfin-web
ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1
EXPOSE 8096 EXPOSE 8096
VOLUME /cache /config /media VOLUME /cache /config /media
ENTRYPOINT dotnet /jellyfin/jellyfin.dll \ ENTRYPOINT ./jellyfin/jellyfin \
--datadir /config \ --datadir /config \
--cachedir /cache \ --cachedir /cache \
--ffmpeg /usr/bin/ffmpeg --ffmpeg /usr/bin/ffmpeg

View File

@ -12,7 +12,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
@ -17,9 +17,4 @@
<Compile Include="..\SharedVersion.cs" /> <Compile Include="..\SharedVersion.cs" />
</ItemGroup> </ItemGroup>
<PropertyGroup>
<!-- We need at least C# 7.1 for the "default literal" feature-->
<LangVersion>latest</LangVersion>
</PropertyGroup>
</Project> </Project>

View File

@ -24,8 +24,9 @@
<!-- Code analysers--> <!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' "> <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" /> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" /> <PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@ -14,7 +14,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>

View File

@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.AppBase
/// <summary> /// <summary>
/// The _configuration sync lock. /// The _configuration sync lock.
/// </summary> /// </summary>
private object _configurationSyncLock = new object(); private readonly object _configurationSyncLock = new object();
/// <summary> /// <summary>
/// The _configuration. /// The _configuration.
@ -98,17 +98,32 @@ namespace Emby.Server.Implementations.AppBase
public IApplicationPaths CommonApplicationPaths { get; private set; } public IApplicationPaths CommonApplicationPaths { get; private set; }
/// <summary> /// <summary>
/// Gets the system configuration /// Gets the system configuration.
/// </summary> /// </summary>
/// <value>The configuration.</value> /// <value>The configuration.</value>
public BaseApplicationConfiguration CommonConfiguration public BaseApplicationConfiguration CommonConfiguration
{ {
get get
{ {
// Lazy load if (_configurationLoaded)
LazyInitializer.EnsureInitialized(ref _configuration, ref _configurationLoaded, ref _configurationSyncLock, () => (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer)); {
return _configuration; return _configuration;
} }
lock (_configurationSyncLock)
{
if (_configurationLoaded)
{
return _configuration;
}
_configuration = (BaseApplicationConfiguration)ConfigurationHelper.GetXmlConfiguration(ConfigurationType, CommonApplicationPaths.SystemConfigurationFilePath, XmlSerializer);
_configurationLoaded = true;
return _configuration;
}
}
protected set protected set
{ {
_configuration = value; _configuration = value;

View File

@ -364,7 +364,7 @@ namespace Emby.Server.Implementations
{ {
_configuration = configuration; _configuration = configuration;
XmlSerializer = new MyXmlSerializer(fileSystem, loggerFactory); XmlSerializer = new MyXmlSerializer();
NetworkManager = networkManager; NetworkManager = networkManager;
networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
@ -912,7 +912,7 @@ namespace Emby.Server.Implementations
_displayPreferencesRepository.Initialize(); _displayPreferencesRepository.Initialize();
var userDataRepo = new SqliteUserDataRepository(LoggerFactory, ApplicationPaths); var userDataRepo = new SqliteUserDataRepository(LoggerFactory.CreateLogger<SqliteUserDataRepository>(), ApplicationPaths);
SetStaticProperties(); SetStaticProperties();

View File

@ -470,7 +470,7 @@ namespace Emby.Server.Implementations.Channels
_libraryManager.CreateItem(item, null); _libraryManager.CreateItem(item, null);
} }
await item.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) await item.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
ForceSave = !isNew && forceUpdate ForceSave = !isNew && forceUpdate
}, cancellationToken); }, cancellationToken);
@ -1156,7 +1156,7 @@ namespace Emby.Server.Implementations.Channels
if (isNew || forceUpdate || item.DateLastRefreshed == default(DateTime)) if (isNew || forceUpdate || item.DateLastRefreshed == default(DateTime))
{ {
_providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), RefreshPriority.Normal); _providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
} }
return item; return item;

View File

@ -149,7 +149,7 @@ namespace Emby.Server.Implementations.Collections
if (options.ItemIdList.Length > 0) if (options.ItemIdList.Length > 0)
{ {
AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
// The initial adding of items is going to create a local metadata file // The initial adding of items is going to create a local metadata file
// This will cause internet metadata to be skipped as a result // This will cause internet metadata to be skipped as a result
@ -158,7 +158,7 @@ namespace Emby.Server.Implementations.Collections
} }
else else
{ {
_providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), RefreshPriority.High); _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.High);
} }
CollectionCreated?.Invoke(this, new CollectionCreatedEventArgs CollectionCreated?.Invoke(this, new CollectionCreatedEventArgs
@ -178,12 +178,12 @@ namespace Emby.Server.Implementations.Collections
public void AddToCollection(Guid collectionId, IEnumerable<string> ids) public void AddToCollection(Guid collectionId, IEnumerable<string> ids)
{ {
AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))); AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
} }
public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids) public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
{ {
AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem))); AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
} }
private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions) private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
@ -287,7 +287,7 @@ namespace Emby.Server.Implementations.Collections
} }
collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None);
_providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) _providerManager.QueueRefresh(collection.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
ForceSave = true ForceSave = true
}, RefreshPriority.High); }, RefreshPriority.High);

View File

@ -110,8 +110,8 @@ namespace Emby.Server.Implementations.Data
using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)")) using (var statement = connection.PrepareStatement("replace into userdisplaypreferences (id, userid, client, data) values (@id, @userId, @client, @data)"))
{ {
statement.TryBind("@id", displayPreferences.Id.ToGuidBlob()); statement.TryBind("@id", new Guid(displayPreferences.Id).ToByteArray());
statement.TryBind("@userId", userId.ToGuidBlob()); statement.TryBind("@userId", userId.ToByteArray());
statement.TryBind("@client", client); statement.TryBind("@client", client);
statement.TryBind("@data", serialized); statement.TryBind("@data", serialized);
@ -170,8 +170,8 @@ namespace Emby.Server.Implementations.Data
{ {
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client")) using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where id = @id and userId=@userId and client=@client"))
{ {
statement.TryBind("@id", guidId.ToGuidBlob()); statement.TryBind("@id", guidId.ToByteArray());
statement.TryBind("@userId", userId.ToGuidBlob()); statement.TryBind("@userId", userId.ToByteArray());
statement.TryBind("@client", client); statement.TryBind("@client", client);
foreach (var row in statement.ExecuteQuery()) foreach (var row in statement.ExecuteQuery())
@ -200,7 +200,7 @@ namespace Emby.Server.Implementations.Data
using (var connection = GetConnection(true)) using (var connection = GetConnection(true))
using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId")) using (var statement = connection.PrepareStatement("select data from userdisplaypreferences where userId=@userId"))
{ {
statement.TryBind("@userId", userId.ToGuidBlob()); statement.TryBind("@userId", userId.ToByteArray());
foreach (var row in statement.ExecuteQuery()) foreach (var row in statement.ExecuteQuery())
{ {

View File

@ -9,6 +9,47 @@ namespace Emby.Server.Implementations.Data
{ {
public static class SqliteExtensions public static class SqliteExtensions
{ {
private const string DatetimeFormatUtc = "yyyy-MM-dd HH:mm:ss.FFFFFFFK";
private const string DatetimeFormatLocal = "yyyy-MM-dd HH:mm:ss.FFFFFFF";
/// <summary>
/// An array of ISO-8601 DateTime formats that we support parsing.
/// </summary>
private static readonly string[] _datetimeFormats = new string[]
{
"THHmmssK",
"THHmmK",
"HH:mm:ss.FFFFFFFK",
"HH:mm:ssK",
"HH:mmK",
DatetimeFormatUtc,
"yyyy-MM-dd HH:mm:ssK",
"yyyy-MM-dd HH:mmK",
"yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
"yyyy-MM-ddTHH:mmK",
"yyyy-MM-ddTHH:mm:ssK",
"yyyyMMddHHmmssK",
"yyyyMMddHHmmK",
"yyyyMMddTHHmmssFFFFFFFK",
"THHmmss",
"THHmm",
"HH:mm:ss.FFFFFFF",
"HH:mm:ss",
"HH:mm",
DatetimeFormatLocal,
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm",
"yyyy-MM-ddTHH:mm:ss.FFFFFFF",
"yyyy-MM-ddTHH:mm",
"yyyy-MM-ddTHH:mm:ss",
"yyyyMMddHHmmss",
"yyyyMMddHHmm",
"yyyyMMddTHHmmssFFFFFFF",
"yyyy-MM-dd",
"yyyyMMdd",
"yy-MM-dd"
};
public static void RunQueries(this SQLiteDatabaseConnection connection, string[] queries) public static void RunQueries(this SQLiteDatabaseConnection connection, string[] queries)
{ {
if (queries == null) if (queries == null)
@ -22,16 +63,6 @@ namespace Emby.Server.Implementations.Data
}); });
} }
public static byte[] ToGuidBlob(this string str)
{
return ToGuidBlob(new Guid(str));
}
public static byte[] ToGuidBlob(this Guid guid)
{
return guid.ToByteArray();
}
public static Guid ReadGuidFromBlob(this IResultSetValue result) public static Guid ReadGuidFromBlob(this IResultSetValue result)
{ {
return new Guid(result.ToBlob()); return new Guid(result.ToBlob());
@ -50,58 +81,16 @@ namespace Emby.Server.Implementations.Data
CultureInfo.InvariantCulture); CultureInfo.InvariantCulture);
} }
private static string GetDateTimeKindFormat( private static string GetDateTimeKindFormat(DateTimeKind kind)
DateTimeKind kind) => (kind == DateTimeKind.Utc) ? DatetimeFormatUtc : DatetimeFormatLocal;
{
return (kind == DateTimeKind.Utc) ? _datetimeFormatUtc : _datetimeFormatLocal;
}
/// <summary>
/// An array of ISO-8601 DateTime formats that we support parsing.
/// </summary>
private static string[] _datetimeFormats = new string[] {
"THHmmssK",
"THHmmK",
"HH:mm:ss.FFFFFFFK",
"HH:mm:ssK",
"HH:mmK",
"yyyy-MM-dd HH:mm:ss.FFFFFFFK", /* NOTE: UTC default (5). */
"yyyy-MM-dd HH:mm:ssK",
"yyyy-MM-dd HH:mmK",
"yyyy-MM-ddTHH:mm:ss.FFFFFFFK",
"yyyy-MM-ddTHH:mmK",
"yyyy-MM-ddTHH:mm:ssK",
"yyyyMMddHHmmssK",
"yyyyMMddHHmmK",
"yyyyMMddTHHmmssFFFFFFFK",
"THHmmss",
"THHmm",
"HH:mm:ss.FFFFFFF",
"HH:mm:ss",
"HH:mm",
"yyyy-MM-dd HH:mm:ss.FFFFFFF", /* NOTE: Non-UTC default (19). */
"yyyy-MM-dd HH:mm:ss",
"yyyy-MM-dd HH:mm",
"yyyy-MM-ddTHH:mm:ss.FFFFFFF",
"yyyy-MM-ddTHH:mm",
"yyyy-MM-ddTHH:mm:ss",
"yyyyMMddHHmmss",
"yyyyMMddHHmm",
"yyyyMMddTHHmmssFFFFFFF",
"yyyy-MM-dd",
"yyyyMMdd",
"yy-MM-dd"
};
private static string _datetimeFormatUtc = _datetimeFormats[5];
private static string _datetimeFormatLocal = _datetimeFormats[19];
public static DateTime ReadDateTime(this IResultSetValue result) public static DateTime ReadDateTime(this IResultSetValue result)
{ {
var dateText = result.ToString(); var dateText = result.ToString();
return DateTime.ParseExact( return DateTime.ParseExact(
dateText, _datetimeFormats, dateText,
_datetimeFormats,
DateTimeFormatInfo.InvariantInfo, DateTimeFormatInfo.InvariantInfo,
DateTimeStyles.None).ToUniversalTime(); DateTimeStyles.None).ToUniversalTime();
} }
@ -139,7 +128,10 @@ namespace Emby.Server.Implementations.Data
public static void Attach(SQLiteDatabaseConnection db, string path, string alias) public static void Attach(SQLiteDatabaseConnection db, string path, string alias)
{ {
var commandText = string.Format("attach @path as {0};", alias); var commandText = string.Format(
CultureInfo.InvariantCulture,
"attach @path as {0};",
alias);
using (var statement = db.PrepareStatement(commandText)) using (var statement = db.PrepareStatement(commandText))
{ {
@ -186,10 +178,7 @@ namespace Emby.Server.Implementations.Data
private static void CheckName(string name) private static void CheckName(string name)
{ {
#if DEBUG #if DEBUG
//if (!name.IndexOf("@", StringComparison.OrdinalIgnoreCase) != 0) throw new ArgumentException("Invalid param name: " + name, nameof(name));
{
throw new Exception("Invalid param name: " + name);
}
#endif #endif
} }
@ -264,7 +253,7 @@ namespace Emby.Server.Implementations.Data
{ {
if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam)) if (statement.BindParameters.TryGetValue(name, out IBindParameter bindParam))
{ {
bindParam.Bind(value.ToGuidBlob()); bindParam.Bind(value.ToByteArray());
} }
else else
{ {
@ -392,8 +381,7 @@ namespace Emby.Server.Implementations.Data
} }
} }
public static IEnumerable<IReadOnlyList<IResultSetValue>> ExecuteQuery( public static IEnumerable<IReadOnlyList<IResultSetValue>> ExecuteQuery(this IStatement This)
this IStatement This)
{ {
while (This.MoveNext()) while (This.MoveNext())
{ {

View File

@ -563,7 +563,7 @@ namespace Emby.Server.Implementations.Data
{ {
using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id")) using (var saveImagesStatement = base.PrepareStatement(db, "Update TypedBaseItems set Images=@Images where guid=@Id"))
{ {
saveImagesStatement.TryBind("@Id", item.Id.ToGuidBlob()); saveImagesStatement.TryBind("@Id", item.Id.ToByteArray());
saveImagesStatement.TryBind("@Images", SerializeImages(item)); saveImagesStatement.TryBind("@Images", SerializeImages(item));
saveImagesStatement.MoveNext(); saveImagesStatement.MoveNext();
@ -2003,7 +2003,7 @@ namespace Emby.Server.Implementations.Data
throw new ArgumentNullException(nameof(chapters)); throw new ArgumentNullException(nameof(chapters));
} }
var idBlob = id.ToGuidBlob(); var idBlob = id.ToByteArray();
using (var connection = GetConnection()) using (var connection = GetConnection())
{ {
@ -3782,7 +3782,7 @@ namespace Emby.Server.Implementations.Data
if (statement != null) if (statement != null)
{ {
statement.TryBind(paramName, personId.ToGuidBlob()); statement.TryBind(paramName, personId.ToByteArray());
} }
index++; index++;
} }
@ -3993,7 +3993,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))");
if (statement != null) if (statement != null)
{ {
statement.TryBind(paramName, artistId.ToGuidBlob()); statement.TryBind(paramName, artistId.ToByteArray());
} }
index++; index++;
} }
@ -4012,7 +4012,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))"); clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=1))");
if (statement != null) if (statement != null)
{ {
statement.TryBind(paramName, artistId.ToGuidBlob()); statement.TryBind(paramName, artistId.ToByteArray());
} }
index++; index++;
} }
@ -4031,7 +4031,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type=1))"); clauses.Add("((select CleanName from TypedBaseItems where guid=" + paramName + ") in (select CleanValue from itemvalues where ItemId=Guid and Type=0) AND (select CleanName from TypedBaseItems where guid=" + paramName + ") not in (select CleanValue from itemvalues where ItemId=Guid and Type=1))");
if (statement != null) if (statement != null)
{ {
statement.TryBind(paramName, artistId.ToGuidBlob()); statement.TryBind(paramName, artistId.ToByteArray());
} }
index++; index++;
} }
@ -4050,7 +4050,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")"); clauses.Add("Album in (select Name from typedbaseitems where guid=" + paramName + ")");
if (statement != null) if (statement != null)
{ {
statement.TryBind(paramName, albumId.ToGuidBlob()); statement.TryBind(paramName, albumId.ToByteArray());
} }
index++; index++;
} }
@ -4069,7 +4069,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("(guid not in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))"); clauses.Add("(guid not in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type<=1))");
if (statement != null) if (statement != null)
{ {
statement.TryBind(paramName, artistId.ToGuidBlob()); statement.TryBind(paramName, artistId.ToByteArray());
} }
index++; index++;
} }
@ -4088,7 +4088,7 @@ namespace Emby.Server.Implementations.Data
clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))"); clauses.Add("(guid in (select itemid from itemvalues where CleanValue = (select CleanName from TypedBaseItems where guid=" + paramName + ") and Type=2))");
if (statement != null) if (statement != null)
{ {
statement.TryBind(paramName, genreId.ToGuidBlob()); statement.TryBind(paramName, genreId.ToByteArray());
} }
index++; index++;
} }
@ -4159,7 +4159,7 @@ namespace Emby.Server.Implementations.Data
if (statement != null) if (statement != null)
{ {
statement.TryBind(paramName, studioId.ToGuidBlob()); statement.TryBind(paramName, studioId.ToByteArray());
} }
index++; index++;
} }
@ -4935,7 +4935,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{ {
connection.RunInTransaction(db => connection.RunInTransaction(db =>
{ {
var idBlob = id.ToGuidBlob(); var idBlob = id.ToByteArray();
// Delete people // Delete people
ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", idBlob); ExecuteWithSingleParam(db, "delete from People where ItemId=@Id", idBlob);
@ -5054,7 +5054,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
whereClauses.Add("ItemId=@ItemId"); whereClauses.Add("ItemId=@ItemId");
if (statement != null) if (statement != null)
{ {
statement.TryBind("@ItemId", query.ItemId.ToGuidBlob()); statement.TryBind("@ItemId", query.ItemId.ToByteArray());
} }
} }
if (!query.AppearsInItemId.Equals(Guid.Empty)) if (!query.AppearsInItemId.Equals(Guid.Empty))
@ -5062,7 +5062,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)"); whereClauses.Add("Name in (Select Name from People where ItemId=@AppearsInItemId)");
if (statement != null) if (statement != null)
{ {
statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToGuidBlob()); statement.TryBind("@AppearsInItemId", query.AppearsInItemId.ToByteArray());
} }
} }
var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList(); var queryPersonTypes = query.PersonTypes.Where(IsValidPersonType).ToList();
@ -5131,7 +5131,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
CheckDisposed(); CheckDisposed();
var itemIdBlob = itemId.ToGuidBlob(); var itemIdBlob = itemId.ToByteArray();
// First delete // First delete
deleteAncestorsStatement.Reset(); deleteAncestorsStatement.Reset();
@ -5165,7 +5165,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
var ancestorId = ancestorIds[i]; var ancestorId = ancestorIds[i];
statement.TryBind("@AncestorId" + index, ancestorId.ToGuidBlob()); statement.TryBind("@AncestorId" + index, ancestorId.ToByteArray());
statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture)); statement.TryBind("@AncestorIdText" + index, ancestorId.ToString("N", CultureInfo.InvariantCulture));
} }
@ -5630,7 +5630,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
CheckDisposed(); CheckDisposed();
var guidBlob = itemId.ToGuidBlob(); var guidBlob = itemId.ToByteArray();
// First delete // First delete
db.Execute("delete from ItemValues where ItemId=@Id", guidBlob); db.Execute("delete from ItemValues where ItemId=@Id", guidBlob);
@ -5654,10 +5654,13 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{ {
if (isSubsequentRow) if (isSubsequentRow)
{ {
insertText.Append(","); insertText.Append(',');
} }
insertText.AppendFormat("(@ItemId, @Type{0}, @Value{0}, @CleanValue{0})", i.ToString(CultureInfo.InvariantCulture)); insertText.AppendFormat(
CultureInfo.InvariantCulture,
"(@ItemId, @Type{0}, @Value{0}, @CleanValue{0})",
i);
isSubsequentRow = true; isSubsequentRow = true;
} }
@ -5710,7 +5713,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{ {
connection.RunInTransaction(db => connection.RunInTransaction(db =>
{ {
var itemIdBlob = itemId.ToGuidBlob(); var itemIdBlob = itemId.ToByteArray();
// First delete chapters // First delete chapters
db.Execute("delete from People where ItemId=@ItemId", itemIdBlob); db.Execute("delete from People where ItemId=@ItemId", itemIdBlob);
@ -5829,7 +5832,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
using (var statement = PrepareStatement(connection, cmdText)) using (var statement = PrepareStatement(connection, cmdText))
{ {
statement.TryBind("@ItemId", query.ItemId.ToGuidBlob()); statement.TryBind("@ItemId", query.ItemId.ToByteArray());
if (query.Type.HasValue) if (query.Type.HasValue)
{ {
@ -5871,7 +5874,7 @@ where AncestorIdText not null and ItemValues.Value not null and ItemValues.Type
{ {
connection.RunInTransaction(db => connection.RunInTransaction(db =>
{ {
var itemIdBlob = id.ToGuidBlob(); var itemIdBlob = id.ToByteArray();
// First delete chapters // First delete chapters
db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob); db.Execute("delete from mediastreams where ItemId=@ItemId", itemIdBlob);

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -15,23 +14,19 @@ namespace Emby.Server.Implementations.Data
public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository public class SqliteUserDataRepository : BaseSqliteRepository, IUserDataRepository
{ {
public SqliteUserDataRepository( public SqliteUserDataRepository(
ILoggerFactory loggerFactory, ILogger<SqliteUserDataRepository> logger,
IApplicationPaths appPaths) IApplicationPaths appPaths)
: base(loggerFactory.CreateLogger(nameof(SqliteUserDataRepository))) : base(logger)
{ {
DbFilePath = Path.Combine(appPaths.DataPath, "library.db"); DbFilePath = Path.Combine(appPaths.DataPath, "library.db");
} }
/// <summary> /// <inheritdoc />
/// Gets the name of the repository
/// </summary>
/// <value>The name.</value>
public string Name => "SQLite"; public string Name => "SQLite";
/// <summary> /// <summary>
/// Opens the connection to the database /// Opens the connection to the database.
/// </summary> /// </summary>
/// <returns>Task.</returns>
public void Initialize(IUserManager userManager, SemaphoreSlim dbLock, SQLiteDatabaseConnection dbConnection) public void Initialize(IUserManager userManager, SemaphoreSlim dbLock, SQLiteDatabaseConnection dbConnection)
{ {
WriteLock.Dispose(); WriteLock.Dispose();
@ -97,7 +92,7 @@ namespace Emby.Server.Implementations.Data
continue; continue;
} }
statement.TryBind("@UserId", user.Id.ToGuidBlob()); statement.TryBind("@UserId", user.Id.ToByteArray());
statement.TryBind("@InternalUserId", user.InternalId); statement.TryBind("@InternalUserId", user.InternalId);
statement.MoveNext(); statement.MoveNext();

View File

@ -116,7 +116,7 @@ namespace Emby.Server.Implementations.Data
{ {
using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)")) using (var statement = db.PrepareStatement("insert into LocalUsersv2 (guid, data) values (@guid, @data)"))
{ {
statement.TryBind("@guid", user.Id.ToGuidBlob()); statement.TryBind("@guid", user.Id.ToByteArray());
statement.TryBind("@data", serialized); statement.TryBind("@data", serialized);
statement.MoveNext(); statement.MoveNext();

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" /> <ProjectReference Include="..\Emby.Naming\Emby.Naming.csproj" />
@ -29,9 +29,9 @@
<PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.ResponseCompression" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" /> <PackageReference Include="Microsoft.AspNetCore.WebSockets" Version="2.2.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Logging" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.0.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.6.0" /> <PackageReference Include="ServiceStack.Text.Core" Version="5.6.0" />
<PackageReference Include="sharpcompress" Version="0.24.0" /> <PackageReference Include="sharpcompress" Version="0.24.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.0.1" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.0.1" />
@ -47,16 +47,12 @@
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<!-- We need at least C# 7.3 to compare tuples-->
<LangVersion>latest</LangVersion>
</PropertyGroup>
<!-- Code analysers--> <!-- Code analysers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' "> <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" /> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" /> <PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@ -27,6 +27,11 @@ namespace Emby.Server.Implementations.EntryPoints
private NatManager _natManager; private NatManager _natManager;
private readonly object _createdRulesLock = new object();
private List<string> _createdRules = new List<string>();
private readonly object _usnsHandledLock = new object();
private List<string> _usnsHandled = new List<string>();
public ExternalPortForwarding(ILoggerFactory loggerFactory, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient) public ExternalPortForwarding(ILoggerFactory loggerFactory, IServerApplicationHost appHost, IServerConfigurationManager config, IDeviceDiscovery deviceDiscovery, IHttpClient httpClient)
{ {
_logger = loggerFactory.CreateLogger("PortMapper"); _logger = loggerFactory.CreateLogger("PortMapper");
@ -127,12 +132,13 @@ namespace Emby.Server.Implementations.EntryPoints
return; return;
} }
lock (_usnsHandled) lock (_usnsHandledLock)
{ {
if (_usnsHandled.Contains(identifier)) if (_usnsHandled.Contains(identifier))
{ {
return; return;
} }
_usnsHandled.Add(identifier); _usnsHandled.Add(identifier);
} }
@ -186,11 +192,12 @@ namespace Emby.Server.Implementations.EntryPoints
private void ClearCreatedRules(object state) private void ClearCreatedRules(object state)
{ {
lock (_createdRules) lock (_createdRulesLock)
{ {
_createdRules.Clear(); _createdRules.Clear();
} }
lock (_usnsHandled)
lock (_usnsHandledLock)
{ {
_usnsHandled.Clear(); _usnsHandled.Clear();
} }
@ -216,8 +223,6 @@ namespace Emby.Server.Implementations.EntryPoints
} }
} }
private List<string> _createdRules = new List<string>();
private List<string> _usnsHandled = new List<string>();
private async void CreateRules(INatDevice device) private async void CreateRules(INatDevice device)
{ {
if (_disposed) if (_disposed)
@ -231,7 +236,7 @@ namespace Emby.Server.Implementations.EntryPoints
var addressString = address.ToString(); var addressString = address.ToString();
lock (_createdRules) lock (_createdRulesLock)
{ {
if (!_createdRules.Contains(addressString)) if (!_createdRules.Contains(addressString))
{ {

View File

@ -54,7 +54,7 @@ namespace Emby.Server.Implementations.EntryPoints
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), cancellationToken).ConfigureAwait(false); await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false);
} }
} }

View File

@ -539,6 +539,11 @@ namespace Emby.Server.Implementations.HttpServer
} }
finally finally
{ {
if (httpRes.StatusCode >= 500)
{
_logger.LogDebug("Sending HTTP Response 500 in response to {Url}", urlToLog);
}
stopWatch.Stop(); stopWatch.Stop();
var elapsed = stopWatch.Elapsed; var elapsed = stopWatch.Elapsed;
if (elapsed.TotalMilliseconds > 500) if (elapsed.TotalMilliseconds > 500)

View File

@ -2,11 +2,11 @@ using System;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Cryptography; using MediaBrowser.Common.Cryptography;
using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Cryptography; using MediaBrowser.Model.Cryptography;
using static MediaBrowser.Common.HexHelper;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
@ -122,7 +122,7 @@ namespace Emby.Server.Implementations.Library
{ {
return string.IsNullOrEmpty(user.EasyPassword) return string.IsNullOrEmpty(user.EasyPassword)
? null ? null
: ToHexString(PasswordHash.Parse(user.EasyPassword).Hash); : Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash);
} }
/// <summary> /// <summary>

View File

@ -1,7 +1,6 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Net;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {

View File

@ -519,7 +519,7 @@ namespace Emby.Server.Implementations.Library
} }
public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null) public BaseItem ResolvePath(FileSystemMetadata fileInfo, Folder parent = null)
=> ResolvePath(fileInfo, new DirectoryService(_logger, _fileSystem), null, parent); => ResolvePath(fileInfo, new DirectoryService(_fileSystem), null, parent);
private BaseItem ResolvePath( private BaseItem ResolvePath(
FileSystemMetadata fileInfo, FileSystemMetadata fileInfo,
@ -1045,7 +1045,7 @@ namespace Emby.Server.Implementations.Library
await RootFolder.ValidateChildren( await RootFolder.ValidateChildren(
new SimpleProgress<double>(), new SimpleProgress<double>(),
cancellationToken, cancellationToken,
new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), new MetadataRefreshOptions(new DirectoryService(_fileSystem)),
recursive: false).ConfigureAwait(false); recursive: false).ConfigureAwait(false);
await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false);
@ -1053,7 +1053,7 @@ namespace Emby.Server.Implementations.Library
await GetUserRootFolder().ValidateChildren( await GetUserRootFolder().ValidateChildren(
new SimpleProgress<double>(), new SimpleProgress<double>(),
cancellationToken, cancellationToken,
new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), new MetadataRefreshOptions(new DirectoryService(_fileSystem)),
recursive: false).ConfigureAwait(false); recursive: false).ConfigureAwait(false);
// Quickly scan CollectionFolders for changes // Quickly scan CollectionFolders for changes
@ -1074,7 +1074,7 @@ namespace Emby.Server.Implementations.Library
innerProgress.RegisterAction(pct => progress.Report(pct * .96)); innerProgress.RegisterAction(pct => progress.Report(pct * .96));
// Now validate the entire media library // Now validate the entire media library
await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), recursive: true).ConfigureAwait(false); await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), recursive: true).ConfigureAwait(false);
progress.Report(96); progress.Report(96);
@ -2135,7 +2135,7 @@ namespace Emby.Server.Implementations.Library
if (refresh) if (refresh)
{ {
item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
_providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), RefreshPriority.Normal); _providerManagerFactory().QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
} }
return item; return item;
@ -2193,7 +2193,7 @@ namespace Emby.Server.Implementations.Library
{ {
_providerManagerFactory().QueueRefresh( _providerManagerFactory().QueueRefresh(
item.Id, item.Id,
new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
// Need to force save to increment DateLastSaved // Need to force save to increment DateLastSaved
ForceSave = true ForceSave = true
@ -2261,7 +2261,7 @@ namespace Emby.Server.Implementations.Library
{ {
_providerManagerFactory().QueueRefresh( _providerManagerFactory().QueueRefresh(
item.Id, item.Id,
new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
// Need to force save to increment DateLastSaved // Need to force save to increment DateLastSaved
ForceSave = true ForceSave = true
@ -2338,7 +2338,7 @@ namespace Emby.Server.Implementations.Library
{ {
_providerManagerFactory().QueueRefresh( _providerManagerFactory().QueueRefresh(
item.Id, item.Id,
new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
// Need to force save to increment DateLastSaved // Need to force save to increment DateLastSaved
ForceSave = true ForceSave = true

View File

@ -160,12 +160,13 @@ namespace Emby.Server.Implementations.Library
if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio || i.Type == MediaStreamType.Video)) if (allowMediaProbe && mediaSources[0].Type != MediaSourceType.Placeholder && !mediaSources[0].MediaStreams.Any(i => i.Type == MediaStreamType.Audio || i.Type == MediaStreamType.Video))
{ {
await item.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) await item.RefreshMetadata(
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
EnableRemoteContentProbe = true, EnableRemoteContentProbe = true,
MetadataRefreshMode = MediaBrowser.Controller.Providers.MetadataRefreshMode.FullRefresh MetadataRefreshMode = MetadataRefreshMode.FullRefresh
},
}, cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user); mediaSources = GetStaticMediaSources(item, enablePathSubstitution, user);
} }

View File

@ -8,6 +8,7 @@ using System.Text;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common;
using MediaBrowser.Common.Cryptography; using MediaBrowser.Common.Cryptography;
using MediaBrowser.Common.Events; using MediaBrowser.Common.Events;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
@ -31,7 +32,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Users; using MediaBrowser.Model.Users;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using static MediaBrowser.Common.HexHelper;
namespace Emby.Server.Implementations.Library namespace Emby.Server.Implementations.Library
{ {
@ -490,7 +490,7 @@ namespace Emby.Server.Implementations.Library
{ {
return string.IsNullOrEmpty(user.EasyPassword) return string.IsNullOrEmpty(user.EasyPassword)
? null ? null
: ToHexString(PasswordHash.Parse(user.EasyPassword).Hash); : Hex.Encode(PasswordHash.Parse(user.EasyPassword).Hash);
} }
private void ResetInvalidLoginAttemptCount(User user) private void ResetInvalidLoginAttemptCount(User user)
@ -639,7 +639,7 @@ namespace Emby.Server.Implementations.Library
{ {
foreach (var user in Users) foreach (var user in Users)
{ {
await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)), cancellationToken).ConfigureAwait(false); await user.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)), cancellationToken).ConfigureAwait(false);
} }
} }

View File

@ -11,16 +11,17 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Library.Validators namespace Emby.Server.Implementations.Library.Validators
{ {
/// <summary> /// <summary>
/// Class PeopleValidator /// Class PeopleValidator.
/// </summary> /// </summary>
public class PeopleValidator public class PeopleValidator
{ {
/// <summary> /// <summary>
/// The _library manager /// The _library manager.
/// </summary> /// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
/// <summary> /// <summary>
/// The _logger /// The _logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger _logger;
@ -62,7 +63,7 @@ namespace Emby.Server.Implementations.Library.Validators
{ {
var item = _libraryManager.GetPerson(person); var item = _libraryManager.GetPerson(person);
var options = new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
ImageRefreshMode = MetadataRefreshMode.ValidationOnly, ImageRefreshMode = MetadataRefreshMode.ValidationOnly,
MetadataRefreshMode = MetadataRefreshMode.ValidationOnly MetadataRefreshMode = MetadataRefreshMode.ValidationOnly
@ -96,12 +97,19 @@ namespace Emby.Server.Implementations.Library.Validators
foreach (var item in deadEntities) foreach (var item in deadEntities)
{ {
_logger.LogInformation("Deleting dead {2} {0} {1}.", item.Id.ToString("N", CultureInfo.InvariantCulture), item.Name, item.GetType().Name); _logger.LogInformation(
"Deleting dead {2} {0} {1}.",
item.Id.ToString("N", CultureInfo.InvariantCulture),
item.Name,
item.GetType().Name);
_libraryManager.DeleteItem(item, new DeleteOptions _libraryManager.DeleteItem(
item,
new DeleteOptions
{ {
DeleteFileLocation = false DeleteFileLocation = false
}, false); },
false);
} }
progress.Report(100); progress.Report(100);

View File

@ -1489,7 +1489,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
{ {
_logger.LogInformation("Refreshing recording parent {path}", item.Path); _logger.LogInformation("Refreshing recording parent {path}", item.Path);
_providerManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) _providerManager.QueueRefresh(
item.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
RefreshPaths = new string[] RefreshPaths = new string[]
{ {
@ -1497,8 +1499,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
Path.GetDirectoryName(path), Path.GetDirectoryName(path),
Path.GetDirectoryName(Path.GetDirectoryName(path)) Path.GetDirectoryName(Path.GetDirectoryName(path))
} }
},
}, RefreshPriority.High); RefreshPriority.High);
} }
} }

View File

@ -1226,12 +1226,13 @@ namespace Emby.Server.Implementations.LiveTv
currentChannel.AddTag("Kids"); currentChannel.AddTag("Kids");
} }
//currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken); currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken);
await currentChannel.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) await currentChannel.RefreshMetadata(
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
ForceSave = true ForceSave = true
},
}, cancellationToken).ConfigureAwait(false); cancellationToken).ConfigureAwait(false);
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
@ -1245,7 +1246,7 @@ namespace Emby.Server.Implementations.LiveTv
numComplete++; numComplete++;
double percent = numComplete / (double)allChannelsList.Count; double percent = numComplete / (double)allChannelsList.Count;
progress.Report(85 * percent + 15); progress.Report((85 * percent) + 15);
} }
progress.Report(100); progress.Report(100);
@ -1278,12 +1279,14 @@ namespace Emby.Server.Implementations.LiveTv
if (item != null) if (item != null)
{ {
_libraryManager.DeleteItem(item, new DeleteOptions _libraryManager.DeleteItem(
item,
new DeleteOptions
{ {
DeleteFileLocation = false, DeleteFileLocation = false,
DeleteFromExternalProvider = false DeleteFromExternalProvider = false
},
}, false); false);
} }
} }
@ -2301,8 +2304,10 @@ namespace Emby.Server.Implementations.LiveTv
if (provider == null) if (provider == null)
{ {
throw new ResourceNotFoundException( throw new ResourceNotFoundException(
string.Format("Couldn't find provider of type: '{0}'", info.Type) string.Format(
); CultureInfo.InvariantCulture,
"Couldn't find provider of type: '{0}'",
info.Type));
} }
await provider.Validate(info, validateLogin, validateListings).ConfigureAwait(false); await provider.Validate(info, validateLogin, validateListings).ConfigureAwait(false);

View File

@ -5,7 +5,7 @@
"Artists": "Artistes", "Artists": "Artistes",
"AuthenticationSucceededWithUserName": "{0} s'est authentifié avec succès", "AuthenticationSucceededWithUserName": "{0} s'est authentifié avec succès",
"Books": "Livres", "Books": "Livres",
"CameraImageUploadedFrom": "Une image de caméra a été chargée depuis {0}", "CameraImageUploadedFrom": "Une nouvelle image de caméra a été chargée depuis {0}",
"Channels": "Chaînes", "Channels": "Chaînes",
"ChapterNameValue": "Chapitre {0}", "ChapterNameValue": "Chapitre {0}",
"Collections": "Collections", "Collections": "Collections",
@ -17,7 +17,7 @@
"Genres": "Genres", "Genres": "Genres",
"HeaderAlbumArtists": "Artistes de l'album", "HeaderAlbumArtists": "Artistes de l'album",
"HeaderCameraUploads": "Photos transférées", "HeaderCameraUploads": "Photos transférées",
"HeaderContinueWatching": "Reprendre", "HeaderContinueWatching": "Continuer à regarder",
"HeaderFavoriteAlbums": "Albums favoris", "HeaderFavoriteAlbums": "Albums favoris",
"HeaderFavoriteArtists": "Artistes favoris", "HeaderFavoriteArtists": "Artistes favoris",
"HeaderFavoriteEpisodes": "Épisodes favoris", "HeaderFavoriteEpisodes": "Épisodes favoris",
@ -34,14 +34,14 @@
"LabelRunningTimeValue": "Durée : {0}", "LabelRunningTimeValue": "Durée : {0}",
"Latest": "Derniers", "Latest": "Derniers",
"MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour", "MessageApplicationUpdated": "Le serveur Jellyfin a été mis à jour",
"MessageApplicationUpdatedTo": "Jellyfin Serveur a été mis à jour en version {0}", "MessageApplicationUpdatedTo": "Le serveur Jellyfin a été mis à jour vers {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour", "MessageNamedServerConfigurationUpdatedWithValue": "La configuration de la section {0} du serveur a été mise à jour",
"MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour", "MessageServerConfigurationUpdated": "La configuration du serveur a été mise à jour",
"MixedContent": "Contenu mixte", "MixedContent": "Contenu mixte",
"Movies": "Films", "Movies": "Films",
"Music": "Musique", "Music": "Musique",
"MusicVideos": "Vidéos musicales", "MusicVideos": "Vidéos musicales",
"NameInstallFailed": "{0} échec d'installation", "NameInstallFailed": "{0} échec de l'installation",
"NameSeasonNumber": "Saison {0}", "NameSeasonNumber": "Saison {0}",
"NameSeasonUnknown": "Saison Inconnue", "NameSeasonUnknown": "Saison Inconnue",
"NewVersionIsAvailable": "Une nouvelle version de Jellyfin Serveur est disponible au téléchargement.", "NewVersionIsAvailable": "Une nouvelle version de Jellyfin Serveur est disponible au téléchargement.",
@ -50,7 +50,7 @@
"NotificationOptionAudioPlayback": "Lecture audio démarrée", "NotificationOptionAudioPlayback": "Lecture audio démarrée",
"NotificationOptionAudioPlaybackStopped": "Lecture audio arrêtée", "NotificationOptionAudioPlaybackStopped": "Lecture audio arrêtée",
"NotificationOptionCameraImageUploaded": "L'image de l'appareil photo a été transférée", "NotificationOptionCameraImageUploaded": "L'image de l'appareil photo a été transférée",
"NotificationOptionInstallationFailed": "Échec d'installation", "NotificationOptionInstallationFailed": "Échec de l'installation",
"NotificationOptionNewLibraryContent": "Nouveau contenu ajouté", "NotificationOptionNewLibraryContent": "Nouveau contenu ajouté",
"NotificationOptionPluginError": "Erreur d'extension", "NotificationOptionPluginError": "Erreur d'extension",
"NotificationOptionPluginInstalled": "Extension installée", "NotificationOptionPluginInstalled": "Extension installée",
@ -91,7 +91,7 @@
"UserPolicyUpdatedWithName": "La politique de l'utilisateur a été mise à jour pour {0}", "UserPolicyUpdatedWithName": "La politique de l'utilisateur a été mise à jour pour {0}",
"UserStartedPlayingItemWithValues": "{0} est en train de lire {1} sur {2}", "UserStartedPlayingItemWithValues": "{0} est en train de lire {1} sur {2}",
"UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}", "UserStoppedPlayingItemWithValues": "{0} vient d'arrêter la lecture de {1} sur {2}",
"ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre librairie", "ValueHasBeenAddedToLibrary": "{0} a été ajouté à votre médiathèque",
"ValueSpecialEpisodeName": "Spécial - {0}", "ValueSpecialEpisodeName": "Spécial - {0}",
"VersionNumber": "Version {0}" "VersionNumber": "Version {0}"
} }

View File

@ -5,7 +5,7 @@
"Artists": "Előadók", "Artists": "Előadók",
"AuthenticationSucceededWithUserName": "{0} sikeresen azonosítva", "AuthenticationSucceededWithUserName": "{0} sikeresen azonosítva",
"Books": "Könyvek", "Books": "Könyvek",
"CameraImageUploadedFrom": "Új kamerakép került feltöltésre {0}", "CameraImageUploadedFrom": "Új kamerakép került feltöltésre innen: {0}",
"Channels": "Csatornák", "Channels": "Csatornák",
"ChapterNameValue": "Jelenet {0}", "ChapterNameValue": "Jelenet {0}",
"Collections": "Gyűjtemények", "Collections": "Gyűjtemények",
@ -15,14 +15,14 @@
"Favorites": "Kedvencek", "Favorites": "Kedvencek",
"Folders": "Könyvtárak", "Folders": "Könyvtárak",
"Genres": "Műfajok", "Genres": "Műfajok",
"HeaderAlbumArtists": "Album Előadók", "HeaderAlbumArtists": "Album előadók",
"HeaderCameraUploads": "Kamera feltöltések", "HeaderCameraUploads": "Kamera feltöltések",
"HeaderContinueWatching": "Folyamatban lévő filmek", "HeaderContinueWatching": "Folyamatban lévő filmek",
"HeaderFavoriteAlbums": "Kedvenc Albumok", "HeaderFavoriteAlbums": "Kedvenc albumok",
"HeaderFavoriteArtists": "Kedvenc Előadók", "HeaderFavoriteArtists": "Kedvenc előadók",
"HeaderFavoriteEpisodes": "Kedvenc Epizódok", "HeaderFavoriteEpisodes": "Kedvenc epizódok",
"HeaderFavoriteShows": "Kedvenc Sorozatok", "HeaderFavoriteShows": "Kedvenc sorozatok",
"HeaderFavoriteSongs": "Kedvenc Dalok", "HeaderFavoriteSongs": "Kedvenc dalok",
"HeaderLiveTV": "Élő TV", "HeaderLiveTV": "Élő TV",
"HeaderNextUp": "Következik", "HeaderNextUp": "Következik",
"HeaderRecordingGroups": "Felvételi csoportok", "HeaderRecordingGroups": "Felvételi csoportok",
@ -34,21 +34,21 @@
"LabelRunningTimeValue": "Futási idő: {0}", "LabelRunningTimeValue": "Futási idő: {0}",
"Latest": "Legújabb", "Latest": "Legújabb",
"MessageApplicationUpdated": "Jellyfin Szerver frissítve", "MessageApplicationUpdated": "Jellyfin Szerver frissítve",
"MessageApplicationUpdatedTo": "Jellyfin Szerver frissítve lett a következőre {0}", "MessageApplicationUpdatedTo": "Jellyfin Szerver frissítve lett a következőre: {0}",
"MessageNamedServerConfigurationUpdatedWithValue": "Szerver konfigurációs rész {0} frissítve", "MessageNamedServerConfigurationUpdatedWithValue": "Szerver konfigurációs rész frissítve: {0}",
"MessageServerConfigurationUpdated": "Szerver konfiguráció frissítve", "MessageServerConfigurationUpdated": "Szerver konfiguráció frissítve",
"MixedContent": "Vegyes tartalom", "MixedContent": "Vegyes tartalom",
"Movies": "Filmek", "Movies": "Filmek",
"Music": "Zene", "Music": "Zene",
"MusicVideos": "Zenei Videók", "MusicVideos": "Zenei videók",
"NameInstallFailed": "{0} sikertelen telepítés", "NameInstallFailed": "{0} sikertelen telepítés",
"NameSeasonNumber": "Évad {0}", "NameSeasonNumber": "Évad {0}",
"NameSeasonUnknown": "Ismeretlen évad", "NameSeasonUnknown": "Ismeretlen évad",
"NewVersionIsAvailable": "Letölthető a Jellyfin Szerver új verziója.", "NewVersionIsAvailable": "Letölthető a Jellyfin Szerver új verziója.",
"NotificationOptionApplicationUpdateAvailable": "Új programfrissítés érhető el", "NotificationOptionApplicationUpdateAvailable": "Frissítés érhető el az alkalmazáshoz",
"NotificationOptionApplicationUpdateInstalled": "Programfrissítés telepítve", "NotificationOptionApplicationUpdateInstalled": "Alkalmazásfrissítés telepítve",
"NotificationOptionAudioPlayback": "Audió lejátszás elkezdve", "NotificationOptionAudioPlayback": "Audió lejátszás elkezdve",
"NotificationOptionAudioPlaybackStopped": "Audió lejátszás befejezve", "NotificationOptionAudioPlaybackStopped": "Audió lejátszás leállítva",
"NotificationOptionCameraImageUploaded": "Kamera kép feltöltve", "NotificationOptionCameraImageUploaded": "Kamera kép feltöltve",
"NotificationOptionInstallationFailed": "Telepítési hiba", "NotificationOptionInstallationFailed": "Telepítési hiba",
"NotificationOptionNewLibraryContent": "Új tartalom hozzáadva", "NotificationOptionNewLibraryContent": "Új tartalom hozzáadva",
@ -60,15 +60,15 @@
"NotificationOptionTaskFailed": "Ütemezett feladat hiba", "NotificationOptionTaskFailed": "Ütemezett feladat hiba",
"NotificationOptionUserLockedOut": "Felhasználó tiltva", "NotificationOptionUserLockedOut": "Felhasználó tiltva",
"NotificationOptionVideoPlayback": "Videó lejátszás elkezdve", "NotificationOptionVideoPlayback": "Videó lejátszás elkezdve",
"NotificationOptionVideoPlaybackStopped": "Videó lejátszás befejezve", "NotificationOptionVideoPlaybackStopped": "Videó lejátszás leállítva",
"Photos": "Fényképek", "Photos": "Fényképek",
"Playlists": "Lejátszási listák", "Playlists": "Lejátszási listák",
"Plugin": "Bővítmény", "Plugin": "Bővítmény",
"PluginInstalledWithName": "{0} telepítve", "PluginInstalledWithName": "{0} telepítve",
"PluginUninstalledWithName": "{0} eltávolítva", "PluginUninstalledWithName": "{0} eltávolítva",
"PluginUpdatedWithName": "{0} frissítve", "PluginUpdatedWithName": "{0} frissítve",
"ProviderValue": "Provider: {0}", "ProviderValue": "Szolgáltató: {0}",
"ScheduledTaskFailedWithName": "{0} hiba", "ScheduledTaskFailedWithName": "{0} sikertelen",
"ScheduledTaskStartedWithName": "{0} elkezdve", "ScheduledTaskStartedWithName": "{0} elkezdve",
"ServerNameNeedsToBeRestarted": "{0}-t újra kell indítani", "ServerNameNeedsToBeRestarted": "{0}-t újra kell indítani",
"Shows": "Műsorok", "Shows": "Műsorok",
@ -76,10 +76,10 @@
"StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek próbáld újra később.", "StartupEmbyServerIsLoading": "A Jellyfin Szerver betöltődik. Kérlek próbáld újra később.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0} ehhez: {1}", "SubtitleDownloadFailureFromForItem": "Nem sikerült a felirat letöltése innen: {0} ehhez: {1}",
"SubtitlesDownloadedForItem": "Letöltött feliratok a következőhöz {0}", "SubtitlesDownloadedForItem": "Letöltött feliratok a következőhöz: {0}",
"Sync": "Szinkronizál", "Sync": "Szinkronizál",
"System": "Rendszer", "System": "Rendszer",
"TvShows": "TV Műsorok", "TvShows": "TV műsorok",
"User": "Felhasználó", "User": "Felhasználó",
"UserCreatedWithName": "{0} felhasználó létrehozva", "UserCreatedWithName": "{0} felhasználó létrehozva",
"UserDeletedWithName": "{0} felhasználó törölve", "UserDeletedWithName": "{0} felhasználó törölve",
@ -88,7 +88,7 @@
"UserOfflineFromDevice": "{0} kijelentkezett innen: {1}", "UserOfflineFromDevice": "{0} kijelentkezett innen: {1}",
"UserOnlineFromDevice": "{0} online itt: {1}", "UserOnlineFromDevice": "{0} online itt: {1}",
"UserPasswordChangedWithName": "Jelszó megváltozott a következő felhasználó számára: {0}", "UserPasswordChangedWithName": "Jelszó megváltozott a következő felhasználó számára: {0}",
"UserPolicyUpdatedWithName": "A felhasználói házirend frissítve lett {0}", "UserPolicyUpdatedWithName": "A felhasználói házirend frissítve lett neki: {0}",
"UserStartedPlayingItemWithValues": "{0} elkezdte játszani a következőt: {1} itt: {2}", "UserStartedPlayingItemWithValues": "{0} elkezdte játszani a következőt: {1} itt: {2}",
"UserStoppedPlayingItemWithValues": "{0} befejezte a következőt: {1} itt: {2}", "UserStoppedPlayingItemWithValues": "{0} befejezte a következőt: {1} itt: {2}",
"ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz", "ValueHasBeenAddedToLibrary": "{0} hozzáadva a médiatárhoz",

View File

@ -5,26 +5,26 @@
"Artists": "Sanatçılar", "Artists": "Sanatçılar",
"AuthenticationSucceededWithUserName": "{0} kimlik başarıyla doğrulandı", "AuthenticationSucceededWithUserName": "{0} kimlik başarıyla doğrulandı",
"Books": "Kitaplar", "Books": "Kitaplar",
"CameraImageUploadedFrom": "A new camera image has been uploaded from {0}", "CameraImageUploadedFrom": "{0} 'den yeni bir kamera resmi yüklendi",
"Channels": "Kanallar", "Channels": "Kanallar",
"ChapterNameValue": "Chapter {0}", "ChapterNameValue": "Bölüm {0}",
"Collections": "Collections", "Collections": "Koleksiyonlar",
"DeviceOfflineWithName": "{0} has disconnected", "DeviceOfflineWithName": "{0} bağlantısı kesildi",
"DeviceOnlineWithName": "{0} is connected", "DeviceOnlineWithName": "{0} bağlı",
"FailedLoginAttemptWithUserName": "Failed login attempt from {0}", "FailedLoginAttemptWithUserName": "{0} adresinden giriş başarısız oldu",
"Favorites": "Favorites", "Favorites": "Favoriler",
"Folders": "Folders", "Folders": "Klasörler",
"Genres": "Genres", "Genres": "Türler",
"HeaderAlbumArtists": "Album Artists", "HeaderAlbumArtists": "Albüm Sanatçıları",
"HeaderCameraUploads": "Camera Uploads", "HeaderCameraUploads": "Kamera Yüklemeleri",
"HeaderContinueWatching": "İzlemeye Devam Et", "HeaderContinueWatching": "İzlemeye Devam Et",
"HeaderFavoriteAlbums": "Favori Albümler", "HeaderFavoriteAlbums": "Favori Albümler",
"HeaderFavoriteArtists": "Favorite Artists", "HeaderFavoriteArtists": "Favori Sanatçılar",
"HeaderFavoriteEpisodes": "Favorite Episodes", "HeaderFavoriteEpisodes": "Favori Bölümler",
"HeaderFavoriteShows": "Favori Showlar", "HeaderFavoriteShows": "Favori Diziler",
"HeaderFavoriteSongs": "Favorite Songs", "HeaderFavoriteSongs": "Favori Şarkılar",
"HeaderLiveTV": "Live TV", "HeaderLiveTV": "Canlı TV",
"HeaderNextUp": "Next Up", "HeaderNextUp": "Sonraki hafta",
"HeaderRecordingGroups": "Recording Groups", "HeaderRecordingGroups": "Recording Groups",
"HomeVideos": "Home videos", "HomeVideos": "Home videos",
"Inherit": "Inherit", "Inherit": "Inherit",
@ -38,7 +38,7 @@
"MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated", "MessageNamedServerConfigurationUpdatedWithValue": "Server configuration section {0} has been updated",
"MessageServerConfigurationUpdated": "Server configuration has been updated", "MessageServerConfigurationUpdated": "Server configuration has been updated",
"MixedContent": "Mixed content", "MixedContent": "Mixed content",
"Movies": "Movies", "Movies": "Filmler",
"Music": "Müzik", "Music": "Müzik",
"MusicVideos": "Müzik videoları", "MusicVideos": "Müzik videoları",
"NameInstallFailed": "{0} kurulum başarısız", "NameInstallFailed": "{0} kurulum başarısız",
@ -61,8 +61,8 @@
"NotificationOptionUserLockedOut": "User locked out", "NotificationOptionUserLockedOut": "User locked out",
"NotificationOptionVideoPlayback": "Video playback started", "NotificationOptionVideoPlayback": "Video playback started",
"NotificationOptionVideoPlaybackStopped": "Video playback stopped", "NotificationOptionVideoPlaybackStopped": "Video playback stopped",
"Photos": "Photos", "Photos": "Fotoğraflar",
"Playlists": "Playlists", "Playlists": "Çalma listeleri",
"Plugin": "Plugin", "Plugin": "Plugin",
"PluginInstalledWithName": "{0} was installed", "PluginInstalledWithName": "{0} was installed",
"PluginUninstalledWithName": "{0} was uninstalled", "PluginUninstalledWithName": "{0} was uninstalled",
@ -71,13 +71,13 @@
"ScheduledTaskFailedWithName": "{0} failed", "ScheduledTaskFailedWithName": "{0} failed",
"ScheduledTaskStartedWithName": "{0} started", "ScheduledTaskStartedWithName": "{0} started",
"ServerNameNeedsToBeRestarted": "{0} needs to be restarted", "ServerNameNeedsToBeRestarted": "{0} needs to be restarted",
"Shows": "Shows", "Shows": "Diziler",
"Songs": "Songs", "Songs": "Şarkılar",
"StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.", "StartupEmbyServerIsLoading": "Jellyfin Server is loading. Please try again shortly.",
"SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}", "SubtitleDownloadFailureForItem": "Subtitles failed to download for {0}",
"SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}", "SubtitleDownloadFailureFromForItem": "Subtitles failed to download from {0} for {1}",
"SubtitlesDownloadedForItem": "Subtitles downloaded for {0}", "SubtitlesDownloadedForItem": "Subtitles downloaded for {0}",
"Sync": "Sync", "Sync": "Eşitle",
"System": "System", "System": "System",
"TvShows": "TV Shows", "TvShows": "TV Shows",
"User": "User", "User": "User",
@ -92,6 +92,6 @@
"UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}", "UserStartedPlayingItemWithValues": "{0} is playing {1} on {2}",
"UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}", "UserStoppedPlayingItemWithValues": "{0} has finished playing {1} on {2}",
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library", "ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
"ValueSpecialEpisodeName": "Special - {0}", "ValueSpecialEpisodeName": "Özel -{0}",
"VersionNumber": "Version {0}" "VersionNumber": "Version {0}"
} }

View File

@ -20,6 +20,9 @@ namespace Emby.Server.Implementations.Networking
private IPAddress[] _localIpAddresses; private IPAddress[] _localIpAddresses;
private readonly object _localIpAddressSyncLock = new object(); private readonly object _localIpAddressSyncLock = new object();
private readonly object _subnetLookupLock = new object();
private Dictionary<string, List<string>> _subnetLookup = new Dictionary<string, List<string>>(StringComparer.Ordinal);
public NetworkManager(ILogger<NetworkManager> logger) public NetworkManager(ILogger<NetworkManager> logger)
{ {
_logger = logger; _logger = logger;
@ -28,10 +31,10 @@ namespace Emby.Server.Implementations.Networking
NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged; NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;
} }
public Func<string[]> LocalSubnetsFn { get; set; }
public event EventHandler NetworkChanged; public event EventHandler NetworkChanged;
public Func<string[]> LocalSubnetsFn { get; set; }
private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) private void OnNetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e)
{ {
_logger.LogDebug("NetworkAvailabilityChanged"); _logger.LogDebug("NetworkAvailabilityChanged");
@ -179,10 +182,9 @@ namespace Emby.Server.Implementations.Networking
return false; return false;
} }
private Dictionary<string, List<string>> _subnetLookup = new Dictionary<string, List<string>>(StringComparer.Ordinal);
private List<string> GetSubnets(string endpointFirstPart) private List<string> GetSubnets(string endpointFirstPart)
{ {
lock (_subnetLookup) lock (_subnetLookupLock)
{ {
if (_subnetLookup.TryGetValue(endpointFirstPart, out var subnets)) if (_subnetLookup.TryGetValue(endpointFirstPart, out var subnets))
{ {
@ -200,7 +202,11 @@ namespace Emby.Server.Implementations.Networking
int subnet_Test = 0; int subnet_Test = 0;
foreach (string part in unicastIPAddressInformation.IPv4Mask.ToString().Split('.')) foreach (string part in unicastIPAddressInformation.IPv4Mask.ToString().Split('.'))
{ {
if (part.Equals("0")) break; if (part.Equals("0", StringComparison.Ordinal))
{
break;
}
subnet_Test++; subnet_Test++;
} }

View File

@ -90,8 +90,7 @@ namespace Emby.Server.Implementations.Playlists
} }
else else
{ {
var folder = item as Folder; if (item is Folder folder)
if (folder != null)
{ {
options.MediaType = folder.GetRecursiveChildren(i => !i.IsFolder && i.SupportsAddingToPlaylist) options.MediaType = folder.GetRecursiveChildren(i => !i.IsFolder && i.SupportsAddingToPlaylist)
.Select(i => i.MediaType) .Select(i => i.MediaType)
@ -140,7 +139,7 @@ namespace Emby.Server.Implementations.Playlists
parentFolder.AddChild(playlist, CancellationToken.None); parentFolder.AddChild(playlist, CancellationToken.None);
await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) { ForceSave = true }, CancellationToken.None) await playlist.RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, CancellationToken.None)
.ConfigureAwait(false); .ConfigureAwait(false);
if (options.ItemIdList.Length > 0) if (options.ItemIdList.Length > 0)
@ -201,7 +200,7 @@ namespace Emby.Server.Implementations.Playlists
var list = new List<LinkedChild>(); var list = new List<LinkedChild>();
var items = (GetPlaylistItems(itemIds, playlist.MediaType, user, options)) var items = GetPlaylistItems(itemIds, playlist.MediaType, user, options)
.Where(i => i.SupportsAddingToPlaylist) .Where(i => i.SupportsAddingToPlaylist)
.ToList(); .ToList();
@ -221,18 +220,18 @@ namespace Emby.Server.Implementations.Playlists
SavePlaylistFile(playlist); SavePlaylistFile(playlist);
} }
_providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) _providerManager.QueueRefresh(
playlist.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
ForceSave = true ForceSave = true
},
}, RefreshPriority.High); RefreshPriority.High);
} }
public void RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds) public void RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds)
{ {
var playlist = _libraryManager.GetItemById(playlistId) as Playlist; if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
if (playlist == null)
{ {
throw new ArgumentException("No Playlist exists with the supplied Id"); throw new ArgumentException("No Playlist exists with the supplied Id");
} }
@ -254,7 +253,7 @@ namespace Emby.Server.Implementations.Playlists
SavePlaylistFile(playlist); SavePlaylistFile(playlist);
} }
_providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) _providerManager.QueueRefresh(playlist.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
ForceSave = true ForceSave = true
@ -263,9 +262,7 @@ namespace Emby.Server.Implementations.Playlists
public void MoveItem(string playlistId, string entryId, int newIndex) public void MoveItem(string playlistId, string entryId, int newIndex)
{ {
var playlist = _libraryManager.GetItemById(playlistId) as Playlist; if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
if (playlist == null)
{ {
throw new ArgumentException("No Playlist exists with the supplied Id"); throw new ArgumentException("No Playlist exists with the supplied Id");
} }

View File

@ -19,16 +19,17 @@ using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.ScheduledTasks namespace Emby.Server.Implementations.ScheduledTasks
{ {
/// <summary> /// <summary>
/// Class ChapterImagesTask /// Class ChapterImagesTask.
/// </summary> /// </summary>
public class ChapterImagesTask : IScheduledTask public class ChapterImagesTask : IScheduledTask
{ {
/// <summary> /// <summary>
/// The _logger /// The _logger.
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger _logger;
/// <summary> /// <summary>
/// The _library manager /// The _library manager.
/// </summary> /// </summary>
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
@ -53,12 +54,12 @@ namespace Emby.Server.Implementations.ScheduledTasks
} }
/// <summary> /// <summary>
/// Creates the triggers that define when the task will run /// Creates the triggers that define when the task will run.
/// </summary> /// </summary>
public IEnumerable<TaskTriggerInfo> GetDefaultTriggers() public IEnumerable<TaskTriggerInfo> GetDefaultTriggers()
{ {
return new[] { return new[]
{
new TaskTriggerInfo new TaskTriggerInfo
{ {
Type = TaskTriggerInfo.TriggerDaily, Type = TaskTriggerInfo.TriggerDaily,
@ -117,7 +118,7 @@ namespace Emby.Server.Implementations.ScheduledTasks
previouslyFailedImages = new List<string>(); previouslyFailedImages = new List<string>();
} }
var directoryService = new DirectoryService(_logger, _fileSystem); var directoryService = new DirectoryService(_fileSystem);
foreach (var video in videos) foreach (var video in videos)
{ {

View File

@ -1,11 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Concurrent;
using System.IO; using System.IO;
using System.Xml; using System.Xml;
using System.Xml.Serialization; using System.Xml.Serialization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Serialization namespace Emby.Server.Implementations.Serialization
{ {
@ -14,35 +12,13 @@ namespace Emby.Server.Implementations.Serialization
/// </summary> /// </summary>
public class MyXmlSerializer : IXmlSerializer public class MyXmlSerializer : IXmlSerializer
{ {
private readonly IFileSystem _fileSystem;
private readonly ILogger _logger;
public MyXmlSerializer(
IFileSystem fileSystem,
ILoggerFactory loggerFactory)
{
_fileSystem = fileSystem;
_logger = loggerFactory.CreateLogger("XmlSerializer");
}
// Need to cache these // Need to cache these
// http://dotnetcodebox.blogspot.com/2013/01/xmlserializer-class-may-result-in.html // http://dotnetcodebox.blogspot.com/2013/01/xmlserializer-class-may-result-in.html
private readonly Dictionary<string, XmlSerializer> _serializers = private static readonly ConcurrentDictionary<string, XmlSerializer> _serializers =
new Dictionary<string, XmlSerializer>(); new ConcurrentDictionary<string, XmlSerializer>();
private XmlSerializer GetSerializer(Type type) private static XmlSerializer GetSerializer(Type type)
{ => _serializers.GetOrAdd(type.FullName, _ => new XmlSerializer(type));
var key = type.FullName;
lock (_serializers)
{
if (!_serializers.TryGetValue(key, out var serializer))
{
serializer = new XmlSerializer(type);
_serializers[key] = serializer;
}
return serializer;
}
}
/// <summary> /// <summary>
/// Serializes to writer. /// Serializes to writer.
@ -91,7 +67,6 @@ namespace Emby.Server.Implementations.Serialization
/// <param name="file">The file.</param> /// <param name="file">The file.</param>
public void SerializeToFile(object obj, string file) public void SerializeToFile(object obj, string file)
{ {
_logger.LogDebug("Serializing to file {0}", file);
using (var stream = new FileStream(file, FileMode.Create)) using (var stream = new FileStream(file, FileMode.Create))
{ {
SerializeToStream(obj, stream); SerializeToStream(obj, stream);
@ -106,7 +81,6 @@ namespace Emby.Server.Implementations.Serialization
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
public object DeserializeFromFile(Type type, string file) public object DeserializeFromFile(Type type, string file)
{ {
_logger.LogDebug("Deserializing file {0}", file);
using (var stream = File.OpenRead(file)) using (var stream = File.OpenRead(file))
{ {
return DeserializeFromStream(type, stream); return DeserializeFromStream(type, stream);

View File

@ -6,7 +6,7 @@ using MediaBrowser.Controller;
namespace Emby.Server.Implementations namespace Emby.Server.Implementations
{ {
/// <summary> /// <summary>
/// Extends BaseApplicationPaths to add paths that are only applicable on the server /// Extends BaseApplicationPaths to add paths that are only applicable on the server.
/// </summary> /// </summary>
public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths public class ServerApplicationPaths : BaseApplicationPaths, IServerApplicationPaths
{ {

View File

@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
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.Net.Http; using System.Net.Http;
@ -19,7 +18,6 @@ using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Updates; using MediaBrowser.Model.Updates;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using static MediaBrowser.Common.HexHelper;
namespace Emby.Server.Implementations.Updates namespace Emby.Server.Implementations.Updates
{ {
@ -28,43 +26,10 @@ namespace Emby.Server.Implementations.Updates
/// </summary> /// </summary>
public class InstallationManager : IInstallationManager public class InstallationManager : IInstallationManager
{ {
public event EventHandler<InstallationEventArgs> PackageInstalling;
public event EventHandler<InstallationEventArgs> PackageInstallationCompleted;
public event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed;
public event EventHandler<InstallationEventArgs> PackageInstallationCancelled;
/// <summary> /// <summary>
/// The current installations /// The _logger.
/// </summary>
private List<(InstallationInfo info, CancellationTokenSource token)> _currentInstallations { get; set; }
/// <summary>
/// The completed installations
/// </summary>
private ConcurrentBag<InstallationInfo> _completedInstallationsInternal;
public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal;
/// <summary>
/// Occurs when [plugin uninstalled].
/// </summary>
public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
/// <summary>
/// Occurs when [plugin updated].
/// </summary>
public event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated;
/// <summary>
/// Occurs when [plugin updated].
/// </summary>
public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
/// <summary>
/// The _logger
/// </summary> /// </summary>
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly IApplicationPaths _appPaths; private readonly IApplicationPaths _appPaths;
private readonly IHttpClient _httpClient; private readonly IHttpClient _httpClient;
private readonly IJsonSerializer _jsonSerializer; private readonly IJsonSerializer _jsonSerializer;
@ -79,6 +44,18 @@ namespace Emby.Server.Implementations.Updates
private readonly IZipClient _zipClient; private readonly IZipClient _zipClient;
private readonly object _currentInstallationsLock = new object();
/// <summary>
/// The current installations.
/// </summary>
private List<(InstallationInfo info, CancellationTokenSource token)> _currentInstallations;
/// <summary>
/// The completed installations.
/// </summary>
private ConcurrentBag<InstallationInfo> _completedInstallationsInternal;
public InstallationManager( public InstallationManager(
ILogger<InstallationManager> logger, ILogger<InstallationManager> logger,
IApplicationHost appHost, IApplicationHost appHost,
@ -107,6 +84,31 @@ namespace Emby.Server.Implementations.Updates
_zipClient = zipClient; _zipClient = zipClient;
} }
public event EventHandler<InstallationEventArgs> PackageInstalling;
public event EventHandler<InstallationEventArgs> PackageInstallationCompleted;
public event EventHandler<InstallationFailedEventArgs> PackageInstallationFailed;
public event EventHandler<InstallationEventArgs> PackageInstallationCancelled;
/// <summary>
/// Occurs when a plugin is uninstalled.
/// </summary>
public event EventHandler<GenericEventArgs<IPlugin>> PluginUninstalled;
/// <summary>
/// Occurs when a plugin plugin is updated.
/// </summary>
public event EventHandler<GenericEventArgs<(IPlugin, PackageVersionInfo)>> PluginUpdated;
/// <summary>
/// Occurs when a plugin plugin is installed.
/// </summary>
public event EventHandler<GenericEventArgs<PackageVersionInfo>> PluginInstalled;
public IEnumerable<InstallationInfo> CompletedInstallations => _completedInstallationsInternal;
/// <summary> /// <summary>
/// Gets all available packages. /// Gets all available packages.
/// </summary> /// </summary>
@ -330,7 +332,7 @@ namespace Emby.Server.Implementations.Updates
var tuple = (installationInfo, innerCancellationTokenSource); var tuple = (installationInfo, innerCancellationTokenSource);
// Add it to the in-progress list // Add it to the in-progress list
lock (_currentInstallations) lock (_currentInstallationsLock)
{ {
_currentInstallations.Add(tuple); _currentInstallations.Add(tuple);
} }
@ -349,7 +351,7 @@ namespace Emby.Server.Implementations.Updates
{ {
await InstallPackageInternal(package, linkedToken).ConfigureAwait(false); await InstallPackageInternal(package, linkedToken).ConfigureAwait(false);
lock (_currentInstallations) lock (_currentInstallationsLock)
{ {
_currentInstallations.Remove(tuple); _currentInstallations.Remove(tuple);
} }
@ -360,7 +362,7 @@ namespace Emby.Server.Implementations.Updates
} }
catch (OperationCanceledException) catch (OperationCanceledException)
{ {
lock (_currentInstallations) lock (_currentInstallationsLock)
{ {
_currentInstallations.Remove(tuple); _currentInstallations.Remove(tuple);
} }
@ -375,7 +377,7 @@ namespace Emby.Server.Implementations.Updates
{ {
_logger.LogError(ex, "Package installation failed"); _logger.LogError(ex, "Package installation failed");
lock (_currentInstallations) lock (_currentInstallationsLock)
{ {
_currentInstallations.Remove(tuple); _currentInstallations.Remove(tuple);
} }
@ -455,7 +457,7 @@ namespace Emby.Server.Implementations.Updates
{ {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
var hash = ToHexString(md5.ComputeHash(stream)); var hash = Hex.Encode(md5.ComputeHash(stream));
if (!string.Equals(package.checksum, hash, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(package.checksum, hash, StringComparison.OrdinalIgnoreCase))
{ {
_logger.LogError( _logger.LogError(
@ -535,7 +537,7 @@ namespace Emby.Server.Implementations.Updates
/// <inheritdoc/> /// <inheritdoc/>
public bool CancelInstallation(Guid id) public bool CancelInstallation(Guid id)
{ {
lock (_currentInstallations) lock (_currentInstallationsLock)
{ {
var install = _currentInstallations.Find(x => x.Item1.Id == id); var install = _currentInstallations.Find(x => x.Item1.Id == id);
if (install == default((InstallationInfo, CancellationTokenSource))) if (install == default((InstallationInfo, CancellationTokenSource)))
@ -563,7 +565,7 @@ namespace Emby.Server.Implementations.Updates
{ {
if (dispose) if (dispose)
{ {
lock (_currentInstallations) lock (_currentInstallationsLock)
{ {
foreach (var tuple in _currentInstallations) foreach (var tuple in _currentInstallations)
{ {

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@ -9,8 +9,6 @@
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<!-- We need at least C# 7.1 for async main-->
<LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup> </PropertyGroup>
@ -25,8 +23,9 @@
<!-- Code analyzers--> <!-- Code analyzers-->
<ItemGroup Condition=" '$(Configuration)' == 'Debug' "> <ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" /> <PackageReference Include="Microsoft.CodeAnalysis.FxCopAnalyzers" Version="2.9.4" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SerilogAnalyzer" Version="0.15.0" /> <PackageReference Include="SerilogAnalyzer" Version="0.15.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.118" />
<PackageReference Include="SmartAnalyzers.MultithreadingAnalyzer" Version="1.1.31" />
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
@ -35,8 +34,8 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.6.0" /> <PackageReference Include="CommandLineParser" Version="2.6.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="2.2.4" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="3.0.0" />
<PackageReference Include="Serilog.AspNetCore" Version="3.0.0" /> <PackageReference Include="Serilog.AspNetCore" Version="3.0.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.4.0" /> <PackageReference Include="Serilog.Sinks.Async" Version="1.4.0" />

View File

@ -376,7 +376,7 @@ namespace Jellyfin.Server
return new ConfigurationBuilder() return new ConfigurationBuilder()
.SetBasePath(appPaths.ConfigurationDirectoryPath) .SetBasePath(appPaths.ConfigurationDirectoryPath)
.AddJsonFile("logging.json") .AddJsonFile("logging.json", false, true)
.AddEnvironmentVariables("JELLYFIN_") .AddEnvironmentVariables("JELLYFIN_")
.AddInMemoryCollection(ConfigurationOptions.Configuration) .AddInMemoryCollection(ConfigurationOptions.Configuration)
.Build(); .Build();

View File

@ -32,7 +32,7 @@ namespace MediaBrowser.Api
if (string.IsNullOrEmpty(val)) if (string.IsNullOrEmpty(val))
{ {
return new ItemFields[] { }; return Array.Empty<ItemFields>();
} }
return val.Split(',').Select(v => return val.Split(',').Select(v =>
@ -41,6 +41,7 @@ namespace MediaBrowser.Api
{ {
return (ItemFields?)value; return (ItemFields?)value;
} }
return null; return null;
}).Where(i => i.HasValue).Select(i => i.Value).ToArray(); }).Where(i => i.HasValue).Select(i => i.Value).ToArray();

View File

@ -227,15 +227,17 @@ namespace MediaBrowser.Api
//item.ProductionYear = request.ProductionYear; //item.ProductionYear = request.ProductionYear;
//item.Name = request.Name; //item.Name = request.Name;
return _providerManager.RefreshFullItem(item, new MetadataRefreshOptions(new DirectoryService(Logger, _fileSystem)) return _providerManager.RefreshFullItem(
item,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
MetadataRefreshMode = MetadataRefreshMode.FullRefresh, MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
ImageRefreshMode = MetadataRefreshMode.FullRefresh, ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllMetadata = true, ReplaceAllMetadata = true,
ReplaceAllImages = request.ReplaceAllImages, ReplaceAllImages = request.ReplaceAllImages,
SearchResult = request SearchResult = request
},
}, CancellationToken.None); CancellationToken.None);
} }
/// <summary> /// <summary>
@ -294,12 +296,10 @@ namespace MediaBrowser.Api
Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath)); Directory.CreateDirectory(Path.GetDirectoryName(fullCachePath));
using (var stream = result.Content) using (var stream = result.Content)
{
using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) using (var filestream = _fileSystem.GetFileStream(fullCachePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{ {
await stream.CopyToAsync(filestream).ConfigureAwait(false); await stream.CopyToAsync(filestream).ConfigureAwait(false);
} }
}
Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath)); Directory.CreateDirectory(Path.GetDirectoryName(pointerCachePath));
File.WriteAllText(pointerCachePath, fullCachePath); File.WriteAllText(pointerCachePath, fullCachePath);
@ -311,9 +311,6 @@ namespace MediaBrowser.Api
/// <param name="filename">The filename.</param> /// <param name="filename">The filename.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private string GetFullCachePath(string filename) private string GetFullCachePath(string filename)
{ => Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename);
return Path.Combine(_appPaths.CachePath, "remote-images", filename.Substring(0, 1), filename);
}
} }
} }

View File

@ -63,7 +63,7 @@ namespace MediaBrowser.Api
private MetadataRefreshOptions GetRefreshOptions(RefreshItem request) private MetadataRefreshOptions GetRefreshOptions(RefreshItem request)
{ {
return new MetadataRefreshOptions(new DirectoryService(_logger, _fileSystem)) return new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
MetadataRefreshMode = request.MetadataRefreshMode, MetadataRefreshMode = request.MetadataRefreshMode,
ImageRefreshMode = request.ImageRefreshMode, ImageRefreshMode = request.ImageRefreshMode,

View File

@ -225,13 +225,15 @@ namespace MediaBrowser.Api
if (displayOrderChanged) if (displayOrderChanged)
{ {
_providerManager.QueueRefresh(series.Id, new MetadataRefreshOptions(new DirectoryService(Logger, _fileSystem)) _providerManager.QueueRefresh(
series.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
MetadataRefreshMode = MetadataRefreshMode.FullRefresh, MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
ImageRefreshMode = MetadataRefreshMode.FullRefresh, ImageRefreshMode = MetadataRefreshMode.FullRefresh,
ReplaceAllMetadata = true ReplaceAllMetadata = true
},
}, RefreshPriority.High); RefreshPriority.High);
} }
} }

View File

@ -8,6 +8,7 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Api.UserLibrary; using MediaBrowser.Api.UserLibrary;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
@ -25,7 +26,6 @@ using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;
using Microsoft.Net.Http.Headers; using Microsoft.Net.Http.Headers;
using static MediaBrowser.Common.HexHelper;
namespace MediaBrowser.Api.LiveTv namespace MediaBrowser.Api.LiveTv
{ {
@ -887,8 +887,9 @@ namespace MediaBrowser.Api.LiveTv
{ {
// SchedulesDirect requires a SHA1 hash of the user's password // SchedulesDirect requires a SHA1 hash of the user's password
// https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#obtain-a-token // https://github.com/SchedulesDirect/JSON-Service/wiki/API-20141201#obtain-a-token
using (SHA1 sha = SHA1.Create()) { using (SHA1 sha = SHA1.Create())
return ToHexString( {
return Hex.Encode(
sha.ComputeHash(Encoding.UTF8.GetBytes(str))); sha.ComputeHash(Encoding.UTF8.GetBytes(str)));
} }
} }

View File

@ -10,7 +10,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@ -289,17 +289,22 @@ namespace MediaBrowser.Api.Playback
throw; throw;
} }
Logger.LogDebug("Launched ffmpeg process");
state.TranscodingJob = transcodingJob; state.TranscodingJob = transcodingJob;
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
_ = new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, logStream); _ = new JobLogger(Logger).StartStreamingLog(state, process.StandardError.BaseStream, logStream);
// Wait for the file to exist before proceeeding // Wait for the file to exist before proceeeding
while (!File.Exists(state.WaitForPath ?? outputPath) && !transcodingJob.HasExited) var ffmpegTargetFile = state.WaitForPath ?? outputPath;
Logger.LogDebug("Waiting for the creation of {0}", ffmpegTargetFile);
while (!File.Exists(ffmpegTargetFile) && !transcodingJob.HasExited)
{ {
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
} }
Logger.LogDebug("File {0} created or transcoding has finished", ffmpegTargetFile);
if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive && !transcodingJob.HasExited) if (state.IsInputVideo && transcodingJob.Type == TranscodingJobType.Progressive && !transcodingJob.HasExited)
{ {
await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false); await Task.Delay(1000, cancellationTokenSource.Token).ConfigureAwait(false);
@ -314,6 +319,7 @@ namespace MediaBrowser.Api.Playback
{ {
StartThrottler(state, transcodingJob); StartThrottler(state, transcodingJob);
} }
Logger.LogDebug("StartFfMpeg() finished successfully");
return transcodingJob; return transcodingJob;
} }

View File

@ -192,6 +192,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (File.Exists(segmentPath)) if (File.Exists(segmentPath))
{ {
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
Logger.LogDebug("returning {0} [it exists, try 1]", segmentPath);
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false); return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false);
} }
@ -207,6 +208,7 @@ namespace MediaBrowser.Api.Playback.Hls
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
transcodingLock.Release(); transcodingLock.Release();
released = true; released = true;
Logger.LogDebug("returning {0} [it exists, try 2]", segmentPath);
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false); return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false);
} }
else else
@ -243,6 +245,7 @@ namespace MediaBrowser.Api.Playback.Hls
request.StartTimeTicks = GetStartPositionTicks(state, requestedIndex); request.StartTimeTicks = GetStartPositionTicks(state, requestedIndex);
state.WaitForPath = segmentPath;
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false); job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
} }
catch catch
@ -277,7 +280,7 @@ namespace MediaBrowser.Api.Playback.Hls
// await Task.Delay(50, cancellationToken).ConfigureAwait(false); // await Task.Delay(50, cancellationToken).ConfigureAwait(false);
//} //}
Logger.LogInformation("returning {0}", segmentPath); Logger.LogDebug("returning {0} [general case]", segmentPath);
job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType); job = job ?? ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType);
return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false); return await GetSegmentResult(state, playlistPath, segmentPath, segmentExtension, requestedIndex, job, cancellationToken).ConfigureAwait(false);
} }
@ -458,56 +461,68 @@ namespace MediaBrowser.Api.Playback.Hls
TranscodingJob transcodingJob, TranscodingJob transcodingJob,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
var segmentFileExists = File.Exists(segmentPath); var segmentExists = File.Exists(segmentPath);
if (segmentExists)
// If all transcoding has completed, just return immediately
if (transcodingJob != null && transcodingJob.HasExited && segmentFileExists)
{ {
if (transcodingJob != null && transcodingJob.HasExited)
{
// Transcoding job is over, so assume all existing files are ready
Logger.LogDebug("serving up {0} as transcode is over", segmentPath);
return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false); return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
} }
if (segmentFileExists)
{
var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension); var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath, segmentExtension);
// If requested segment is less than transcoding position, we can't transcode backwards, so assume it's ready // If requested segment is less than transcoding position, we can't transcode backwards, so assume it's ready
if (segmentIndex < currentTranscodingIndex) if (segmentIndex < currentTranscodingIndex)
{ {
Logger.LogDebug("serving up {0} as transcode index {1} is past requested point {2}", segmentPath, currentTranscodingIndex, segmentIndex);
return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false); return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
} }
} }
var segmentFilename = Path.GetFileName(segmentPath); var nextSegmentPath = GetSegmentPath(state, playlistPath, segmentIndex + 1);
if (transcodingJob != null)
while (!cancellationToken.IsCancellationRequested)
{ {
try while (!cancellationToken.IsCancellationRequested && !transcodingJob.HasExited)
{ {
var text = File.ReadAllText(playlistPath, Encoding.UTF8); // To be considered ready, the segment file has to exist AND
// either the transcoding job should be done or next segment should also exist
// If it appears in the playlist, it's done if (segmentExists)
if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
{ {
if (!segmentFileExists) if (transcodingJob.HasExited || File.Exists(nextSegmentPath))
{
segmentFileExists = File.Exists(segmentPath);
}
if (segmentFileExists)
{ {
Logger.LogDebug("serving up {0} as it deemed ready", segmentPath);
return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false); return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
} }
//break;
} }
} else
catch (IOException)
{ {
// May get an error if the file is locked segmentExists = File.Exists(segmentPath);
if (segmentExists)
{
continue; // avoid unnecessary waiting if segment just became available
}
} }
await Task.Delay(100, cancellationToken).ConfigureAwait(false); await Task.Delay(100, cancellationToken).ConfigureAwait(false);
} }
if (!File.Exists(segmentPath))
{
Logger.LogWarning("cannot serve {0} as transcoding quit before we got there", segmentPath);
}
else
{
Logger.LogDebug("serving {0} as it's on disk and transcoding stopped", segmentPath);
}
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
}
else
{
Logger.LogWarning("cannot serve {0} as it doesn't exist and no transcode is running", segmentPath);
}
return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false); return await GetSegmentResult(state, segmentPath, segmentIndex, transcodingJob).ConfigureAwait(false);
} }
@ -521,6 +536,7 @@ namespace MediaBrowser.Api.Playback.Hls
FileShare = FileShareMode.ReadWrite, FileShare = FileShareMode.ReadWrite,
OnComplete = () => OnComplete = () =>
{ {
Logger.LogDebug("finished serving {0}", segmentPath);
if (transcodingJob != null) if (transcodingJob != null)
{ {
transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks); transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks);
@ -909,9 +925,23 @@ namespace MediaBrowser.Api.Playback.Hls
else else
{ {
var keyFrameArg = string.Format( var keyFrameArg = string.Format(
CultureInfo.InvariantCulture,
" -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"", " -force_key_frames:0 \"expr:gte(t,{0}+n_forced*{1})\"",
GetStartNumber(state) * state.SegmentLength, GetStartNumber(state) * state.SegmentLength,
state.SegmentLength.ToString(CultureInfo.InvariantCulture)); state.SegmentLength);
if (state.TargetFramerate.HasValue)
{
// This is to make sure keyframe interval is limited to our segment,
// as forcing keyframes is not enough.
// Example: we encoded half of desired length, then codec detected
// scene cut and inserted a keyframe; next forced keyframe would
// be created outside of segment, which breaks seeking.
keyFrameArg += string.Format(
CultureInfo.InvariantCulture,
" -g {0} -keyint_min {0}",
(int)(state.SegmentLength * state.TargetFramerate)
);
}
var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode; var hasGraphicalSubs = state.SubtitleStream != null && !state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode;
@ -955,6 +985,15 @@ namespace MediaBrowser.Api.Playback.Hls
var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, videoCodec); var threads = EncodingHelper.GetNumberOfThreads(state, encodingOptions, videoCodec);
if (state.BaseRequest.BreakOnNonKeyFrames)
{
// FIXME: this is actually a workaround, as ideally it really should be the client which decides whether non-keyframe
// breakpoints are supported; but current implementation always uses "ffmpeg input seeking" which is liable
// to produce a missing part of video stream before first keyframe is encountered, which may lead to
// awkward cases like a few starting HLS segments having no video whatsoever, which breaks hls.js
Logger.LogInformation("Current HLS implementation doesn't support non-keyframe breaks but one is requested, ignoring that request");
state.BaseRequest.BreakOnNonKeyFrames = false;
}
var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions); var inputModifier = EncodingHelper.GetInputModifier(state, encodingOptions);
// If isEncoding is true we're actually starting ffmpeg // If isEncoding is true we're actually starting ffmpeg
@ -965,14 +1004,6 @@ namespace MediaBrowser.Api.Playback.Hls
var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request); var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state.Request);
var timeDeltaParam = string.Empty;
if (isEncoding && state.TargetFramerate > 0)
{
float startTime = 1 / (state.TargetFramerate.Value * 2);
timeDeltaParam = string.Format(CultureInfo.InvariantCulture, "-segment_time_delta {0:F3}", startTime);
}
var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.'); var segmentFormat = GetSegmentFileExtension(state.Request).TrimStart('.');
if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase)) if (string.Equals(segmentFormat, "ts", StringComparison.OrdinalIgnoreCase))
{ {
@ -980,7 +1011,7 @@ namespace MediaBrowser.Api.Playback.Hls
} }
return string.Format( return string.Format(
"{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format {11} -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f hls -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"",
inputModifier, inputModifier,
EncodingHelper.GetInputArgument(state, encodingOptions), EncodingHelper.GetInputArgument(state, encodingOptions),
threads, threads,
@ -988,11 +1019,10 @@ namespace MediaBrowser.Api.Playback.Hls
GetVideoArguments(state, encodingOptions), GetVideoArguments(state, encodingOptions),
GetAudioArguments(state, encodingOptions), GetAudioArguments(state, encodingOptions),
state.SegmentLength.ToString(CultureInfo.InvariantCulture), state.SegmentLength.ToString(CultureInfo.InvariantCulture),
segmentFormat,
startNumberParam, startNumberParam,
outputPath,
outputTsArg, outputTsArg,
timeDeltaParam, outputPath
segmentFormat
).Trim(); ).Trim();
} }
} }

View File

@ -279,13 +279,12 @@ namespace MediaBrowser.Api.Subtitles
await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None) await _subtitleManager.DownloadSubtitles(video, request.SubtitleId, CancellationToken.None)
.ConfigureAwait(false); .ConfigureAwait(false);
_providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(new DirectoryService(Logger, _fileSystem)), RefreshPriority.High); _providerManager.QueueRefresh(video.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.High);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Error downloading subtitles"); Logger.LogError(ex, "Error downloading subtitles");
} }
}); });
} }
} }

View File

@ -109,6 +109,11 @@ namespace MediaBrowser.Api.UserLibrary
NameContains = query.NameContains ?? query.SearchTerm NameContains = query.NameContains ?? query.SearchTerm
}); });
if (query.IsFavorite ?? false && query.User != null)
{
items = items.Where(i => UserDataRepository.GetUserData(query.User, i).IsFavorite).ToList();
}
return new QueryResult<(BaseItem, ItemCounts)> return new QueryResult<(BaseItem, ItemCounts)>
{ {
TotalRecordCount = items.Count, TotalRecordCount = items.Count,

View File

@ -413,7 +413,7 @@ namespace MediaBrowser.Api.UserLibrary
if (!hasMetdata) if (!hasMetdata)
{ {
var options = new MetadataRefreshOptions(new DirectoryService(Logger, _fileSystem)) var options = new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {
MetadataRefreshMode = MetadataRefreshMode.FullRefresh, MetadataRefreshMode = MetadataRefreshMode.FullRefresh,
ImageRefreshMode = MetadataRefreshMode.FullRefresh, ImageRefreshMode = MetadataRefreshMode.FullRefresh,

View File

@ -4,7 +4,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text; using System.Text;
using static MediaBrowser.Common.HexHelper;
namespace MediaBrowser.Common.Cryptography namespace MediaBrowser.Common.Cryptography
{ {
@ -102,13 +101,13 @@ namespace MediaBrowser.Common.Cryptography
// Check if the string also contains a salt // Check if the string also contains a salt
if (splitted.Length - index == 2) if (splitted.Length - index == 2)
{ {
salt = FromHexString(splitted[index++]); salt = Hex.Decode(splitted[index++]);
hash = FromHexString(splitted[index++]); hash = Hex.Decode(splitted[index++]);
} }
else else
{ {
salt = Array.Empty<byte>(); salt = Array.Empty<byte>();
hash = FromHexString(splitted[index++]); hash = Hex.Decode(splitted[index++]);
} }
return new PasswordHash(id, hash, salt, parameters); return new PasswordHash(id, hash, salt, parameters);
@ -124,10 +123,10 @@ namespace MediaBrowser.Common.Cryptography
stringBuilder.Append('$'); stringBuilder.Append('$');
foreach (var pair in _parameters) foreach (var pair in _parameters)
{ {
stringBuilder.Append(pair.Key); stringBuilder.Append(pair.Key)
stringBuilder.Append('='); .Append('=')
stringBuilder.Append(pair.Value); .Append(pair.Value)
stringBuilder.Append(','); .Append(',');
} }
// Remove last ',' // Remove last ','
@ -137,21 +136,19 @@ namespace MediaBrowser.Common.Cryptography
/// <inheritdoc /> /// <inheritdoc />
public override string ToString() public override string ToString()
{ {
var str = new StringBuilder(); var str = new StringBuilder()
str.Append('$'); .Append('$')
str.Append(Id); .Append(Id);
SerializeParameters(str); SerializeParameters(str);
if (Salt.Length != 0) if (Salt.Length != 0)
{ {
str.Append('$'); str.Append('$')
str.Append(ToHexString(Salt)); .Append(Hex.Encode(Salt, false));
} }
str.Append('$'); return str.Append('$')
str.Append(ToHexString(Hash)); .Append(Hex.Encode(Hash, false)).ToString();
return str.ToString();
} }
} }
} }

View File

@ -1,48 +0,0 @@
#pragma warning disable CS1591
using System.Collections.Generic;
namespace MediaBrowser.Common.Extensions
{
// The MS CollectionExtensions are only available in netcoreapp
public static class CollectionExtensions
{
public static TValue GetValueOrDefault<TKey, TValue>(this IReadOnlyDictionary<TKey, TValue> dictionary, TKey key)
{
dictionary.TryGetValue(key, out var ret);
return ret;
}
/// <summary>
/// Copies all the elements of the current collection to the specified list
/// starting at the specified destination array index. The index is specified as a 32-bit integer.
/// </summary>
/// <param name="source">The current collection that is the source of the elements.</param>
/// <param name="destination">The list that is the destination of the elements copied from the current collection.</param>
/// <param name="index">A 32-bit integer that represents the index in <c>destination</c> at which copying begins.</param>
/// <typeparam name="T"></typeparam>
public static void CopyTo<T>(this IReadOnlyList<T> source, IList<T> destination, int index = 0)
{
for (int i = 0; i < source.Count; i++)
{
destination[index + i] = source[i];
}
}
/// <summary>
/// Copies all the elements of the current collection to the specified list
/// starting at the specified destination array index. The index is specified as a 32-bit integer.
/// </summary>
/// <param name="source">The current collection that is the source of the elements.</param>
/// <param name="destination">The list that is the destination of the elements copied from the current collection.</param>
/// <param name="index">A 32-bit integer that represents the index in <c>destination</c> at which copying begins.</param>
/// <typeparam name="T"></typeparam>
public static void CopyTo<T>(this IReadOnlyCollection<T> source, IList<T> destination, int index = 0)
{
foreach (T item in source)
{
destination[index++] = item;
}
}
}
}

View File

@ -0,0 +1,26 @@
using System.Collections.Generic;
namespace MediaBrowser.Common.Extensions
{
/// <summary>
/// Provides <c>CopyTo</c> extensions methods for <see cref="IReadOnlyList{T}" />.
/// </summary>
public static class CollectionExtensions
{
/// <summary>
/// Copies all the elements of the current collection to the specified list
/// starting at the specified destination array index. The index is specified as a 32-bit integer.
/// </summary>
/// <param name="source">The current collection that is the source of the elements.</param>
/// <param name="destination">The list that is the destination of the elements copied from the current collection.</param>
/// <param name="index">A 32-bit integer that represents the index in <c>destination</c> at which copying begins.</param>
/// <typeparam name="T"></typeparam>
public static void CopyTo<T>(this IReadOnlyList<T> source, IList<T> destination, int index = 0)
{
for (int i = 0; i < source.Count; i++)
{
destination[index + i] = source[i];
}
}
}
}

View File

@ -0,0 +1,94 @@
using System;
using System.Diagnostics.CodeAnalysis;
namespace MediaBrowser.Common
{
/// <summary>
/// Encoding and decoding hex strings.
/// </summary>
public static class Hex
{
internal const string HexCharsLower = "0123456789abcdef";
internal const string HexCharsUpper = "0123456789ABCDEF";
internal const int LastHexSymbol = 0x66; // 102: f
/// <summary>
/// Map from an ASCII char to its hex value shifted,
/// e.g. <c>b</c> -> 11. 0xFF means it's not a hex symbol.
/// </summary>
/// <value></value>
internal static ReadOnlySpan<byte> HexLookup => new byte[] {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
};
/// <summary>
/// Encodes <c>bytes</c> as a hex string.
/// </summary>
/// <param name="bytes"></param>
/// <param name="lowercase"></param>
/// <returns><c>bytes</c> as a hex string.</returns>
public static string Encode(ReadOnlySpan<byte> bytes, bool lowercase = true)
{
var hexChars = lowercase ? HexCharsLower : HexCharsUpper;
// TODO: use string.Create when it's supports spans
// Ref: https://github.com/dotnet/corefx/issues/29120
char[] s = new char[bytes.Length * 2];
int j = 0;
for (int i = 0; i < bytes.Length; i++)
{
s[j++] = hexChars[bytes[i] >> 4];
s[j++] = hexChars[bytes[i] & 0x0f];
}
return new string(s);
}
/// <summary>
/// Decodes a hex string into bytes.
/// </summary>
/// <param name="str">The <see cref="string" />.</param>
/// <returns>The decoded bytes.</returns>
public static byte[] Decode(ReadOnlySpan<char> str)
{
if (str.Length == 0)
{
return Array.Empty<byte>();
}
var unHex = HexLookup;
int byteLen = str.Length / 2;
byte[] bytes = new byte[byteLen];
int i = 0;
for (int j = 0; j < byteLen; j++)
{
byte a;
byte b;
if (str[i] > LastHexSymbol
|| (a = unHex[str[i++]]) == 0xFF
|| str[i] > LastHexSymbol
|| (b = unHex[str[i++]]) == 0xFF)
{
ThrowArgumentException(nameof(str));
break; // Unreachable
}
bytes[j] = (byte)((a * 16) | b);
}
return bytes;
}
[DoesNotReturn]
private static void ThrowArgumentException(string paramName)
=> throw new ArgumentException("Character is not a hex symbol.", paramName);
}
}

View File

@ -1,24 +0,0 @@
#pragma warning disable CS1591
using System;
using System.Globalization;
namespace MediaBrowser.Common
{
public static class HexHelper
{
public static byte[] FromHexString(string str)
{
byte[] bytes = new byte[str.Length / 2];
for (int i = 0; i < str.Length; i += 2)
{
bytes[i / 2] = byte.Parse(str.Substring(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture);
}
return bytes;
}
public static string ToHexString(byte[] bytes)
=> BitConverter.ToString(bytes).Replace("-", "");
}
}

View File

@ -12,7 +12,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.0" />
<PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.0" /> <PackageReference Include="Microsoft.Net.Http.Headers" Version="2.2.0" />
</ItemGroup> </ItemGroup>
@ -21,7 +21,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors> <TreatWarningsAsErrors>true</TreatWarningsAsErrors>

View File

@ -89,7 +89,7 @@ namespace MediaBrowser.Controller.Entities
{ {
var locations = PhysicalLocations; var locations = PhysicalLocations;
var newLocations = CreateResolveArgs(new DirectoryService(Logger, FileSystem), false).PhysicalLocations; var newLocations = CreateResolveArgs(new DirectoryService(FileSystem), false).PhysicalLocations;
if (!locations.SequenceEqual(newLocations)) if (!locations.SequenceEqual(newLocations))
{ {

View File

@ -1345,7 +1345,7 @@ namespace MediaBrowser.Controller.Entities
public Task RefreshMetadata(CancellationToken cancellationToken) public Task RefreshMetadata(CancellationToken cancellationToken)
{ {
return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)), cancellationToken); return RefreshMetadata(new MetadataRefreshOptions(new DirectoryService(FileSystem)), cancellationToken);
} }
protected virtual void TriggerOnRefreshStart() protected virtual void TriggerOnRefreshStart()
@ -2198,7 +2198,7 @@ namespace MediaBrowser.Controller.Entities
/// <returns>Task.</returns> /// <returns>Task.</returns>
public virtual void ChangedExternally() public virtual void ChangedExternally()
{ {
ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)) ProviderManager.QueueRefresh(Id, new MetadataRefreshOptions(new DirectoryService(FileSystem))
{ {
}, RefreshPriority.High); }, RefreshPriority.High);

View File

@ -171,7 +171,7 @@ namespace MediaBrowser.Controller.Entities
{ {
var locations = PhysicalLocations; var locations = PhysicalLocations;
var newLocations = CreateResolveArgs(new DirectoryService(Logger, FileSystem), false).PhysicalLocations; var newLocations = CreateResolveArgs(new DirectoryService(FileSystem), false).PhysicalLocations;
if (!locations.SequenceEqual(newLocations)) if (!locations.SequenceEqual(newLocations))
{ {

View File

@ -216,7 +216,7 @@ namespace MediaBrowser.Controller.Entities
public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken) public Task ValidateChildren(IProgress<double> progress, CancellationToken cancellationToken)
{ {
return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem))); return ValidateChildren(progress, cancellationToken, new MetadataRefreshOptions(new DirectoryService(FileSystem)));
} }
/// <summary> /// <summary>

View File

@ -148,7 +148,7 @@ namespace MediaBrowser.Controller.Entities
Name = newName; Name = newName;
return RefreshMetadata( return RefreshMetadata(
new MetadataRefreshOptions(new DirectoryService(Logger, FileSystem)) new MetadataRefreshOptions(new DirectoryService(FileSystem))
{ {
ReplaceAllMetadata = true, ReplaceAllMetadata = true,
ImageRefreshMode = MetadataRefreshMode.FullRefresh, ImageRefreshMode = MetadataRefreshMode.FullRefresh,

View File

@ -17,7 +17,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@ -2168,7 +2168,8 @@ namespace MediaBrowser.Controller.MediaEncoding
// Important: If this is ever re-enabled, make sure not to use it with wtv because it breaks seeking // Important: If this is ever re-enabled, make sure not to use it with wtv because it breaks seeking
if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase)
&& state.TranscodingType != TranscodingJobType.Progressive && state.TranscodingType != TranscodingJobType.Progressive
&& state.EnableBreakOnNonKeyFrames(outputVideoCodec)) && !state.EnableBreakOnNonKeyFrames(outputVideoCodec)
&& (state.BaseRequest.StartTimeTicks ?? 0) > 0)
{ {
inputModifier += " -noaccurate_seek"; inputModifier += " -noaccurate_seek";
} }

View File

@ -2,13 +2,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Controller.Providers namespace MediaBrowser.Controller.Providers
{ {
public class DirectoryService : IDirectoryService public class DirectoryService : IDirectoryService
{ {
private readonly ILogger _logger;
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly Dictionary<string, FileSystemMetadata[]> _cache = new Dictionary<string, FileSystemMetadata[]>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, FileSystemMetadata[]> _cache = new Dictionary<string, FileSystemMetadata[]>(StringComparer.OrdinalIgnoreCase);
@ -17,9 +15,8 @@ namespace MediaBrowser.Controller.Providers
private readonly Dictionary<string, List<string>> _filePathCache = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase); private readonly Dictionary<string, List<string>> _filePathCache = new Dictionary<string, List<string>>(StringComparer.OrdinalIgnoreCase);
public DirectoryService(ILogger logger, IFileSystem fileSystem) public DirectoryService(IFileSystem fileSystem)
{ {
_logger = logger;
_fileSystem = fileSystem; _fileSystem = fileSystem;
} }
@ -27,8 +24,6 @@ namespace MediaBrowser.Controller.Providers
{ {
if (!_cache.TryGetValue(path, out FileSystemMetadata[] entries)) if (!_cache.TryGetValue(path, out FileSystemMetadata[] entries))
{ {
//_logger.LogDebug("Getting files for " + path);
entries = _fileSystem.GetFileSystemEntries(path).ToArray(); entries = _fileSystem.GetFileSystemEntries(path).ToArray();
//_cache.TryAdd(path, entries); //_cache.TryAdd(path, entries);
@ -49,6 +44,7 @@ namespace MediaBrowser.Controller.Providers
list.Add(item); list.Add(item);
} }
} }
return list; return list;
} }
@ -89,6 +85,5 @@ namespace MediaBrowser.Controller.Providers
return result; return result;
} }
} }
} }

View File

@ -24,7 +24,8 @@ namespace MediaBrowser.Controller.Subtitles
/// <summary> /// <summary>
/// Searches the subtitles. /// Searches the subtitles.
/// </summary> /// </summary>
Task<RemoteSubtitleInfo[]> SearchSubtitles(Video video, Task<RemoteSubtitleInfo[]> SearchSubtitles(
Video video,
string language, string language,
bool? isPerfectMatch, bool? isPerfectMatch,
CancellationToken cancellationToken); CancellationToken cancellationToken);
@ -34,8 +35,9 @@ namespace MediaBrowser.Controller.Subtitles
/// </summary> /// </summary>
/// <param name="request">The request.</param> /// <param name="request">The request.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{IEnumerable{RemoteSubtitleInfo}}.</returns> /// <returns>Task{RemoteSubtitleInfo[]}.</returns>
Task<RemoteSubtitleInfo[]> SearchSubtitles(SubtitleSearchRequest request, Task<RemoteSubtitleInfo[]> SearchSubtitles(
SubtitleSearchRequest request,
CancellationToken cancellationToken); CancellationToken cancellationToken);
/// <summary> /// <summary>
@ -53,7 +55,7 @@ namespace MediaBrowser.Controller.Subtitles
/// </summary> /// </summary>
/// <param name="id">The identifier.</param> /// <param name="id">The identifier.</param>
/// <param name="cancellationToken">The cancellation token.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{SubtitleResponse}.</returns> /// <returns><see cref="Task{SubtitleResponse}" />.</returns>
Task<SubtitleResponse> GetRemoteSubtitles(string id, CancellationToken cancellationToken); Task<SubtitleResponse> GetRemoteSubtitles(string id, CancellationToken cancellationToken);
/// <summary> /// <summary>

View File

@ -10,7 +10,7 @@
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>
@ -18,7 +18,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="System.Text.Encoding.CodePages" Version="4.5.1" /> <PackageReference Include="System.Text.Encoding.CodePages" Version="4.6.0" />
<PackageReference Include="UTF.Unknown" Version="2.1.0" /> <PackageReference Include="UTF.Unknown" Version="2.1.0" />
</ItemGroup> </ItemGroup>

View File

@ -1,17 +1,17 @@
namespace MediaBrowser.Model.IO namespace MediaBrowser.Model.IO
{ {
/// <summary> /// <summary>
/// Class StreamDefaults /// Class StreamDefaults.
/// </summary> /// </summary>
public static class StreamDefaults public static class StreamDefaults
{ {
/// <summary> /// <summary>
/// The default copy to buffer size /// The default copy to buffer size.
/// </summary> /// </summary>
public const int DefaultCopyToBufferSize = 81920; public const int DefaultCopyToBufferSize = 81920;
/// <summary> /// <summary>
/// The default file stream buffer size /// The default file stream buffer size.
/// </summary> /// </summary>
public const int DefaultFileStreamBufferSize = 4096; public const int DefaultFileStreamBufferSize = 4096;
} }

View File

@ -15,7 +15,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="3.0.0" />
<PackageReference Include="System.Text.Json" Version="4.6.0" /> <PackageReference Include="System.Text.Json" Version="4.6.0" />
</ItemGroup> </ItemGroup>

View File

@ -16,9 +16,8 @@ namespace MediaBrowser.Providers.Books
ILogger logger, ILogger logger,
IProviderManager providerManager, IProviderManager providerManager,
IFileSystem fileSystem, IFileSystem fileSystem,
IUserDataManager userDataManager,
ILibraryManager libraryManager) ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{ {
} }

View File

@ -11,6 +11,17 @@ namespace MediaBrowser.Providers.Books
{ {
public class BookMetadataService : MetadataService<Book, BookInfo> public class BookMetadataService : MetadataService<Book, BookInfo>
{ {
public BookMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<Book> source, MetadataResult<Book> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
@ -20,9 +31,5 @@ namespace MediaBrowser.Providers.Books
target.Item.SeriesName = source.Item.SeriesName; target.Item.SeriesName = source.Item.SeriesName;
} }
} }
public BookMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
} }
} }

View File

@ -14,11 +14,35 @@ namespace MediaBrowser.Providers.BoxSets
{ {
public class BoxSetMetadataService : MetadataService<BoxSet, BoxSetInfo> public class BoxSetMetadataService : MetadataService<BoxSet, BoxSetInfo>
{ {
public BoxSetMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override bool EnableUpdatingGenresFromChildren => true;
/// <inheritdoc />
protected override bool EnableUpdatingOfficialRatingFromChildren => true;
/// <inheritdoc />
protected override bool EnableUpdatingStudiosFromChildren => true;
/// <inheritdoc />
protected override bool EnableUpdatingPremiereDateFromChildren => true;
/// <inheritdoc />
protected override IList<BaseItem> GetChildrenForMetadataUpdates(BoxSet item) protected override IList<BaseItem> GetChildrenForMetadataUpdates(BoxSet item)
{ {
return item.GetLinkedChildren(); return item.GetLinkedChildren();
} }
/// <inheritdoc />
protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<BoxSet> source, MetadataResult<BoxSet> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
@ -32,6 +56,7 @@ namespace MediaBrowser.Providers.BoxSets
} }
} }
/// <inheritdoc />
protected override ItemUpdateType BeforeSaveInternal(BoxSet item, bool isFullRefresh, ItemUpdateType currentUpdateType) protected override ItemUpdateType BeforeSaveInternal(BoxSet item, bool isFullRefresh, ItemUpdateType currentUpdateType)
{ {
var updateType = base.BeforeSaveInternal(item, isFullRefresh, currentUpdateType); var updateType = base.BeforeSaveInternal(item, isFullRefresh, currentUpdateType);
@ -47,17 +72,5 @@ namespace MediaBrowser.Providers.BoxSets
return updateType; return updateType;
} }
public BoxSetMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
protected override bool EnableUpdatingGenresFromChildren => true;
protected override bool EnableUpdatingOfficialRatingFromChildren => true;
protected override bool EnableUpdatingStudiosFromChildren => true;
protected override bool EnableUpdatingPremiereDateFromChildren => true;
} }
} }

View File

@ -11,13 +11,20 @@ namespace MediaBrowser.Providers.Channels
{ {
public class ChannelMetadataService : MetadataService<Channel, ItemLookupInfo> public class ChannelMetadataService : MetadataService<Channel, ItemLookupInfo>
{ {
public ChannelMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<Channel> source, MetadataResult<Channel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
} }
public ChannelMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
} }
} }

View File

@ -12,13 +12,20 @@ namespace MediaBrowser.Providers.Folders
{ {
public class CollectionFolderMetadataService : MetadataService<CollectionFolder, ItemLookupInfo> public class CollectionFolderMetadataService : MetadataService<CollectionFolder, ItemLookupInfo>
{ {
public CollectionFolderMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<CollectionFolder> source, MetadataResult<CollectionFolder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
} }
public CollectionFolderMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
} }
} }

View File

@ -11,16 +11,24 @@ namespace MediaBrowser.Providers.Folders
{ {
public class FolderMetadataService : MetadataService<Folder, ItemLookupInfo> public class FolderMetadataService : MetadataService<Folder, ItemLookupInfo>
{ {
public FolderMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
// Make sure the type-specific services get picked first // Make sure the type-specific services get picked first
public override int Order => 10; public override int Order => 10;
/// <inheritdoc />
protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<Folder> source, MetadataResult<Folder> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
} }
public FolderMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
} }
} }

View File

@ -11,13 +11,20 @@ namespace MediaBrowser.Providers.Folders
{ {
public class UserViewMetadataService : MetadataService<UserView, ItemLookupInfo> public class UserViewMetadataService : MetadataService<UserView, ItemLookupInfo>
{ {
public UserViewMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<UserView> source, MetadataResult<UserView> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
} }
public UserViewMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
} }
} }

View File

@ -11,13 +11,20 @@ namespace MediaBrowser.Providers.Genres
{ {
public class GenreMetadataService : MetadataService<Genre, ItemLookupInfo> public class GenreMetadataService : MetadataService<Genre, ItemLookupInfo>
{ {
public GenreMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<Genre> source, MetadataResult<Genre> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
} }
public GenreMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
} }
} }

View File

@ -11,13 +11,20 @@ namespace MediaBrowser.Providers.LiveTv
{ {
public class LiveTvMetadataService : MetadataService<LiveTvChannel, ItemLookupInfo> public class LiveTvMetadataService : MetadataService<LiveTvChannel, ItemLookupInfo>
{ {
public LiveTvMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<LiveTvChannel> source, MetadataResult<LiveTvChannel> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
} }
public LiveTvMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
} }
} }

View File

@ -340,7 +340,7 @@ namespace MediaBrowser.Providers.Manager
if (deleted) if (deleted)
{ {
item.ValidateImages(new DirectoryService(_logger, _fileSystem)); item.ValidateImages(new DirectoryService(_fileSystem));
} }
} }

View File

@ -23,16 +23,14 @@ namespace MediaBrowser.Providers.Manager
protected readonly ILogger Logger; protected readonly ILogger Logger;
protected readonly IProviderManager ProviderManager; protected readonly IProviderManager ProviderManager;
protected readonly IFileSystem FileSystem; protected readonly IFileSystem FileSystem;
protected readonly IUserDataManager UserDataManager;
protected readonly ILibraryManager LibraryManager; protected readonly ILibraryManager LibraryManager;
protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) protected MetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, ILibraryManager libraryManager)
{ {
ServerConfigurationManager = serverConfigurationManager; ServerConfigurationManager = serverConfigurationManager;
Logger = logger; Logger = logger;
ProviderManager = providerManager; ProviderManager = providerManager;
FileSystem = fileSystem; FileSystem = fileSystem;
UserDataManager = userDataManager;
LibraryManager = libraryManager; LibraryManager = libraryManager;
} }
@ -44,7 +42,7 @@ namespace MediaBrowser.Providers.Manager
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.LogError(ex, "Error getting file {path}", path); Logger.LogError(ex, "Error getting file {Path}", path);
return null; return null;
} }
} }

View File

@ -328,7 +328,7 @@ namespace MediaBrowser.Providers.Manager
return GetImageProviders(item, libraryOptions, options, return GetImageProviders(item, libraryOptions, options,
new ImageRefreshOptions( new ImageRefreshOptions(
new DirectoryService(_logger, _fileSystem)), new DirectoryService(_fileSystem)),
includeDisabled) includeDisabled)
.OfType<IRemoteImageProvider>(); .OfType<IRemoteImageProvider>();
} }
@ -507,7 +507,7 @@ namespace MediaBrowser.Providers.Manager
var imageProviders = GetImageProviders(dummy, libraryOptions, options, var imageProviders = GetImageProviders(dummy, libraryOptions, options,
new ImageRefreshOptions( new ImageRefreshOptions(
new DirectoryService(_logger, _fileSystem)), new DirectoryService(_fileSystem)),
true) true)
.ToList(); .ToList();

View File

@ -11,15 +11,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Configuration" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="2.2.0" /> <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.0.0" />
<PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" /> <PackageReference Include="OptimizedPriorityQueue" Version="4.2.0" />
<PackageReference Include="PlaylistsNET" Version="1.0.4" /> <PackageReference Include="PlaylistsNET" Version="1.0.4" />
<PackageReference Include="TvDbSharper" Version="2.0.0" /> <PackageReference Include="TvDbSharper" Version="2.0.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework> <TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo> <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup> </PropertyGroup>

View File

@ -9,17 +9,20 @@ namespace MediaBrowser.Providers.Movies
{ {
public class ImdbExternalId : IExternalId public class ImdbExternalId : IExternalId
{ {
/// <inheritdoc />
public string Name => "IMDb"; public string Name => "IMDb";
/// <inheritdoc />
public string Key => MetadataProviders.Imdb.ToString(); public string Key => MetadataProviders.Imdb.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.imdb.com/title/{0}"; public string UrlFormatString => "https://www.imdb.com/title/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) public bool Supports(IHasProviderIds item)
{ {
// Supports images for tv movies // Supports images for tv movies
var tvProgram = item as LiveTvProgram; if (item is LiveTvProgram tvProgram && tvProgram.IsMovie)
if (tvProgram != null && tvProgram.IsMovie)
{ {
return true; return true;
} }
@ -28,18 +31,18 @@ namespace MediaBrowser.Providers.Movies
} }
} }
public class ImdbPersonExternalId : IExternalId public class ImdbPersonExternalId : IExternalId
{ {
/// <inheritdoc />
public string Name => "IMDb"; public string Name => "IMDb";
/// <inheritdoc />
public string Key => MetadataProviders.Imdb.ToString(); public string Key => MetadataProviders.Imdb.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.imdb.com/name/{0}"; public string UrlFormatString => "https://www.imdb.com/name/{0}";
public bool Supports(IHasProviderIds item) /// <inheritdoc />
{ public bool Supports(IHasProviderIds item) => item is Person;
return item is Person;
}
} }
} }

View File

@ -1,5 +1,4 @@
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
@ -12,6 +11,17 @@ namespace MediaBrowser.Providers.Movies
{ {
public class MovieMetadataService : MetadataService<Movie, MovieInfo> public class MovieMetadataService : MetadataService<Movie, MovieInfo>
{ {
public MovieMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override bool IsFullLocalMetadata(Movie item) protected override bool IsFullLocalMetadata(Movie item)
{ {
if (string.IsNullOrWhiteSpace(item.Overview)) if (string.IsNullOrWhiteSpace(item.Overview))
@ -25,6 +35,7 @@ namespace MediaBrowser.Providers.Movies
return base.IsFullLocalMetadata(item); return base.IsFullLocalMetadata(item);
} }
/// <inheritdoc />
protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<Movie> source, MetadataResult<Movie> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
@ -37,40 +48,5 @@ namespace MediaBrowser.Providers.Movies
targetItem.CollectionName = sourceItem.CollectionName; targetItem.CollectionName = sourceItem.CollectionName;
} }
} }
public MovieMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
} }
} }
public class TrailerMetadataService : MetadataService<Trailer, TrailerInfo>
{
protected override bool IsFullLocalMetadata(Trailer item)
{
if (string.IsNullOrWhiteSpace(item.Overview))
{
return false;
}
if (!item.ProductionYear.HasValue)
{
return false;
}
return base.IsFullLocalMetadata(item);
}
protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
if (replaceData || target.Item.TrailerTypes.Length == 0)
{
target.Item.TrailerTypes = source.Item.TrailerTypes;
}
}
public TrailerMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
}
}

View File

@ -0,0 +1,49 @@
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Providers.Manager;
using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Movies
{
public class TrailerMetadataService : MetadataService<Trailer, TrailerInfo>
{
public TrailerMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override bool IsFullLocalMetadata(Trailer item)
{
if (string.IsNullOrWhiteSpace(item.Overview))
{
return false;
}
if (!item.ProductionYear.HasValue)
{
return false;
}
return base.IsFullLocalMetadata(item);
}
/// <inheritdoc />
protected override void MergeData(MetadataResult<Trailer> source, MetadataResult<Trailer> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
if (replaceData || target.Item.TrailerTypes.Length == 0)
{
target.Item.TrailerTypes = source.Item.TrailerTypes;
}
}
}
}

View File

@ -20,9 +20,8 @@ namespace MediaBrowser.Providers.Music
ILogger logger, ILogger logger,
IProviderManager providerManager, IProviderManager providerManager,
IFileSystem fileSystem, IFileSystem fileSystem,
IUserDataManager userDataManager,
ILibraryManager libraryManager) ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{ {
} }
@ -37,10 +36,7 @@ namespace MediaBrowser.Providers.Music
/// <inheritdoc /> /// <inheritdoc />
protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicAlbum item) protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicAlbum item)
{ => item.GetRecursiveChildren(i => i is Audio);
return item.GetRecursiveChildren(i => i is Audio)
.ToList();
}
/// <inheritdoc /> /// <inheritdoc />
protected override ItemUpdateType UpdateMetadataFromChildren(MusicAlbum item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType) protected override ItemUpdateType UpdateMetadataFromChildren(MusicAlbum item, IList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
@ -53,20 +49,18 @@ namespace MediaBrowser.Providers.Music
{ {
var name = children.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i)); var name = children.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i));
if (!string.IsNullOrEmpty(name)) if (!string.IsNullOrEmpty(name)
{ && !string.Equals(item.Name, name, StringComparison.Ordinal))
if (!string.Equals(item.Name, name, StringComparison.Ordinal))
{ {
item.Name = name; item.Name = name;
updateType = updateType | ItemUpdateType.MetadataEdit; updateType |= ItemUpdateType.MetadataEdit;
}
} }
} }
var songs = children.Cast<Audio>().ToArray(); var songs = children.Cast<Audio>().ToArray();
updateType = updateType | SetAlbumArtistFromSongs(item, songs); updateType |= SetAlbumArtistFromSongs(item, songs);
updateType = updateType | SetArtistsFromSongs(item, songs); updateType |= SetArtistsFromSongs(item, songs);
} }
return updateType; return updateType;

View File

@ -13,26 +13,35 @@ namespace MediaBrowser.Providers.Music
{ {
public class ArtistMetadataService : MetadataService<MusicArtist, ArtistInfo> public class ArtistMetadataService : MetadataService<MusicArtist, ArtistInfo>
{ {
public ArtistMetadataService(
IServerConfigurationManager serverConfigurationManager,
ILogger logger,
IProviderManager providerManager,
IFileSystem fileSystem,
ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{
}
/// <inheritdoc />
protected override bool EnableUpdatingGenresFromChildren => true;
/// <inheritdoc />
protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicArtist item) protected override IList<BaseItem> GetChildrenForMetadataUpdates(MusicArtist item)
{ {
return item.IsAccessedByName ? return item.IsAccessedByName
item.GetTaggedItems(new InternalItemsQuery ? item.GetTaggedItems(new InternalItemsQuery
{ {
Recursive = true, Recursive = true,
IsFolder = false IsFolder = false
}) : })
item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder); : item.GetRecursiveChildren(i => i is IHasArtist && !i.IsFolder);
} }
protected override bool EnableUpdatingGenresFromChildren => true; /// <inheritdoc />
protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings) protected override void MergeData(MetadataResult<MusicArtist> source, MetadataResult<MusicArtist> target, MetadataFields[] lockedFields, bool replaceData, bool mergeMetadataSettings)
{ {
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
} }
public ArtistMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager)
{
}
} }
} }

View File

@ -25,6 +25,14 @@ namespace MediaBrowser.Providers.Music
_json = json; _json = json;
} }
/// <inheritdoc />
public string Name => "TheAudioDB";
/// <inheritdoc />
// After embedded and fanart
public int Order => 2;
/// <inheritdoc />
public IEnumerable<ImageType> GetSupportedImages(BaseItem item) public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{ {
return new List<ImageType> return new List<ImageType>
@ -34,6 +42,7 @@ namespace MediaBrowser.Providers.Music
}; };
} }
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{ {
var id = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup); var id = item.GetProviderId(MetadataProviders.MusicBrainzReleaseGroup);
@ -82,6 +91,7 @@ namespace MediaBrowser.Providers.Music
return list; return list;
} }
/// <inheritdoc />
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{ {
return _httpClient.GetResponse(new HttpRequestOptions return _httpClient.GetResponse(new HttpRequestOptions
@ -91,13 +101,7 @@ namespace MediaBrowser.Providers.Music
}); });
} }
public string Name => "TheAudioDB"; /// <inheritdoc />
// After embedded and fanart public bool Supports(BaseItem item) => item is MusicAlbum;
public int Order => 2;
public bool Supports(BaseItem item)
{
return item is MusicAlbum;
}
} }
} }

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@ -26,8 +27,6 @@ namespace MediaBrowser.Providers.Music
public static AudioDbAlbumProvider Current; public static AudioDbAlbumProvider Current;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json) public AudioDbAlbumProvider(IServerConfigurationManager config, IFileSystem fileSystem, IHttpClient httpClient, IJsonSerializer json)
{ {
_config = config; _config = config;
@ -38,11 +37,18 @@ namespace MediaBrowser.Providers.Music
Current = this; Current = this;
} }
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken) /// <inheritdoc />
{ public string Name => "TheAudioDB";
return Task.FromResult((IEnumerable<RemoteSearchResult>)new List<RemoteSearchResult>());
}
/// <inheritdoc />
// After music brainz
public int Order => 1;
/// <inheritdoc />
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
=> Task.FromResult(Enumerable.Empty<RemoteSearchResult>());
/// <inheritdoc />
public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo info, CancellationToken cancellationToken) public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo info, CancellationToken cancellationToken)
{ {
var result = new MetadataResult<MusicAlbum>(); var result = new MetadataResult<MusicAlbum>();
@ -77,7 +83,7 @@ namespace MediaBrowser.Providers.Music
if (!string.IsNullOrEmpty(result.intYearReleased)) if (!string.IsNullOrEmpty(result.intYearReleased))
{ {
item.ProductionYear = int.Parse(result.intYearReleased, _usCulture); item.ProductionYear = int.Parse(result.intYearReleased, CultureInfo.InvariantCulture);
} }
if (!string.IsNullOrEmpty(result.strGenre)) if (!string.IsNullOrEmpty(result.strGenre))
@ -126,8 +132,6 @@ namespace MediaBrowser.Providers.Music
item.Overview = (overview ?? string.Empty).StripHtml(); item.Overview = (overview ?? string.Empty).StripHtml();
} }
public string Name => "TheAudioDB";
internal Task EnsureInfo(string musicBrainzReleaseGroupId, CancellationToken cancellationToken) internal Task EnsureInfo(string musicBrainzReleaseGroupId, CancellationToken cancellationToken)
{ {
var xmlPath = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId); var xmlPath = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId);
@ -155,22 +159,20 @@ namespace MediaBrowser.Providers.Music
Directory.CreateDirectory(Path.GetDirectoryName(path)); Directory.CreateDirectory(Path.GetDirectoryName(path));
using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions using (var httpResponse = await _httpClient.SendAsync(
new HttpRequestOptions
{ {
Url = url, Url = url,
CancellationToken = cancellationToken CancellationToken = cancellationToken
}, "GET").ConfigureAwait(false)) },
{ "GET").ConfigureAwait(false))
using (var response = httpResponse.Content) using (var response = httpResponse.Content)
{
using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{ {
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
} }
} }
}
}
private static string GetAlbumDataPath(IApplicationPaths appPaths, string musicBrainzReleaseGroupId) private static string GetAlbumDataPath(IApplicationPaths appPaths, string musicBrainzReleaseGroupId)
{ {
@ -192,8 +194,6 @@ namespace MediaBrowser.Providers.Music
return Path.Combine(dataPath, "album.json"); return Path.Combine(dataPath, "album.json");
} }
// After music brainz
public int Order => 1;
public class Album public class Album
{ {
@ -242,6 +242,7 @@ namespace MediaBrowser.Providers.Music
public List<Album> album { get; set; } public List<Album> album { get; set; }
} }
/// <inheritdoc />
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View File

@ -25,6 +25,14 @@ namespace MediaBrowser.Providers.Music
_httpClient = httpClient; _httpClient = httpClient;
} }
/// <inheritdoc />
public string Name => "TheAudioDB";
/// <inheritdoc />
// After fanart
public int Order => 1;
/// <inheritdoc />
public IEnumerable<ImageType> GetSupportedImages(BaseItem item) public IEnumerable<ImageType> GetSupportedImages(BaseItem item)
{ {
return new List<ImageType> return new List<ImageType>
@ -36,6 +44,7 @@ namespace MediaBrowser.Providers.Music
}; };
} }
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{ {
var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist); var id = item.GetProviderId(MetadataProviders.MusicBrainzArtist);
@ -133,13 +142,7 @@ namespace MediaBrowser.Providers.Music
}); });
} }
public string Name => "TheAudioDB"; /// <inheritdoc />
public bool Supports(BaseItem item) => item is MusicArtist;
public bool Supports(BaseItem item)
{
return item is MusicArtist;
}
// After fanart
public int Order => 1;
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
@ -37,11 +38,18 @@ namespace MediaBrowser.Providers.Music
Current = this; Current = this;
} }
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) /// <inheritdoc />
{ public string Name => "TheAudioDB";
return Task.FromResult((IEnumerable<RemoteSearchResult>)new List<RemoteSearchResult>());
}
/// <inheritdoc />
// After musicbrainz
public int Order => 1;
/// <inheritdoc />
public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
=> Task.FromResult(Enumerable.Empty<RemoteSearchResult>());
/// <inheritdoc />
public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken) public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
{ {
var result = new MetadataResult<MusicArtist>(); var result = new MetadataResult<MusicArtist>();
@ -114,21 +122,17 @@ namespace MediaBrowser.Providers.Music
item.Overview = (overview ?? string.Empty).StripHtml(); item.Overview = (overview ?? string.Empty).StripHtml();
} }
public string Name => "TheAudioDB";
internal Task EnsureArtistInfo(string musicBrainzId, CancellationToken cancellationToken) internal Task EnsureArtistInfo(string musicBrainzId, CancellationToken cancellationToken)
{ {
var xmlPath = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); var xmlPath = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
var fileInfo = _fileSystem.GetFileSystemInfo(xmlPath); var fileInfo = _fileSystem.GetFileSystemInfo(xmlPath);
if (fileInfo.Exists) if (fileInfo.Exists
{ && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
if ((DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
{ {
return Task.CompletedTask; return Task.CompletedTask;
} }
}
return DownloadArtistInfo(musicBrainzId, cancellationToken); return DownloadArtistInfo(musicBrainzId, cancellationToken);
} }
@ -141,14 +145,14 @@ namespace MediaBrowser.Providers.Music
var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId); var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
using (var httpResponse = await _httpClient.SendAsync(new HttpRequestOptions using (var httpResponse = await _httpClient.SendAsync(
new HttpRequestOptions
{ {
Url = url, Url = url,
CancellationToken = cancellationToken, CancellationToken = cancellationToken,
BufferContent = true BufferContent = true
},
}, "GET").ConfigureAwait(false)) "GET").ConfigureAwait(false))
{
using (var response = httpResponse.Content) using (var response = httpResponse.Content)
{ {
Directory.CreateDirectory(Path.GetDirectoryName(path)); Directory.CreateDirectory(Path.GetDirectoryName(path));
@ -159,7 +163,6 @@ namespace MediaBrowser.Providers.Music
} }
} }
} }
}
/// <summary> /// <summary>
/// Gets the artist data path. /// Gets the artist data path.
@ -168,11 +171,7 @@ namespace MediaBrowser.Providers.Music
/// <param name="musicBrainzArtistId">The music brainz artist identifier.</param> /// <param name="musicBrainzArtistId">The music brainz artist identifier.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private static string GetArtistDataPath(IApplicationPaths appPaths, string musicBrainzArtistId) private static string GetArtistDataPath(IApplicationPaths appPaths, string musicBrainzArtistId)
{ => Path.Combine(GetArtistDataPath(appPaths), musicBrainzArtistId);
var dataPath = Path.Combine(GetArtistDataPath(appPaths), musicBrainzArtistId);
return dataPath;
}
/// <summary> /// <summary>
/// Gets the artist data path. /// Gets the artist data path.
@ -180,11 +179,7 @@ namespace MediaBrowser.Providers.Music
/// <param name="appPaths">The application paths.</param> /// <param name="appPaths">The application paths.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
private static string GetArtistDataPath(IApplicationPaths appPaths) private static string GetArtistDataPath(IApplicationPaths appPaths)
{ => Path.Combine(appPaths.CachePath, "audiodb-artist");
var dataPath = Path.Combine(appPaths.CachePath, "audiodb-artist");
return dataPath;
}
internal static string GetArtistInfoPath(IApplicationPaths appPaths, string musicBrainzArtistId) internal static string GetArtistInfoPath(IApplicationPaths appPaths, string musicBrainzArtistId)
{ {
@ -242,9 +237,8 @@ namespace MediaBrowser.Providers.Music
{ {
public List<Artist> artists { get; set; } public List<Artist> artists { get; set; }
} }
// After musicbrainz
public int Order => 1;
/// <inheritdoc />
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
{ {
throw new NotImplementedException(); throw new NotImplementedException();

View File

@ -6,58 +6,63 @@ namespace MediaBrowser.Providers.Music
{ {
public class AudioDbAlbumExternalId : IExternalId public class AudioDbAlbumExternalId : IExternalId
{ {
/// <inheritdoc />
public string Name => "TheAudioDb"; public string Name => "TheAudioDb";
/// <inheritdoc />
public string Key => MetadataProviders.AudioDbAlbum.ToString(); public string Key => MetadataProviders.AudioDbAlbum.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) public bool Supports(IHasProviderIds item)
{ => item is MusicAlbum;
return item is MusicAlbum;
}
} }
public class AudioDbOtherAlbumExternalId : IExternalId public class AudioDbOtherAlbumExternalId : IExternalId
{ {
/// <inheritdoc />
public string Name => "TheAudioDb Album"; public string Name => "TheAudioDb Album";
/// <inheritdoc />
public string Key => MetadataProviders.AudioDbAlbum.ToString(); public string Key => MetadataProviders.AudioDbAlbum.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/album/{0}"; public string UrlFormatString => "https://www.theaudiodb.com/album/{0}";
public bool Supports(IHasProviderIds item) /// <inheritdoc />
{ public bool Supports(IHasProviderIds item) => item is Audio;
return item is Audio;
}
} }
public class AudioDbArtistExternalId : IExternalId public class AudioDbArtistExternalId : IExternalId
{ {
/// <inheritdoc />
public string Name => "TheAudioDb"; public string Name => "TheAudioDb";
/// <inheritdoc />
public string Key => MetadataProviders.AudioDbArtist.ToString(); public string Key => MetadataProviders.AudioDbArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
public bool Supports(IHasProviderIds item) /// <inheritdoc />
{ public bool Supports(IHasProviderIds item) => item is MusicArtist;
return item is MusicArtist;
}
} }
public class AudioDbOtherArtistExternalId : IExternalId public class AudioDbOtherArtistExternalId : IExternalId
{ {
/// <inheritdoc />
public string Name => "TheAudioDb Artist"; public string Name => "TheAudioDb Artist";
/// <inheritdoc />
public string Key => MetadataProviders.AudioDbArtist.ToString(); public string Key => MetadataProviders.AudioDbArtist.ToString();
/// <inheritdoc />
public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}"; public string UrlFormatString => "https://www.theaudiodb.com/artist/{0}";
/// <inheritdoc />
public bool Supports(IHasProviderIds item) public bool Supports(IHasProviderIds item)
{ => item is Audio || item is MusicAlbum;
return item is Audio || item is MusicAlbum;
} }
} }
}

View File

@ -16,9 +16,8 @@ namespace MediaBrowser.Providers.Music
ILogger logger, ILogger logger,
IProviderManager providerManager, IProviderManager providerManager,
IFileSystem fileSystem, IFileSystem fileSystem,
IUserDataManager userDataManager,
ILibraryManager libraryManager) ILibraryManager libraryManager)
: base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager)
{ {
} }

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