mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-31 14:33:54 -04:00
Merge remote-tracking branch 'upstream/master' into syncplay
This commit is contained in:
commit
029bb80910
@ -1,59 +0,0 @@
|
|||||||
VERSION := $(shell sed -ne '/^Version:/s/.* *//p' \
|
|
||||||
deployment/fedora-package-x64/pkg-src/jellyfin.spec)
|
|
||||||
|
|
||||||
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; \
|
|
||||||
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 \
|
|
||||||
--define "_sourcedir $$PWD/pkg-src/" \
|
|
||||||
--define "_srcrpmdir $(outdir)"
|
|
1
.copr/Makefile
Symbolic link
1
.copr/Makefile
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../fedora/Makefile
|
@ -13,7 +13,7 @@ charset = utf-8
|
|||||||
trim_trailing_whitespace = true
|
trim_trailing_whitespace = true
|
||||||
insert_final_newline = true
|
insert_final_newline = true
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
max_line_length = null
|
max_line_length = off
|
||||||
|
|
||||||
# YAML indentation
|
# YAML indentation
|
||||||
[*.{yml,yaml}]
|
[*.{yml,yaml}]
|
||||||
@ -22,6 +22,7 @@ indent_size = 2
|
|||||||
# XML indentation
|
# XML indentation
|
||||||
[*.{csproj,xml}]
|
[*.{csproj,xml}]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# .NET Coding Conventions #
|
# .NET Coding Conventions #
|
||||||
###############################
|
###############################
|
||||||
@ -51,11 +52,12 @@ dotnet_style_explicit_tuple_names = true:suggestion
|
|||||||
dotnet_style_null_propagation = true:suggestion
|
dotnet_style_null_propagation = true:suggestion
|
||||||
dotnet_style_coalesce_expression = true:suggestion
|
dotnet_style_coalesce_expression = true:suggestion
|
||||||
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent
|
||||||
dotnet_prefer_inferred_tuple_names = true:suggestion
|
dotnet_style_prefer_inferred_tuple_names = true:suggestion
|
||||||
dotnet_prefer_inferred_anonymous_type_member_names = true:suggestion
|
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
|
||||||
dotnet_style_prefer_auto_properties = true:silent
|
dotnet_style_prefer_auto_properties = true:silent
|
||||||
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
dotnet_style_prefer_conditional_expression_over_assignment = true:silent
|
||||||
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
dotnet_style_prefer_conditional_expression_over_return = true:silent
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# Naming Conventions #
|
# Naming Conventions #
|
||||||
###############################
|
###############################
|
||||||
@ -67,7 +69,7 @@ dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non
|
|||||||
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
|
dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style
|
||||||
|
|
||||||
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
|
dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field
|
||||||
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected
|
dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected_internal, private_protected
|
||||||
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
|
dotnet_naming_symbols.non_private_static_fields.required_modifiers = static
|
||||||
|
|
||||||
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
|
dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case
|
||||||
@ -159,6 +161,7 @@ csharp_style_deconstructed_variable_declaration = true:suggestion
|
|||||||
csharp_prefer_simple_default_expression = true:suggestion
|
csharp_prefer_simple_default_expression = true:suggestion
|
||||||
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
csharp_style_pattern_local_over_anonymous_function = true:suggestion
|
||||||
csharp_style_inlined_variable_declaration = true:suggestion
|
csharp_style_inlined_variable_declaration = true:suggestion
|
||||||
|
|
||||||
###############################
|
###############################
|
||||||
# C# Formatting Rules #
|
# C# Formatting Rules #
|
||||||
###############################
|
###############################
|
||||||
@ -189,9 +192,3 @@ csharp_space_between_method_call_empty_parameter_list_parentheses = false
|
|||||||
# Wrapping preferences
|
# Wrapping preferences
|
||||||
csharp_preserve_single_line_statements = true
|
csharp_preserve_single_line_statements = true
|
||||||
csharp_preserve_single_line_blocks = true
|
csharp_preserve_single_line_blocks = true
|
||||||
###############################
|
|
||||||
# VB Coding Conventions #
|
|
||||||
###############################
|
|
||||||
[*.vb]
|
|
||||||
# Modifier preferences
|
|
||||||
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
|
|
||||||
|
3
.github/CODEOWNERS
vendored
Normal file
3
.github/CODEOWNERS
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Joshua must review all changes to deployment and build.sh
|
||||||
|
deployment/* @joshuaboniface
|
||||||
|
build.sh @joshuaboniface
|
19
.gitignore
vendored
19
.gitignore
vendored
@ -245,14 +245,14 @@ pip-log.txt
|
|||||||
#########################
|
#########################
|
||||||
|
|
||||||
# Artifacts for debian-x64
|
# Artifacts for debian-x64
|
||||||
deployment/debian-package-x64/pkg-src/.debhelper/
|
debian/.debhelper/
|
||||||
deployment/debian-package-x64/pkg-src/*.debhelper
|
debian/*.debhelper
|
||||||
deployment/debian-package-x64/pkg-src/debhelper-build-stamp
|
debian/debhelper-build-stamp
|
||||||
deployment/debian-package-x64/pkg-src/files
|
debian/files
|
||||||
deployment/debian-package-x64/pkg-src/jellyfin.substvars
|
debian/jellyfin.substvars
|
||||||
deployment/debian-package-x64/pkg-src/jellyfin/
|
debian/jellyfin/
|
||||||
# Don't ignore the debian/bin folder
|
# Don't ignore the debian/bin folder
|
||||||
!deployment/debian-package-x64/pkg-src/bin/
|
!debian/bin/
|
||||||
|
|
||||||
deployment/**/dist/
|
deployment/**/dist/
|
||||||
deployment/**/pkg-dist/
|
deployment/**/pkg-dist/
|
||||||
@ -272,3 +272,8 @@ dist
|
|||||||
|
|
||||||
# BenchmarkDotNet artifacts
|
# BenchmarkDotNet artifacts
|
||||||
BenchmarkDotNet.Artifacts
|
BenchmarkDotNet.Artifacts
|
||||||
|
|
||||||
|
# Ignore web artifacts from native builds
|
||||||
|
web/
|
||||||
|
web-src.*
|
||||||
|
MediaBrowser.WebDashboard/jellyfin-web/
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System.Buffers.Binary;
|
using System.Buffers.Binary;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
<TargetFramework>netstandard2.1</TargetFramework>
|
<TargetFramework>netstandard2.1</TargetFramework>
|
||||||
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
|
||||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace DvdLib.Ifo
|
namespace DvdLib.Ifo
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace DvdLib.Ifo
|
namespace DvdLib.Ifo
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace DvdLib.Ifo
|
namespace DvdLib.Ifo
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
namespace DvdLib.Ifo
|
namespace DvdLib.Ifo
|
||||||
{
|
{
|
||||||
public class Chapter
|
public class Chapter
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace DvdLib.Ifo
|
namespace DvdLib.Ifo
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace DvdLib.Ifo
|
namespace DvdLib.Ifo
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace DvdLib.Ifo
|
namespace DvdLib.Ifo
|
||||||
|
@ -908,7 +908,8 @@ namespace Emby.Dlna.PlayTo
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
|
/// <inheritdoc />
|
||||||
|
public Task SendMessage<T>(string name, Guid messageId, T data, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
{
|
{
|
||||||
@ -924,10 +925,12 @@ namespace Emby.Dlna.PlayTo
|
|||||||
{
|
{
|
||||||
return SendPlayCommand(data as PlayRequest, cancellationToken);
|
return SendPlayCommand(data as PlayRequest, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
|
return SendPlaystateCommand(data as PlaystateRequest, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return SendGeneralCommand(data as GeneralCommand, cancellationToken);
|
return SendGeneralCommand(data as GeneralCommand, cancellationToken);
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
|
#nullable enable
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using Emby.Naming.Common;
|
using Emby.Naming.Common;
|
||||||
|
|
||||||
@ -21,8 +21,7 @@ namespace Emby.Naming.Audio
|
|||||||
public bool IsMultiPart(string path)
|
public bool IsMultiPart(string path)
|
||||||
{
|
{
|
||||||
var filename = Path.GetFileName(path);
|
var filename = Path.GetFileName(path);
|
||||||
|
if (filename.Length == 0)
|
||||||
if (string.IsNullOrEmpty(filename))
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -39,18 +38,22 @@ namespace Emby.Naming.Audio
|
|||||||
filename = filename.Replace(')', ' ');
|
filename = filename.Replace(')', ' ');
|
||||||
filename = Regex.Replace(filename, @"\s+", " ");
|
filename = Regex.Replace(filename, @"\s+", " ");
|
||||||
|
|
||||||
filename = filename.TrimStart();
|
ReadOnlySpan<char> trimmedFilename = filename.TrimStart();
|
||||||
|
|
||||||
foreach (var prefix in _options.AlbumStackingPrefixes)
|
foreach (var prefix in _options.AlbumStackingPrefixes)
|
||||||
{
|
{
|
||||||
if (filename.IndexOf(prefix, StringComparison.OrdinalIgnoreCase) != 0)
|
if (!trimmedFilename.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tmp = filename.Substring(prefix.Length);
|
var tmp = trimmedFilename.Slice(prefix.Length).Trim();
|
||||||
|
|
||||||
tmp = tmp.Trim().Split(' ').FirstOrDefault() ?? string.Empty;
|
int index = tmp.IndexOf(' ');
|
||||||
|
if (index != -1)
|
||||||
|
{
|
||||||
|
tmp = tmp.Slice(0, index);
|
||||||
|
}
|
||||||
|
|
||||||
if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
|
if (int.TryParse(tmp, NumberStyles.Integer, CultureInfo.InvariantCulture, out _))
|
||||||
{
|
{
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#nullable enable
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -11,7 +12,7 @@ namespace Emby.Naming.Audio
|
|||||||
{
|
{
|
||||||
public static bool IsAudioFile(string path, NamingOptions options)
|
public static bool IsAudioFile(string path, NamingOptions options)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(path) ?? string.Empty;
|
var extension = Path.GetExtension(path);
|
||||||
return options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
return options.AudioFileExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,6 @@ namespace Emby.Naming.Common
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public EpisodeExpression()
|
|
||||||
: this(null)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public string Expression
|
public string Expression
|
||||||
{
|
{
|
||||||
get => _expression;
|
get => _expression;
|
||||||
@ -48,6 +43,6 @@ namespace Emby.Naming.Common
|
|||||||
|
|
||||||
public string[] DateTimeFormats { get; set; }
|
public string[] DateTimeFormats { get; set; }
|
||||||
|
|
||||||
public Regex Regex => _regex ?? (_regex = new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled));
|
public Regex Regex => _regex ??= new Regex(Expression, RegexOptions.IgnoreCase | RegexOptions.Compiled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#nullable enable
|
||||||
#pragma warning disable CS1591
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
@ -16,11 +17,11 @@ namespace Emby.Naming.Subtitles
|
|||||||
_options = options;
|
_options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SubtitleInfo ParseFile(string path)
|
public SubtitleInfo? ParseFile(string path)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(path))
|
if (path.Length == 0)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(path));
|
throw new ArgumentException("File path can't be empty.", nameof(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
var extension = Path.GetExtension(path);
|
var extension = Path.GetExtension(path);
|
||||||
@ -52,11 +53,6 @@ namespace Emby.Naming.Subtitles
|
|||||||
|
|
||||||
private string[] GetFlags(string path)
|
private string[] GetFlags(string path)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: the tags need be be surrounded be either a space ( ), hyphen -, dot . or underscore _.
|
// Note: the tags need be be surrounded be either a space ( ), hyphen -, dot . or underscore _.
|
||||||
|
|
||||||
var file = Path.GetFileName(path);
|
var file = Path.GetFileName(path);
|
||||||
|
@ -89,14 +89,14 @@ namespace Emby.Naming.Video
|
|||||||
if (parseName)
|
if (parseName)
|
||||||
{
|
{
|
||||||
var cleanDateTimeResult = CleanDateTime(name);
|
var cleanDateTimeResult = CleanDateTime(name);
|
||||||
|
name = cleanDateTimeResult.Name;
|
||||||
|
year = cleanDateTimeResult.Year;
|
||||||
|
|
||||||
if (extraResult.ExtraType == null
|
if (extraResult.ExtraType == null
|
||||||
&& TryCleanString(cleanDateTimeResult.Name, out ReadOnlySpan<char> newName))
|
&& TryCleanString(name, out ReadOnlySpan<char> newName))
|
||||||
{
|
{
|
||||||
name = newName.ToString();
|
name = newName.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
year = cleanDateTimeResult.Year;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return new VideoFileInfo
|
return new VideoFileInfo
|
||||||
|
@ -143,7 +143,7 @@ namespace Emby.Notifications
|
|||||||
|
|
||||||
var notification = new NotificationRequest
|
var notification = new NotificationRequest
|
||||||
{
|
{
|
||||||
Description = "Please see jellyfin.media for details.",
|
Description = "Please see jellyfin.org for details.",
|
||||||
NotificationType = type,
|
NotificationType = type,
|
||||||
Name = _localization.GetLocalizedString("NewVersionIsAvailable")
|
Name = _localization.GetLocalizedString("NewVersionIsAvailable")
|
||||||
};
|
};
|
||||||
|
@ -44,7 +44,6 @@ using Emby.Server.Implementations.Security;
|
|||||||
using Emby.Server.Implementations.Serialization;
|
using Emby.Server.Implementations.Serialization;
|
||||||
using Emby.Server.Implementations.Services;
|
using Emby.Server.Implementations.Services;
|
||||||
using Emby.Server.Implementations.Session;
|
using Emby.Server.Implementations.Session;
|
||||||
using Emby.Server.Implementations.SocketSharp;
|
|
||||||
using Emby.Server.Implementations.TV;
|
using Emby.Server.Implementations.TV;
|
||||||
using Emby.Server.Implementations.Updates;
|
using Emby.Server.Implementations.Updates;
|
||||||
using Emby.Server.Implementations.SyncPlay;
|
using Emby.Server.Implementations.SyncPlay;
|
||||||
@ -96,7 +95,6 @@ using MediaBrowser.Model.Serialization;
|
|||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using MediaBrowser.Model.System;
|
using MediaBrowser.Model.System;
|
||||||
using MediaBrowser.Model.Tasks;
|
using MediaBrowser.Model.Tasks;
|
||||||
using MediaBrowser.Model.Updates;
|
|
||||||
using MediaBrowser.Providers.Chapters;
|
using MediaBrowser.Providers.Chapters;
|
||||||
using MediaBrowser.Providers.Manager;
|
using MediaBrowser.Providers.Manager;
|
||||||
using MediaBrowser.Providers.Plugins.TheTvdb;
|
using MediaBrowser.Providers.Plugins.TheTvdb;
|
||||||
@ -104,9 +102,9 @@ using MediaBrowser.Providers.Subtitles;
|
|||||||
using MediaBrowser.WebDashboard.Api;
|
using MediaBrowser.WebDashboard.Api;
|
||||||
using MediaBrowser.XbmcMetadata.Providers;
|
using MediaBrowser.XbmcMetadata.Providers;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Extensions;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Prometheus.DotNetRuntime;
|
||||||
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
|
using OperatingSystem = MediaBrowser.Common.System.OperatingSystem;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations
|
namespace Emby.Server.Implementations
|
||||||
@ -261,6 +259,12 @@ namespace Emby.Server.Implementations
|
|||||||
|
|
||||||
_startupOptions = options;
|
_startupOptions = options;
|
||||||
|
|
||||||
|
// Initialize runtime stat collection
|
||||||
|
if (ServerConfigurationManager.Configuration.EnableMetrics)
|
||||||
|
{
|
||||||
|
DotNetRuntimeStatsBuilder.Default().StartCollecting();
|
||||||
|
}
|
||||||
|
|
||||||
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
|
fileSystem.AddShortcutHandler(new MbLinkShortcutHandler(fileSystem));
|
||||||
|
|
||||||
_networkManager.NetworkChanged += OnNetworkChanged;
|
_networkManager.NetworkChanged += OnNetworkChanged;
|
||||||
@ -498,32 +502,8 @@ namespace Emby.Server.Implementations
|
|||||||
RegisterServices(serviceCollection);
|
RegisterServices(serviceCollection);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task ExecuteWebsocketHandlerAsync(HttpContext context, Func<Task> next)
|
public Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
|
||||||
{
|
=> _httpServer.RequestHandler(context);
|
||||||
if (!context.WebSockets.IsWebSocketRequest)
|
|
||||||
{
|
|
||||||
await next().ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await _httpServer.ProcessWebSocketRequest(context).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
|
|
||||||
{
|
|
||||||
if (context.WebSockets.IsWebSocketRequest)
|
|
||||||
{
|
|
||||||
await next().ConfigureAwait(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var request = context.Request;
|
|
||||||
var response = context.Response;
|
|
||||||
var localPath = context.Request.Path.ToString();
|
|
||||||
|
|
||||||
var req = new WebSocketSharpRequest(request, response, request.Path, LoggerFactory.CreateLogger<WebSocketSharpRequest>());
|
|
||||||
await _httpServer.RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers services/resources with the service collection that will be available via DI.
|
/// Registers services/resources with the service collection that will be available via DI.
|
||||||
@ -541,13 +521,6 @@ namespace Emby.Server.Implementations
|
|||||||
|
|
||||||
serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
|
serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
|
||||||
|
|
||||||
// TODO: Remove support for injecting ILogger completely
|
|
||||||
serviceCollection.AddSingleton((provider) =>
|
|
||||||
{
|
|
||||||
Logger.LogWarning("Injecting ILogger directly is deprecated and should be replaced with ILogger<T>");
|
|
||||||
return Logger;
|
|
||||||
});
|
|
||||||
|
|
||||||
serviceCollection.AddSingleton(_fileSystemManager);
|
serviceCollection.AddSingleton(_fileSystemManager);
|
||||||
serviceCollection.AddSingleton<TvdbClientManager>();
|
serviceCollection.AddSingleton<TvdbClientManager>();
|
||||||
|
|
||||||
@ -616,7 +589,6 @@ namespace Emby.Server.Implementations
|
|||||||
serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
|
serviceCollection.AddSingleton<ISearchEngine, SearchEngine>();
|
||||||
|
|
||||||
serviceCollection.AddSingleton<ServiceController>();
|
serviceCollection.AddSingleton<ServiceController>();
|
||||||
serviceCollection.AddSingleton<IHttpListener, WebSocketSharpListener>();
|
|
||||||
serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
|
serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
|
||||||
|
|
||||||
serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
|
serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
|
||||||
@ -1147,9 +1119,6 @@ namespace Emby.Server.Implementations
|
|||||||
ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
|
ItemsByNamePath = ApplicationPaths.InternalMetadataPath,
|
||||||
InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
|
InternalMetadataPath = ApplicationPaths.InternalMetadataPath,
|
||||||
CachePath = ApplicationPaths.CachePath,
|
CachePath = ApplicationPaths.CachePath,
|
||||||
HttpServerPortNumber = HttpPort,
|
|
||||||
SupportsHttps = SupportsHttps,
|
|
||||||
HttpsPortNumber = HttpsPort,
|
|
||||||
OperatingSystem = OperatingSystem.Id.ToString(),
|
OperatingSystem = OperatingSystem.Id.ToString(),
|
||||||
OperatingSystemDisplayName = OperatingSystem.Name,
|
OperatingSystemDisplayName = OperatingSystem.Name,
|
||||||
CanSelfRestart = CanSelfRestart,
|
CanSelfRestart = CanSelfRestart,
|
||||||
@ -1185,23 +1154,22 @@ namespace Emby.Server.Implementations
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool EnableHttps => SupportsHttps && ServerConfigurationManager.Configuration.EnableHttps;
|
/// <inheritdoc/>
|
||||||
|
public bool ListenWithHttps => Certificate != null && ServerConfigurationManager.Configuration.EnableHttps;
|
||||||
|
|
||||||
public bool SupportsHttps => Certificate != null || ServerConfigurationManager.Configuration.IsBehindProxy;
|
/// <inheritdoc/>
|
||||||
|
public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken)
|
||||||
public async Task<string> GetLocalApiUrl(CancellationToken cancellationToken, bool forceHttp = false)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Return the first matched address, if found, or the first known local address
|
// Return the first matched address, if found, or the first known local address
|
||||||
var addresses = await GetLocalIpAddressesInternal(false, 1, cancellationToken).ConfigureAwait(false);
|
var addresses = await GetLocalIpAddressesInternal(false, 1, cancellationToken).ConfigureAwait(false);
|
||||||
|
if (addresses.Count == 0)
|
||||||
foreach (var address in addresses)
|
|
||||||
{
|
{
|
||||||
return GetLocalApiUrl(address, forceHttp);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return GetLocalApiUrl(addresses.First());
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -1228,7 +1196,7 @@ namespace Emby.Server.Implementations
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string GetLocalApiUrl(IPAddress ipAddress, bool forceHttp = false)
|
public string GetLocalApiUrl(IPAddress ipAddress)
|
||||||
{
|
{
|
||||||
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
|
if (ipAddress.AddressFamily == AddressFamily.InterNetworkV6)
|
||||||
{
|
{
|
||||||
@ -1238,29 +1206,30 @@ namespace Emby.Server.Implementations
|
|||||||
str.CopyTo(span.Slice(1));
|
str.CopyTo(span.Slice(1));
|
||||||
span[^1] = ']';
|
span[^1] = ']';
|
||||||
|
|
||||||
return GetLocalApiUrl(span, forceHttp);
|
return GetLocalApiUrl(span);
|
||||||
}
|
}
|
||||||
|
|
||||||
return GetLocalApiUrl(ipAddress.ToString(), forceHttp);
|
return GetLocalApiUrl(ipAddress.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc/>
|
||||||
public string GetLocalApiUrl(ReadOnlySpan<char> host, bool forceHttp = false)
|
public string GetLoopbackHttpApiUrl()
|
||||||
{
|
{
|
||||||
var url = new StringBuilder(64);
|
return GetLocalApiUrl("127.0.0.1", Uri.UriSchemeHttp, HttpPort);
|
||||||
bool useHttps = EnableHttps && !forceHttp;
|
|
||||||
url.Append(useHttps ? "https://" : "http://")
|
|
||||||
.Append(host)
|
|
||||||
.Append(':')
|
|
||||||
.Append(useHttps ? HttpsPort : HttpPort);
|
|
||||||
|
|
||||||
string baseUrl = ServerConfigurationManager.Configuration.BaseUrl;
|
|
||||||
if (baseUrl.Length != 0)
|
|
||||||
{
|
|
||||||
url.Append(baseUrl);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return url.ToString();
|
/// <inheritdoc/>
|
||||||
|
public string GetLocalApiUrl(ReadOnlySpan<char> host, string scheme = null, int? port = null)
|
||||||
|
{
|
||||||
|
// NOTE: If no BaseUrl is set then UriBuilder appends a trailing slash, but if there is no BaseUrl it does
|
||||||
|
// not. For consistency, always trim the trailing slash.
|
||||||
|
return new UriBuilder
|
||||||
|
{
|
||||||
|
Scheme = scheme ?? (ListenWithHttps ? Uri.UriSchemeHttps : Uri.UriSchemeHttp),
|
||||||
|
Host = host.ToString(),
|
||||||
|
Port = port ?? (ListenWithHttps ? HttpsPort : HttpPort),
|
||||||
|
Path = ServerConfigurationManager.Configuration.BaseUrl
|
||||||
|
}.ToString().TrimEnd('/');
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken)
|
public Task<List<IPAddress>> GetLocalIpAddresses(CancellationToken cancellationToken)
|
||||||
@ -1294,7 +1263,7 @@ namespace Emby.Server.Implementations
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var valid = await IsIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false);
|
var valid = await IsLocalIpAddressValidAsync(address, cancellationToken).ConfigureAwait(false);
|
||||||
if (valid)
|
if (valid)
|
||||||
{
|
{
|
||||||
resultList.Add(address);
|
resultList.Add(address);
|
||||||
@ -1328,7 +1297,7 @@ namespace Emby.Server.Implementations
|
|||||||
|
|
||||||
private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
private async Task<bool> IsIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
|
private async Task<bool> IsLocalIpAddressValidAsync(IPAddress address, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (address.Equals(IPAddress.Loopback)
|
if (address.Equals(IPAddress.Loopback)
|
||||||
|| address.Equals(IPAddress.IPv6Loopback))
|
|| address.Equals(IPAddress.IPv6Loopback))
|
||||||
@ -1336,8 +1305,7 @@ namespace Emby.Server.Implementations
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var apiUrl = GetLocalApiUrl(address);
|
var apiUrl = GetLocalApiUrl(address) + "/system/ping";
|
||||||
apiUrl += "/system/ping";
|
|
||||||
|
|
||||||
if (_validAddressResults.TryGetValue(apiUrl, out var cachedResult))
|
if (_validAddressResults.TryGetValue(apiUrl, out var cachedResult))
|
||||||
{
|
{
|
||||||
|
@ -31,18 +31,18 @@ namespace Emby.Server.Implementations.Browser
|
|||||||
/// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored.
|
/// Opens the specified URL in an external browser window. Any exceptions will be logged, but ignored.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="appHost">The application host.</param>
|
/// <param name="appHost">The application host.</param>
|
||||||
/// <param name="url">The URL.</param>
|
/// <param name="relativeUrl">The URL to open, relative to the server base URL.</param>
|
||||||
private static void TryOpenUrl(IServerApplicationHost appHost, string url)
|
private static void TryOpenUrl(IServerApplicationHost appHost, string relativeUrl)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string baseUrl = appHost.GetLocalApiUrl("localhost");
|
string baseUrl = appHost.GetLocalApiUrl("localhost");
|
||||||
appHost.LaunchUrl(baseUrl + url);
|
appHost.LaunchUrl(baseUrl + relativeUrl);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
var logger = appHost.Resolve<ILogger>();
|
var logger = appHost.Resolve<ILogger>();
|
||||||
logger?.LogError(ex, "Failed to open browser window with URL {URL}", url);
|
logger?.LogError(ex, "Failed to open browser window with URL {URL}", relativeUrl);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Emby.Server.Implementations.HttpServer;
|
using Emby.Server.Implementations.HttpServer;
|
||||||
using Emby.Server.Implementations.Updates;
|
using Emby.Server.Implementations.Updates;
|
||||||
using MediaBrowser.Providers.Music;
|
|
||||||
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
|
using static MediaBrowser.Controller.Extensions.ConfigurationExtensions;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations
|
namespace Emby.Server.Implementations
|
||||||
|
@ -375,5 +375,15 @@ namespace Emby.Server.Implementations.Data
|
|||||||
|
|
||||||
return userData;
|
return userData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
/// <remarks>
|
||||||
|
/// There is nothing to dispose here since <see cref="BaseSqliteRepository.WriteLock"/> and
|
||||||
|
/// <see cref="BaseSqliteRepository.WriteConnection"/> are managed by <see cref="SqliteItemRepository"/>.
|
||||||
|
/// See <see cref="Initialize(IUserManager, SemaphoreSlim, SQLiteDatabaseConnection)"/>.
|
||||||
|
/// </remarks>
|
||||||
|
protected override void Dispose(bool dispose)
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@
|
|||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" />
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.3" />
|
||||||
<PackageReference Include="Mono.Nat" Version="2.0.1" />
|
<PackageReference Include="Mono.Nat" Version="2.0.1" />
|
||||||
|
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" />
|
||||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" />
|
<PackageReference Include="ServiceStack.Text.Core" Version="5.8.0" />
|
||||||
<PackageReference Include="sharpcompress" Version="0.25.0" />
|
<PackageReference Include="sharpcompress" Version="0.25.0" />
|
||||||
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
|
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
|
||||||
|
@ -64,7 +64,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
.Append(config.PublicHttpsPort).Append(Separator)
|
.Append(config.PublicHttpsPort).Append(Separator)
|
||||||
.Append(_appHost.HttpPort).Append(Separator)
|
.Append(_appHost.HttpPort).Append(Separator)
|
||||||
.Append(_appHost.HttpsPort).Append(Separator)
|
.Append(_appHost.HttpsPort).Append(Separator)
|
||||||
.Append(_appHost.EnableHttps).Append(Separator)
|
.Append(_appHost.ListenWithHttps).Append(Separator)
|
||||||
.Append(config.EnableRemoteAccess).Append(Separator)
|
.Append(config.EnableRemoteAccess).Append(Separator)
|
||||||
.ToString();
|
.ToString();
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ namespace Emby.Server.Implementations.EntryPoints
|
|||||||
{
|
{
|
||||||
yield return CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort);
|
yield return CreatePortMap(device, _appHost.HttpPort, _config.Configuration.PublicPort);
|
||||||
|
|
||||||
if (_appHost.EnableHttps)
|
if (_appHost.ListenWithHttps)
|
||||||
{
|
{
|
||||||
yield return CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
|
yield return CreatePortMap(device, _appHost.HttpsPort, _config.Configuration.PublicHttpsPort);
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,12 @@ using System.Diagnostics;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net.Sockets;
|
using System.Net.Sockets;
|
||||||
|
using System.Net.WebSockets;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Emby.Server.Implementations.Net;
|
|
||||||
using Emby.Server.Implementations.Services;
|
using Emby.Server.Implementations.Services;
|
||||||
|
using Emby.Server.Implementations.SocketSharp;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using MediaBrowser.Controller;
|
using MediaBrowser.Controller;
|
||||||
@ -22,15 +23,17 @@ using MediaBrowser.Model.Globalization;
|
|||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Http.Extensions;
|
||||||
using Microsoft.AspNetCore.WebUtilities;
|
using Microsoft.AspNetCore.WebUtilities;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Primitives;
|
||||||
using ServiceStack.Text.Jsv;
|
using ServiceStack.Text.Jsv;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.HttpServer
|
namespace Emby.Server.Implementations.HttpServer
|
||||||
{
|
{
|
||||||
public class HttpListenerHost : IHttpServer, IDisposable
|
public class HttpListenerHost : IHttpServer
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The key for a setting that specifies the default redirect path
|
/// The key for a setting that specifies the default redirect path
|
||||||
@ -39,17 +42,17 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath";
|
public const string DefaultRedirectKey = "HttpListenerHost:DefaultRedirectPath";
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
private readonly INetworkManager _networkManager;
|
private readonly INetworkManager _networkManager;
|
||||||
private readonly IServerApplicationHost _appHost;
|
private readonly IServerApplicationHost _appHost;
|
||||||
private readonly IJsonSerializer _jsonSerializer;
|
private readonly IJsonSerializer _jsonSerializer;
|
||||||
private readonly IXmlSerializer _xmlSerializer;
|
private readonly IXmlSerializer _xmlSerializer;
|
||||||
private readonly IHttpListener _socketListener;
|
|
||||||
private readonly Func<Type, Func<string, object>> _funcParseFn;
|
private readonly Func<Type, Func<string, object>> _funcParseFn;
|
||||||
private readonly string _defaultRedirectPath;
|
private readonly string _defaultRedirectPath;
|
||||||
private readonly string _baseUrlPrefix;
|
private readonly string _baseUrlPrefix;
|
||||||
|
|
||||||
private readonly Dictionary<Type, Type> _serviceOperationsMap = new Dictionary<Type, Type>();
|
private readonly Dictionary<Type, Type> _serviceOperationsMap = new Dictionary<Type, Type>();
|
||||||
private readonly List<IWebSocketConnection> _webSocketConnections = new List<IWebSocketConnection>();
|
|
||||||
private readonly IHostEnvironment _hostEnvironment;
|
private readonly IHostEnvironment _hostEnvironment;
|
||||||
|
|
||||||
private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
|
private IWebSocketListener[] _webSocketListeners = Array.Empty<IWebSocketListener>();
|
||||||
@ -63,10 +66,10 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
INetworkManager networkManager,
|
INetworkManager networkManager,
|
||||||
IJsonSerializer jsonSerializer,
|
IJsonSerializer jsonSerializer,
|
||||||
IXmlSerializer xmlSerializer,
|
IXmlSerializer xmlSerializer,
|
||||||
IHttpListener socketListener,
|
|
||||||
ILocalizationManager localizationManager,
|
ILocalizationManager localizationManager,
|
||||||
ServiceController serviceController,
|
ServiceController serviceController,
|
||||||
IHostEnvironment hostEnvironment)
|
IHostEnvironment hostEnvironment,
|
||||||
|
ILoggerFactory loggerFactory)
|
||||||
{
|
{
|
||||||
_appHost = applicationHost;
|
_appHost = applicationHost;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
@ -76,11 +79,9 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
_networkManager = networkManager;
|
_networkManager = networkManager;
|
||||||
_jsonSerializer = jsonSerializer;
|
_jsonSerializer = jsonSerializer;
|
||||||
_xmlSerializer = xmlSerializer;
|
_xmlSerializer = xmlSerializer;
|
||||||
_socketListener = socketListener;
|
|
||||||
ServiceController = serviceController;
|
ServiceController = serviceController;
|
||||||
|
|
||||||
_socketListener.WebSocketConnected = OnWebSocketConnected;
|
|
||||||
_hostEnvironment = hostEnvironment;
|
_hostEnvironment = hostEnvironment;
|
||||||
|
_loggerFactory = loggerFactory;
|
||||||
|
|
||||||
_funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
|
_funcParseFn = t => s => JsvReader.GetParseFn(t)(s);
|
||||||
|
|
||||||
@ -172,38 +173,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
return attributes;
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnWebSocketConnected(WebSocketConnectEventArgs e)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var connection = new WebSocketConnection(e.WebSocket, e.Endpoint, _jsonSerializer, _logger)
|
|
||||||
{
|
|
||||||
OnReceive = ProcessWebSocketMessageReceived,
|
|
||||||
Url = e.Url,
|
|
||||||
QueryString = e.QueryString
|
|
||||||
};
|
|
||||||
|
|
||||||
connection.Closed += OnConnectionClosed;
|
|
||||||
|
|
||||||
lock (_webSocketConnections)
|
|
||||||
{
|
|
||||||
_webSocketConnections.Add(connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
WebSocketConnected?.Invoke(this, new GenericEventArgs<IWebSocketConnection>(connection));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnConnectionClosed(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
lock (_webSocketConnections)
|
|
||||||
{
|
|
||||||
_webSocketConnections.Remove((IWebSocketConnection)sender);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Exception GetActualException(Exception ex)
|
private static Exception GetActualException(Exception ex)
|
||||||
{
|
{
|
||||||
if (ex is AggregateException agg)
|
if (ex is AggregateException agg)
|
||||||
@ -289,32 +258,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
.Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase);
|
.Replace(_config.ApplicationPaths.ProgramDataPath, string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shut down the Web Service
|
|
||||||
/// </summary>
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
List<IWebSocketConnection> connections;
|
|
||||||
|
|
||||||
lock (_webSocketConnections)
|
|
||||||
{
|
|
||||||
connections = _webSocketConnections.ToList();
|
|
||||||
_webSocketConnections.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var connection in connections)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
connection.Dispose();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error disposing connection");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string RemoveQueryStringByKey(string url, string key)
|
public static string RemoveQueryStringByKey(string url, string key)
|
||||||
{
|
{
|
||||||
var uri = new Uri(url);
|
var uri = new Uri(url);
|
||||||
@ -424,11 +367,15 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Validate a connection from a remote IP address to a URL to see if a redirection to HTTPS is required.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>True if the request is valid, or false if the request is not valid and an HTTPS redirect is required.</returns>
|
||||||
private bool ValidateSsl(string remoteIp, string urlString)
|
private bool ValidateSsl(string remoteIp, string urlString)
|
||||||
{
|
{
|
||||||
if (_config.Configuration.RequireHttps && _appHost.EnableHttps && !_config.Configuration.IsBehindProxy)
|
if (_config.Configuration.RequireHttps
|
||||||
{
|
&& _appHost.ListenWithHttps
|
||||||
if (urlString.IndexOf("https://", StringComparison.OrdinalIgnoreCase) == -1)
|
&& !urlString.Contains("https://", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
// These are hacks, but if these ever occur on ipv6 in the local network they could be incorrectly redirected
|
// These are hacks, but if these ever occur on ipv6 in the local network they could be incorrectly redirected
|
||||||
if (urlString.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) != -1
|
if (urlString.IndexOf("system/ping", StringComparison.OrdinalIgnoreCase) != -1
|
||||||
@ -442,15 +389,30 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public Task RequestHandler(HttpContext context)
|
||||||
|
{
|
||||||
|
if (context.WebSockets.IsWebSocketRequest)
|
||||||
|
{
|
||||||
|
return WebSocketRequestHandler(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
var request = context.Request;
|
||||||
|
var response = context.Response;
|
||||||
|
var localPath = context.Request.Path.ToString();
|
||||||
|
|
||||||
|
var req = new WebSocketSharpRequest(request, response, request.Path, _logger);
|
||||||
|
return RequestHandler(req, request.GetDisplayUrl(), request.Host.ToString(), localPath, context.RequestAborted);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Overridable method that can be used to implement a custom handler.
|
/// Overridable method that can be used to implement a custom handler.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken)
|
private async Task RequestHandler(IHttpRequest httpReq, string urlString, string host, string localPath, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var stopWatch = new Stopwatch();
|
var stopWatch = new Stopwatch();
|
||||||
stopWatch.Start();
|
stopWatch.Start();
|
||||||
@ -493,9 +455,10 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(httpReq.Verb, "OPTIONS", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
httpRes.StatusCode = 200;
|
httpRes.StatusCode = 200;
|
||||||
httpRes.Headers.Add("Access-Control-Allow-Origin", "*");
|
foreach(var (key, value) in GetDefaultCorsHeaders(httpReq))
|
||||||
httpRes.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
{
|
||||||
httpRes.Headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization");
|
httpRes.Headers.Add(key, value);
|
||||||
|
}
|
||||||
httpRes.ContentType = "text/plain";
|
httpRes.ContentType = "text/plain";
|
||||||
await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
|
await httpRes.WriteAsync(string.Empty, cancellationToken).ConfigureAwait(false);
|
||||||
return;
|
return;
|
||||||
@ -578,6 +541,68 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task WebSocketRequestHandler(HttpContext context)
|
||||||
|
{
|
||||||
|
if (_disposed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation("WS {IP} request", context.Connection.RemoteIpAddress);
|
||||||
|
|
||||||
|
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync().ConfigureAwait(false);
|
||||||
|
|
||||||
|
var connection = new WebSocketConnection(
|
||||||
|
_loggerFactory.CreateLogger<WebSocketConnection>(),
|
||||||
|
webSocket,
|
||||||
|
context.Connection.RemoteIpAddress,
|
||||||
|
context.Request.Query)
|
||||||
|
{
|
||||||
|
OnReceive = ProcessWebSocketMessageReceived
|
||||||
|
};
|
||||||
|
|
||||||
|
WebSocketConnected?.Invoke(this, new GenericEventArgs<IWebSocketConnection>(connection));
|
||||||
|
|
||||||
|
await connection.ProcessAsync().ConfigureAwait(false);
|
||||||
|
_logger.LogInformation("WS {IP} closed", context.Connection.RemoteIpAddress);
|
||||||
|
}
|
||||||
|
catch (Exception ex) // Otherwise ASP.Net will ignore the exception
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "WS {IP} WebSocketRequestHandler error", context.Connection.RemoteIpAddress);
|
||||||
|
if (!context.Response.HasStarted)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Get the default CORS headers
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="req"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public IDictionary<string, string> GetDefaultCorsHeaders(IRequest req)
|
||||||
|
{
|
||||||
|
var origin = req.Headers["Origin"];
|
||||||
|
if (origin == StringValues.Empty)
|
||||||
|
{
|
||||||
|
origin = req.Headers["Host"];
|
||||||
|
if (origin == StringValues.Empty)
|
||||||
|
{
|
||||||
|
origin = "*";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var headers = new Dictionary<string, string>();
|
||||||
|
headers.Add("Access-Control-Allow-Origin", origin);
|
||||||
|
headers.Add("Access-Control-Allow-Credentials", "true");
|
||||||
|
headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
||||||
|
headers.Add("Access-Control-Allow-Headers", "Content-Type, Authorization, Range, X-MediaBrowser-Token, X-Emby-Authorization, Cookie");
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
|
||||||
// Entry point for HttpListener
|
// Entry point for HttpListener
|
||||||
public ServiceHandler GetServiceHandler(IHttpRequest httpReq)
|
public ServiceHandler GetServiceHandler(IHttpRequest httpReq)
|
||||||
{
|
{
|
||||||
@ -624,7 +649,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
ResponseFilters = new Action<IRequest, HttpResponse, object>[]
|
ResponseFilters = new Action<IRequest, HttpResponse, object>[]
|
||||||
{
|
{
|
||||||
new ResponseFilter(_logger).FilterResponse
|
new ResponseFilter(this, _logger).FilterResponse
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -685,11 +710,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
return _jsonSerializer.DeserializeFromStreamAsync(stream, type);
|
return _jsonSerializer.DeserializeFromStreamAsync(stream, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task ProcessWebSocketRequest(HttpContext context)
|
|
||||||
{
|
|
||||||
return _socketListener.ProcessWebSocketRequest(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string NormalizeEmbyRoutePath(string path)
|
private string NormalizeEmbyRoutePath(string path)
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Normalizing /emby route");
|
_logger.LogDebug("Normalizing /emby route");
|
||||||
@ -708,28 +728,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
return _baseUrlPrefix + NormalizeUrlPath(path);
|
return _baseUrlPrefix + NormalizeUrlPath(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
Stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Processes the web socket message received.
|
/// Processes the web socket message received.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -741,8 +739,6 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogDebug("Websocket message received: {0}", result.MessageType);
|
|
||||||
|
|
||||||
IEnumerable<Task> GetTasks()
|
IEnumerable<Task> GetTasks()
|
||||||
{
|
{
|
||||||
foreach (var x in _webSocketListeners)
|
foreach (var x in _webSocketListeners)
|
||||||
|
@ -426,7 +426,7 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
|
|
||||||
if (!noCache)
|
if (!noCache)
|
||||||
{
|
{
|
||||||
if (!DateTime.TryParseExact(requestContext.Headers[HeaderNames.IfModifiedSince], HttpDateFormat, _enUSculture, DateTimeStyles.AssumeUniversal, out var ifModifiedSinceHeader))
|
if (!DateTime.TryParseExact(requestContext.Headers[HeaderNames.IfModifiedSince], HttpDateFormat, _enUSculture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out var ifModifiedSinceHeader))
|
||||||
{
|
{
|
||||||
_logger.LogDebug("Failed to parse If-Modified-Since header date: {0}", requestContext.Headers[HeaderNames.IfModifiedSince]);
|
_logger.LogDebug("Failed to parse If-Modified-Since header date: {0}", requestContext.Headers[HeaderNames.IfModifiedSince]);
|
||||||
return null;
|
return null;
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Emby.Server.Implementations.Net;
|
|
||||||
using MediaBrowser.Model.Services;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.HttpServer
|
|
||||||
{
|
|
||||||
public interface IHttpListener : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the error handler.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The error handler.</value>
|
|
||||||
Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the request handler.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The request handler.</value>
|
|
||||||
Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the web socket handler.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The web socket handler.</value>
|
|
||||||
Action<WebSocketConnectEventArgs> WebSocketConnected { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops this instance.
|
|
||||||
/// </summary>
|
|
||||||
Task Stop();
|
|
||||||
|
|
||||||
Task ProcessWebSocketRequest(HttpContext ctx);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.Services;
|
using MediaBrowser.Model.Services;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -13,14 +15,17 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ResponseFilter
|
public class ResponseFilter
|
||||||
{
|
{
|
||||||
|
private readonly IHttpServer _server;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="ResponseFilter"/> class.
|
/// Initializes a new instance of the <see cref="ResponseFilter"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="server">The HTTP server.</param>
|
||||||
/// <param name="logger">The logger.</param>
|
/// <param name="logger">The logger.</param>
|
||||||
public ResponseFilter(ILogger logger)
|
public ResponseFilter(IHttpServer server, ILogger logger)
|
||||||
{
|
{
|
||||||
|
_server = server;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,10 +37,16 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
/// <param name="dto">The dto.</param>
|
/// <param name="dto">The dto.</param>
|
||||||
public void FilterResponse(IRequest req, HttpResponse res, object dto)
|
public void FilterResponse(IRequest req, HttpResponse res, object dto)
|
||||||
{
|
{
|
||||||
|
foreach(var (key, value) in _server.GetDefaultCorsHeaders(req))
|
||||||
|
{
|
||||||
|
res.Headers.Add(key, value);
|
||||||
|
}
|
||||||
// Try to prevent compatibility view
|
// Try to prevent compatibility view
|
||||||
res.Headers.Add("Access-Control-Allow-Headers", "Accept, Accept-Language, Authorization, Cache-Control, Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, Content-Type, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, X-Emby-Authorization");
|
res.Headers["Access-Control-Allow-Headers"] = ("Accept, Accept-Language, Authorization, Cache-Control, " +
|
||||||
res.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS");
|
"Content-Disposition, Content-Encoding, Content-Language, Content-Length, Content-MD5, Content-Range, " +
|
||||||
res.Headers.Add("Access-Control-Allow-Origin", "*");
|
"Content-Type, Cookie, Date, Host, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, " +
|
||||||
|
"Origin, OriginToken, Pragma, Range, Slug, Transfer-Encoding, Want-Digest, X-MediaBrowser-Token, " +
|
||||||
|
"X-Emby-Authorization");
|
||||||
|
|
||||||
if (dto is Exception exception)
|
if (dto is Exception exception)
|
||||||
{
|
{
|
||||||
@ -82,6 +93,10 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
else if (inString.Length == 0)
|
||||||
|
{
|
||||||
|
return inString;
|
||||||
|
}
|
||||||
|
|
||||||
var newString = new StringBuilder(inString.Length);
|
var newString = new StringBuilder(inString.Length);
|
||||||
|
|
||||||
|
@ -1,15 +1,18 @@
|
|||||||
using System;
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Buffers;
|
||||||
|
using System.IO.Pipelines;
|
||||||
|
using System.Net;
|
||||||
using System.Net.WebSockets;
|
using System.Net.WebSockets;
|
||||||
using System.Text;
|
using System.Text.Json;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Emby.Server.Implementations.Net;
|
using MediaBrowser.Common.Json;
|
||||||
using MediaBrowser.Controller.Net;
|
using MediaBrowser.Controller.Net;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Serialization;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using UtfUnknown;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.HttpServer
|
namespace Emby.Server.Implementations.HttpServer
|
||||||
{
|
{
|
||||||
@ -24,69 +27,50 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The json serializer.
|
/// The json serializer options.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IJsonSerializer _jsonSerializer;
|
private readonly JsonSerializerOptions _jsonOptions;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The socket.
|
/// The socket.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly IWebSocket _socket;
|
private readonly WebSocket _socket;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="WebSocketConnection" /> class.
|
/// Initializes a new instance of the <see cref="WebSocketConnection" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="logger">The logger.</param>
|
||||||
/// <param name="socket">The socket.</param>
|
/// <param name="socket">The socket.</param>
|
||||||
/// <param name="remoteEndPoint">The remote end point.</param>
|
/// <param name="remoteEndPoint">The remote end point.</param>
|
||||||
/// <param name="jsonSerializer">The json serializer.</param>
|
/// <param name="query">The query.</param>
|
||||||
/// <param name="logger">The logger.</param>
|
public WebSocketConnection(
|
||||||
/// <exception cref="ArgumentNullException">socket</exception>
|
ILogger<WebSocketConnection> logger,
|
||||||
public WebSocketConnection(IWebSocket socket, string remoteEndPoint, IJsonSerializer jsonSerializer, ILogger logger)
|
WebSocket socket,
|
||||||
|
IPAddress? remoteEndPoint,
|
||||||
|
IQueryCollection query)
|
||||||
{
|
{
|
||||||
if (socket == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(socket));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(remoteEndPoint))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(remoteEndPoint));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (jsonSerializer == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(jsonSerializer));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(logger));
|
|
||||||
}
|
|
||||||
|
|
||||||
Id = Guid.NewGuid();
|
|
||||||
_jsonSerializer = jsonSerializer;
|
|
||||||
_socket = socket;
|
|
||||||
_socket.OnReceiveBytes = OnReceiveInternal;
|
|
||||||
|
|
||||||
RemoteEndPoint = remoteEndPoint;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_socket = socket;
|
||||||
|
RemoteEndPoint = remoteEndPoint;
|
||||||
|
QueryString = query;
|
||||||
|
|
||||||
socket.Closed += OnSocketClosed;
|
_jsonOptions = JsonDefaults.GetOptions();
|
||||||
|
LastActivityDate = DateTime.Now;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public event EventHandler<EventArgs> Closed;
|
public event EventHandler<EventArgs>? Closed;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the remote end point.
|
/// Gets or sets the remote end point.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string RemoteEndPoint { get; private set; }
|
public IPAddress? RemoteEndPoint { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the receive action.
|
/// Gets or sets the receive action.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The receive action.</value>
|
/// <value>The receive action.</value>
|
||||||
public Func<WebSocketMessageInfo, Task> OnReceive { get; set; }
|
public Func<WebSocketMessageInfo, Task>? OnReceive { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the last activity date.
|
/// Gets the last activity date.
|
||||||
@ -97,23 +81,11 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public DateTime LastKeepAliveDate { get; set; }
|
public DateTime LastKeepAliveDate { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the id.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The id.</value>
|
|
||||||
public Guid Id { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the URL.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The URL.</value>
|
|
||||||
public string Url { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the query string.
|
/// Gets or sets the query string.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <value>The query string.</value>
|
/// <value>The query string.</value>
|
||||||
public IQueryCollection QueryString { get; set; }
|
public IQueryCollection QueryString { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the state.
|
/// Gets the state.
|
||||||
@ -121,54 +93,131 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
/// <value>The state.</value>
|
/// <value>The state.</value>
|
||||||
public WebSocketState State => _socket.State;
|
public WebSocketState State => _socket.State;
|
||||||
|
|
||||||
void OnSocketClosed(object sender, EventArgs e)
|
/// <summary>
|
||||||
|
/// Sends a message asynchronously.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="message">The message.</param>
|
||||||
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
|
/// <returns>Task.</returns>
|
||||||
|
public Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
Closed?.Invoke(this, EventArgs.Empty);
|
var json = JsonSerializer.SerializeToUtf8Bytes(message, _jsonOptions);
|
||||||
|
return _socket.SendAsync(json, WebSocketMessageType.Text, true, cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc />
|
||||||
/// Called when [receive].
|
public async Task ProcessAsync(CancellationToken cancellationToken = default)
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">The bytes.</param>
|
|
||||||
private void OnReceiveInternal(byte[] bytes)
|
|
||||||
{
|
{
|
||||||
|
var pipe = new Pipe();
|
||||||
|
var writer = pipe.Writer;
|
||||||
|
|
||||||
|
ValueWebSocketReceiveResult receiveresult;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
// Allocate at least 512 bytes from the PipeWriter
|
||||||
|
Memory<byte> memory = writer.GetMemory(512);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
receiveresult = await _socket.ReceiveAsync(memory, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (WebSocketException ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("WS {IP} error receiving data: {Message}", RemoteEndPoint, ex.Message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesRead = receiveresult.Count;
|
||||||
|
if (bytesRead == 0)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tell the PipeWriter how much was read from the Socket
|
||||||
|
writer.Advance(bytesRead);
|
||||||
|
|
||||||
|
// Make the data available to the PipeReader
|
||||||
|
FlushResult flushResult = await writer.FlushAsync();
|
||||||
|
if (flushResult.IsCompleted)
|
||||||
|
{
|
||||||
|
// The PipeReader stopped reading
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
LastActivityDate = DateTime.UtcNow;
|
LastActivityDate = DateTime.UtcNow;
|
||||||
|
|
||||||
|
if (receiveresult.EndOfMessage)
|
||||||
|
{
|
||||||
|
await ProcessInternal(pipe.Reader).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
} while (
|
||||||
|
(_socket.State == WebSocketState.Open || _socket.State == WebSocketState.Connecting)
|
||||||
|
&& receiveresult.MessageType != WebSocketMessageType.Close);
|
||||||
|
|
||||||
|
Closed?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
|
if (_socket.State == WebSocketState.Open
|
||||||
|
|| _socket.State == WebSocketState.CloseReceived
|
||||||
|
|| _socket.State == WebSocketState.CloseSent)
|
||||||
|
{
|
||||||
|
await _socket.CloseAsync(
|
||||||
|
WebSocketCloseStatus.NormalClosure,
|
||||||
|
string.Empty,
|
||||||
|
cancellationToken).ConfigureAwait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ProcessInternal(PipeReader reader)
|
||||||
|
{
|
||||||
|
ReadResult result = await reader.ReadAsync().ConfigureAwait(false);
|
||||||
|
ReadOnlySequence<byte> buffer = result.Buffer;
|
||||||
|
|
||||||
if (OnReceive == null)
|
if (OnReceive == null)
|
||||||
{
|
{
|
||||||
|
// Tell the PipeReader how much of the buffer we have consumed
|
||||||
|
reader.AdvanceTo(buffer.End);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var charset = CharsetDetector.DetectFromBytes(bytes).Detected?.EncodingName;
|
|
||||||
|
|
||||||
if (string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase))
|
WebSocketMessage<object> stub;
|
||||||
|
try
|
||||||
{
|
{
|
||||||
OnReceiveInternal(Encoding.UTF8.GetString(bytes, 0, bytes.Length));
|
|
||||||
|
if (buffer.IsSingleSegment)
|
||||||
|
{
|
||||||
|
stub = JsonSerializer.Deserialize<WebSocketMessage<object>>(buffer.FirstSpan, _jsonOptions);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OnReceiveInternal(Encoding.ASCII.GetString(bytes, 0, bytes.Length));
|
var buf = ArrayPool<byte>.Shared.Rent(Convert.ToInt32(buffer.Length));
|
||||||
|
try
|
||||||
|
{
|
||||||
|
buffer.CopyTo(buf);
|
||||||
|
stub = JsonSerializer.Deserialize<WebSocketMessage<object>>(buf, _jsonOptions);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
ArrayPool<byte>.Shared.Return(buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private void OnReceiveInternal(string message)
|
catch (JsonException ex)
|
||||||
{
|
{
|
||||||
LastActivityDate = DateTime.UtcNow;
|
// Tell the PipeReader how much of the buffer we have consumed
|
||||||
|
reader.AdvanceTo(buffer.End);
|
||||||
if (!message.StartsWith("{", StringComparison.OrdinalIgnoreCase))
|
_logger.LogError(ex, "Error processing web socket message");
|
||||||
{
|
|
||||||
// This info is useful sometimes but also clogs up the log
|
|
||||||
_logger.LogDebug("Received web socket message that is not a json structure: {message}", message);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
// Tell the PipeReader how much of the buffer we have consumed
|
||||||
{
|
reader.AdvanceTo(buffer.End);
|
||||||
var stub = (WebSocketMessage<object>)_jsonSerializer.DeserializeFromString(message, typeof(WebSocketMessage<object>));
|
|
||||||
|
_logger.LogDebug("WS {IP} received message: {@Message}", RemoteEndPoint, stub);
|
||||||
|
|
||||||
var info = new WebSocketMessageInfo
|
var info = new WebSocketMessageInfo
|
||||||
{
|
{
|
||||||
MessageType = stub.MessageType,
|
MessageType = stub.MessageType,
|
||||||
Data = stub.Data?.ToString(),
|
Data = stub.Data?.ToString(), // Data can be null
|
||||||
Connection = this
|
Connection = this
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -178,65 +227,9 @@ namespace Emby.Server.Implementations.HttpServer
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OnReceive?.Invoke(info);
|
await OnReceive(info).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error processing web socket message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a message asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="message">The message.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
/// <exception cref="ArgumentNullException">message</exception>
|
|
||||||
public Task SendAsync<T>(WebSocketMessage<T> message, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (message == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = _jsonSerializer.SerializeToString(message);
|
|
||||||
|
|
||||||
return SendAsync(json, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends a message asynchronously.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="buffer">The buffer.</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
public Task SendAsync(byte[] buffer, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (buffer == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
return _socket.SendAsync(buffer, true, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public Task SendAsync(string text, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(text))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
cancellationToken.ThrowIfCancellationRequested();
|
|
||||||
|
|
||||||
return _socket.SendAsync(text, true, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SendKeepAliveResponse()
|
private void SendKeepAliveResponse()
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,8 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int? GetDefaultSubtitleStreamIndex(List<MediaStream> streams,
|
public static int? GetDefaultSubtitleStreamIndex(
|
||||||
|
List<MediaStream> streams,
|
||||||
string[] preferredLanguages,
|
string[] preferredLanguages,
|
||||||
SubtitlePlaybackMode mode,
|
SubtitlePlaybackMode mode,
|
||||||
string audioTrackLanguage)
|
string audioTrackLanguage)
|
||||||
@ -115,7 +116,8 @@ namespace Emby.Server.Implementations.Library
|
|||||||
.ThenBy(i => i.Index);
|
.ThenBy(i => i.Index);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void SetSubtitleStreamScores(List<MediaStream> streams,
|
public static void SetSubtitleStreamScores(
|
||||||
|
List<MediaStream> streams,
|
||||||
string[] preferredLanguages,
|
string[] preferredLanguages,
|
||||||
SubtitlePlaybackMode mode,
|
SubtitlePlaybackMode mode,
|
||||||
string audioTrackLanguage)
|
string audioTrackLanguage)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
@ -12,24 +14,24 @@ namespace Emby.Server.Implementations.Library
|
|||||||
/// Gets the attribute value.
|
/// Gets the attribute value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="str">The STR.</param>
|
/// <param name="str">The STR.</param>
|
||||||
/// <param name="attrib">The attrib.</param>
|
/// <param name="attribute">The attrib.</param>
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
/// <exception cref="ArgumentNullException">attrib</exception>
|
/// <exception cref="ArgumentException"><paramref name="str" /> or <paramref name="attribute" /> is empty.</exception>
|
||||||
public static string GetAttributeValue(this string str, string attrib)
|
public static string? GetAttributeValue(this string str, string attribute)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(str))
|
if (str.Length == 0)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(str));
|
throw new ArgumentException("String can't be empty.", nameof(str));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(attrib))
|
if (attribute.Length == 0)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(attrib));
|
throw new ArgumentException("String can't be empty.", nameof(attribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
string srch = "[" + attrib + "=";
|
string srch = "[" + attribute + "=";
|
||||||
int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
|
int start = str.IndexOf(srch, StringComparison.OrdinalIgnoreCase);
|
||||||
if (start > -1)
|
if (start != -1)
|
||||||
{
|
{
|
||||||
start += srch.Length;
|
start += srch.Length;
|
||||||
int end = str.IndexOf(']', start);
|
int end = str.IndexOf(']', start);
|
||||||
@ -37,7 +39,7 @@ namespace Emby.Server.Implementations.Library
|
|||||||
}
|
}
|
||||||
|
|
||||||
// for imdbid we also accept pattern matching
|
// for imdbid we also accept pattern matching
|
||||||
if (string.Equals(attrib, "imdbid", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(attribute, "imdbid", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
|
var m = Regex.Match(str, "tt([0-9]{7,8})", RegexOptions.IgnoreCase);
|
||||||
return m.Success ? m.Value : null;
|
return m.Success ? m.Value : null;
|
||||||
|
@ -118,10 +118,12 @@ namespace Emby.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(fileSystem));
|
throw new ArgumentNullException(nameof(fileSystem));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item == null)
|
if (item == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(item));
|
throw new ArgumentNullException(nameof(item));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args == null)
|
if (args == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(args));
|
throw new ArgumentNullException(nameof(args));
|
||||||
|
@ -16,7 +16,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class MusicAlbumResolver : ItemResolver<MusicAlbum>
|
public class MusicAlbumResolver : ItemResolver<MusicAlbum>
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger<MusicAlbumResolver> _logger;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||||||
/// <param name="logger">The logger.</param>
|
/// <param name="logger">The logger.</param>
|
||||||
/// <param name="fileSystem">The file system.</param>
|
/// <param name="fileSystem">The file system.</param>
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
/// <param name="libraryManager">The library manager.</param>
|
||||||
public MusicAlbumResolver(ILogger logger, IFileSystem fileSystem, ILibraryManager libraryManager)
|
public MusicAlbumResolver(ILogger<MusicAlbumResolver> logger, IFileSystem fileSystem, ILibraryManager libraryManager)
|
||||||
{
|
{
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
_fileSystem = fileSystem;
|
_fileSystem = fileSystem;
|
||||||
|
@ -15,7 +15,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class MusicArtistResolver : ItemResolver<MusicArtist>
|
public class MusicArtistResolver : ItemResolver<MusicArtist>
|
||||||
{
|
{
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger<MusicAlbumResolver> _logger;
|
||||||
private readonly IFileSystem _fileSystem;
|
private readonly IFileSystem _fileSystem;
|
||||||
private readonly ILibraryManager _libraryManager;
|
private readonly ILibraryManager _libraryManager;
|
||||||
private readonly IServerConfigurationManager _config;
|
private readonly IServerConfigurationManager _config;
|
||||||
@ -23,12 +23,12 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="MusicArtistResolver"/> class.
|
/// Initializes a new instance of the <see cref="MusicArtistResolver"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="logger">The logger.</param>
|
/// <param name="logger">The logger for the created <see cref="MusicAlbumResolver"/> instances.</param>
|
||||||
/// <param name="fileSystem">The file system.</param>
|
/// <param name="fileSystem">The file system.</param>
|
||||||
/// <param name="libraryManager">The library manager.</param>
|
/// <param name="libraryManager">The library manager.</param>
|
||||||
/// <param name="config">The configuration manager.</param>
|
/// <param name="config">The configuration manager.</param>
|
||||||
public MusicArtistResolver(
|
public MusicArtistResolver(
|
||||||
ILogger<MusicArtistResolver> logger,
|
ILogger<MusicAlbumResolver> logger,
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
ILibraryManager libraryManager,
|
ILibraryManager libraryManager,
|
||||||
IServerConfigurationManager config)
|
IServerConfigurationManager config)
|
||||||
|
@ -608,6 +608,31 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PublicUserDto GetPublicUserDto(User user, string remoteEndPoint = null)
|
||||||
|
{
|
||||||
|
if (user == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
IAuthenticationProvider authenticationProvider = GetAuthenticationProvider(user);
|
||||||
|
bool hasConfiguredPassword = authenticationProvider.HasPassword(user);
|
||||||
|
bool hasConfiguredEasyPassword = !string.IsNullOrEmpty(authenticationProvider.GetEasyPasswordHash(user));
|
||||||
|
|
||||||
|
bool hasPassword = user.Configuration.EnableLocalPassword &&
|
||||||
|
!string.IsNullOrEmpty(remoteEndPoint) &&
|
||||||
|
_networkManager.IsInLocalNetwork(remoteEndPoint) ? hasConfiguredEasyPassword : hasConfiguredPassword;
|
||||||
|
|
||||||
|
PublicUserDto dto = new PublicUserDto
|
||||||
|
{
|
||||||
|
Name = user.Name,
|
||||||
|
HasPassword = hasPassword,
|
||||||
|
HasConfiguredPassword = hasConfiguredPassword,
|
||||||
|
};
|
||||||
|
|
||||||
|
return dto;
|
||||||
|
}
|
||||||
|
|
||||||
public UserDto GetOfflineUserDto(User user)
|
public UserDto GetOfflineUserDto(User user)
|
||||||
{
|
{
|
||||||
var dto = GetUserDto(user);
|
var dto = GetUserDto(user);
|
||||||
|
@ -1059,7 +1059,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
|||||||
{
|
{
|
||||||
var stream = new MediaSourceInfo
|
var stream = new MediaSourceInfo
|
||||||
{
|
{
|
||||||
EncoderPath = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
|
EncoderPath = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveRecordings/" + info.Id + "/stream",
|
||||||
EncoderProtocol = MediaProtocol.Http,
|
EncoderProtocol = MediaProtocol.Http,
|
||||||
Path = info.Path,
|
Path = info.Path,
|
||||||
Protocol = MediaProtocol.File,
|
Protocol = MediaProtocol.File,
|
||||||
|
@ -121,7 +121,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
|||||||
//OpenedMediaSource.Path = tempFile;
|
//OpenedMediaSource.Path = tempFile;
|
||||||
//OpenedMediaSource.ReadAtNativeFramerate = true;
|
//OpenedMediaSource.ReadAtNativeFramerate = true;
|
||||||
|
|
||||||
MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
||||||
MediaSource.Protocol = MediaProtocol.Http;
|
MediaSource.Protocol = MediaProtocol.Http;
|
||||||
//OpenedMediaSource.SupportsDirectPlay = false;
|
//OpenedMediaSource.SupportsDirectPlay = false;
|
||||||
//OpenedMediaSource.SupportsDirectStream = true;
|
//OpenedMediaSource.SupportsDirectStream = true;
|
||||||
|
@ -35,7 +35,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
public M3UTunerHost(
|
public M3UTunerHost(
|
||||||
IServerConfigurationManager config,
|
IServerConfigurationManager config,
|
||||||
IMediaSourceManager mediaSourceManager,
|
IMediaSourceManager mediaSourceManager,
|
||||||
ILogger logger,
|
ILogger<M3UTunerHost> logger,
|
||||||
IJsonSerializer jsonSerializer,
|
IJsonSerializer jsonSerializer,
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
IHttpClient httpClient,
|
IHttpClient httpClient,
|
||||||
@ -83,7 +83,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
return Task.FromResult(list);
|
return Task.FromResult(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly string[] _disallowedSharedStreamExtensions = new string[]
|
private static readonly string[] _disallowedSharedStreamExtensions =
|
||||||
{
|
{
|
||||||
".mkv",
|
".mkv",
|
||||||
".mp4",
|
".mp4",
|
||||||
|
@ -106,7 +106,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
|
|||||||
//OpenedMediaSource.Path = tempFile;
|
//OpenedMediaSource.Path = tempFile;
|
||||||
//OpenedMediaSource.ReadAtNativeFramerate = true;
|
//OpenedMediaSource.ReadAtNativeFramerate = true;
|
||||||
|
|
||||||
MediaSource.Path = _appHost.GetLocalApiUrl("127.0.0.1", true) + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
MediaSource.Path = _appHost.GetLoopbackHttpApiUrl() + "/LiveTv/LiveStreamFiles/" + UniqueId + "/stream.ts";
|
||||||
MediaSource.Protocol = MediaProtocol.Http;
|
MediaSource.Protocol = MediaProtocol.Http;
|
||||||
|
|
||||||
//OpenedMediaSource.Path = TempFilePath;
|
//OpenedMediaSource.Path = TempFilePath;
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
"Folders": "Fouers",
|
"Folders": "Fouers",
|
||||||
"Favorites": "Gunstelinge",
|
"Favorites": "Gunstelinge",
|
||||||
"HeaderFavoriteShows": "Gunsteling Vertonings",
|
"HeaderFavoriteShows": "Gunsteling Vertonings",
|
||||||
"ValueSpecialEpisodeName": "Spesiaal - {0}",
|
"ValueSpecialEpisodeName": "Spesiale - {0}",
|
||||||
"HeaderAlbumArtists": "Album Kunstenaars",
|
"HeaderAlbumArtists": "Album Kunstenaars",
|
||||||
"Books": "Boeke",
|
"Books": "Boeke",
|
||||||
"HeaderNextUp": "Volgende",
|
"HeaderNextUp": "Volgende",
|
||||||
|
@ -91,5 +91,7 @@
|
|||||||
"HeaderNextUp": "এরপরে আসছে",
|
"HeaderNextUp": "এরপরে আসছে",
|
||||||
"HeaderLiveTV": "লাইভ টিভি",
|
"HeaderLiveTV": "লাইভ টিভি",
|
||||||
"HeaderFavoriteSongs": "প্রিয় গানগুলো",
|
"HeaderFavoriteSongs": "প্রিয় গানগুলো",
|
||||||
"HeaderFavoriteShows": "প্রিয় শোগুলো"
|
"HeaderFavoriteShows": "প্রিয় শোগুলো",
|
||||||
|
"TasksLibraryCategory": "গ্রন্থাগার",
|
||||||
|
"TasksMaintenanceCategory": "রক্ষণাবেক্ষণ"
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
"Collections": "Colecciones",
|
"Collections": "Colecciones",
|
||||||
"DeviceOfflineWithName": "{0} se ha desconectado",
|
"DeviceOfflineWithName": "{0} se ha desconectado",
|
||||||
"DeviceOnlineWithName": "{0} está conectado",
|
"DeviceOnlineWithName": "{0} está conectado",
|
||||||
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión de {0}",
|
"FailedLoginAttemptWithUserName": "Intento fallido de inicio de sesión desde {0}",
|
||||||
"Favorites": "Favoritos",
|
"Favorites": "Favoritos",
|
||||||
"Folders": "Carpetas",
|
"Folders": "Carpetas",
|
||||||
"Genres": "Géneros",
|
"Genres": "Géneros",
|
||||||
|
@ -71,7 +71,7 @@
|
|||||||
"ScheduledTaskFailedWithName": "{0} falló",
|
"ScheduledTaskFailedWithName": "{0} falló",
|
||||||
"ScheduledTaskStartedWithName": "{0} iniciada",
|
"ScheduledTaskStartedWithName": "{0} iniciada",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
|
"ServerNameNeedsToBeRestarted": "{0} necesita ser reiniciado",
|
||||||
"Shows": "Series",
|
"Shows": "Mostrar",
|
||||||
"Songs": "Canciones",
|
"Songs": "Canciones",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
|
"StartupEmbyServerIsLoading": "Jellyfin Server se está cargando. Vuelve a intentarlo en breve.",
|
||||||
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",
|
"SubtitleDownloadFailureForItem": "Error al descargar subtítulos para {0}",
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"HeaderLiveTV": "Suorat lähetykset",
|
"HeaderLiveTV": "Live-TV",
|
||||||
"NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa.",
|
"NewVersionIsAvailable": "Uusi versio Jellyfin palvelimesta on ladattavissa.",
|
||||||
"NameSeasonUnknown": "Tuntematon Kausi",
|
"NameSeasonUnknown": "Tuntematon Kausi",
|
||||||
"NameSeasonNumber": "Kausi {0}",
|
"NameSeasonNumber": "Kausi {0}",
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusryhmä {0} on päivitetty",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Palvelimen asetusryhmä {0} on päivitetty",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty versioon {0}",
|
"MessageApplicationUpdatedTo": "Jellyfin palvelin on päivitetty versioon {0}",
|
||||||
"MessageApplicationUpdated": "Jellyfin palvelin on päivitetty",
|
"MessageApplicationUpdated": "Jellyfin palvelin on päivitetty",
|
||||||
"Latest": "Viimeisin",
|
"Latest": "Uusimmat",
|
||||||
"LabelRunningTimeValue": "Toiston kesto: {0}",
|
"LabelRunningTimeValue": "Toiston kesto: {0}",
|
||||||
"LabelIpAddressValue": "IP-osoite: {0}",
|
"LabelIpAddressValue": "IP-osoite: {0}",
|
||||||
"ItemRemovedWithName": "{0} poistettiin kirjastosta",
|
"ItemRemovedWithName": "{0} poistettiin kirjastosta",
|
||||||
@ -41,7 +41,7 @@
|
|||||||
"CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}",
|
"CameraImageUploadedFrom": "Uusi kamerakuva on ladattu {0}",
|
||||||
"Books": "Kirjat",
|
"Books": "Kirjat",
|
||||||
"AuthenticationSucceededWithUserName": "{0} todennus onnistui",
|
"AuthenticationSucceededWithUserName": "{0} todennus onnistui",
|
||||||
"Artists": "Esiintyjät",
|
"Artists": "Artistit",
|
||||||
"Application": "Sovellus",
|
"Application": "Sovellus",
|
||||||
"AppDeviceValues": "Sovellus: {0}, Laite: {1}",
|
"AppDeviceValues": "Sovellus: {0}, Laite: {1}",
|
||||||
"Albums": "Albumit",
|
"Albums": "Albumit",
|
||||||
@ -67,21 +67,21 @@
|
|||||||
"UserDownloadingItemWithValues": "{0} lataa {1}",
|
"UserDownloadingItemWithValues": "{0} lataa {1}",
|
||||||
"UserDeletedWithName": "Käyttäjä {0} poistettu",
|
"UserDeletedWithName": "Käyttäjä {0} poistettu",
|
||||||
"UserCreatedWithName": "Käyttäjä {0} luotu",
|
"UserCreatedWithName": "Käyttäjä {0} luotu",
|
||||||
"TvShows": "TV-Ohjelmat",
|
"TvShows": "TV-sarjat",
|
||||||
"Sync": "Synkronoi",
|
"Sync": "Synkronoi",
|
||||||
"SubtitleDownloadFailureFromForItem": "Tekstityksen lataaminen epäonnistui {0} - {1}",
|
"SubtitleDownloadFailureFromForItem": "Tekstitysten lataus ({0} -> {1}) epäonnistui //this string would have to be generated for each provider and movie because of finnish cases, sorry",
|
||||||
"StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Kokeile hetken kuluttua uudelleen.",
|
"StartupEmbyServerIsLoading": "Jellyfin palvelin latautuu. Kokeile hetken kuluttua uudelleen.",
|
||||||
"Songs": "Kappaleet",
|
"Songs": "Kappaleet",
|
||||||
"Shows": "Ohjelmat",
|
"Shows": "Sarjat",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} vaatii uudelleenkäynnistyksen",
|
"ServerNameNeedsToBeRestarted": "{0} täytyy käynnistää uudelleen",
|
||||||
"ProviderValue": "Tarjoaja: {0}",
|
"ProviderValue": "Tarjoaja: {0}",
|
||||||
"Plugin": "Liitännäinen",
|
"Plugin": "Liitännäinen",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Videon toisto pysäytetty",
|
"NotificationOptionVideoPlaybackStopped": "Videon toisto pysäytetty",
|
||||||
"NotificationOptionVideoPlayback": "Videon toisto aloitettu",
|
"NotificationOptionVideoPlayback": "Videota toistetaan",
|
||||||
"NotificationOptionUserLockedOut": "Käyttäjä lukittu",
|
"NotificationOptionUserLockedOut": "Käyttäjä kirjautui ulos",
|
||||||
"NotificationOptionTaskFailed": "Ajastettu tehtävä epäonnistui",
|
"NotificationOptionTaskFailed": "Ajastettu tehtävä epäonnistui",
|
||||||
"NotificationOptionServerRestartRequired": "Palvelimen uudelleenkäynnistys vaaditaan",
|
"NotificationOptionServerRestartRequired": "Palvelin pitää käynnistää uudelleen",
|
||||||
"NotificationOptionPluginUpdateInstalled": "Lisäosan päivitys asennettu",
|
"NotificationOptionPluginUpdateInstalled": "Liitännäinen päivitetty",
|
||||||
"NotificationOptionPluginUninstalled": "Liitännäinen poistettu",
|
"NotificationOptionPluginUninstalled": "Liitännäinen poistettu",
|
||||||
"NotificationOptionPluginInstalled": "Liitännäinen asennettu",
|
"NotificationOptionPluginInstalled": "Liitännäinen asennettu",
|
||||||
"NotificationOptionPluginError": "Ongelma liitännäisessä",
|
"NotificationOptionPluginError": "Ongelma liitännäisessä",
|
||||||
@ -90,8 +90,8 @@
|
|||||||
"NotificationOptionCameraImageUploaded": "Kameran kuva ladattu",
|
"NotificationOptionCameraImageUploaded": "Kameran kuva ladattu",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Äänen toisto lopetettu",
|
"NotificationOptionAudioPlaybackStopped": "Äänen toisto lopetettu",
|
||||||
"NotificationOptionAudioPlayback": "Toistetaan ääntä",
|
"NotificationOptionAudioPlayback": "Toistetaan ääntä",
|
||||||
"NotificationOptionApplicationUpdateInstalled": "Uusi sovellusversio asennettu",
|
"NotificationOptionApplicationUpdateInstalled": "Sovelluspäivitys asennettu",
|
||||||
"NotificationOptionApplicationUpdateAvailable": "Sovelluksesta on uusi versio saatavilla",
|
"NotificationOptionApplicationUpdateAvailable": "Ohjelmistopäivitys saatavilla",
|
||||||
"TasksMaintenanceCategory": "Ylläpito",
|
"TasksMaintenanceCategory": "Ylläpito",
|
||||||
"TaskDownloadMissingSubtitlesDescription": "Etsii puuttuvia tekstityksiä videon metadatatietojen pohjalta.",
|
"TaskDownloadMissingSubtitlesDescription": "Etsii puuttuvia tekstityksiä videon metadatatietojen pohjalta.",
|
||||||
"TaskDownloadMissingSubtitles": "Lataa puuttuvat tekstitykset",
|
"TaskDownloadMissingSubtitles": "Lataa puuttuvat tekstitykset",
|
||||||
|
@ -94,5 +94,23 @@
|
|||||||
"ValueSpecialEpisodeName": "Spécial - {0}",
|
"ValueSpecialEpisodeName": "Spécial - {0}",
|
||||||
"VersionNumber": "Version {0}",
|
"VersionNumber": "Version {0}",
|
||||||
"TasksLibraryCategory": "Bibliothèque",
|
"TasksLibraryCategory": "Bibliothèque",
|
||||||
"TasksMaintenanceCategory": "Entretien"
|
"TasksMaintenanceCategory": "Entretien",
|
||||||
|
"TaskDownloadMissingSubtitlesDescription": "Recherche l'internet pour des sous-titres manquants à base de métadonnées configurées.",
|
||||||
|
"TaskDownloadMissingSubtitles": "Télécharger des sous-titres manquants",
|
||||||
|
"TaskRefreshChannelsDescription": "Rafraîchit des informations des chaines d'internet.",
|
||||||
|
"TaskRefreshChannels": "Rafraîchir des chaines",
|
||||||
|
"TaskCleanTranscodeDescription": "Retirer des fichiers de transcodage de plus qu'un jour.",
|
||||||
|
"TaskCleanTranscode": "Nettoyer le directoire de transcodage",
|
||||||
|
"TaskUpdatePluginsDescription": "Télécharger et installer des mises à jours des plugins qui sont configurés m.à.j. automisés.",
|
||||||
|
"TaskUpdatePlugins": "Mise à jour des plugins",
|
||||||
|
"TaskRefreshPeopleDescription": "Met à jour les métadonnées pour les acteurs et réalisateurs dans votre bibliothèque.",
|
||||||
|
"TaskRefreshPeople": "Rafraîchir les acteurs",
|
||||||
|
"TaskCleanLogsDescription": "Retire les données qui ont plus que {0} jours.",
|
||||||
|
"TaskCleanLogs": "Nettoyer les données de directoire",
|
||||||
|
"TaskRefreshLibraryDescription": "Analyse votre bibliothèque média pour des nouveaux fichiers et rafraîchit les métadonnées.",
|
||||||
|
"TaskRefreshChapterImages": "Extraire des images du chapitre",
|
||||||
|
"TaskRefreshChapterImagesDescription": "Créer des vignettes pour des vidéos qui ont des chapitres",
|
||||||
|
"TaskRefreshLibrary": "Analyser la bibliothèque de média",
|
||||||
|
"TaskCleanCache": "Nettoyer le cache de directoire",
|
||||||
|
"TasksApplicationCategory": "Application"
|
||||||
}
|
}
|
||||||
|
@ -1,41 +1,41 @@
|
|||||||
{
|
{
|
||||||
"Albums": "Albom",
|
"Albums": "Alben",
|
||||||
"AppDeviceValues": "App: {0}, Grät: {1}",
|
"AppDeviceValues": "App: {0}, Gerät: {1}",
|
||||||
"Application": "Aawändig",
|
"Application": "Anwendung",
|
||||||
"Artists": "Könstler",
|
"Artists": "Künstler",
|
||||||
"AuthenticationSucceededWithUserName": "{0} het sech aagmäudet",
|
"AuthenticationSucceededWithUserName": "{0} hat sich angemeldet",
|
||||||
"Books": "Büecher",
|
"Books": "Bücher",
|
||||||
"CameraImageUploadedFrom": "Es nöis Foti esch ufeglade worde vo {0}",
|
"CameraImageUploadedFrom": "Ein neues Foto wurde von {0} hochgeladen",
|
||||||
"Channels": "Kanäu",
|
"Channels": "Kanäle",
|
||||||
"ChapterNameValue": "Kapitu {0}",
|
"ChapterNameValue": "Kapitel {0}",
|
||||||
"Collections": "Sammlige",
|
"Collections": "Sammlungen",
|
||||||
"DeviceOfflineWithName": "{0} esch offline gange",
|
"DeviceOfflineWithName": "{0} wurde getrennt",
|
||||||
"DeviceOnlineWithName": "{0} esch online cho",
|
"DeviceOnlineWithName": "{0} ist verbunden",
|
||||||
"FailedLoginAttemptWithUserName": "Fäugschlagne Aamäudeversuech vo {0}",
|
"FailedLoginAttemptWithUserName": "Fehlgeschlagener Anmeldeversuch von {0}",
|
||||||
"Favorites": "Favorite",
|
"Favorites": "Favoriten",
|
||||||
"Folders": "Ordner",
|
"Folders": "Ordner",
|
||||||
"Genres": "Genres",
|
"Genres": "Genres",
|
||||||
"HeaderAlbumArtists": "Albom-Könstler",
|
"HeaderAlbumArtists": "Album-Künstler",
|
||||||
"HeaderCameraUploads": "Kamera-Uploads",
|
"HeaderCameraUploads": "Kamera-Uploads",
|
||||||
"HeaderContinueWatching": "Wiiterluege",
|
"HeaderContinueWatching": "weiter schauen",
|
||||||
"HeaderFavoriteAlbums": "Lieblingsalbe",
|
"HeaderFavoriteAlbums": "Lieblingsalben",
|
||||||
"HeaderFavoriteArtists": "Lieblings-Interprete",
|
"HeaderFavoriteArtists": "Lieblings-Künstler",
|
||||||
"HeaderFavoriteEpisodes": "Lieblingsepisode",
|
"HeaderFavoriteEpisodes": "Lieblingsepisoden",
|
||||||
"HeaderFavoriteShows": "Lieblingsserie",
|
"HeaderFavoriteShows": "Lieblingsserien",
|
||||||
"HeaderFavoriteSongs": "Lieblingslieder",
|
"HeaderFavoriteSongs": "Lieblingslieder",
|
||||||
"HeaderLiveTV": "Live-Färnseh",
|
"HeaderLiveTV": "Live-Fernseh",
|
||||||
"HeaderNextUp": "Als nächts",
|
"HeaderNextUp": "Als Nächstes",
|
||||||
"HeaderRecordingGroups": "Ufnahmegruppe",
|
"HeaderRecordingGroups": "Aufnahme-Gruppen",
|
||||||
"HomeVideos": "Heimfilmli",
|
"HomeVideos": "Heimvideos",
|
||||||
"Inherit": "Hinzuefüege",
|
"Inherit": "Vererben",
|
||||||
"ItemAddedWithName": "{0} esch de Bibliothek dezuegfüegt worde",
|
"ItemAddedWithName": "{0} wurde der Bibliothek hinzugefügt",
|
||||||
"ItemRemovedWithName": "{0} esch vo de Bibliothek entfärnt worde",
|
"ItemRemovedWithName": "{0} wurde aus der Bibliothek entfernt",
|
||||||
"LabelIpAddressValue": "IP-Adrässe: {0}",
|
"LabelIpAddressValue": "IP-Adresse: {0}",
|
||||||
"LabelRunningTimeValue": "Loufziit: {0}",
|
"LabelRunningTimeValue": "Laufzeit: {0}",
|
||||||
"Latest": "Nöischti",
|
"Latest": "Neueste",
|
||||||
"MessageApplicationUpdated": "Jellyfin Server esch aktualisiert worde",
|
"MessageApplicationUpdated": "Jellyfin-Server wurde aktualisiert",
|
||||||
"MessageApplicationUpdatedTo": "Jellyfin Server esch of Version {0} aktualisiert worde",
|
"MessageApplicationUpdatedTo": "Jellyfin-Server wurde auf Version {0} aktualisiert",
|
||||||
"MessageNamedServerConfigurationUpdatedWithValue": "De Serveriistöuigsberiich {0} esch aktualisiert worde",
|
"MessageNamedServerConfigurationUpdatedWithValue": "Der Server-Einstellungsbereich {0} wurde aktualisiert",
|
||||||
"MessageServerConfigurationUpdated": "Serveriistöuige send aktualisiert worde",
|
"MessageServerConfigurationUpdated": "Serveriistöuige send aktualisiert worde",
|
||||||
"MixedContent": "Gmeschti Inhäut",
|
"MixedContent": "Gmeschti Inhäut",
|
||||||
"Movies": "Film",
|
"Movies": "Film",
|
||||||
@ -50,7 +50,7 @@
|
|||||||
"NotificationOptionAudioPlayback": "Audiowedergab gstartet",
|
"NotificationOptionAudioPlayback": "Audiowedergab gstartet",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Audiwedergab gstoppt",
|
"NotificationOptionAudioPlaybackStopped": "Audiwedergab gstoppt",
|
||||||
"NotificationOptionCameraImageUploaded": "Foti ueglade",
|
"NotificationOptionCameraImageUploaded": "Foti ueglade",
|
||||||
"NotificationOptionInstallationFailed": "Installationsfäuer",
|
"NotificationOptionInstallationFailed": "Installationsfehler",
|
||||||
"NotificationOptionNewLibraryContent": "Nöie Inhaut hinzuegfüegt",
|
"NotificationOptionNewLibraryContent": "Nöie Inhaut hinzuegfüegt",
|
||||||
"NotificationOptionPluginError": "Plugin-Fäuer",
|
"NotificationOptionPluginError": "Plugin-Fäuer",
|
||||||
"NotificationOptionPluginInstalled": "Plugin installiert",
|
"NotificationOptionPluginInstalled": "Plugin installiert",
|
||||||
@ -92,5 +92,27 @@
|
|||||||
"UserStoppedPlayingItemWithValues": "{0} het d'Wedergab vo {1} of {2} gstoppt",
|
"UserStoppedPlayingItemWithValues": "{0} het d'Wedergab vo {1} of {2} gstoppt",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} esch dinnere Biblithek hinzuegfüegt worde",
|
"ValueHasBeenAddedToLibrary": "{0} esch dinnere Biblithek hinzuegfüegt worde",
|
||||||
"ValueSpecialEpisodeName": "Extra - {0}",
|
"ValueSpecialEpisodeName": "Extra - {0}",
|
||||||
"VersionNumber": "Version {0}"
|
"VersionNumber": "Version {0}",
|
||||||
|
"TaskCleanLogs": "Lösche Log Pfad",
|
||||||
|
"TaskRefreshLibraryDescription": "Scanne alle Bibliotheken für hinzugefügte Datein und erneuere Metadaten.",
|
||||||
|
"TaskRefreshLibrary": "Scanne alle Bibliotheken",
|
||||||
|
"TaskRefreshChapterImagesDescription": "Kreiert Vorschaubilder für Videos welche Kapitel haben.",
|
||||||
|
"TaskRefreshChapterImages": "Extrahiere Kapitel-Bilder",
|
||||||
|
"TaskCleanCacheDescription": "Löscht Zwischenspeicherdatein die nicht länger von System gebraucht werden.",
|
||||||
|
"TaskCleanCache": "Leere Cache Pfad",
|
||||||
|
"TasksChannelsCategory": "Internet Kanäle",
|
||||||
|
"TasksApplicationCategory": "Applikation",
|
||||||
|
"TasksLibraryCategory": "Bibliothek",
|
||||||
|
"TasksMaintenanceCategory": "Verwaltung",
|
||||||
|
"TaskDownloadMissingSubtitlesDescription": "Durchsucht das Internet nach fehlenden Untertiteln, basierend auf den Metadaten Einstellungen.",
|
||||||
|
"TaskDownloadMissingSubtitles": "Lade fehlende Untertitel herunter",
|
||||||
|
"TaskRefreshChannelsDescription": "Aktualisiert Internet Kanal Informationen.",
|
||||||
|
"TaskRefreshChannels": "Aktualisiere Kanäle",
|
||||||
|
"TaskCleanTranscodeDescription": "Löscht Transkodierdateien welche älter als ein Tag sind.",
|
||||||
|
"TaskCleanTranscode": "Räume Transcodier Verzeichnis auf",
|
||||||
|
"TaskUpdatePluginsDescription": "Lädt Aktualisierungen für Erweiterungen herunter und installiert diese, für welche automatische Aktualisierungen konfiguriert sind.",
|
||||||
|
"TaskUpdatePlugins": "Aktualisiere Erweiterungen",
|
||||||
|
"TaskRefreshPeopleDescription": "Aktualisiert Metadaten für Schausteller und Regisseure in deiner Bibliothek.",
|
||||||
|
"TaskRefreshPeople": "Aktualisiere Schauspieler",
|
||||||
|
"TaskCleanLogsDescription": "Löscht Log Dateien die älter als {0} Tage sind."
|
||||||
}
|
}
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
"NotificationOptionVideoPlayback": "Video playback started",
|
"NotificationOptionVideoPlayback": "Video playback started",
|
||||||
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
|
"NotificationOptionVideoPlaybackStopped": "Video playback stopped",
|
||||||
"Photos": "תמונות",
|
"Photos": "תמונות",
|
||||||
"Playlists": "רשימות ניגון",
|
"Playlists": "רשימות הפעלה",
|
||||||
"Plugin": "Plugin",
|
"Plugin": "Plugin",
|
||||||
"PluginInstalledWithName": "{0} was installed",
|
"PluginInstalledWithName": "{0} was installed",
|
||||||
"PluginUninstalledWithName": "{0} was uninstalled",
|
"PluginUninstalledWithName": "{0} was uninstalled",
|
||||||
@ -99,5 +99,13 @@
|
|||||||
"TaskCleanCache": "נקה תיקיית מטמון",
|
"TaskCleanCache": "נקה תיקיית מטמון",
|
||||||
"TasksApplicationCategory": "יישום",
|
"TasksApplicationCategory": "יישום",
|
||||||
"TasksLibraryCategory": "ספרייה",
|
"TasksLibraryCategory": "ספרייה",
|
||||||
"TasksMaintenanceCategory": "תחזוקה"
|
"TasksMaintenanceCategory": "תחזוקה",
|
||||||
|
"TaskUpdatePlugins": "עדכן תוספים",
|
||||||
|
"TaskRefreshPeopleDescription": "מעדכן מטא נתונים עבור שחקנים ובמאים בספריית המדיה שלך.",
|
||||||
|
"TaskRefreshPeople": "רענן אנשים",
|
||||||
|
"TaskCleanLogsDescription": "מוחק קבצי יומן בני יותר מ- {0} ימים.",
|
||||||
|
"TaskCleanLogs": "נקה תיקיית יומן",
|
||||||
|
"TaskRefreshLibraryDescription": "סורק את ספריית המדיה שלך אחר קבצים חדשים ומרענן מטא נתונים.",
|
||||||
|
"TaskRefreshChapterImagesDescription": "יוצר תמונות ממוזערות לסרטונים שיש להם פרקים.",
|
||||||
|
"TasksChannelsCategory": "ערוצי אינטרנט"
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@
|
|||||||
"Inherit": "Naslijedi",
|
"Inherit": "Naslijedi",
|
||||||
"ItemAddedWithName": "{0} je dodano u biblioteku",
|
"ItemAddedWithName": "{0} je dodano u biblioteku",
|
||||||
"ItemRemovedWithName": "{0} je uklonjen iz biblioteke",
|
"ItemRemovedWithName": "{0} je uklonjen iz biblioteke",
|
||||||
"LabelIpAddressValue": "Ip adresa: {0}",
|
"LabelIpAddressValue": "IP adresa: {0}",
|
||||||
"LabelRunningTimeValue": "Vrijeme rada: {0}",
|
"LabelRunningTimeValue": "Vrijeme rada: {0}",
|
||||||
"Latest": "Najnovije",
|
"Latest": "Najnovije",
|
||||||
"MessageApplicationUpdated": "Jellyfin Server je ažuriran",
|
"MessageApplicationUpdated": "Jellyfin Server je ažuriran",
|
||||||
@ -92,5 +92,13 @@
|
|||||||
"UserStoppedPlayingItemWithValues": "{0} je zaustavio {1}",
|
"UserStoppedPlayingItemWithValues": "{0} je zaustavio {1}",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
|
"ValueHasBeenAddedToLibrary": "{0} has been added to your media library",
|
||||||
"ValueSpecialEpisodeName": "Specijal - {0}",
|
"ValueSpecialEpisodeName": "Specijal - {0}",
|
||||||
"VersionNumber": "Verzija {0}"
|
"VersionNumber": "Verzija {0}",
|
||||||
|
"TaskRefreshLibraryDescription": "Skenira vašu medijsku knjižnicu sa novim datotekama i osvježuje metapodatke.",
|
||||||
|
"TaskRefreshLibrary": "Skeniraj medijsku knjižnicu",
|
||||||
|
"TaskRefreshChapterImagesDescription": "Stvara sličice za videozapise koji imaju poglavlja.",
|
||||||
|
"TaskRefreshChapterImages": "Raspakiraj slike poglavlja",
|
||||||
|
"TaskCleanCacheDescription": "Briše priručne datoteke nepotrebne za sistem.",
|
||||||
|
"TaskCleanCache": "Očisti priručnu memoriju",
|
||||||
|
"TasksApplicationCategory": "Aplikacija",
|
||||||
|
"TasksMaintenanceCategory": "Održavanje"
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"Artists": "Artisti",
|
"Artists": "Artisti",
|
||||||
"AuthenticationSucceededWithUserName": "{0} autenticato con successo",
|
"AuthenticationSucceededWithUserName": "{0} autenticato con successo",
|
||||||
"Books": "Libri",
|
"Books": "Libri",
|
||||||
"CameraImageUploadedFrom": "È stata caricata una nuova immagine della fotocamera dal device {0}",
|
"CameraImageUploadedFrom": "È stata caricata una nuova fotografia da {0}",
|
||||||
"Channels": "Canali",
|
"Channels": "Canali",
|
||||||
"ChapterNameValue": "Capitolo {0}",
|
"ChapterNameValue": "Capitolo {0}",
|
||||||
"Collections": "Collezioni",
|
"Collections": "Collezioni",
|
||||||
|
@ -91,5 +91,12 @@
|
|||||||
"Songs": "Песни",
|
"Songs": "Песни",
|
||||||
"Shows": "Серии",
|
"Shows": "Серии",
|
||||||
"ServerNameNeedsToBeRestarted": "{0} треба да се рестартира",
|
"ServerNameNeedsToBeRestarted": "{0} треба да се рестартира",
|
||||||
"ScheduledTaskStartedWithName": "{0} започна"
|
"ScheduledTaskStartedWithName": "{0} започна",
|
||||||
|
"TaskRefreshChapterImages": "Извези Слики од Поглавје",
|
||||||
|
"TaskCleanCacheDescription": "Ги брише кешираните фајлови што не се повеќе потребни од системот.",
|
||||||
|
"TaskCleanCache": "Исчисти Го Кешот",
|
||||||
|
"TasksChannelsCategory": "Интернет Канали",
|
||||||
|
"TasksApplicationCategory": "Апликација",
|
||||||
|
"TasksLibraryCategory": "Библиотека",
|
||||||
|
"TasksMaintenanceCategory": "Одржување"
|
||||||
}
|
}
|
||||||
|
@ -97,5 +97,9 @@
|
|||||||
"TasksApplicationCategory": "Applikasjon",
|
"TasksApplicationCategory": "Applikasjon",
|
||||||
"TasksLibraryCategory": "Bibliotek",
|
"TasksLibraryCategory": "Bibliotek",
|
||||||
"TasksMaintenanceCategory": "Vedlikehold",
|
"TasksMaintenanceCategory": "Vedlikehold",
|
||||||
"TaskCleanCache": "Tøm buffer katalog"
|
"TaskCleanCache": "Tøm buffer katalog",
|
||||||
|
"TaskRefreshLibrary": "Skann mediebibliotek",
|
||||||
|
"TaskRefreshChapterImagesDescription": "Lager forhåndsvisningsbilder for videoer som har kapitler.",
|
||||||
|
"TaskRefreshChapterImages": "Trekk ut Kapittelbilder",
|
||||||
|
"TaskCleanCacheDescription": "Sletter mellomlagrede filer som ikke lengre trengs av systemet."
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
"Artists": "Artiesten",
|
"Artists": "Artiesten",
|
||||||
"AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
|
"AuthenticationSucceededWithUserName": "{0} is succesvol geverifieerd",
|
||||||
"Books": "Boeken",
|
"Books": "Boeken",
|
||||||
"CameraImageUploadedFrom": "Er is een nieuwe afbeelding toegevoegd via {0}",
|
"CameraImageUploadedFrom": "Er is een nieuwe camera afbeelding toegevoegd via {0}",
|
||||||
"Channels": "Kanalen",
|
"Channels": "Kanalen",
|
||||||
"ChapterNameValue": "Hoofdstuk {0}",
|
"ChapterNameValue": "Hoofdstuk {0}",
|
||||||
"Collections": "Verzamelingen",
|
"Collections": "Verzamelingen",
|
||||||
@ -26,7 +26,7 @@
|
|||||||
"HeaderLiveTV": "Live TV",
|
"HeaderLiveTV": "Live TV",
|
||||||
"HeaderNextUp": "Volgende",
|
"HeaderNextUp": "Volgende",
|
||||||
"HeaderRecordingGroups": "Opnamegroepen",
|
"HeaderRecordingGroups": "Opnamegroepen",
|
||||||
"HomeVideos": "Start video's",
|
"HomeVideos": "Home video's",
|
||||||
"Inherit": "Overerven",
|
"Inherit": "Overerven",
|
||||||
"ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
|
"ItemAddedWithName": "{0} is toegevoegd aan de bibliotheek",
|
||||||
"ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
|
"ItemRemovedWithName": "{0} is verwijderd uit de bibliotheek",
|
||||||
@ -50,7 +50,7 @@
|
|||||||
"NotificationOptionAudioPlayback": "Muziek gestart",
|
"NotificationOptionAudioPlayback": "Muziek gestart",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Muziek gestopt",
|
"NotificationOptionAudioPlaybackStopped": "Muziek gestopt",
|
||||||
"NotificationOptionCameraImageUploaded": "Camera-afbeelding geüpload",
|
"NotificationOptionCameraImageUploaded": "Camera-afbeelding geüpload",
|
||||||
"NotificationOptionInstallationFailed": "Installatie mislukking",
|
"NotificationOptionInstallationFailed": "Installatie mislukt",
|
||||||
"NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd",
|
"NotificationOptionNewLibraryContent": "Nieuwe content toegevoegd",
|
||||||
"NotificationOptionPluginError": "Plug-in fout",
|
"NotificationOptionPluginError": "Plug-in fout",
|
||||||
"NotificationOptionPluginInstalled": "Plug-in geïnstalleerd",
|
"NotificationOptionPluginInstalled": "Plug-in geïnstalleerd",
|
||||||
|
@ -92,5 +92,26 @@
|
|||||||
"UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}",
|
"UserStoppedPlayingItemWithValues": "{0} je nehal predvajati {1} na {2}",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici",
|
"ValueHasBeenAddedToLibrary": "{0} je bil dodan vaši knjižnici",
|
||||||
"ValueSpecialEpisodeName": "Poseben - {0}",
|
"ValueSpecialEpisodeName": "Poseben - {0}",
|
||||||
"VersionNumber": "Različica {0}"
|
"VersionNumber": "Različica {0}",
|
||||||
|
"TaskDownloadMissingSubtitles": "Prenesi manjkajoče podnapise",
|
||||||
|
"TaskRefreshChannelsDescription": "Osveži podatke spletnih kanalov.",
|
||||||
|
"TaskRefreshChannels": "Osveži kanale",
|
||||||
|
"TaskCleanTranscodeDescription": "Izbriše več kot dan stare datoteke prekodiranja.",
|
||||||
|
"TaskCleanTranscode": "Počisti mapo prekodiranja",
|
||||||
|
"TaskUpdatePluginsDescription": "Prenese in namesti posodobitve za dodatke, ki imajo omogočene samodejne posodobitve.",
|
||||||
|
"TaskUpdatePlugins": "Posodobi dodatke",
|
||||||
|
"TaskRefreshPeopleDescription": "Osveži metapodatke za igralce in režiserje v vaši knjižnici.",
|
||||||
|
"TaskRefreshPeople": "Osveži osebe",
|
||||||
|
"TaskCleanLogsDescription": "Izbriše dnevniške datoteke starejše od {0} dni.",
|
||||||
|
"TaskCleanLogs": "Počisti mapo dnevnika",
|
||||||
|
"TaskRefreshLibraryDescription": "Preišče vašo knjižnico za nove datoteke in osveži metapodatke.",
|
||||||
|
"TaskRefreshLibrary": "Preišči knjižnico predstavnosti",
|
||||||
|
"TaskRefreshChapterImagesDescription": "Ustvari sličice za poglavja videoposnetkov.",
|
||||||
|
"TaskRefreshChapterImages": "Izvleči slike poglavij",
|
||||||
|
"TaskCleanCacheDescription": "Izbriše predpomnjene datoteke, ki niso več potrebne.",
|
||||||
|
"TaskCleanCache": "Počisti mapo predpomnilnika",
|
||||||
|
"TasksChannelsCategory": "Spletni kanali",
|
||||||
|
"TasksApplicationCategory": "Aplikacija",
|
||||||
|
"TasksLibraryCategory": "Knjižnica",
|
||||||
|
"TasksMaintenanceCategory": "Vzdrževanje"
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
"Channels": "Kanaler",
|
"Channels": "Kanaler",
|
||||||
"ChapterNameValue": "Kapitel {0}",
|
"ChapterNameValue": "Kapitel {0}",
|
||||||
"Collections": "Samlingar",
|
"Collections": "Samlingar",
|
||||||
"DeviceOfflineWithName": "{0} har tappat anslutningen",
|
"DeviceOfflineWithName": "{0} har kopplat från",
|
||||||
"DeviceOnlineWithName": "{0} är ansluten",
|
"DeviceOnlineWithName": "{0} är ansluten",
|
||||||
"FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}",
|
"FailedLoginAttemptWithUserName": "Misslyckat inloggningsförsök från {0}",
|
||||||
"Favorites": "Favoriter",
|
"Favorites": "Favoriter",
|
||||||
@ -50,7 +50,7 @@
|
|||||||
"NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats",
|
"NotificationOptionAudioPlayback": "Ljuduppspelning har påbörjats",
|
||||||
"NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppades",
|
"NotificationOptionAudioPlaybackStopped": "Ljuduppspelning stoppades",
|
||||||
"NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp",
|
"NotificationOptionCameraImageUploaded": "Kamerabild har laddats upp",
|
||||||
"NotificationOptionInstallationFailed": "Fel vid installation",
|
"NotificationOptionInstallationFailed": "Installationen misslyckades",
|
||||||
"NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till",
|
"NotificationOptionNewLibraryContent": "Nytt innehåll har lagts till",
|
||||||
"NotificationOptionPluginError": "Fel uppstod med tillägget",
|
"NotificationOptionPluginError": "Fel uppstod med tillägget",
|
||||||
"NotificationOptionPluginInstalled": "Tillägg har installerats",
|
"NotificationOptionPluginInstalled": "Tillägg har installerats",
|
||||||
@ -113,5 +113,6 @@
|
|||||||
"TasksChannelsCategory": "Internetkanaler",
|
"TasksChannelsCategory": "Internetkanaler",
|
||||||
"TasksApplicationCategory": "Applikation",
|
"TasksApplicationCategory": "Applikation",
|
||||||
"TasksLibraryCategory": "Bibliotek",
|
"TasksLibraryCategory": "Bibliotek",
|
||||||
"TasksMaintenanceCategory": "Underhåll"
|
"TasksMaintenanceCategory": "Underhåll",
|
||||||
|
"TaskRefreshPeople": "Uppdatera Personer"
|
||||||
}
|
}
|
||||||
|
36
Emby.Server.Implementations/Localization/Core/uk.json
Normal file
36
Emby.Server.Implementations/Localization/Core/uk.json
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"MusicVideos": "Музичні відео",
|
||||||
|
"Music": "Музика",
|
||||||
|
"Movies": "Фільми",
|
||||||
|
"MessageApplicationUpdatedTo": "Jellyfin Server був оновлений до версії {0}",
|
||||||
|
"MessageApplicationUpdated": "Jellyfin Server був оновлений",
|
||||||
|
"Latest": "Останні",
|
||||||
|
"LabelIpAddressValue": "IP-адреси: {0}",
|
||||||
|
"ItemRemovedWithName": "{0} видалено з бібліотеки",
|
||||||
|
"ItemAddedWithName": "{0} додано до бібліотеки",
|
||||||
|
"HeaderNextUp": "Наступний",
|
||||||
|
"HeaderLiveTV": "Ефірне ТБ",
|
||||||
|
"HeaderFavoriteSongs": "Улюблені пісні",
|
||||||
|
"HeaderFavoriteShows": "Улюблені шоу",
|
||||||
|
"HeaderFavoriteEpisodes": "Улюблені серії",
|
||||||
|
"HeaderFavoriteArtists": "Улюблені виконавці",
|
||||||
|
"HeaderFavoriteAlbums": "Улюблені альбоми",
|
||||||
|
"HeaderContinueWatching": "Продовжити перегляд",
|
||||||
|
"HeaderCameraUploads": "Завантажено з камери",
|
||||||
|
"HeaderAlbumArtists": "Виконавці альбомів",
|
||||||
|
"Genres": "Жанри",
|
||||||
|
"Folders": "Директорії",
|
||||||
|
"Favorites": "Улюблені",
|
||||||
|
"DeviceOnlineWithName": "{0} під'єднано",
|
||||||
|
"DeviceOfflineWithName": "{0} від'єднано",
|
||||||
|
"Collections": "Колекції",
|
||||||
|
"ChapterNameValue": "Глава {0}",
|
||||||
|
"Channels": "Канали",
|
||||||
|
"CameraImageUploadedFrom": "Нова фотографія завантажена з {0}",
|
||||||
|
"Books": "Книги",
|
||||||
|
"AuthenticationSucceededWithUserName": "{0} успішно авторизовані",
|
||||||
|
"Artists": "Виконавці",
|
||||||
|
"Application": "Додаток",
|
||||||
|
"AppDeviceValues": "Додаток: {0}, Пристрій: {1}",
|
||||||
|
"Albums": "Альбоми"
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"Albums": "專輯",
|
"Albums": "專輯",
|
||||||
"AppDeviceValues": "軟體: {0}, 設備: {1}",
|
"AppDeviceValues": "軟件: {0}, 設備: {1}",
|
||||||
"Application": "應用程式",
|
"Application": "應用程式",
|
||||||
"Artists": "藝人",
|
"Artists": "藝人",
|
||||||
"AuthenticationSucceededWithUserName": "{0} 授權成功",
|
"AuthenticationSucceededWithUserName": "{0} 授權成功",
|
||||||
@ -92,5 +92,8 @@
|
|||||||
"UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}",
|
"UserStoppedPlayingItemWithValues": "{0} 已在 {2} 上停止播放 {1}",
|
||||||
"ValueHasBeenAddedToLibrary": "{0} 已添加到你的媒體庫",
|
"ValueHasBeenAddedToLibrary": "{0} 已添加到你的媒體庫",
|
||||||
"ValueSpecialEpisodeName": "特典 - {0}",
|
"ValueSpecialEpisodeName": "特典 - {0}",
|
||||||
"VersionNumber": "版本{0}"
|
"VersionNumber": "版本{0}",
|
||||||
|
"TaskDownloadMissingSubtitles": "下載遺失的字幕",
|
||||||
|
"TaskUpdatePlugins": "更新插件",
|
||||||
|
"TasksApplicationCategory": "應用程式"
|
||||||
}
|
}
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using WebSocketManager = Emby.Server.Implementations.WebSockets.WebSocketManager;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Middleware
|
|
||||||
{
|
|
||||||
public class WebSocketMiddleware
|
|
||||||
{
|
|
||||||
private readonly RequestDelegate _next;
|
|
||||||
private readonly ILogger<WebSocketMiddleware> _logger;
|
|
||||||
private readonly WebSocketManager _webSocketManager;
|
|
||||||
|
|
||||||
public WebSocketMiddleware(RequestDelegate next, ILogger<WebSocketMiddleware> logger, WebSocketManager webSocketManager)
|
|
||||||
{
|
|
||||||
_next = next;
|
|
||||||
_logger = logger;
|
|
||||||
_webSocketManager = webSocketManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task Invoke(HttpContext httpContext)
|
|
||||||
{
|
|
||||||
_logger.LogInformation("Handling request: " + httpContext.Request.Path);
|
|
||||||
|
|
||||||
if (httpContext.WebSockets.IsWebSocketRequest)
|
|
||||||
{
|
|
||||||
var webSocketContext = await httpContext.WebSockets.AcceptWebSocketAsync(null).ConfigureAwait(false);
|
|
||||||
if (webSocketContext != null)
|
|
||||||
{
|
|
||||||
await _webSocketManager.OnWebSocketConnected(webSocketContext).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await _next.Invoke(httpContext).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Net
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Interface IWebSocket
|
|
||||||
/// </summary>
|
|
||||||
public interface IWebSocket : IDisposable
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Occurs when [closed].
|
|
||||||
/// </summary>
|
|
||||||
event EventHandler<EventArgs> Closed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the state.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The state.</value>
|
|
||||||
WebSocketState State { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the receive action.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The receive action.</value>
|
|
||||||
Action<byte[]> OnReceiveBytes { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends the async.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">The bytes.</param>
|
|
||||||
/// <param name="endOfMessage">if set to <c>true</c> [end of message].</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends the asynchronous.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="text">The text.</param>
|
|
||||||
/// <param name="endOfMessage">if set to <c>true</c> [end of message].</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Net
|
|
||||||
{
|
|
||||||
public class WebSocketConnectEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the URL.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The URL.</value>
|
|
||||||
public string Url { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the query string.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The query string.</value>
|
|
||||||
public IQueryCollection QueryString { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the web socket.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The web socket.</value>
|
|
||||||
public IWebSocket WebSocket { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the endpoint.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The endpoint.</value>
|
|
||||||
public string Endpoint { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Services
|
namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
@ -81,7 +82,7 @@ namespace Emby.Server.Implementations.Services
|
|||||||
if (propertySerializerEntry.PropertyType == typeof(bool))
|
if (propertySerializerEntry.PropertyType == typeof(bool))
|
||||||
{
|
{
|
||||||
//InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value
|
//InputExtensions.cs#530 MVC Checkbox helper emits extra hidden input field, generating 2 values, first is the real value
|
||||||
propertyTextValue = LeftPart(propertyTextValue, ',');
|
propertyTextValue = StringExtensions.LeftPart(propertyTextValue, ',').ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = propertySerializerEntry.PropertyParseStringFn(propertyTextValue);
|
var value = propertySerializerEntry.PropertyParseStringFn(propertyTextValue);
|
||||||
@ -95,19 +96,6 @@ namespace Emby.Server.Implementations.Services
|
|||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string LeftPart(string strVal, char needle)
|
|
||||||
{
|
|
||||||
if (strVal == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pos = strVal.IndexOf(needle);
|
|
||||||
return pos == -1
|
|
||||||
? strVal
|
|
||||||
: strVal.Substring(0, pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class TypeAccessor
|
internal static class TypeAccessor
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Services
|
namespace Emby.Server.Implementations.Services
|
||||||
{
|
{
|
||||||
@ -13,25 +14,12 @@ namespace Emby.Server.Implementations.Services
|
|||||||
public static string GetMethodName(this Type type)
|
public static string GetMethodName(this Type type)
|
||||||
{
|
{
|
||||||
var typeName = type.FullName != null // can be null, e.g. generic types
|
var typeName = type.FullName != null // can be null, e.g. generic types
|
||||||
? LeftPart(type.FullName, "[[") // Generic Fullname
|
? StringExtensions.LeftPart(type.FullName, "[[", StringComparison.Ordinal).ToString() // Generic Fullname
|
||||||
.Replace(type.Namespace + ".", string.Empty) // Trim Namespaces
|
.Replace(type.Namespace + ".", string.Empty, StringComparison.Ordinal) // Trim Namespaces
|
||||||
.Replace("+", ".") // Convert nested into normal type
|
.Replace("+", ".", StringComparison.Ordinal) // Convert nested into normal type
|
||||||
: type.Name;
|
: type.Name;
|
||||||
|
|
||||||
return type.IsGenericParameter ? "'" + typeName : typeName;
|
return type.IsGenericParameter ? "'" + typeName : typeName;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string LeftPart(string strVal, string needle)
|
|
||||||
{
|
|
||||||
if (strVal == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pos = strVal.IndexOf(needle, StringComparison.OrdinalIgnoreCase);
|
|
||||||
return pos == -1
|
|
||||||
? strVal
|
|
||||||
: strVal.Substring(0, pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,191 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MediaBrowser.Common.Net;
|
|
||||||
using MediaBrowser.Controller.Session;
|
|
||||||
using MediaBrowser.Model.Serialization;
|
|
||||||
using MediaBrowser.Model.Session;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Session
|
|
||||||
{
|
|
||||||
public class HttpSessionController : ISessionController
|
|
||||||
{
|
|
||||||
private readonly IHttpClient _httpClient;
|
|
||||||
private readonly IJsonSerializer _json;
|
|
||||||
private readonly ISessionManager _sessionManager;
|
|
||||||
|
|
||||||
public SessionInfo Session { get; private set; }
|
|
||||||
|
|
||||||
private readonly string _postUrl;
|
|
||||||
|
|
||||||
public HttpSessionController(IHttpClient httpClient,
|
|
||||||
IJsonSerializer json,
|
|
||||||
SessionInfo session,
|
|
||||||
string postUrl, ISessionManager sessionManager)
|
|
||||||
{
|
|
||||||
_httpClient = httpClient;
|
|
||||||
_json = json;
|
|
||||||
Session = session;
|
|
||||||
_postUrl = postUrl;
|
|
||||||
_sessionManager = sessionManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string PostUrl => string.Format("http://{0}{1}", Session.RemoteEndPoint, _postUrl);
|
|
||||||
|
|
||||||
public bool IsSessionActive => (DateTime.UtcNow - Session.LastActivityDate).TotalMinutes <= 5;
|
|
||||||
|
|
||||||
public bool SupportsMediaControl => true;
|
|
||||||
|
|
||||||
private Task SendMessage(string name, string messageId, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return SendMessage(name, messageId, new Dictionary<string, string>(), cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task SendMessage(string name, string messageId, Dictionary<string, string> args, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
args["messageId"] = messageId;
|
|
||||||
var url = PostUrl + "/" + name + ToQueryString(args);
|
|
||||||
|
|
||||||
return SendRequest(new HttpRequestOptions
|
|
||||||
{
|
|
||||||
Url = url,
|
|
||||||
CancellationToken = cancellationToken,
|
|
||||||
BufferContent = false
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task SendPlayCommand(PlayRequest command, string messageId, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var dict = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
dict["ItemIds"] = string.Join(",", command.ItemIds.Select(i => i.ToString("N", CultureInfo.InvariantCulture)).ToArray());
|
|
||||||
|
|
||||||
if (command.StartPositionTicks.HasValue)
|
|
||||||
{
|
|
||||||
dict["StartPositionTicks"] = command.StartPositionTicks.Value.ToString(CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
if (command.AudioStreamIndex.HasValue)
|
|
||||||
{
|
|
||||||
dict["AudioStreamIndex"] = command.AudioStreamIndex.Value.ToString(CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
if (command.SubtitleStreamIndex.HasValue)
|
|
||||||
{
|
|
||||||
dict["SubtitleStreamIndex"] = command.SubtitleStreamIndex.Value.ToString(CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
if (command.StartIndex.HasValue)
|
|
||||||
{
|
|
||||||
dict["StartIndex"] = command.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrEmpty(command.MediaSourceId))
|
|
||||||
{
|
|
||||||
dict["MediaSourceId"] = command.MediaSourceId;
|
|
||||||
}
|
|
||||||
|
|
||||||
return SendMessage(command.PlayCommand.ToString(), messageId, dict, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Task SendPlaystateCommand(PlaystateRequest command, string messageId, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
var args = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
if (command.Command == PlaystateCommand.Seek)
|
|
||||||
{
|
|
||||||
if (!command.SeekPositionTicks.HasValue)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("SeekPositionTicks cannot be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
args["SeekPositionTicks"] = command.SeekPositionTicks.Value.ToString(CultureInfo.InvariantCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
return SendMessage(command.Command.ToString(), messageId, args, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string[] _supportedMessages = Array.Empty<string>();
|
|
||||||
public Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
if (!IsSessionActive)
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.Equals(name, "Play", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return SendPlayCommand(data as PlayRequest, messageId, cancellationToken);
|
|
||||||
}
|
|
||||||
if (string.Equals(name, "PlayState", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return SendPlaystateCommand(data as PlaystateRequest, messageId, cancellationToken);
|
|
||||||
}
|
|
||||||
if (string.Equals(name, "GeneralCommand", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
var command = data as GeneralCommand;
|
|
||||||
return SendMessage(command.Name, messageId, command.Arguments, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_supportedMessages.Contains(name, StringComparer.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = PostUrl + "/" + name;
|
|
||||||
|
|
||||||
url += "?messageId=" + messageId;
|
|
||||||
|
|
||||||
var options = new HttpRequestOptions
|
|
||||||
{
|
|
||||||
Url = url,
|
|
||||||
CancellationToken = cancellationToken,
|
|
||||||
BufferContent = false
|
|
||||||
};
|
|
||||||
|
|
||||||
if (data != null)
|
|
||||||
{
|
|
||||||
if (typeof(T) == typeof(string))
|
|
||||||
{
|
|
||||||
var str = data as string;
|
|
||||||
if (!string.IsNullOrEmpty(str))
|
|
||||||
{
|
|
||||||
options.RequestContent = str;
|
|
||||||
options.RequestContentType = "application/json";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
options.RequestContent = _json.SerializeToString(data);
|
|
||||||
options.RequestContentType = "application/json";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return SendRequest(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task SendRequest(HttpRequestOptions options)
|
|
||||||
{
|
|
||||||
using (var response = await _httpClient.Post(options).ConfigureAwait(false))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string ToQueryString(Dictionary<string, string> nvc)
|
|
||||||
{
|
|
||||||
var array = (from item in nvc
|
|
||||||
select string.Format("{0}={1}", WebUtility.UrlEncode(item.Key), WebUtility.UrlEncode(item.Value)))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var args = string.Join("&", array);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(args))
|
|
||||||
{
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "?" + args;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -478,8 +478,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
Client = appName,
|
Client = appName,
|
||||||
DeviceId = deviceId,
|
DeviceId = deviceId,
|
||||||
ApplicationVersion = appVersion,
|
ApplicationVersion = appVersion,
|
||||||
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture),
|
Id = key.GetMD5().ToString("N", CultureInfo.InvariantCulture)
|
||||||
ServerId = _appHost.SystemId
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var username = user?.Name;
|
var username = user?.Name;
|
||||||
@ -1043,12 +1042,12 @@ namespace Emby.Server.Implementations.Session
|
|||||||
|
|
||||||
private static async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
|
private static async Task SendMessageToSession<T>(SessionInfo session, string name, T data, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var controllers = session.SessionControllers.ToArray();
|
var controllers = session.SessionControllers;
|
||||||
var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
|
var messageId = Guid.NewGuid();
|
||||||
|
|
||||||
foreach (var controller in controllers)
|
foreach (var controller in controllers)
|
||||||
{
|
{
|
||||||
await controller.SendMessage(name, messageId, data, controllers, cancellationToken).ConfigureAwait(false);
|
await controller.SendMessage(name, messageId, data, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1056,13 +1055,13 @@ namespace Emby.Server.Implementations.Session
|
|||||||
{
|
{
|
||||||
IEnumerable<Task> GetTasks()
|
IEnumerable<Task> GetTasks()
|
||||||
{
|
{
|
||||||
var messageId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
|
var messageId = Guid.NewGuid();
|
||||||
foreach (var session in sessions)
|
foreach (var session in sessions)
|
||||||
{
|
{
|
||||||
var controllers = session.SessionControllers;
|
var controllers = session.SessionControllers;
|
||||||
foreach (var controller in controllers)
|
foreach (var controller in controllers)
|
||||||
{
|
{
|
||||||
yield return controller.SendMessage(name, messageId, data, controllers, cancellationToken);
|
yield return controller.SendMessage(name, messageId, data, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1779,7 +1778,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
throw new ArgumentNullException(nameof(info));
|
throw new ArgumentNullException(nameof(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
var user = info.UserId.Equals(Guid.Empty)
|
var user = info.UserId == Guid.Empty
|
||||||
? null
|
? null
|
||||||
: _userManager.GetUserById(info.UserId);
|
: _userManager.GetUserById(info.UserId);
|
||||||
|
|
||||||
|
@ -8,7 +8,6 @@ using MediaBrowser.Controller.Net;
|
|||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
using MediaBrowser.Model.Net;
|
using MediaBrowser.Model.Net;
|
||||||
using MediaBrowser.Model.Serialization;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
@ -17,7 +16,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class SessionWebSocketListener
|
/// Class SessionWebSocketListener
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SessionWebSocketListener : IWebSocketListener, IDisposable
|
public sealed class SessionWebSocketListener : IWebSocketListener, IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The timeout in seconds after which a WebSocket is considered to be lost.
|
/// The timeout in seconds after which a WebSocket is considered to be lost.
|
||||||
@ -43,11 +42,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
/// The _logger
|
/// The _logger
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
private readonly ILoggerFactory _loggerFactory;
|
||||||
/// <summary>
|
|
||||||
/// The _dto service
|
|
||||||
/// </summary>
|
|
||||||
private readonly IJsonSerializer _json;
|
|
||||||
|
|
||||||
private readonly IHttpServer _httpServer;
|
private readonly IHttpServer _httpServer;
|
||||||
|
|
||||||
@ -74,23 +69,27 @@ namespace Emby.Server.Implementations.Session
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
|
/// Initializes a new instance of the <see cref="SessionWebSocketListener" /> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="logger">The logger.</param>
|
||||||
/// <param name="sessionManager">The session manager.</param>
|
/// <param name="sessionManager">The session manager.</param>
|
||||||
/// <param name="loggerFactory">The logger factory.</param>
|
/// <param name="loggerFactory">The logger factory.</param>
|
||||||
/// <param name="json">The json.</param>
|
|
||||||
/// <param name="httpServer">The HTTP server.</param>
|
/// <param name="httpServer">The HTTP server.</param>
|
||||||
public SessionWebSocketListener(ISessionManager sessionManager, ILoggerFactory loggerFactory, IJsonSerializer json, IHttpServer httpServer)
|
public SessionWebSocketListener(
|
||||||
|
ILogger<SessionWebSocketListener> logger,
|
||||||
|
ISessionManager sessionManager,
|
||||||
|
ILoggerFactory loggerFactory,
|
||||||
|
IHttpServer httpServer)
|
||||||
{
|
{
|
||||||
|
_logger = logger;
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
_logger = loggerFactory.CreateLogger(GetType().Name);
|
_loggerFactory = loggerFactory;
|
||||||
_json = json;
|
|
||||||
_httpServer = httpServer;
|
_httpServer = httpServer;
|
||||||
|
|
||||||
httpServer.WebSocketConnected += OnServerManagerWebSocketConnected;
|
httpServer.WebSocketConnected += OnServerManagerWebSocketConnected;
|
||||||
}
|
}
|
||||||
|
|
||||||
void OnServerManagerWebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e)
|
private void OnServerManagerWebSocketConnected(object sender, GenericEventArgs<IWebSocketConnection> e)
|
||||||
{
|
{
|
||||||
var session = GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint);
|
var session = GetSession(e.Argument.QueryString, e.Argument.RemoteEndPoint.ToString());
|
||||||
|
|
||||||
if (session != null)
|
if (session != null)
|
||||||
{
|
{
|
||||||
EnsureController(session, e.Argument);
|
EnsureController(session, e.Argument);
|
||||||
@ -98,7 +97,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogWarning("Unable to determine session based on url: {0}", e.Argument.Url);
|
_logger.LogWarning("Unable to determine session based on query string: {0}", e.Argument.QueryString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,6 +118,7 @@ namespace Emby.Server.Implementations.Session
|
|||||||
return _sessionManager.GetSessionByAuthenticationToken(token, deviceId, remoteEndpoint);
|
return _sessionManager.GetSessionByAuthenticationToken(token, deviceId, remoteEndpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
_httpServer.WebSocketConnected -= OnServerManagerWebSocketConnected;
|
_httpServer.WebSocketConnected -= OnServerManagerWebSocketConnected;
|
||||||
@ -135,7 +135,8 @@ namespace Emby.Server.Implementations.Session
|
|||||||
|
|
||||||
private void EnsureController(SessionInfo session, IWebSocketConnection connection)
|
private void EnsureController(SessionInfo session, IWebSocketConnection connection)
|
||||||
{
|
{
|
||||||
var controllerInfo = session.EnsureController<WebSocketController>(s => new WebSocketController(s, _logger, _sessionManager));
|
var controllerInfo = session.EnsureController<WebSocketController>(
|
||||||
|
s => new WebSocketController(_loggerFactory.CreateLogger<WebSocketController>(), s, _sessionManager));
|
||||||
|
|
||||||
var controller = (WebSocketController)controllerInfo.Item1;
|
var controller = (WebSocketController)controllerInfo.Item1;
|
||||||
controller.AddWebSocket(connection);
|
controller.AddWebSocket(connection);
|
||||||
|
@ -1,3 +1,7 @@
|
|||||||
|
#pragma warning disable CS1591
|
||||||
|
#pragma warning disable SA1600
|
||||||
|
#nullable enable
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@ -11,60 +15,63 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace Emby.Server.Implementations.Session
|
namespace Emby.Server.Implementations.Session
|
||||||
{
|
{
|
||||||
public class WebSocketController : ISessionController, IDisposable
|
public sealed class WebSocketController : ISessionController, IDisposable
|
||||||
{
|
{
|
||||||
public SessionInfo Session { get; private set; }
|
|
||||||
public IReadOnlyList<IWebSocketConnection> Sockets { get; private set; }
|
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
private readonly ISessionManager _sessionManager;
|
private readonly ISessionManager _sessionManager;
|
||||||
|
private readonly SessionInfo _session;
|
||||||
|
|
||||||
public WebSocketController(SessionInfo session, ILogger logger, ISessionManager sessionManager)
|
private readonly List<IWebSocketConnection> _sockets;
|
||||||
|
private bool _disposed = false;
|
||||||
|
|
||||||
|
public WebSocketController(
|
||||||
|
ILogger<WebSocketController> logger,
|
||||||
|
SessionInfo session,
|
||||||
|
ISessionManager sessionManager)
|
||||||
{
|
{
|
||||||
Session = session;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_session = session;
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
Sockets = new List<IWebSocketConnection>();
|
_sockets = new List<IWebSocketConnection>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool HasOpenSockets => GetActiveSockets().Any();
|
private bool HasOpenSockets => GetActiveSockets().Any();
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public bool SupportsMediaControl => HasOpenSockets;
|
public bool SupportsMediaControl => HasOpenSockets;
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public bool IsSessionActive => HasOpenSockets;
|
public bool IsSessionActive => HasOpenSockets;
|
||||||
|
|
||||||
private IEnumerable<IWebSocketConnection> GetActiveSockets()
|
private IEnumerable<IWebSocketConnection> GetActiveSockets()
|
||||||
{
|
=> _sockets.Where(i => i.State == WebSocketState.Open);
|
||||||
return Sockets
|
|
||||||
.OrderByDescending(i => i.LastActivityDate)
|
|
||||||
.Where(i => i.State == WebSocketState.Open);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddWebSocket(IWebSocketConnection connection)
|
public void AddWebSocket(IWebSocketConnection connection)
|
||||||
{
|
{
|
||||||
var sockets = Sockets.ToList();
|
_logger.LogDebug("Adding websocket to session {Session}", _session.Id);
|
||||||
sockets.Add(connection);
|
_sockets.Add(connection);
|
||||||
|
|
||||||
Sockets = sockets;
|
connection.Closed += OnConnectionClosed;
|
||||||
|
|
||||||
connection.Closed += connection_Closed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void connection_Closed(object sender, EventArgs e)
|
private void OnConnectionClosed(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var connection = (IWebSocketConnection)sender;
|
var connection = (IWebSocketConnection)sender;
|
||||||
var sockets = Sockets.ToList();
|
_logger.LogDebug("Removing websocket from session {Session}", _session.Id);
|
||||||
sockets.Remove(connection);
|
_sockets.Remove(connection);
|
||||||
|
connection.Closed -= OnConnectionClosed;
|
||||||
Sockets = sockets;
|
_sessionManager.CloseIfNeeded(_session);
|
||||||
|
|
||||||
_sessionManager.CloseIfNeeded(Session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task SendMessage<T>(string name, string messageId, T data, ISessionController[] allControllers, CancellationToken cancellationToken)
|
/// <inheritdoc />
|
||||||
|
public Task SendMessage<T>(
|
||||||
|
string name,
|
||||||
|
Guid messageId,
|
||||||
|
T data,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var socket = GetActiveSockets()
|
var socket = GetActiveSockets()
|
||||||
|
.OrderByDescending(i => i.LastActivityDate)
|
||||||
.FirstOrDefault();
|
.FirstOrDefault();
|
||||||
|
|
||||||
if (socket == null)
|
if (socket == null)
|
||||||
@ -72,21 +79,30 @@ namespace Emby.Server.Implementations.Session
|
|||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
return socket.SendAsync(new WebSocketMessage<T>
|
return socket.SendAsync(
|
||||||
|
new WebSocketMessage<T>
|
||||||
{
|
{
|
||||||
Data = data,
|
Data = data,
|
||||||
MessageType = name,
|
MessageType = name,
|
||||||
MessageId = messageId
|
MessageId = messageId
|
||||||
|
},
|
||||||
}, cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
foreach (var socket in Sockets.ToList())
|
if (_disposed)
|
||||||
{
|
{
|
||||||
socket.Closed -= connection_Closed;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var socket in _sockets)
|
||||||
|
{
|
||||||
|
socket.Closed -= OnConnectionClosed;
|
||||||
|
}
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,105 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Emby.Server.Implementations.Net;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.SocketSharp
|
|
||||||
{
|
|
||||||
public class SharpWebSocket : IWebSocket
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The logger
|
|
||||||
/// </summary>
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
public event EventHandler<EventArgs> Closed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the web socket.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The web socket.</value>
|
|
||||||
private readonly WebSocket _webSocket;
|
|
||||||
|
|
||||||
private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
public SharpWebSocket(WebSocket socket, ILogger logger)
|
|
||||||
{
|
|
||||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
|
||||||
_webSocket = socket ?? throw new ArgumentNullException(nameof(socket));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the state.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The state.</value>
|
|
||||||
public WebSocketState State => _webSocket.State;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends the async.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="bytes">The bytes.</param>
|
|
||||||
/// <param name="endOfMessage">if set to <c>true</c> [end of message].</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
public Task SendAsync(byte[] bytes, bool endOfMessage, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return _webSocket.SendAsync(new ArraySegment<byte>(bytes), WebSocketMessageType.Binary, endOfMessage, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Sends the asynchronous.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="text">The text.</param>
|
|
||||||
/// <param name="endOfMessage">if set to <c>true</c> [end of message].</param>
|
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
public Task SendAsync(string text, bool endOfMessage, CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
return _webSocket.SendAsync(new ArraySegment<byte>(Encoding.UTF8.GetBytes(text)), WebSocketMessageType.Text, endOfMessage, cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases unmanaged and - optionally - managed resources.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
|
|
||||||
protected virtual void Dispose(bool dispose)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dispose)
|
|
||||||
{
|
|
||||||
_cancellationTokenSource.Cancel();
|
|
||||||
if (_webSocket.State == WebSocketState.Open)
|
|
||||||
{
|
|
||||||
_webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closed by client",
|
|
||||||
CancellationToken.None);
|
|
||||||
}
|
|
||||||
Closed?.Invoke(this, EventArgs.Empty);
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the receive action.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The receive action.</value>
|
|
||||||
public Action<byte[]> OnReceiveBytes { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Emby.Server.Implementations.HttpServer;
|
|
||||||
using Emby.Server.Implementations.Net;
|
|
||||||
using MediaBrowser.Model.Services;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Http.Extensions;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Net.Http.Headers;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.SocketSharp
|
|
||||||
{
|
|
||||||
public class WebSocketSharpListener : IHttpListener
|
|
||||||
{
|
|
||||||
private readonly ILogger _logger;
|
|
||||||
|
|
||||||
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource();
|
|
||||||
private CancellationToken _disposeCancellationToken;
|
|
||||||
|
|
||||||
public WebSocketSharpListener(ILogger<WebSocketSharpListener> logger)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_disposeCancellationToken = _disposeCancellationTokenSource.Token;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Func<Exception, IRequest, bool, bool, Task> ErrorHandler { get; set; }
|
|
||||||
|
|
||||||
public Func<IHttpRequest, string, string, string, CancellationToken, Task> RequestHandler { get; set; }
|
|
||||||
|
|
||||||
public Action<WebSocketConnectEventArgs> WebSocketConnected { get; set; }
|
|
||||||
|
|
||||||
private static void LogRequest(ILogger logger, HttpRequest request)
|
|
||||||
{
|
|
||||||
var url = request.GetDisplayUrl();
|
|
||||||
|
|
||||||
logger.LogInformation("WS {Url}. UserAgent: {UserAgent}", url, request.Headers[HeaderNames.UserAgent].ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ProcessWebSocketRequest(HttpContext ctx)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
LogRequest(_logger, ctx.Request);
|
|
||||||
var endpoint = ctx.Connection.RemoteIpAddress.ToString();
|
|
||||||
var url = ctx.Request.GetDisplayUrl();
|
|
||||||
|
|
||||||
var webSocketContext = await ctx.WebSockets.AcceptWebSocketAsync(null).ConfigureAwait(false);
|
|
||||||
var socket = new SharpWebSocket(webSocketContext, _logger);
|
|
||||||
|
|
||||||
WebSocketConnected(new WebSocketConnectEventArgs
|
|
||||||
{
|
|
||||||
Url = url,
|
|
||||||
QueryString = ctx.Request.Query,
|
|
||||||
WebSocket = socket,
|
|
||||||
Endpoint = endpoint
|
|
||||||
});
|
|
||||||
|
|
||||||
WebSocketReceiveResult result;
|
|
||||||
var message = new List<byte>();
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
var buffer = WebSocket.CreateServerBuffer(4096);
|
|
||||||
result = await webSocketContext.ReceiveAsync(buffer, _disposeCancellationToken);
|
|
||||||
message.AddRange(buffer.Array.Take(result.Count));
|
|
||||||
|
|
||||||
if (result.EndOfMessage)
|
|
||||||
{
|
|
||||||
socket.OnReceiveBytes(message.ToArray());
|
|
||||||
message.Clear();
|
|
||||||
}
|
|
||||||
} while (socket.State == WebSocketState.Open && result.MessageType != WebSocketMessageType.Close);
|
|
||||||
|
|
||||||
|
|
||||||
if (webSocketContext.State == WebSocketState.Open)
|
|
||||||
{
|
|
||||||
await webSocketContext.CloseAsync(
|
|
||||||
result.CloseStatus ?? WebSocketCloseStatus.NormalClosure,
|
|
||||||
result.CloseStatusDescription,
|
|
||||||
_disposeCancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.Dispose();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "AcceptWebSocketAsync error");
|
|
||||||
if (!ctx.Response.HasStarted)
|
|
||||||
{
|
|
||||||
ctx.Response.StatusCode = 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task Stop()
|
|
||||||
{
|
|
||||||
_disposeCancellationTokenSource.Cancel();
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases the unmanaged resources and disposes of the managed resources used.
|
|
||||||
/// </summary>
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
Dispose(true);
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Releases the unmanaged resources and disposes of the managed resources used.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="disposing">Whether or not the managed resources should be disposed.</param>
|
|
||||||
protected virtual void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disposing)
|
|
||||||
{
|
|
||||||
Stop().GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
_disposed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Net.Mime;
|
using System.Net.Mime;
|
||||||
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Http.Extensions;
|
using Microsoft.AspNetCore.Http.Extensions;
|
||||||
@ -62,6 +63,9 @@ namespace Emby.Server.Implementations.SocketSharp
|
|||||||
if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip))
|
if (!IPAddress.TryParse(GetHeader(CustomHeaderNames.XRealIP), out ip))
|
||||||
{
|
{
|
||||||
ip = Request.HttpContext.Connection.RemoteIpAddress;
|
ip = Request.HttpContext.Connection.RemoteIpAddress;
|
||||||
|
|
||||||
|
// Default to the loopback address if no RemoteIpAddress is specified (i.e. during integration tests)
|
||||||
|
ip ??= IPAddress.Loopback;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +93,10 @@ namespace Emby.Server.Implementations.SocketSharp
|
|||||||
|
|
||||||
public IQueryCollection QueryString => Request.Query;
|
public IQueryCollection QueryString => Request.Query;
|
||||||
|
|
||||||
public bool IsLocal => Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress);
|
public bool IsLocal =>
|
||||||
|
(Request.HttpContext.Connection.LocalIpAddress == null
|
||||||
|
&& Request.HttpContext.Connection.RemoteIpAddress == null)
|
||||||
|
|| Request.HttpContext.Connection.LocalIpAddress.Equals(Request.HttpContext.Connection.RemoteIpAddress);
|
||||||
|
|
||||||
public string HttpMethod => Request.Method;
|
public string HttpMethod => Request.Method;
|
||||||
|
|
||||||
@ -216,14 +223,14 @@ namespace Emby.Server.Implementations.SocketSharp
|
|||||||
pi = pi.Slice(1);
|
pi = pi.Slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
format = LeftPart(pi, '/');
|
format = pi.LeftPart('/');
|
||||||
if (format.Length > FormatMaxLength)
|
if (format.Length > FormatMaxLength)
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
format = LeftPart(format, '.');
|
format = format.LeftPart('.');
|
||||||
if (format.Contains("json", StringComparison.OrdinalIgnoreCase))
|
if (format.Contains("json", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return "application/json";
|
return "application/json";
|
||||||
@ -235,16 +242,5 @@ namespace Emby.Server.Implementations.SocketSharp
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ReadOnlySpan<char> LeftPart(ReadOnlySpan<char> strVal, char needle)
|
|
||||||
{
|
|
||||||
if (strVal == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pos = strVal.IndexOf(needle);
|
|
||||||
return pos == -1 ? strVal : strVal.Slice(0, pos);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
using System.Threading.Tasks;
|
|
||||||
using MediaBrowser.Model.Net;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.WebSockets
|
|
||||||
{
|
|
||||||
public interface IWebSocketHandler
|
|
||||||
{
|
|
||||||
Task ProcessMessage(WebSocketMessage<object> message, TaskCompletionSource<bool> taskCompletionSource);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net.WebSockets;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using MediaBrowser.Model.Net;
|
|
||||||
using MediaBrowser.Model.Serialization;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using UtfUnknown;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.WebSockets
|
|
||||||
{
|
|
||||||
public class WebSocketManager
|
|
||||||
{
|
|
||||||
private readonly IWebSocketHandler[] _webSocketHandlers;
|
|
||||||
private readonly IJsonSerializer _jsonSerializer;
|
|
||||||
private readonly ILogger<WebSocketManager> _logger;
|
|
||||||
private const int BufferSize = 4096;
|
|
||||||
|
|
||||||
public WebSocketManager(IWebSocketHandler[] webSocketHandlers, IJsonSerializer jsonSerializer, ILogger<WebSocketManager> logger)
|
|
||||||
{
|
|
||||||
_webSocketHandlers = webSocketHandlers;
|
|
||||||
_jsonSerializer = jsonSerializer;
|
|
||||||
_logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task OnWebSocketConnected(WebSocket webSocket)
|
|
||||||
{
|
|
||||||
var taskCompletionSource = new TaskCompletionSource<bool>();
|
|
||||||
var cancellationToken = new CancellationTokenSource().Token;
|
|
||||||
WebSocketReceiveResult result;
|
|
||||||
var message = new List<byte>();
|
|
||||||
|
|
||||||
// Keep listening for incoming messages, otherwise the socket closes automatically
|
|
||||||
do
|
|
||||||
{
|
|
||||||
var buffer = WebSocket.CreateServerBuffer(BufferSize);
|
|
||||||
result = await webSocket.ReceiveAsync(buffer, cancellationToken).ConfigureAwait(false);
|
|
||||||
message.AddRange(buffer.Array.Take(result.Count));
|
|
||||||
|
|
||||||
if (result.EndOfMessage)
|
|
||||||
{
|
|
||||||
await ProcessMessage(message.ToArray(), taskCompletionSource).ConfigureAwait(false);
|
|
||||||
message.Clear();
|
|
||||||
}
|
|
||||||
} while (!taskCompletionSource.Task.IsCompleted &&
|
|
||||||
webSocket.State == WebSocketState.Open &&
|
|
||||||
result.MessageType != WebSocketMessageType.Close);
|
|
||||||
|
|
||||||
if (webSocket.State == WebSocketState.Open)
|
|
||||||
{
|
|
||||||
await webSocket.CloseAsync(
|
|
||||||
result.CloseStatus ?? WebSocketCloseStatus.NormalClosure,
|
|
||||||
result.CloseStatusDescription,
|
|
||||||
cancellationToken).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task ProcessMessage(byte[] messageBytes, TaskCompletionSource<bool> taskCompletionSource)
|
|
||||||
{
|
|
||||||
var charset = CharsetDetector.DetectFromBytes(messageBytes).Detected?.EncodingName;
|
|
||||||
var message = string.Equals(charset, "utf-8", StringComparison.OrdinalIgnoreCase)
|
|
||||||
? Encoding.UTF8.GetString(messageBytes, 0, messageBytes.Length)
|
|
||||||
: Encoding.ASCII.GetString(messageBytes, 0, messageBytes.Length);
|
|
||||||
|
|
||||||
// All messages are expected to be valid JSON objects
|
|
||||||
if (!message.StartsWith("{", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
_logger.LogDebug("Received web socket message that is not a json structure: {Message}", message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var info = _jsonSerializer.DeserializeFromString<WebSocketMessage<object>>(message);
|
|
||||||
|
|
||||||
_logger.LogDebug("Websocket message received: {0}", info.MessageType);
|
|
||||||
|
|
||||||
var tasks = _webSocketHandlers.Select(handler => Task.Run(() =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
handler.ProcessMessage(info, taskCompletionSource).ConfigureAwait(false);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "{HandlerType} failed processing WebSocket message {MessageType}",
|
|
||||||
handler.GetType().Name, info.MessageType ?? string.Empty);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
await Task.WhenAll(tasks);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.LogError(ex, "Error processing web socket message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
195
Jellyfin.Data/Entities/Artwork.cs
Normal file
195
Jellyfin.Data/Entities/Artwork.cs
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Artwork
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected Artwork()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static Artwork CreateArtworkUnsafe()
|
||||||
|
{
|
||||||
|
return new Artwork();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path"></param>
|
||||||
|
/// <param name="kind"></param>
|
||||||
|
/// <param name="_metadata0"></param>
|
||||||
|
/// <param name="_personrole1"></param>
|
||||||
|
public Artwork(string path, Enums.ArtKind kind, Metadata _metadata0, PersonRole _personrole1)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
|
||||||
|
this.Path = path;
|
||||||
|
|
||||||
|
this.Kind = kind;
|
||||||
|
|
||||||
|
if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0));
|
||||||
|
_metadata0.Artwork.Add(this);
|
||||||
|
|
||||||
|
if (_personrole1 == null) throw new ArgumentNullException(nameof(_personrole1));
|
||||||
|
_personrole1.Artwork = this;
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path"></param>
|
||||||
|
/// <param name="kind"></param>
|
||||||
|
/// <param name="_metadata0"></param>
|
||||||
|
/// <param name="_personrole1"></param>
|
||||||
|
public static Artwork Create(string path, Enums.ArtKind kind, Metadata _metadata0, PersonRole _personrole1)
|
||||||
|
{
|
||||||
|
return new Artwork(path, kind, _metadata0, _personrole1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Path
|
||||||
|
/// </summary>
|
||||||
|
protected string _Path;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Path to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetPath(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Path to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetPath(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Max length = 65535
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(65535)]
|
||||||
|
[StringLength(65535)]
|
||||||
|
public string Path
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Path;
|
||||||
|
GetPath(ref value);
|
||||||
|
return (_Path = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Path;
|
||||||
|
SetPath(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Path = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Kind
|
||||||
|
/// </summary>
|
||||||
|
internal Enums.ArtKind _Kind;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Kind to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetKind(Enums.ArtKind oldValue, ref Enums.ArtKind newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Kind to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetKind(ref Enums.ArtKind result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public Enums.ArtKind Kind
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Enums.ArtKind value = _Kind;
|
||||||
|
GetKind(ref value);
|
||||||
|
return (_Kind = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Enums.ArtKind oldValue = _Kind;
|
||||||
|
SetKind(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Kind = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
69
Jellyfin.Data/Entities/Book.cs
Normal file
69
Jellyfin.Data/Entities/Book.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Book : LibraryItem
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected Book()
|
||||||
|
{
|
||||||
|
BookMetadata = new HashSet<BookMetadata>();
|
||||||
|
Releases = new HashSet<Release>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static Book CreateBookUnsafe()
|
||||||
|
{
|
||||||
|
return new Book();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
|
||||||
|
public Book(Guid urlid, DateTime dateadded)
|
||||||
|
{
|
||||||
|
this.UrlId = urlid;
|
||||||
|
|
||||||
|
this.BookMetadata = new HashSet<BookMetadata>();
|
||||||
|
this.Releases = new HashSet<Release>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
|
||||||
|
public static Book Create(Guid urlid, DateTime dateadded)
|
||||||
|
{
|
||||||
|
return new Book(urlid, dateadded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
[ForeignKey("BookMetadata_BookMetadata_Id")]
|
||||||
|
public virtual ICollection<BookMetadata> BookMetadata { get; protected set; }
|
||||||
|
|
||||||
|
[ForeignKey("Release_Releases_Id")]
|
||||||
|
public virtual ICollection<Release> Releases { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
107
Jellyfin.Data/Entities/BookMetadata.cs
Normal file
107
Jellyfin.Data/Entities/BookMetadata.cs
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class BookMetadata : Metadata
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected BookMetadata()
|
||||||
|
{
|
||||||
|
Publishers = new HashSet<Company>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static BookMetadata CreateBookMetadataUnsafe()
|
||||||
|
{
|
||||||
|
return new BookMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_book0"></param>
|
||||||
|
public BookMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Book _book0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title));
|
||||||
|
this.Title = title;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language));
|
||||||
|
this.Language = language;
|
||||||
|
|
||||||
|
if (_book0 == null) throw new ArgumentNullException(nameof(_book0));
|
||||||
|
_book0.BookMetadata.Add(this);
|
||||||
|
|
||||||
|
this.Publishers = new HashSet<Company>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_book0"></param>
|
||||||
|
public static BookMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Book _book0)
|
||||||
|
{
|
||||||
|
return new BookMetadata(title, language, dateadded, datemodified, _book0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for ISBN
|
||||||
|
/// </summary>
|
||||||
|
protected long? _ISBN;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of ISBN to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetISBN(long? oldValue, ref long? newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of ISBN to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetISBN(ref long? result);
|
||||||
|
|
||||||
|
public long? ISBN
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
long? value = _ISBN;
|
||||||
|
GetISBN(ref value);
|
||||||
|
return (_ISBN = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
long? oldValue = _ISBN;
|
||||||
|
SetISBN(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_ISBN = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
[ForeignKey("Company_Publishers_Id")]
|
||||||
|
public virtual ICollection<Company> Publishers { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
263
Jellyfin.Data/Entities/Chapter.cs
Normal file
263
Jellyfin.Data/Entities/Chapter.cs
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Chapter
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected Chapter()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static Chapter CreateChapterUnsafe()
|
||||||
|
{
|
||||||
|
return new Chapter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="timestart"></param>
|
||||||
|
/// <param name="_release0"></param>
|
||||||
|
public Chapter(string language, long timestart, Release _release0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language));
|
||||||
|
this.Language = language;
|
||||||
|
|
||||||
|
this.TimeStart = timestart;
|
||||||
|
|
||||||
|
if (_release0 == null) throw new ArgumentNullException(nameof(_release0));
|
||||||
|
_release0.Chapters.Add(this);
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="timestart"></param>
|
||||||
|
/// <param name="_release0"></param>
|
||||||
|
public static Chapter Create(string language, long timestart, Release _release0)
|
||||||
|
{
|
||||||
|
return new Chapter(language, timestart, _release0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Name
|
||||||
|
/// </summary>
|
||||||
|
protected string _Name;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetName(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetName(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Name;
|
||||||
|
GetName(ref value);
|
||||||
|
return (_Name = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Name;
|
||||||
|
SetName(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Name = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Language
|
||||||
|
/// </summary>
|
||||||
|
protected string _Language;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Language to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetLanguage(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Language to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetLanguage(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Min length = 3, Max length = 3
|
||||||
|
/// ISO-639-3 3-character language codes
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MinLength(3)]
|
||||||
|
[MaxLength(3)]
|
||||||
|
[StringLength(3)]
|
||||||
|
public string Language
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Language;
|
||||||
|
GetLanguage(ref value);
|
||||||
|
return (_Language = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Language;
|
||||||
|
SetLanguage(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Language = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for TimeStart
|
||||||
|
/// </summary>
|
||||||
|
protected long _TimeStart;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of TimeStart to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetTimeStart(long oldValue, ref long newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of TimeStart to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetTimeStart(ref long result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public long TimeStart
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
long value = _TimeStart;
|
||||||
|
GetTimeStart(ref value);
|
||||||
|
return (_TimeStart = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
long oldValue = _TimeStart;
|
||||||
|
SetTimeStart(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_TimeStart = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for TimeEnd
|
||||||
|
/// </summary>
|
||||||
|
protected long? _TimeEnd;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of TimeEnd to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetTimeEnd(long? oldValue, ref long? newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of TimeEnd to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetTimeEnd(ref long? result);
|
||||||
|
|
||||||
|
public long? TimeEnd
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
long? value = _TimeEnd;
|
||||||
|
GetTimeEnd(ref value);
|
||||||
|
return (_TimeEnd = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
long? oldValue = _TimeEnd;
|
||||||
|
SetTimeEnd(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_TimeEnd = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
120
Jellyfin.Data/Entities/Collection.cs
Normal file
120
Jellyfin.Data/Entities/Collection.cs
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Collection
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor
|
||||||
|
/// </summary>
|
||||||
|
public Collection()
|
||||||
|
{
|
||||||
|
CollectionItem = new LinkedList<CollectionItem>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Name
|
||||||
|
/// </summary>
|
||||||
|
protected string _Name;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetName(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetName(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Name;
|
||||||
|
GetName(ref value);
|
||||||
|
return (_Name = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Name;
|
||||||
|
SetName(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Name = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
[ForeignKey("CollectionItem_CollectionItem_Id")]
|
||||||
|
public virtual ICollection<CollectionItem> CollectionItem { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
143
Jellyfin.Data/Entities/CollectionItem.cs
Normal file
143
Jellyfin.Data/Entities/CollectionItem.cs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class CollectionItem
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected CollectionItem()
|
||||||
|
{
|
||||||
|
// NOTE: This class has one-to-one associations with CollectionItem.
|
||||||
|
// One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static CollectionItem CreateCollectionItemUnsafe()
|
||||||
|
{
|
||||||
|
return new CollectionItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="_collection0"></param>
|
||||||
|
/// <param name="_collectionitem1"></param>
|
||||||
|
/// <param name="_collectionitem2"></param>
|
||||||
|
public CollectionItem(Collection _collection0, CollectionItem _collectionitem1, CollectionItem _collectionitem2)
|
||||||
|
{
|
||||||
|
// NOTE: This class has one-to-one associations with CollectionItem.
|
||||||
|
// One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
|
||||||
|
|
||||||
|
if (_collection0 == null) throw new ArgumentNullException(nameof(_collection0));
|
||||||
|
_collection0.CollectionItem.Add(this);
|
||||||
|
|
||||||
|
if (_collectionitem1 == null) throw new ArgumentNullException(nameof(_collectionitem1));
|
||||||
|
_collectionitem1.Next = this;
|
||||||
|
|
||||||
|
if (_collectionitem2 == null) throw new ArgumentNullException(nameof(_collectionitem2));
|
||||||
|
_collectionitem2.Previous = this;
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="_collection0"></param>
|
||||||
|
/// <param name="_collectionitem1"></param>
|
||||||
|
/// <param name="_collectionitem2"></param>
|
||||||
|
public static CollectionItem Create(Collection _collection0, CollectionItem _collectionitem1, CollectionItem _collectionitem2)
|
||||||
|
{
|
||||||
|
return new CollectionItem(_collection0, _collectionitem1, _collectionitem2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[ForeignKey("LibraryItem_Id")]
|
||||||
|
public virtual LibraryItem LibraryItem { get; set; }
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// TODO check if this properly updated dependant and has the proper principal relationship
|
||||||
|
/// </remarks>
|
||||||
|
[ForeignKey("CollectionItem_Next_Id")]
|
||||||
|
public virtual CollectionItem Next { get; set; }
|
||||||
|
|
||||||
|
/// <remarks>
|
||||||
|
/// TODO check if this properly updated dependant and has the proper principal relationship
|
||||||
|
/// </remarks>
|
||||||
|
[ForeignKey("CollectionItem_Previous_Id")]
|
||||||
|
public virtual CollectionItem Previous { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
137
Jellyfin.Data/Entities/Company.cs
Normal file
137
Jellyfin.Data/Entities/Company.cs
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Company
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected Company()
|
||||||
|
{
|
||||||
|
CompanyMetadata = new HashSet<CompanyMetadata>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static Company CreateCompanyUnsafe()
|
||||||
|
{
|
||||||
|
return new Company();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="_moviemetadata0"></param>
|
||||||
|
/// <param name="_seriesmetadata1"></param>
|
||||||
|
/// <param name="_musicalbummetadata2"></param>
|
||||||
|
/// <param name="_bookmetadata3"></param>
|
||||||
|
/// <param name="_company4"></param>
|
||||||
|
public Company(MovieMetadata _moviemetadata0, SeriesMetadata _seriesmetadata1, MusicAlbumMetadata _musicalbummetadata2, BookMetadata _bookmetadata3, Company _company4)
|
||||||
|
{
|
||||||
|
if (_moviemetadata0 == null) throw new ArgumentNullException(nameof(_moviemetadata0));
|
||||||
|
_moviemetadata0.Studios.Add(this);
|
||||||
|
|
||||||
|
if (_seriesmetadata1 == null) throw new ArgumentNullException(nameof(_seriesmetadata1));
|
||||||
|
_seriesmetadata1.Networks.Add(this);
|
||||||
|
|
||||||
|
if (_musicalbummetadata2 == null) throw new ArgumentNullException(nameof(_musicalbummetadata2));
|
||||||
|
_musicalbummetadata2.Labels.Add(this);
|
||||||
|
|
||||||
|
if (_bookmetadata3 == null) throw new ArgumentNullException(nameof(_bookmetadata3));
|
||||||
|
_bookmetadata3.Publishers.Add(this);
|
||||||
|
|
||||||
|
if (_company4 == null) throw new ArgumentNullException(nameof(_company4));
|
||||||
|
_company4.Parent = this;
|
||||||
|
|
||||||
|
this.CompanyMetadata = new HashSet<CompanyMetadata>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="_moviemetadata0"></param>
|
||||||
|
/// <param name="_seriesmetadata1"></param>
|
||||||
|
/// <param name="_musicalbummetadata2"></param>
|
||||||
|
/// <param name="_bookmetadata3"></param>
|
||||||
|
/// <param name="_company4"></param>
|
||||||
|
public static Company Create(MovieMetadata _moviemetadata0, SeriesMetadata _seriesmetadata1, MusicAlbumMetadata _musicalbummetadata2, BookMetadata _bookmetadata3, Company _company4)
|
||||||
|
{
|
||||||
|
return new Company(_moviemetadata0, _seriesmetadata1, _musicalbummetadata2, _bookmetadata3, _company4);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
[ForeignKey("CompanyMetadata_CompanyMetadata_Id")]
|
||||||
|
public virtual ICollection<CompanyMetadata> CompanyMetadata { get; protected set; }
|
||||||
|
[ForeignKey("Company_Parent_Id")]
|
||||||
|
public virtual Company Parent { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
216
Jellyfin.Data/Entities/CompanyMetadata.cs
Normal file
216
Jellyfin.Data/Entities/CompanyMetadata.cs
Normal file
@ -0,0 +1,216 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class CompanyMetadata : Metadata
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected CompanyMetadata()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static CompanyMetadata CreateCompanyMetadataUnsafe()
|
||||||
|
{
|
||||||
|
return new CompanyMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_company0"></param>
|
||||||
|
public CompanyMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Company _company0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title));
|
||||||
|
this.Title = title;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language));
|
||||||
|
this.Language = language;
|
||||||
|
|
||||||
|
if (_company0 == null) throw new ArgumentNullException(nameof(_company0));
|
||||||
|
_company0.CompanyMetadata.Add(this);
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_company0"></param>
|
||||||
|
public static CompanyMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Company _company0)
|
||||||
|
{
|
||||||
|
return new CompanyMetadata(title, language, dateadded, datemodified, _company0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Description
|
||||||
|
/// </summary>
|
||||||
|
protected string _Description;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Description to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetDescription(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Description to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetDescription(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 65535
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(65535)]
|
||||||
|
[StringLength(65535)]
|
||||||
|
public string Description
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Description;
|
||||||
|
GetDescription(ref value);
|
||||||
|
return (_Description = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Description;
|
||||||
|
SetDescription(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Description = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Headquarters
|
||||||
|
/// </summary>
|
||||||
|
protected string _Headquarters;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Headquarters to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetHeadquarters(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Headquarters to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetHeadquarters(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 255
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(255)]
|
||||||
|
[StringLength(255)]
|
||||||
|
public string Headquarters
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Headquarters;
|
||||||
|
GetHeadquarters(ref value);
|
||||||
|
return (_Headquarters = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Headquarters;
|
||||||
|
SetHeadquarters(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Headquarters = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Country
|
||||||
|
/// </summary>
|
||||||
|
protected string _Country;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Country to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetCountry(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Country to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetCountry(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 2
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(2)]
|
||||||
|
[StringLength(2)]
|
||||||
|
public string Country
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Country;
|
||||||
|
GetCountry(ref value);
|
||||||
|
return (_Country = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Country;
|
||||||
|
SetCountry(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Country = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Homepage
|
||||||
|
/// </summary>
|
||||||
|
protected string _Homepage;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Homepage to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetHomepage(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Homepage to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetHomepage(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Homepage
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Homepage;
|
||||||
|
GetHomepage(ref value);
|
||||||
|
return (_Homepage = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Homepage;
|
||||||
|
SetHomepage(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Homepage = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
68
Jellyfin.Data/Entities/CustomItem.cs
Normal file
68
Jellyfin.Data/Entities/CustomItem.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class CustomItem : LibraryItem
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected CustomItem()
|
||||||
|
{
|
||||||
|
CustomItemMetadata = new HashSet<CustomItemMetadata>();
|
||||||
|
Releases = new HashSet<Release>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static CustomItem CreateCustomItemUnsafe()
|
||||||
|
{
|
||||||
|
return new CustomItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
|
||||||
|
public CustomItem(Guid urlid, DateTime dateadded)
|
||||||
|
{
|
||||||
|
this.UrlId = urlid;
|
||||||
|
|
||||||
|
this.CustomItemMetadata = new HashSet<CustomItemMetadata>();
|
||||||
|
this.Releases = new HashSet<Release>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
|
||||||
|
public static CustomItem Create(Guid urlid, DateTime dateadded)
|
||||||
|
{
|
||||||
|
return new CustomItem(urlid, dateadded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
[ForeignKey("CustomItemMetadata_CustomItemMetadata_Id")]
|
||||||
|
public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; protected set; }
|
||||||
|
|
||||||
|
[ForeignKey("Release_Releases_Id")]
|
||||||
|
public virtual ICollection<Release> Releases { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
67
Jellyfin.Data/Entities/CustomItemMetadata.cs
Normal file
67
Jellyfin.Data/Entities/CustomItemMetadata.cs
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class CustomItemMetadata : Metadata
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected CustomItemMetadata()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static CustomItemMetadata CreateCustomItemMetadataUnsafe()
|
||||||
|
{
|
||||||
|
return new CustomItemMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_customitem0"></param>
|
||||||
|
public CustomItemMetadata(string title, string language, DateTime dateadded, DateTime datemodified, CustomItem _customitem0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title));
|
||||||
|
this.Title = title;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language));
|
||||||
|
this.Language = language;
|
||||||
|
|
||||||
|
if (_customitem0 == null) throw new ArgumentNullException(nameof(_customitem0));
|
||||||
|
_customitem0.CustomItemMetadata.Add(this);
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_customitem0"></param>
|
||||||
|
public static CustomItemMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, CustomItem _customitem0)
|
||||||
|
{
|
||||||
|
return new CustomItemMetadata(title, language, dateadded, datemodified, _customitem0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
110
Jellyfin.Data/Entities/Episode.cs
Normal file
110
Jellyfin.Data/Entities/Episode.cs
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Episode : LibraryItem
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected Episode()
|
||||||
|
{
|
||||||
|
// NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
|
||||||
|
// One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
|
||||||
|
|
||||||
|
Releases = new HashSet<Release>();
|
||||||
|
EpisodeMetadata = new HashSet<EpisodeMetadata>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static Episode CreateEpisodeUnsafe()
|
||||||
|
{
|
||||||
|
return new Episode();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
|
||||||
|
/// <param name="_season0"></param>
|
||||||
|
public Episode(Guid urlid, DateTime dateadded, Season _season0)
|
||||||
|
{
|
||||||
|
// NOTE: This class has one-to-one associations with LibraryRoot, LibraryItem and CollectionItem.
|
||||||
|
// One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
|
||||||
|
|
||||||
|
this.UrlId = urlid;
|
||||||
|
|
||||||
|
if (_season0 == null) throw new ArgumentNullException(nameof(_season0));
|
||||||
|
_season0.Episodes.Add(this);
|
||||||
|
|
||||||
|
this.Releases = new HashSet<Release>();
|
||||||
|
this.EpisodeMetadata = new HashSet<EpisodeMetadata>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
|
||||||
|
/// <param name="_season0"></param>
|
||||||
|
public static Episode Create(Guid urlid, DateTime dateadded, Season _season0)
|
||||||
|
{
|
||||||
|
return new Episode(urlid, dateadded, _season0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for EpisodeNumber
|
||||||
|
/// </summary>
|
||||||
|
protected int? _EpisodeNumber;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of EpisodeNumber to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetEpisodeNumber(int? oldValue, ref int? newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of EpisodeNumber to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetEpisodeNumber(ref int? result);
|
||||||
|
|
||||||
|
public int? EpisodeNumber
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int? value = _EpisodeNumber;
|
||||||
|
GetEpisodeNumber(ref value);
|
||||||
|
return (_EpisodeNumber = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
int? oldValue = _EpisodeNumber;
|
||||||
|
SetEpisodeNumber(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_EpisodeNumber = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
[ForeignKey("Release_Releases_Id")]
|
||||||
|
public virtual ICollection<Release> Releases { get; protected set; }
|
||||||
|
[ForeignKey("EpisodeMetadata_EpisodeMetadata_Id")]
|
||||||
|
public virtual ICollection<EpisodeMetadata> EpisodeMetadata { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
179
Jellyfin.Data/Entities/EpisodeMetadata.cs
Normal file
179
Jellyfin.Data/Entities/EpisodeMetadata.cs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class EpisodeMetadata : Metadata
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected EpisodeMetadata()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static EpisodeMetadata CreateEpisodeMetadataUnsafe()
|
||||||
|
{
|
||||||
|
return new EpisodeMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_episode0"></param>
|
||||||
|
public EpisodeMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Episode _episode0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title));
|
||||||
|
this.Title = title;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language));
|
||||||
|
this.Language = language;
|
||||||
|
|
||||||
|
if (_episode0 == null) throw new ArgumentNullException(nameof(_episode0));
|
||||||
|
_episode0.EpisodeMetadata.Add(this);
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_episode0"></param>
|
||||||
|
public static EpisodeMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Episode _episode0)
|
||||||
|
{
|
||||||
|
return new EpisodeMetadata(title, language, dateadded, datemodified, _episode0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Outline
|
||||||
|
/// </summary>
|
||||||
|
protected string _Outline;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Outline to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetOutline(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Outline to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetOutline(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Outline
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Outline;
|
||||||
|
GetOutline(ref value);
|
||||||
|
return (_Outline = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Outline;
|
||||||
|
SetOutline(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Outline = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Plot
|
||||||
|
/// </summary>
|
||||||
|
protected string _Plot;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Plot to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetPlot(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Plot to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetPlot(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 65535
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(65535)]
|
||||||
|
[StringLength(65535)]
|
||||||
|
public string Plot
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Plot;
|
||||||
|
GetPlot(ref value);
|
||||||
|
return (_Plot = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Plot;
|
||||||
|
SetPlot(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Plot = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Tagline
|
||||||
|
/// </summary>
|
||||||
|
protected string _Tagline;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Tagline to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetTagline(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Tagline to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetTagline(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Tagline
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Tagline;
|
||||||
|
GetTagline(ref value);
|
||||||
|
return (_Tagline = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Tagline;
|
||||||
|
SetTagline(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Tagline = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
152
Jellyfin.Data/Entities/Genre.cs
Normal file
152
Jellyfin.Data/Entities/Genre.cs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Genre
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected Genre()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static Genre CreateGenreUnsafe()
|
||||||
|
{
|
||||||
|
return new Genre();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="_metadata0"></param>
|
||||||
|
public Genre(string name, Metadata _metadata0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
|
||||||
|
this.Name = name;
|
||||||
|
|
||||||
|
if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0));
|
||||||
|
_metadata0.Genres.Add(this);
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="_metadata0"></param>
|
||||||
|
public static Genre Create(string name, Metadata _metadata0)
|
||||||
|
{
|
||||||
|
return new Genre(name, _metadata0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Name
|
||||||
|
/// </summary>
|
||||||
|
internal string _Name;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetName(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetName(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indexed, Required, Max length = 255
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
[StringLength(255)]
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Name;
|
||||||
|
GetName(ref value);
|
||||||
|
return (_Name = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Name;
|
||||||
|
SetName(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Name = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
109
Jellyfin.Data/Entities/Group.cs
Normal file
109
Jellyfin.Data/Entities/Group.cs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Group
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected Group()
|
||||||
|
{
|
||||||
|
GroupPermissions = new HashSet<Permission>();
|
||||||
|
ProviderMappings = new HashSet<ProviderMapping>();
|
||||||
|
Preferences = new HashSet<Preference>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static Group CreateGroupUnsafe()
|
||||||
|
{
|
||||||
|
return new Group();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="_user0"></param>
|
||||||
|
public Group(string name, User _user0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
|
||||||
|
this.Name = name;
|
||||||
|
|
||||||
|
if (_user0 == null) throw new ArgumentNullException(nameof(_user0));
|
||||||
|
_user0.Groups.Add(this);
|
||||||
|
|
||||||
|
this.GroupPermissions = new HashSet<Permission>();
|
||||||
|
this.ProviderMappings = new HashSet<ProviderMapping>();
|
||||||
|
this.Preferences = new HashSet<Preference>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
/// <param name="_user0"></param>
|
||||||
|
public static Group Create(string name, User _user0)
|
||||||
|
{
|
||||||
|
return new Group(name, _user0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Max length = 255
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
[StringLength(255)]
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
[ForeignKey("Permission_GroupPermissions_Id")]
|
||||||
|
public virtual ICollection<Permission> GroupPermissions { get; protected set; }
|
||||||
|
|
||||||
|
[ForeignKey("ProviderMapping_ProviderMappings_Id")]
|
||||||
|
public virtual ICollection<ProviderMapping> ProviderMappings { get; protected set; }
|
||||||
|
|
||||||
|
[ForeignKey("Preference_Preferences_Id")]
|
||||||
|
public virtual ICollection<Preference> Preferences { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
147
Jellyfin.Data/Entities/Library.cs
Normal file
147
Jellyfin.Data/Entities/Library.cs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Library
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected Library()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static Library CreateLibraryUnsafe()
|
||||||
|
{
|
||||||
|
return new Library();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
public Library(string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
|
||||||
|
this.Name = name;
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
public static Library Create(string name)
|
||||||
|
{
|
||||||
|
return new Library(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Name
|
||||||
|
/// </summary>
|
||||||
|
protected string _Name;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetName(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetName(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Name;
|
||||||
|
GetName(ref value);
|
||||||
|
return (_Name = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Name;
|
||||||
|
SetName(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Name = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
170
Jellyfin.Data/Entities/LibraryItem.cs
Normal file
170
Jellyfin.Data/Entities/LibraryItem.cs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public abstract partial class LibraryItem
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to being abstract.
|
||||||
|
/// </summary>
|
||||||
|
protected LibraryItem()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
|
||||||
|
protected LibraryItem(Guid urlid, DateTime dateadded)
|
||||||
|
{
|
||||||
|
this.UrlId = urlid;
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for UrlId
|
||||||
|
/// </summary>
|
||||||
|
internal Guid _UrlId;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of UrlId to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetUrlId(Guid oldValue, ref Guid newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of UrlId to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetUrlId(ref Guid result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indexed, Required
|
||||||
|
/// This is whats gets displayed in the Urls and API requests. This could also be a string.
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public Guid UrlId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Guid value = _UrlId;
|
||||||
|
GetUrlId(ref value);
|
||||||
|
return (_UrlId = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Guid oldValue = _UrlId;
|
||||||
|
SetUrlId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_UrlId = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for DateAdded
|
||||||
|
/// </summary>
|
||||||
|
protected DateTime _DateAdded;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of DateAdded to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetDateAdded(DateTime oldValue, ref DateTime newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of DateAdded to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetDateAdded(ref DateTime result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public DateTime DateAdded
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
DateTime value = _DateAdded;
|
||||||
|
GetDateAdded(ref value);
|
||||||
|
return (_DateAdded = value);
|
||||||
|
}
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
DateTime oldValue = _DateAdded;
|
||||||
|
SetDateAdded(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_DateAdded = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[ForeignKey("LibraryRoot_Id")]
|
||||||
|
public virtual LibraryRoot LibraryRoot { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
192
Jellyfin.Data/Entities/LibraryRoot.cs
Normal file
192
Jellyfin.Data/Entities/LibraryRoot.cs
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class LibraryRoot
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected LibraryRoot()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static LibraryRoot CreateLibraryRootUnsafe()
|
||||||
|
{
|
||||||
|
return new LibraryRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Absolute Path</param>
|
||||||
|
public LibraryRoot(string path)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
|
||||||
|
this.Path = path;
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Absolute Path</param>
|
||||||
|
public static LibraryRoot Create(string path)
|
||||||
|
{
|
||||||
|
return new LibraryRoot(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Path
|
||||||
|
/// </summary>
|
||||||
|
protected string _Path;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Path to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetPath(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Path to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetPath(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Max length = 65535
|
||||||
|
/// Absolute Path
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(65535)]
|
||||||
|
[StringLength(65535)]
|
||||||
|
public string Path
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Path;
|
||||||
|
GetPath(ref value);
|
||||||
|
return (_Path = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Path;
|
||||||
|
SetPath(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Path = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for NetworkPath
|
||||||
|
/// </summary>
|
||||||
|
protected string _NetworkPath;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of NetworkPath to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetNetworkPath(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of NetworkPath to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetNetworkPath(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 65535
|
||||||
|
/// Absolute network path, for example for transcoding sattelites.
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(65535)]
|
||||||
|
[StringLength(65535)]
|
||||||
|
public string NetworkPath
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _NetworkPath;
|
||||||
|
GetNetworkPath(ref value);
|
||||||
|
return (_NetworkPath = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _NetworkPath;
|
||||||
|
SetNetworkPath(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_NetworkPath = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[ForeignKey("Library_Id")]
|
||||||
|
public virtual Library Library { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
200
Jellyfin.Data/Entities/MediaFile.cs
Normal file
200
Jellyfin.Data/Entities/MediaFile.cs
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class MediaFile
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected MediaFile()
|
||||||
|
{
|
||||||
|
MediaFileStreams = new HashSet<MediaFileStream>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static MediaFile CreateMediaFileUnsafe()
|
||||||
|
{
|
||||||
|
return new MediaFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Relative to the LibraryRoot</param>
|
||||||
|
/// <param name="kind"></param>
|
||||||
|
/// <param name="_release0"></param>
|
||||||
|
public MediaFile(string path, Enums.MediaFileKind kind, Release _release0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(path)) throw new ArgumentNullException(nameof(path));
|
||||||
|
this.Path = path;
|
||||||
|
|
||||||
|
this.Kind = kind;
|
||||||
|
|
||||||
|
if (_release0 == null) throw new ArgumentNullException(nameof(_release0));
|
||||||
|
_release0.MediaFiles.Add(this);
|
||||||
|
|
||||||
|
this.MediaFileStreams = new HashSet<MediaFileStream>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">Relative to the LibraryRoot</param>
|
||||||
|
/// <param name="kind"></param>
|
||||||
|
/// <param name="_release0"></param>
|
||||||
|
public static MediaFile Create(string path, Enums.MediaFileKind kind, Release _release0)
|
||||||
|
{
|
||||||
|
return new MediaFile(path, kind, _release0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Path
|
||||||
|
/// </summary>
|
||||||
|
protected string _Path;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Path to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetPath(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Path to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetPath(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Max length = 65535
|
||||||
|
/// Relative to the LibraryRoot
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(65535)]
|
||||||
|
[StringLength(65535)]
|
||||||
|
public string Path
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Path;
|
||||||
|
GetPath(ref value);
|
||||||
|
return (_Path = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Path;
|
||||||
|
SetPath(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Path = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Kind
|
||||||
|
/// </summary>
|
||||||
|
protected Enums.MediaFileKind _Kind;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Kind to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetKind(Enums.MediaFileKind oldValue, ref Enums.MediaFileKind newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Kind to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetKind(ref Enums.MediaFileKind result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public Enums.MediaFileKind Kind
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
Enums.MediaFileKind value = _Kind;
|
||||||
|
GetKind(ref value);
|
||||||
|
return (_Kind = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
Enums.MediaFileKind oldValue = _Kind;
|
||||||
|
SetKind(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Kind = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
[ForeignKey("MediaFileStream_MediaFileStreams_Id")]
|
||||||
|
public virtual ICollection<MediaFileStream> MediaFileStreams { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
149
Jellyfin.Data/Entities/MediaFileStream.cs
Normal file
149
Jellyfin.Data/Entities/MediaFileStream.cs
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class MediaFileStream
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected MediaFileStream()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static MediaFileStream CreateMediaFileStreamUnsafe()
|
||||||
|
{
|
||||||
|
return new MediaFileStream();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="streamnumber"></param>
|
||||||
|
/// <param name="_mediafile0"></param>
|
||||||
|
public MediaFileStream(int streamnumber, MediaFile _mediafile0)
|
||||||
|
{
|
||||||
|
this.StreamNumber = streamnumber;
|
||||||
|
|
||||||
|
if (_mediafile0 == null) throw new ArgumentNullException(nameof(_mediafile0));
|
||||||
|
_mediafile0.MediaFileStreams.Add(this);
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="streamnumber"></param>
|
||||||
|
/// <param name="_mediafile0"></param>
|
||||||
|
public static MediaFileStream Create(int streamnumber, MediaFile _mediafile0)
|
||||||
|
{
|
||||||
|
return new MediaFileStream(streamnumber, _mediafile0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for StreamNumber
|
||||||
|
/// </summary>
|
||||||
|
protected int _StreamNumber;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of StreamNumber to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetStreamNumber(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of StreamNumber to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetStreamNumber(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public int StreamNumber
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _StreamNumber;
|
||||||
|
GetStreamNumber(ref value);
|
||||||
|
return (_StreamNumber = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
int oldValue = _StreamNumber;
|
||||||
|
SetStreamNumber(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_StreamNumber = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
380
Jellyfin.Data/Entities/Metadata.cs
Normal file
380
Jellyfin.Data/Entities/Metadata.cs
Normal file
@ -0,0 +1,380 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public abstract partial class Metadata
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to being abstract.
|
||||||
|
/// </summary>
|
||||||
|
protected Metadata()
|
||||||
|
{
|
||||||
|
PersonRoles = new HashSet<PersonRole>();
|
||||||
|
Genres = new HashSet<Genre>();
|
||||||
|
Artwork = new HashSet<Artwork>();
|
||||||
|
Ratings = new HashSet<Rating>();
|
||||||
|
Sources = new HashSet<MetadataProviderId>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
protected Metadata(string title, string language, DateTime dateadded, DateTime datemodified)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title));
|
||||||
|
this.Title = title;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language));
|
||||||
|
this.Language = language;
|
||||||
|
|
||||||
|
this.PersonRoles = new HashSet<PersonRole>();
|
||||||
|
this.Genres = new HashSet<Genre>();
|
||||||
|
this.Artwork = new HashSet<Artwork>();
|
||||||
|
this.Ratings = new HashSet<Rating>();
|
||||||
|
this.Sources = new HashSet<MetadataProviderId>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Title
|
||||||
|
/// </summary>
|
||||||
|
protected string _Title;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Title to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetTitle(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Title to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetTitle(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Max length = 1024
|
||||||
|
/// The title or name of the object
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Title;
|
||||||
|
GetTitle(ref value);
|
||||||
|
return (_Title = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Title;
|
||||||
|
SetTitle(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Title = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for OriginalTitle
|
||||||
|
/// </summary>
|
||||||
|
protected string _OriginalTitle;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of OriginalTitle to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetOriginalTitle(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of OriginalTitle to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetOriginalTitle(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string OriginalTitle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _OriginalTitle;
|
||||||
|
GetOriginalTitle(ref value);
|
||||||
|
return (_OriginalTitle = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _OriginalTitle;
|
||||||
|
SetOriginalTitle(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_OriginalTitle = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for SortTitle
|
||||||
|
/// </summary>
|
||||||
|
protected string _SortTitle;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of SortTitle to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetSortTitle(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of SortTitle to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetSortTitle(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string SortTitle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _SortTitle;
|
||||||
|
GetSortTitle(ref value);
|
||||||
|
return (_SortTitle = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _SortTitle;
|
||||||
|
SetSortTitle(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_SortTitle = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Language
|
||||||
|
/// </summary>
|
||||||
|
protected string _Language;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Language to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetLanguage(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Language to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetLanguage(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Min length = 3, Max length = 3
|
||||||
|
/// ISO-639-3 3-character language codes
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MinLength(3)]
|
||||||
|
[MaxLength(3)]
|
||||||
|
[StringLength(3)]
|
||||||
|
public string Language
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Language;
|
||||||
|
GetLanguage(ref value);
|
||||||
|
return (_Language = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Language;
|
||||||
|
SetLanguage(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Language = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for ReleaseDate
|
||||||
|
/// </summary>
|
||||||
|
protected DateTimeOffset? _ReleaseDate;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of ReleaseDate to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetReleaseDate(DateTimeOffset? oldValue, ref DateTimeOffset? newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of ReleaseDate to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetReleaseDate(ref DateTimeOffset? result);
|
||||||
|
|
||||||
|
public DateTimeOffset? ReleaseDate
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
DateTimeOffset? value = _ReleaseDate;
|
||||||
|
GetReleaseDate(ref value);
|
||||||
|
return (_ReleaseDate = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
DateTimeOffset? oldValue = _ReleaseDate;
|
||||||
|
SetReleaseDate(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_ReleaseDate = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for DateAdded
|
||||||
|
/// </summary>
|
||||||
|
protected DateTime _DateAdded;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of DateAdded to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetDateAdded(DateTime oldValue, ref DateTime newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of DateAdded to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetDateAdded(ref DateTime result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public DateTime DateAdded
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
DateTime value = _DateAdded;
|
||||||
|
GetDateAdded(ref value);
|
||||||
|
return (_DateAdded = value);
|
||||||
|
}
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
DateTime oldValue = _DateAdded;
|
||||||
|
SetDateAdded(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_DateAdded = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for DateModified
|
||||||
|
/// </summary>
|
||||||
|
protected DateTime _DateModified;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of DateModified to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetDateModified(DateTime oldValue, ref DateTime newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of DateModified to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetDateModified(ref DateTime result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public DateTime DateModified
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
DateTime value = _DateModified;
|
||||||
|
GetDateModified(ref value);
|
||||||
|
return (_DateModified = value);
|
||||||
|
}
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
DateTime oldValue = _DateModified;
|
||||||
|
SetDateModified(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_DateModified = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
[ForeignKey("PersonRole_PersonRoles_Id")]
|
||||||
|
public virtual ICollection<PersonRole> PersonRoles { get; protected set; }
|
||||||
|
|
||||||
|
[ForeignKey("PersonRole_PersonRoles_Id")]
|
||||||
|
public virtual ICollection<Genre> Genres { get; protected set; }
|
||||||
|
|
||||||
|
[ForeignKey("PersonRole_PersonRoles_Id")]
|
||||||
|
public virtual ICollection<Artwork> Artwork { get; protected set; }
|
||||||
|
|
||||||
|
[ForeignKey("PersonRole_PersonRoles_Id")]
|
||||||
|
public virtual ICollection<Rating> Ratings { get; protected set; }
|
||||||
|
|
||||||
|
[ForeignKey("PersonRole_PersonRoles_Id")]
|
||||||
|
public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
147
Jellyfin.Data/Entities/MetadataProvider.cs
Normal file
147
Jellyfin.Data/Entities/MetadataProvider.cs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class MetadataProvider
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected MetadataProvider()
|
||||||
|
{
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static MetadataProvider CreateMetadataProviderUnsafe()
|
||||||
|
{
|
||||||
|
return new MetadataProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
public MetadataProvider(string name)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(name)) throw new ArgumentNullException(nameof(name));
|
||||||
|
this.Name = name;
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="name"></param>
|
||||||
|
public static MetadataProvider Create(string name)
|
||||||
|
{
|
||||||
|
return new MetadataProvider(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Name
|
||||||
|
/// </summary>
|
||||||
|
protected string _Name;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetName(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Name to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetName(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Name
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Name;
|
||||||
|
GetName(ref value);
|
||||||
|
return (_Name = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Name;
|
||||||
|
SetName(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Name = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
179
Jellyfin.Data/Entities/MetadataProviderId.cs
Normal file
179
Jellyfin.Data/Entities/MetadataProviderId.cs
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class MetadataProviderId
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected MetadataProviderId()
|
||||||
|
{
|
||||||
|
// NOTE: This class has one-to-one associations with MetadataProviderId.
|
||||||
|
// One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static MetadataProviderId CreateMetadataProviderIdUnsafe()
|
||||||
|
{
|
||||||
|
return new MetadataProviderId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="providerid"></param>
|
||||||
|
/// <param name="_metadata0"></param>
|
||||||
|
/// <param name="_person1"></param>
|
||||||
|
/// <param name="_personrole2"></param>
|
||||||
|
/// <param name="_ratingsource3"></param>
|
||||||
|
public MetadataProviderId(string providerid, Metadata _metadata0, Person _person1, PersonRole _personrole2, RatingSource _ratingsource3)
|
||||||
|
{
|
||||||
|
// NOTE: This class has one-to-one associations with MetadataProviderId.
|
||||||
|
// One-to-one associations are not validated in constructors since this causes a scenario where each one must be constructed before the other.
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(providerid)) throw new ArgumentNullException(nameof(providerid));
|
||||||
|
this.ProviderId = providerid;
|
||||||
|
|
||||||
|
if (_metadata0 == null) throw new ArgumentNullException(nameof(_metadata0));
|
||||||
|
_metadata0.Sources.Add(this);
|
||||||
|
|
||||||
|
if (_person1 == null) throw new ArgumentNullException(nameof(_person1));
|
||||||
|
_person1.Sources.Add(this);
|
||||||
|
|
||||||
|
if (_personrole2 == null) throw new ArgumentNullException(nameof(_personrole2));
|
||||||
|
_personrole2.Sources.Add(this);
|
||||||
|
|
||||||
|
if (_ratingsource3 == null) throw new ArgumentNullException(nameof(_ratingsource3));
|
||||||
|
_ratingsource3.Source = this;
|
||||||
|
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="providerid"></param>
|
||||||
|
/// <param name="_metadata0"></param>
|
||||||
|
/// <param name="_person1"></param>
|
||||||
|
/// <param name="_personrole2"></param>
|
||||||
|
/// <param name="_ratingsource3"></param>
|
||||||
|
public static MetadataProviderId Create(string providerid, Metadata _metadata0, Person _person1, PersonRole _personrole2, RatingSource _ratingsource3)
|
||||||
|
{
|
||||||
|
return new MetadataProviderId(providerid, _metadata0, _person1, _personrole2, _ratingsource3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Id
|
||||||
|
/// </summary>
|
||||||
|
internal int _Id;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetId(int oldValue, ref int newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Id to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetId(ref int result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Identity, Indexed, Required
|
||||||
|
/// </summary>
|
||||||
|
[Key]
|
||||||
|
[Required]
|
||||||
|
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
|
||||||
|
public int Id
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
int value = _Id;
|
||||||
|
GetId(ref value);
|
||||||
|
return (_Id = value);
|
||||||
|
}
|
||||||
|
protected set
|
||||||
|
{
|
||||||
|
int oldValue = _Id;
|
||||||
|
SetId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Id = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for ProviderId
|
||||||
|
/// </summary>
|
||||||
|
protected string _ProviderId;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of ProviderId to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetProviderId(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of ProviderId to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetProviderId(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, Max length = 255
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
[MaxLength(255)]
|
||||||
|
[StringLength(255)]
|
||||||
|
public string ProviderId
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _ProviderId;
|
||||||
|
GetProviderId(ref value);
|
||||||
|
return (_ProviderId = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _ProviderId;
|
||||||
|
SetProviderId(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_ProviderId = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required, ConcurrenyToken
|
||||||
|
/// </summary>
|
||||||
|
[ConcurrencyCheck]
|
||||||
|
[Required]
|
||||||
|
public uint RowVersion { get; set; }
|
||||||
|
|
||||||
|
public void OnSavingChanges()
|
||||||
|
{
|
||||||
|
RowVersion++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Required
|
||||||
|
/// </summary>
|
||||||
|
[ForeignKey("MetadataProvider_Id")]
|
||||||
|
public virtual MetadataProvider MetadataProvider { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
69
Jellyfin.Data/Entities/Movie.cs
Normal file
69
Jellyfin.Data/Entities/Movie.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class Movie : LibraryItem
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected Movie()
|
||||||
|
{
|
||||||
|
Releases = new HashSet<Release>();
|
||||||
|
MovieMetadata = new HashSet<MovieMetadata>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static Movie CreateMovieUnsafe()
|
||||||
|
{
|
||||||
|
return new Movie();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
|
||||||
|
public Movie(Guid urlid, DateTime dateadded)
|
||||||
|
{
|
||||||
|
this.UrlId = urlid;
|
||||||
|
|
||||||
|
this.Releases = new HashSet<Release>();
|
||||||
|
this.MovieMetadata = new HashSet<MovieMetadata>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="urlid">This is whats gets displayed in the Urls and API requests. This could also be a string.</param>
|
||||||
|
public static Movie Create(Guid urlid, DateTime dateadded)
|
||||||
|
{
|
||||||
|
return new Movie(urlid, dateadded);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
[ForeignKey("Release_Releases_Id")]
|
||||||
|
public virtual ICollection<Release> Releases { get; protected set; }
|
||||||
|
|
||||||
|
[ForeignKey("MovieMetadata_MovieMetadata_Id")]
|
||||||
|
public virtual ICollection<MovieMetadata> MovieMetadata { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
223
Jellyfin.Data/Entities/MovieMetadata.cs
Normal file
223
Jellyfin.Data/Entities/MovieMetadata.cs
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
using System.ComponentModel.DataAnnotations.Schema;
|
||||||
|
|
||||||
|
namespace Jellyfin.Data.Entities
|
||||||
|
{
|
||||||
|
public partial class MovieMetadata : Metadata
|
||||||
|
{
|
||||||
|
partial void Init();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default constructor. Protected due to required properties, but present because EF needs it.
|
||||||
|
/// </summary>
|
||||||
|
protected MovieMetadata()
|
||||||
|
{
|
||||||
|
Studios = new HashSet<Company>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Replaces default constructor, since it's protected. Caller assumes responsibility for setting all required values before saving.
|
||||||
|
/// </summary>
|
||||||
|
public static MovieMetadata CreateMovieMetadataUnsafe()
|
||||||
|
{
|
||||||
|
return new MovieMetadata();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Public constructor with required data
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_movie0"></param>
|
||||||
|
public MovieMetadata(string title, string language, DateTime dateadded, DateTime datemodified, Movie _movie0)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(title)) throw new ArgumentNullException(nameof(title));
|
||||||
|
this.Title = title;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(language)) throw new ArgumentNullException(nameof(language));
|
||||||
|
this.Language = language;
|
||||||
|
|
||||||
|
if (_movie0 == null) throw new ArgumentNullException(nameof(_movie0));
|
||||||
|
_movie0.MovieMetadata.Add(this);
|
||||||
|
|
||||||
|
this.Studios = new HashSet<Company>();
|
||||||
|
|
||||||
|
Init();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static create function (for use in LINQ queries, etc.)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="title">The title or name of the object</param>
|
||||||
|
/// <param name="language">ISO-639-3 3-character language codes</param>
|
||||||
|
/// <param name="_movie0"></param>
|
||||||
|
public static MovieMetadata Create(string title, string language, DateTime dateadded, DateTime datemodified, Movie _movie0)
|
||||||
|
{
|
||||||
|
return new MovieMetadata(title, language, dateadded, datemodified, _movie0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Properties
|
||||||
|
*************************************************************************/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Outline
|
||||||
|
/// </summary>
|
||||||
|
protected string _Outline;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Outline to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetOutline(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Outline to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetOutline(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Outline
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Outline;
|
||||||
|
GetOutline(ref value);
|
||||||
|
return (_Outline = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Outline;
|
||||||
|
SetOutline(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Outline = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Plot
|
||||||
|
/// </summary>
|
||||||
|
protected string _Plot;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Plot to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetPlot(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Plot to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetPlot(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 65535
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(65535)]
|
||||||
|
[StringLength(65535)]
|
||||||
|
public string Plot
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Plot;
|
||||||
|
GetPlot(ref value);
|
||||||
|
return (_Plot = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Plot;
|
||||||
|
SetPlot(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Plot = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Tagline
|
||||||
|
/// </summary>
|
||||||
|
protected string _Tagline;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Tagline to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetTagline(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Tagline to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetTagline(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 1024
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(1024)]
|
||||||
|
[StringLength(1024)]
|
||||||
|
public string Tagline
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Tagline;
|
||||||
|
GetTagline(ref value);
|
||||||
|
return (_Tagline = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Tagline;
|
||||||
|
SetTagline(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Tagline = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Backing field for Country
|
||||||
|
/// </summary>
|
||||||
|
protected string _Country;
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Country to be changed before setting.
|
||||||
|
/// </summary>
|
||||||
|
partial void SetCountry(string oldValue, ref string newValue);
|
||||||
|
/// <summary>
|
||||||
|
/// When provided in a partial class, allows value of Country to be changed before returning.
|
||||||
|
/// </summary>
|
||||||
|
partial void GetCountry(ref string result);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Max length = 2
|
||||||
|
/// </summary>
|
||||||
|
[MaxLength(2)]
|
||||||
|
[StringLength(2)]
|
||||||
|
public string Country
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
string value = _Country;
|
||||||
|
GetCountry(ref value);
|
||||||
|
return (_Country = value);
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
string oldValue = _Country;
|
||||||
|
SetCountry(oldValue, ref value);
|
||||||
|
if (oldValue != value)
|
||||||
|
{
|
||||||
|
_Country = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* Navigation properties
|
||||||
|
*************************************************************************/
|
||||||
|
[ForeignKey("Company_Studios_Id")]
|
||||||
|
public virtual ICollection<Company> Studios { get; protected set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user