Merge branch 'master' into library-pictures

This commit is contained in:
David Ullmer 2020-08-31 16:16:01 +02:00
commit b7c07f6821
450 changed files with 5622 additions and 8074 deletions

View File

@ -147,19 +147,42 @@ jobs:
displayName: 'Publish NuGet packages' displayName: 'Publish NuGet packages'
dependsOn: dependsOn:
- BuildPackage - BuildPackage
condition: and(succeeded('BuildPackage'), startsWith(variables['Build.SourceBranch'], 'refs/tags')) condition: succeeded('BuildPackage')
pool: pool:
vmImage: 'ubuntu-latest' vmImage: 'ubuntu-latest'
steps: steps:
- task: NuGetCommand@2 - task: DotNetCoreCLI@2
displayName: 'Build Stable Nuget packages'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
inputs: inputs:
command: 'pack' command: 'pack'
packagesToPack: Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj packagesToPack: 'Jellyfin.Data/Jellyfin.Data.csproj;MediaBrowser.Common/MediaBrowser.Common.csproj;MediaBrowser.Controller/MediaBrowser.Controller.csproj;MediaBrowser.Model/MediaBrowser.Model.csproj;Emby.Naming/Emby.Naming.csproj'
packDestination: '$(Build.ArtifactStagingDirectory)' versioningScheme: 'off'
- task: DotNetCoreCLI@2
displayName: 'Build Unstable Nuget packages'
inputs:
command: 'custom'
projects: |
Jellyfin.Data/Jellyfin.Data.csproj
MediaBrowser.Common/MediaBrowser.Common.csproj
MediaBrowser.Controller/MediaBrowser.Controller.csproj
MediaBrowser.Model/MediaBrowser.Model.csproj
Emby.Naming/Emby.Naming.csproj
custom: 'pack'
arguments: '--version-suffix $(Build.BuildNumber) -o $(Build.ArtifactStagingDirectory)'
- task: PublishBuildArtifacts@1
displayName: 'Publish Nuget packages'
inputs:
pathToPublish: $(Build.ArtifactStagingDirectory)
artifactName: Jellyfin Nuget Packages
- task: NuGetCommand@2 - task: NuGetCommand@2
displayName: 'Push Nuget packages to feed'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
inputs: inputs:
command: 'push' command: 'push'
packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg' packagesToPush: '$(Build.ArtifactStagingDirectory)/**/*.nupkg'

View File

@ -14,7 +14,7 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.Eventing namespace Emby.Dlna.Eventing
{ {
public class EventManager : IEventManager public class DlnaEventManager : IDlnaEventManager
{ {
private readonly ConcurrentDictionary<string, EventSubscription> _subscriptions = private readonly ConcurrentDictionary<string, EventSubscription> _subscriptions =
new ConcurrentDictionary<string, EventSubscription>(StringComparer.OrdinalIgnoreCase); new ConcurrentDictionary<string, EventSubscription>(StringComparer.OrdinalIgnoreCase);
@ -24,7 +24,7 @@ namespace Emby.Dlna.Eventing
private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly CultureInfo _usCulture = new CultureInfo("en-US");
public EventManager(ILogger logger, IHttpClient httpClient) public DlnaEventManager(ILogger logger, IHttpClient httpClient)
{ {
_httpClient = httpClient; _httpClient = httpClient;
_logger = logger; _logger = logger;

View File

@ -2,7 +2,7 @@
namespace Emby.Dlna namespace Emby.Dlna
{ {
public interface IConnectionManager : IEventManager, IUpnpService public interface IConnectionManager : IDlnaEventManager, IUpnpService
{ {
} }
} }

View File

@ -2,7 +2,7 @@
namespace Emby.Dlna namespace Emby.Dlna
{ {
public interface IContentDirectory : IEventManager, IUpnpService public interface IContentDirectory : IDlnaEventManager, IUpnpService
{ {
} }
} }

View File

@ -2,7 +2,7 @@
namespace Emby.Dlna namespace Emby.Dlna
{ {
public interface IEventManager public interface IDlnaEventManager
{ {
/// <summary> /// <summary>
/// Cancels the event subscription. /// Cancels the event subscription.

View File

@ -2,7 +2,7 @@
namespace Emby.Dlna namespace Emby.Dlna
{ {
public interface IMediaReceiverRegistrar : IEventManager, IUpnpService public interface IMediaReceiverRegistrar : IDlnaEventManager, IUpnpService
{ {
} }
} }

View File

@ -8,6 +8,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Dlna.Didl; using Emby.Dlna.Didl;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller.Dlna; using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
@ -18,7 +19,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using Microsoft.AspNetCore.WebUtilities; using Microsoft.AspNetCore.WebUtilities;

View File

@ -6,6 +6,7 @@ using System.Linq;
using System.Net; using System.Net;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
@ -16,7 +17,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -6,17 +6,17 @@ using Microsoft.Extensions.Logging;
namespace Emby.Dlna.Service namespace Emby.Dlna.Service
{ {
public class BaseService : IEventManager public class BaseService : IDlnaEventManager
{ {
protected BaseService(ILogger<BaseService> logger, IHttpClient httpClient) protected BaseService(ILogger<BaseService> logger, IHttpClient httpClient)
{ {
Logger = logger; Logger = logger;
HttpClient = httpClient; HttpClient = httpClient;
EventManager = new EventManager(logger, HttpClient); EventManager = new DlnaEventManager(logger, HttpClient);
} }
protected IEventManager EventManager { get; } protected IDlnaEventManager EventManager { get; }
protected IHttpClient HttpClient { get; } protected IHttpClient HttpClient { get; }

View File

@ -3,9 +3,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Jellyfin.Data.Events;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
using Rssdp; using Rssdp;
using Rssdp.Infrastructure; using Rssdp.Infrastructure;

View File

@ -23,8 +23,9 @@
<PropertyGroup> <PropertyGroup>
<Authors>Jellyfin Contributors</Authors> <Authors>Jellyfin Contributors</Authors>
<PackageId>Jellyfin.Naming</PackageId> <PackageId>Jellyfin.Naming</PackageId>
<PackageLicenseUrl>https://www.gnu.org/licenses/old-licenses/gpl-2.0.txt</PackageLicenseUrl> <VersionPrefix>10.7.0</VersionPrefix>
<RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl> <RepositoryUrl>https://github.com/jellyfin/jellyfin</RepositoryUrl>
<PackageLicenseExpression>GPL-3.0-only</PackageLicenseExpression>
</PropertyGroup> </PropertyGroup>
<!-- Code Analyzers--> <!-- Code Analyzers-->

View File

@ -4,6 +4,7 @@ using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -13,7 +14,6 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Notifications; using MediaBrowser.Controller.Notifications;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Activity; using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Notifications; using MediaBrowser.Model.Notifications;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -1,590 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Controller.Subtitles;
using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Notifications;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Updates;
using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.Activity
{
/// <summary>
/// Entry point for the activity logger.
/// </summary>
public sealed class ActivityLogEntryPoint : IServerEntryPoint
{
private readonly ILogger<ActivityLogEntryPoint> _logger;
private readonly IInstallationManager _installationManager;
private readonly ISessionManager _sessionManager;
private readonly ITaskManager _taskManager;
private readonly IActivityManager _activityManager;
private readonly ILocalizationManager _localization;
private readonly ISubtitleManager _subManager;
private readonly IUserManager _userManager;
/// <summary>
/// Initializes a new instance of the <see cref="ActivityLogEntryPoint"/> class.
/// </summary>
/// <param name="logger">The logger.</param>
/// <param name="sessionManager">The session manager.</param>
/// <param name="taskManager">The task manager.</param>
/// <param name="activityManager">The activity manager.</param>
/// <param name="localization">The localization manager.</param>
/// <param name="installationManager">The installation manager.</param>
/// <param name="subManager">The subtitle manager.</param>
/// <param name="userManager">The user manager.</param>
public ActivityLogEntryPoint(
ILogger<ActivityLogEntryPoint> logger,
ISessionManager sessionManager,
ITaskManager taskManager,
IActivityManager activityManager,
ILocalizationManager localization,
IInstallationManager installationManager,
ISubtitleManager subManager,
IUserManager userManager)
{
_logger = logger;
_sessionManager = sessionManager;
_taskManager = taskManager;
_activityManager = activityManager;
_localization = localization;
_installationManager = installationManager;
_subManager = subManager;
_userManager = userManager;
}
/// <inheritdoc />
public Task RunAsync()
{
_taskManager.TaskCompleted += OnTaskCompleted;
_installationManager.PluginInstalled += OnPluginInstalled;
_installationManager.PluginUninstalled += OnPluginUninstalled;
_installationManager.PluginUpdated += OnPluginUpdated;
_installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
_sessionManager.SessionStarted += OnSessionStarted;
_sessionManager.AuthenticationFailed += OnAuthenticationFailed;
_sessionManager.AuthenticationSucceeded += OnAuthenticationSucceeded;
_sessionManager.SessionEnded += OnSessionEnded;
_sessionManager.PlaybackStart += OnPlaybackStart;
_sessionManager.PlaybackStopped += OnPlaybackStopped;
_subManager.SubtitleDownloadFailure += OnSubtitleDownloadFailure;
_userManager.OnUserCreated += OnUserCreated;
_userManager.OnUserPasswordChanged += OnUserPasswordChanged;
_userManager.OnUserDeleted += OnUserDeleted;
_userManager.OnUserLockedOut += OnUserLockedOut;
return Task.CompletedTask;
}
private async void OnUserLockedOut(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserLockedOutWithName"),
e.Argument.Username),
NotificationType.UserLockedOut.ToString(),
e.Argument.Id)
{
LogSeverity = LogLevel.Error
}).ConfigureAwait(false);
}
private async void OnSubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"),
e.Provider,
Notifications.NotificationEntryPoint.GetItemName(e.Item)),
"SubtitleDownloadFailure",
Guid.Empty)
{
ItemId = e.Item.Id.ToString("N", CultureInfo.InvariantCulture),
ShortOverview = e.Exception.Message
}).ConfigureAwait(false);
}
private async void OnPlaybackStopped(object sender, PlaybackStopEventArgs e)
{
var item = e.MediaInfo;
if (item == null)
{
_logger.LogWarning("PlaybackStopped reported with null media info.");
return;
}
if (e.Item != null && e.Item.IsThemeMedia)
{
// Don't report theme song or local trailer playback
return;
}
if (e.Users.Count == 0)
{
return;
}
var user = e.Users[0];
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"),
user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackStoppedNotificationType(item.MediaType),
user.Id))
.ConfigureAwait(false);
}
private async void OnPlaybackStart(object sender, PlaybackProgressEventArgs e)
{
var item = e.MediaInfo;
if (item == null)
{
_logger.LogWarning("PlaybackStart reported with null media info.");
return;
}
if (e.Item != null && e.Item.IsThemeMedia)
{
// Don't report theme song or local trailer playback
return;
}
if (e.Users.Count == 0)
{
return;
}
var user = e.Users.First();
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserStartedPlayingItemWithValues"),
user.Username,
GetItemName(item),
e.DeviceName),
GetPlaybackNotificationType(item.MediaType),
user.Id))
.ConfigureAwait(false);
}
private static string GetItemName(BaseItemDto item)
{
var name = item.Name;
if (!string.IsNullOrEmpty(item.SeriesName))
{
name = item.SeriesName + " - " + name;
}
if (item.Artists != null && item.Artists.Count > 0)
{
name = item.Artists[0] + " - " + name;
}
return name;
}
private static string GetPlaybackNotificationType(string mediaType)
{
if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.AudioPlayback.ToString();
}
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.VideoPlayback.ToString();
}
return null;
}
private static string GetPlaybackStoppedNotificationType(string mediaType)
{
if (string.Equals(mediaType, MediaType.Audio, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.AudioPlaybackStopped.ToString();
}
if (string.Equals(mediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase))
{
return NotificationType.VideoPlaybackStopped.ToString();
}
return null;
}
private async void OnSessionEnded(object sender, SessionEventArgs e)
{
var session = e.SessionInfo;
if (string.IsNullOrEmpty(session.UserName))
{
return;
}
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserOfflineFromDevice"),
session.UserName,
session.DeviceName),
"SessionEnded",
session.UserId)
{
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("LabelIpAddressValue"),
session.RemoteEndPoint),
}).ConfigureAwait(false);
}
private async void OnAuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> e)
{
var user = e.Argument.User;
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("AuthenticationSucceededWithUserName"),
user.Name),
"AuthenticationSucceeded",
user.Id)
{
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("LabelIpAddressValue"),
e.Argument.SessionInfo.RemoteEndPoint),
}).ConfigureAwait(false);
}
private async void OnAuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("FailedLoginAttemptWithUserName"),
e.Argument.Username),
"AuthenticationFailed",
Guid.Empty)
{
LogSeverity = LogLevel.Error,
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("LabelIpAddressValue"),
e.Argument.RemoteEndPoint),
}).ConfigureAwait(false);
}
private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserDeletedWithName"),
e.Argument.Username),
"UserDeleted",
Guid.Empty))
.ConfigureAwait(false);
}
private async void OnUserPasswordChanged(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserPasswordChangedWithName"),
e.Argument.Username),
"UserPasswordChanged",
e.Argument.Id))
.ConfigureAwait(false);
}
private async void OnUserCreated(object sender, GenericEventArgs<User> e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserCreatedWithName"),
e.Argument.Username),
"UserCreated",
e.Argument.Id))
.ConfigureAwait(false);
}
private async void OnSessionStarted(object sender, SessionEventArgs e)
{
var session = e.SessionInfo;
if (string.IsNullOrEmpty(session.UserName))
{
return;
}
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("UserOnlineFromDevice"),
session.UserName,
session.DeviceName),
"SessionStarted",
session.UserId)
{
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("LabelIpAddressValue"),
session.RemoteEndPoint)
}).ConfigureAwait(false);
}
private async void OnPluginUpdated(object sender, InstallationInfo e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("PluginUpdatedWithName"),
e.Name),
NotificationType.PluginUpdateInstalled.ToString(),
Guid.Empty)
{
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
e.Version),
Overview = e.Changelog
}).ConfigureAwait(false);
}
private async void OnPluginUninstalled(object sender, IPlugin e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("PluginUninstalledWithName"),
e.Name),
NotificationType.PluginUninstalled.ToString(),
Guid.Empty))
.ConfigureAwait(false);
}
private async void OnPluginInstalled(object sender, InstallationInfo e)
{
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("PluginInstalledWithName"),
e.Name),
NotificationType.PluginInstalled.ToString(),
Guid.Empty)
{
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
e.Version)
}).ConfigureAwait(false);
}
private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
{
var installationInfo = e.InstallationInfo;
await CreateLogEntry(new ActivityLog(
string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("NameInstallFailed"),
installationInfo.Name),
NotificationType.InstallationFailed.ToString(),
Guid.Empty)
{
ShortOverview = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("VersionNumber"),
installationInfo.Version),
Overview = e.Exception.Message
}).ConfigureAwait(false);
}
private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
{
var result = e.Result;
var task = e.Task;
if (task.ScheduledTask is IConfigurableScheduledTask activityTask
&& !activityTask.IsLogged)
{
return;
}
var time = result.EndTimeUtc - result.StartTimeUtc;
var runningTime = string.Format(
CultureInfo.InvariantCulture,
_localization.GetLocalizedString("LabelRunningTimeValue"),
ToUserFriendlyString(time));
if (result.Status == TaskCompletionStatus.Failed)
{
var vals = new List<string>();
if (!string.IsNullOrEmpty(e.Result.ErrorMessage))
{
vals.Add(e.Result.ErrorMessage);
}
if (!string.IsNullOrEmpty(e.Result.LongErrorMessage))
{
vals.Add(e.Result.LongErrorMessage);
}
await CreateLogEntry(new ActivityLog(
string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
NotificationType.TaskFailed.ToString(),
Guid.Empty)
{
LogSeverity = LogLevel.Error,
Overview = string.Join(Environment.NewLine, vals),
ShortOverview = runningTime
}).ConfigureAwait(false);
}
}
private async Task CreateLogEntry(ActivityLog entry)
=> await _activityManager.CreateAsync(entry).ConfigureAwait(false);
/// <inheritdoc />
public void Dispose()
{
_taskManager.TaskCompleted -= OnTaskCompleted;
_installationManager.PluginInstalled -= OnPluginInstalled;
_installationManager.PluginUninstalled -= OnPluginUninstalled;
_installationManager.PluginUpdated -= OnPluginUpdated;
_installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
_sessionManager.SessionStarted -= OnSessionStarted;
_sessionManager.AuthenticationFailed -= OnAuthenticationFailed;
_sessionManager.AuthenticationSucceeded -= OnAuthenticationSucceeded;
_sessionManager.SessionEnded -= OnSessionEnded;
_sessionManager.PlaybackStart -= OnPlaybackStart;
_sessionManager.PlaybackStopped -= OnPlaybackStopped;
_subManager.SubtitleDownloadFailure -= OnSubtitleDownloadFailure;
_userManager.OnUserCreated -= OnUserCreated;
_userManager.OnUserPasswordChanged -= OnUserPasswordChanged;
_userManager.OnUserDeleted -= OnUserDeleted;
_userManager.OnUserLockedOut -= OnUserLockedOut;
}
/// <summary>
/// Constructs a user-friendly string for this TimeSpan instance.
/// </summary>
private static string ToUserFriendlyString(TimeSpan span)
{
const int DaysInYear = 365;
const int DaysInMonth = 30;
// Get each non-zero value from TimeSpan component
var values = new List<string>();
// Number of years
int days = span.Days;
if (days >= DaysInYear)
{
int years = days / DaysInYear;
values.Add(CreateValueString(years, "year"));
days %= DaysInYear;
}
// Number of months
if (days >= DaysInMonth)
{
int months = days / DaysInMonth;
values.Add(CreateValueString(months, "month"));
days = days % DaysInMonth;
}
// Number of days
if (days >= 1)
{
values.Add(CreateValueString(days, "day"));
}
// Number of hours
if (span.Hours >= 1)
{
values.Add(CreateValueString(span.Hours, "hour"));
}
// Number of minutes
if (span.Minutes >= 1)
{
values.Add(CreateValueString(span.Minutes, "minute"));
}
// Number of seconds (include when 0 if no other components included)
if (span.Seconds >= 1 || values.Count == 0)
{
values.Add(CreateValueString(span.Seconds, "second"));
}
// Combine values into string
var builder = new StringBuilder();
for (int i = 0; i < values.Count; i++)
{
if (builder.Length > 0)
{
builder.Append(i == values.Count - 1 ? " and " : ", ");
}
builder.Append(values[i]);
}
// Return result
return builder.ToString();
}
/// <summary>
/// Constructs a string description of a time-span value.
/// </summary>
/// <param name="value">The value of this item.</param>
/// <param name="description">The name of this item (singular form).</param>
private static string CreateValueString(int value, string description)
{
return string.Format(
CultureInfo.InvariantCulture,
"{0:#,##0} {1}",
value,
value == 1 ? description : string.Format(CultureInfo.InvariantCulture, "{0}s", description));
}
}
}

View File

@ -53,7 +53,6 @@ using MediaBrowser.Common.Net;
using MediaBrowser.Common.Plugins; using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates; using MediaBrowser.Common.Updates;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Chapters;
using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Collections;
@ -173,6 +172,8 @@ namespace Emby.Server.Implementations
/// </summary> /// </summary>
protected ILogger<ApplicationHost> Logger { get; } protected ILogger<ApplicationHost> Logger { get; }
protected IServiceCollection ServiceCollection { get; }
private IPlugin[] _plugins; private IPlugin[] _plugins;
/// <summary> /// <summary>
@ -238,9 +239,11 @@ namespace Emby.Server.Implementations
ILoggerFactory loggerFactory, ILoggerFactory loggerFactory,
IStartupOptions options, IStartupOptions options,
IFileSystem fileSystem, IFileSystem fileSystem,
INetworkManager networkManager) INetworkManager networkManager,
IServiceCollection serviceCollection)
{ {
_xmlSerializer = new MyXmlSerializer(); _xmlSerializer = new MyXmlSerializer();
ServiceCollection = serviceCollection;
_networkManager = networkManager; _networkManager = networkManager;
networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets; networkManager.LocalSubnetsFn = GetConfiguredLocalSubnets;
@ -464,7 +467,7 @@ namespace Emby.Server.Implementations
} }
/// <inheritdoc/> /// <inheritdoc/>
public void Init(IServiceCollection serviceCollection) public void Init()
{ {
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber; HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber; HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
@ -493,7 +496,7 @@ namespace Emby.Server.Implementations
DiscoverTypes(); DiscoverTypes();
RegisterServices(serviceCollection); RegisterServices();
} }
public Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next) public Task ExecuteHttpHandlerAsync(HttpContext context, Func<Task> next)
@ -502,139 +505,139 @@ namespace Emby.Server.Implementations
/// <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.
/// </summary> /// </summary>
protected virtual void RegisterServices(IServiceCollection serviceCollection) protected virtual void RegisterServices()
{ {
serviceCollection.AddSingleton(_startupOptions); ServiceCollection.AddSingleton(_startupOptions);
serviceCollection.AddMemoryCache(); ServiceCollection.AddMemoryCache();
serviceCollection.AddSingleton(ConfigurationManager); ServiceCollection.AddSingleton(ConfigurationManager);
serviceCollection.AddSingleton<IApplicationHost>(this); ServiceCollection.AddSingleton<IApplicationHost>(this);
serviceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths); ServiceCollection.AddSingleton<IApplicationPaths>(ApplicationPaths);
serviceCollection.AddSingleton<IJsonSerializer, JsonSerializer>(); ServiceCollection.AddSingleton<IJsonSerializer, JsonSerializer>();
serviceCollection.AddSingleton(_fileSystemManager); ServiceCollection.AddSingleton(_fileSystemManager);
serviceCollection.AddSingleton<TvdbClientManager>(); ServiceCollection.AddSingleton<TvdbClientManager>();
serviceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>(); ServiceCollection.AddSingleton<IHttpClient, HttpClientManager.HttpClientManager>();
serviceCollection.AddSingleton(_networkManager); ServiceCollection.AddSingleton(_networkManager);
serviceCollection.AddSingleton<IIsoManager, IsoManager>(); ServiceCollection.AddSingleton<IIsoManager, IsoManager>();
serviceCollection.AddSingleton<ITaskManager, TaskManager>(); ServiceCollection.AddSingleton<ITaskManager, TaskManager>();
serviceCollection.AddSingleton(_xmlSerializer); ServiceCollection.AddSingleton(_xmlSerializer);
serviceCollection.AddSingleton<IStreamHelper, StreamHelper>(); ServiceCollection.AddSingleton<IStreamHelper, StreamHelper>();
serviceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>(); ServiceCollection.AddSingleton<ICryptoProvider, CryptographyProvider>();
serviceCollection.AddSingleton<ISocketFactory, SocketFactory>(); ServiceCollection.AddSingleton<ISocketFactory, SocketFactory>();
serviceCollection.AddSingleton<IInstallationManager, InstallationManager>(); ServiceCollection.AddSingleton<IInstallationManager, InstallationManager>();
serviceCollection.AddSingleton<IZipClient, ZipClient>(); ServiceCollection.AddSingleton<IZipClient, ZipClient>();
serviceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>(); ServiceCollection.AddSingleton<IHttpResultFactory, HttpResultFactory>();
serviceCollection.AddSingleton<IServerApplicationHost>(this); ServiceCollection.AddSingleton<IServerApplicationHost>(this);
serviceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths); ServiceCollection.AddSingleton<IServerApplicationPaths>(ApplicationPaths);
serviceCollection.AddSingleton(ServerConfigurationManager); ServiceCollection.AddSingleton(ServerConfigurationManager);
serviceCollection.AddSingleton<ILocalizationManager, LocalizationManager>(); ServiceCollection.AddSingleton<ILocalizationManager, LocalizationManager>();
serviceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>(); ServiceCollection.AddSingleton<IBlurayExaminer, BdInfoExaminer>();
serviceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>(); ServiceCollection.AddSingleton<IUserDataRepository, SqliteUserDataRepository>();
serviceCollection.AddSingleton<IUserDataManager, UserDataManager>(); ServiceCollection.AddSingleton<IUserDataManager, UserDataManager>();
serviceCollection.AddSingleton<IItemRepository, SqliteItemRepository>(); ServiceCollection.AddSingleton<IItemRepository, SqliteItemRepository>();
serviceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>(); ServiceCollection.AddSingleton<IAuthenticationRepository, AuthenticationRepository>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
serviceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>)); ServiceCollection.AddTransient(provider => new Lazy<IDtoService>(provider.GetRequiredService<IDtoService>));
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
serviceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>)); ServiceCollection.AddTransient(provider => new Lazy<EncodingHelper>(provider.GetRequiredService<EncodingHelper>));
serviceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>(); ServiceCollection.AddSingleton<IMediaEncoder, MediaBrowser.MediaEncoding.Encoder.MediaEncoder>();
// TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required // TODO: Refactor to eliminate the circular dependencies here so that Lazy<T> isn't required
serviceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>)); ServiceCollection.AddTransient(provider => new Lazy<ILibraryMonitor>(provider.GetRequiredService<ILibraryMonitor>));
serviceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>)); ServiceCollection.AddTransient(provider => new Lazy<IProviderManager>(provider.GetRequiredService<IProviderManager>));
serviceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>)); ServiceCollection.AddTransient(provider => new Lazy<IUserViewManager>(provider.GetRequiredService<IUserViewManager>));
serviceCollection.AddSingleton<ILibraryManager, LibraryManager>(); ServiceCollection.AddSingleton<ILibraryManager, LibraryManager>();
serviceCollection.AddSingleton<IMusicManager, MusicManager>(); ServiceCollection.AddSingleton<IMusicManager, MusicManager>();
serviceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>(); ServiceCollection.AddSingleton<ILibraryMonitor, LibraryMonitor>();
serviceCollection.AddSingleton<ISearchEngine, SearchEngine>(); ServiceCollection.AddSingleton<ISearchEngine, SearchEngine>();
serviceCollection.AddSingleton<ServiceController>(); ServiceCollection.AddSingleton<ServiceController>();
serviceCollection.AddSingleton<IHttpServer, HttpListenerHost>(); ServiceCollection.AddSingleton<IHttpServer, HttpListenerHost>();
serviceCollection.AddSingleton<IImageProcessor, ImageProcessor>(); ServiceCollection.AddSingleton<IImageProcessor, ImageProcessor>();
serviceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>(); ServiceCollection.AddSingleton<ITVSeriesManager, TVSeriesManager>();
serviceCollection.AddSingleton<IDeviceManager, DeviceManager>(); ServiceCollection.AddSingleton<IDeviceManager, DeviceManager>();
serviceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>(); ServiceCollection.AddSingleton<IMediaSourceManager, MediaSourceManager>();
serviceCollection.AddSingleton<ISubtitleManager, SubtitleManager>(); ServiceCollection.AddSingleton<ISubtitleManager, SubtitleManager>();
serviceCollection.AddSingleton<IProviderManager, ProviderManager>(); ServiceCollection.AddSingleton<IProviderManager, ProviderManager>();
// TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required // TODO: Refactor to eliminate the circular dependency here so that Lazy<T> isn't required
serviceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>)); ServiceCollection.AddTransient(provider => new Lazy<ILiveTvManager>(provider.GetRequiredService<ILiveTvManager>));
serviceCollection.AddSingleton<IDtoService, DtoService>(); ServiceCollection.AddSingleton<IDtoService, DtoService>();
serviceCollection.AddSingleton<IChannelManager, ChannelManager>(); ServiceCollection.AddSingleton<IChannelManager, ChannelManager>();
serviceCollection.AddSingleton<ISessionManager, SessionManager>(); ServiceCollection.AddSingleton<ISessionManager, SessionManager>();
serviceCollection.AddSingleton<IDlnaManager, DlnaManager>(); ServiceCollection.AddSingleton<IDlnaManager, DlnaManager>();
serviceCollection.AddSingleton<ICollectionManager, CollectionManager>(); ServiceCollection.AddSingleton<ICollectionManager, CollectionManager>();
serviceCollection.AddSingleton<IPlaylistManager, PlaylistManager>(); ServiceCollection.AddSingleton<IPlaylistManager, PlaylistManager>();
serviceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>(); ServiceCollection.AddSingleton<ISyncPlayManager, SyncPlayManager>();
serviceCollection.AddSingleton<LiveTvDtoService>(); ServiceCollection.AddSingleton<LiveTvDtoService>();
serviceCollection.AddSingleton<ILiveTvManager, LiveTvManager>(); ServiceCollection.AddSingleton<ILiveTvManager, LiveTvManager>();
serviceCollection.AddSingleton<IUserViewManager, UserViewManager>(); ServiceCollection.AddSingleton<IUserViewManager, UserViewManager>();
serviceCollection.AddSingleton<INotificationManager, NotificationManager>(); ServiceCollection.AddSingleton<INotificationManager, NotificationManager>();
serviceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>(); ServiceCollection.AddSingleton<IDeviceDiscovery, DeviceDiscovery>();
serviceCollection.AddSingleton<IChapterManager, ChapterManager>(); ServiceCollection.AddSingleton<IChapterManager, ChapterManager>();
serviceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>(); ServiceCollection.AddSingleton<IEncodingManager, MediaEncoder.EncodingManager>();
serviceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>(); ServiceCollection.AddSingleton<IAuthorizationContext, AuthorizationContext>();
serviceCollection.AddSingleton<ISessionContext, SessionContext>(); ServiceCollection.AddSingleton<ISessionContext, SessionContext>();
serviceCollection.AddSingleton<IAuthService, AuthService>(); ServiceCollection.AddSingleton<IAuthService, AuthService>();
serviceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>(); ServiceCollection.AddSingleton<ISubtitleEncoder, MediaBrowser.MediaEncoding.Subtitles.SubtitleEncoder>();
serviceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>(); ServiceCollection.AddSingleton<IResourceFileManager, ResourceFileManager>();
serviceCollection.AddSingleton<EncodingHelper>(); ServiceCollection.AddSingleton<EncodingHelper>();
serviceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>(); ServiceCollection.AddSingleton<IAttachmentExtractor, MediaBrowser.MediaEncoding.Attachments.AttachmentExtractor>();
serviceCollection.AddSingleton<TranscodingJobHelper>(); ServiceCollection.AddSingleton<TranscodingJobHelper>();
serviceCollection.AddScoped<MediaInfoHelper>(); ServiceCollection.AddScoped<MediaInfoHelper>();
serviceCollection.AddScoped<AudioHelper>(); ServiceCollection.AddScoped<AudioHelper>();
serviceCollection.AddScoped<DynamicHlsHelper>(); ServiceCollection.AddScoped<DynamicHlsHelper>();
} }
/// <summary> /// <summary>
@ -834,6 +837,8 @@ namespace Emby.Server.Implementations
{ {
hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s)); hasPluginConfiguration.SetStartupInfo(s => Directory.CreateDirectory(s));
} }
plugin.RegisterServices(ServiceCollection);
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -746,12 +746,21 @@ namespace Emby.Server.Implementations.Channels
// null if came from cache // null if came from cache
if (itemsResult != null) if (itemsResult != null)
{ {
var internalItems = itemsResult.Items var items = itemsResult.Items;
.Select(i => GetChannelItemEntity(i, channelProvider, channel.Id, parentItem, cancellationToken)) var itemsLen = items.Count;
.ToArray(); var internalItems = new Guid[itemsLen];
for (int i = 0; i < itemsLen; i++)
{
internalItems[i] = (await GetChannelItemEntityAsync(
items[i],
channelProvider,
channel.Id,
parentItem,
cancellationToken).ConfigureAwait(false)).Id;
}
var existingIds = _libraryManager.GetItemIds(query); var existingIds = _libraryManager.GetItemIds(query);
var deadIds = existingIds.Except(internalItems.Select(i => i.Id)) var deadIds = existingIds.Except(internalItems)
.ToArray(); .ToArray();
foreach (var deadId in deadIds) foreach (var deadId in deadIds)
@ -963,7 +972,7 @@ namespace Emby.Server.Implementations.Channels
return item; return item;
} }
private BaseItem GetChannelItemEntity(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken) private async Task<BaseItem> GetChannelItemEntityAsync(ChannelItemInfo info, IChannel channelProvider, Guid internalChannelId, BaseItem parentFolder, CancellationToken cancellationToken)
{ {
var parentFolderId = parentFolder.Id; var parentFolderId = parentFolder.Id;
@ -1165,7 +1174,7 @@ namespace Emby.Server.Implementations.Channels
} }
else if (forceUpdate) else if (forceUpdate)
{ {
item.UpdateToRepository(ItemUpdateType.None, cancellationToken); await item.UpdateToRepositoryAsync(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
} }
if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media) if ((isNew || forceUpdate) && info.Type == ChannelItemType.Media)

View File

@ -132,7 +132,7 @@ namespace Emby.Server.Implementations.Collections
} }
/// <inheritdoc /> /// <inheritdoc />
public BoxSet CreateCollection(CollectionCreationOptions options) public async Task<BoxSet> CreateCollectionAsync(CollectionCreationOptions options)
{ {
var name = options.Name; var name = options.Name;
@ -141,7 +141,7 @@ namespace Emby.Server.Implementations.Collections
// This could cause it to get re-resolved as a plain folder // This could cause it to get re-resolved as a plain folder
var folderName = _fileSystem.GetValidFilename(name) + " [boxset]"; var folderName = _fileSystem.GetValidFilename(name) + " [boxset]";
var parentFolder = GetCollectionsFolder(true).GetAwaiter().GetResult(); var parentFolder = await GetCollectionsFolder(true).ConfigureAwait(false);
if (parentFolder == null) if (parentFolder == null)
{ {
@ -169,12 +169,16 @@ namespace Emby.Server.Implementations.Collections
if (options.ItemIdList.Length > 0) if (options.ItemIdList.Length > 0)
{ {
AddToCollection(collection.Id, options.ItemIdList, false, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) await AddToCollectionAsync(
{ collection.Id,
// The initial adding of items is going to create a local metadata file options.ItemIdList.Select(x => new Guid(x)),
// This will cause internet metadata to be skipped as a result false,
MetadataRefreshMode = MetadataRefreshMode.FullRefresh new MetadataRefreshOptions(new DirectoryService(_fileSystem))
}); {
// The initial adding of items is going to create a local metadata file
// This will cause internet metadata to be skipped as a result
MetadataRefreshMode = MetadataRefreshMode.FullRefresh
}).ConfigureAwait(false);
} }
else else
{ {
@ -197,18 +201,10 @@ namespace Emby.Server.Implementations.Collections
} }
/// <inheritdoc /> /// <inheritdoc />
public void AddToCollection(Guid collectionId, IEnumerable<string> ids) public Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids)
{ => AddToCollectionAsync(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
AddToCollection(collectionId, ids, true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
}
/// <inheritdoc /> private async Task AddToCollectionAsync(Guid collectionId, IEnumerable<Guid> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
public void AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
{
AddToCollection(collectionId, ids.Select(i => i.ToString("N", CultureInfo.InvariantCulture)), true, new MetadataRefreshOptions(new DirectoryService(_fileSystem)));
}
private void AddToCollection(Guid collectionId, IEnumerable<string> ids, bool fireEvent, MetadataRefreshOptions refreshOptions)
{ {
var collection = _libraryManager.GetItemById(collectionId) as BoxSet; var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
if (collection == null) if (collection == null)
@ -224,15 +220,14 @@ namespace Emby.Server.Implementations.Collections
foreach (var id in ids) foreach (var id in ids)
{ {
var guidId = new Guid(id); var item = _libraryManager.GetItemById(id);
var item = _libraryManager.GetItemById(guidId);
if (item == null) if (item == null)
{ {
throw new ArgumentException("No item exists with the supplied Id"); throw new ArgumentException("No item exists with the supplied Id");
} }
if (!currentLinkedChildrenIds.Contains(guidId)) if (!currentLinkedChildrenIds.Contains(id))
{ {
itemList.Add(item); itemList.Add(item);
@ -249,7 +244,7 @@ namespace Emby.Server.Implementations.Collections
collection.UpdateRatingToItems(linkedChildrenList); collection.UpdateRatingToItems(linkedChildrenList);
collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
refreshOptions.ForceSave = true; refreshOptions.ForceSave = true;
_providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High); _providerManager.QueueRefresh(collection.Id, refreshOptions, RefreshPriority.High);
@ -266,13 +261,7 @@ namespace Emby.Server.Implementations.Collections
} }
/// <inheritdoc /> /// <inheritdoc />
public void RemoveFromCollection(Guid collectionId, IEnumerable<string> itemIds) public async Task RemoveFromCollectionAsync(Guid collectionId, IEnumerable<Guid> itemIds)
{
RemoveFromCollection(collectionId, itemIds.Select(i => new Guid(i)));
}
/// <inheritdoc />
public void RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
{ {
var collection = _libraryManager.GetItemById(collectionId) as BoxSet; var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
@ -309,7 +298,7 @@ namespace Emby.Server.Implementations.Collections
collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray(); collection.LinkedChildren = collection.LinkedChildren.Except(list).ToArray();
} }
collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
_providerManager.QueueRefresh( _providerManager.QueueRefresh(
collection.Id, collection.Id,
new MetadataRefreshOptions(new DirectoryService(_fileSystem)) new MetadataRefreshOptions(new DirectoryService(_fileSystem))

View File

@ -2,11 +2,11 @@ using System;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using Emby.Server.Implementations.AppBase; using Emby.Server.Implementations.AppBase;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -7,13 +7,13 @@ using System.IO;
using System.Linq; using System.Linq;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Security;
using MediaBrowser.Model.Devices; using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;

View File

@ -22,7 +22,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="IPNetwork2" Version="2.5.211" /> <PackageReference Include="IPNetwork2" Version="2.5.224" />
<PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" /> <PackageReference Include="Jellyfin.XmlTv" Version="10.6.2" />
<PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" /> <PackageReference Include="Microsoft.AspNetCore.Hosting" Version="2.2.7" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" /> <PackageReference Include="Microsoft.AspNetCore.Hosting.Abstractions" Version="2.2.0" />
@ -37,11 +37,11 @@
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" /> <PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="3.1.6" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" /> <PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.1.6" />
<PackageReference Include="Mono.Nat" Version="2.0.2" /> <PackageReference Include="Mono.Nat" Version="2.0.2" />
<PackageReference Include="prometheus-net.DotNetRuntime" Version="3.3.1" /> <PackageReference Include="prometheus-net.DotNetRuntime" Version="3.4.0" />
<PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" /> <PackageReference Include="ServiceStack.Text.Core" Version="5.9.2" />
<PackageReference Include="sharpcompress" Version="0.26.0" /> <PackageReference Include="sharpcompress" Version="0.26.0" />
<PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" /> <PackageReference Include="SQLitePCL.pretty.netstandard" Version="2.1.0" />
<PackageReference Include="DotNet.Glob" Version="3.0.9" /> <PackageReference Include="DotNet.Glob" Version="3.1.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -7,11 +7,11 @@ using System.Net;
using System.Text; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Events;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Mono.Nat; using Mono.Nat;

View File

@ -7,6 +7,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Events;
using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Channels;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
@ -15,7 +16,6 @@ using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Emby.Server.Implementations.EntryPoints namespace Emby.Server.Implementations.EntryPoints

View File

@ -5,6 +5,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Plugins; using MediaBrowser.Controller.Plugins;
@ -43,22 +44,22 @@ namespace Emby.Server.Implementations.EntryPoints
return Task.CompletedTask; return Task.CompletedTask;
} }
private async void OnLiveTvManagerSeriesTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) private async void OnLiveTvManagerSeriesTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
{ {
await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false); await SendMessage("SeriesTimerCreated", e.Argument).ConfigureAwait(false);
} }
private async void OnLiveTvManagerTimerCreated(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) private async void OnLiveTvManagerTimerCreated(object sender, GenericEventArgs<TimerEventInfo> e)
{ {
await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false); await SendMessage("TimerCreated", e.Argument).ConfigureAwait(false);
} }
private async void OnLiveTvManagerSeriesTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) private async void OnLiveTvManagerSeriesTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
{ {
await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false); await SendMessage("SeriesTimerCancelled", e.Argument).ConfigureAwait(false);
} }
private async void OnLiveTvManagerTimerCancelled(object sender, MediaBrowser.Model.Events.GenericEventArgs<TimerEventInfo> e) private async void OnLiveTvManagerTimerCancelled(object sender, GenericEventArgs<TimerEventInfo> e)
{ {
await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false); await SendMessage("TimerCancelled", e.Argument).ConfigureAwait(false);
} }

View File

@ -1,210 +0,0 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Data.Entities;
using MediaBrowser.Common.Plugins;
using MediaBrowser.Common.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Plugins;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Tasks;
using MediaBrowser.Model.Updates;
namespace Emby.Server.Implementations.EntryPoints
{
/// <summary>
/// Class WebSocketEvents.
/// </summary>
public class ServerEventNotifier : IServerEntryPoint
{
/// <summary>
/// The user manager.
/// </summary>
private readonly IUserManager _userManager;
/// <summary>
/// The installation manager.
/// </summary>
private readonly IInstallationManager _installationManager;
/// <summary>
/// The kernel.
/// </summary>
private readonly IServerApplicationHost _appHost;
/// <summary>
/// The task manager.
/// </summary>
private readonly ITaskManager _taskManager;
private readonly ISessionManager _sessionManager;
/// <summary>
/// Initializes a new instance of the <see cref="ServerEventNotifier"/> class.
/// </summary>
/// <param name="appHost">The application host.</param>
/// <param name="userManager">The user manager.</param>
/// <param name="installationManager">The installation manager.</param>
/// <param name="taskManager">The task manager.</param>
/// <param name="sessionManager">The session manager.</param>
public ServerEventNotifier(
IServerApplicationHost appHost,
IUserManager userManager,
IInstallationManager installationManager,
ITaskManager taskManager,
ISessionManager sessionManager)
{
_userManager = userManager;
_installationManager = installationManager;
_appHost = appHost;
_taskManager = taskManager;
_sessionManager = sessionManager;
}
/// <inheritdoc />
public Task RunAsync()
{
_userManager.OnUserDeleted += OnUserDeleted;
_userManager.OnUserUpdated += OnUserUpdated;
_appHost.HasPendingRestartChanged += OnHasPendingRestartChanged;
_installationManager.PluginUninstalled += OnPluginUninstalled;
_installationManager.PackageInstalling += OnPackageInstalling;
_installationManager.PackageInstallationCancelled += OnPackageInstallationCancelled;
_installationManager.PackageInstallationCompleted += OnPackageInstallationCompleted;
_installationManager.PackageInstallationFailed += OnPackageInstallationFailed;
_taskManager.TaskCompleted += OnTaskCompleted;
return Task.CompletedTask;
}
private async void OnPackageInstalling(object sender, InstallationInfo e)
{
await SendMessageToAdminSessions("PackageInstalling", e).ConfigureAwait(false);
}
private async void OnPackageInstallationCancelled(object sender, InstallationInfo e)
{
await SendMessageToAdminSessions("PackageInstallationCancelled", e).ConfigureAwait(false);
}
private async void OnPackageInstallationCompleted(object sender, InstallationInfo e)
{
await SendMessageToAdminSessions("PackageInstallationCompleted", e).ConfigureAwait(false);
}
private async void OnPackageInstallationFailed(object sender, InstallationFailedEventArgs e)
{
await SendMessageToAdminSessions("PackageInstallationFailed", e.InstallationInfo).ConfigureAwait(false);
}
private async void OnTaskCompleted(object sender, TaskCompletionEventArgs e)
{
await SendMessageToAdminSessions("ScheduledTaskEnded", e.Result).ConfigureAwait(false);
}
/// <summary>
/// Installations the manager_ plugin uninstalled.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
private async void OnPluginUninstalled(object sender, IPlugin e)
{
await SendMessageToAdminSessions("PluginUninstalled", e).ConfigureAwait(false);
}
/// <summary>
/// Handles the HasPendingRestartChanged event of the kernel control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param>
private async void OnHasPendingRestartChanged(object sender, EventArgs e)
{
await _sessionManager.SendRestartRequiredNotification(CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// Users the manager_ user updated.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
private async void OnUserUpdated(object sender, GenericEventArgs<User> e)
{
var dto = _userManager.GetUserDto(e.Argument);
await SendMessageToUserSession(e.Argument, "UserUpdated", dto).ConfigureAwait(false);
}
/// <summary>
/// Users the manager_ user deleted.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="e">The e.</param>
private async void OnUserDeleted(object sender, GenericEventArgs<User> e)
{
await SendMessageToUserSession(e.Argument, "UserDeleted", e.Argument.Id.ToString("N", CultureInfo.InvariantCulture)).ConfigureAwait(false);
}
private async Task SendMessageToAdminSessions<T>(string name, T data)
{
try
{
await _sessionManager.SendMessageToAdminSessions(name, data, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception)
{
}
}
private async Task SendMessageToUserSession<T>(User user, string name, T data)
{
try
{
await _sessionManager.SendMessageToUserSessions(
new List<Guid> { user.Id },
name,
data,
CancellationToken.None).ConfigureAwait(false);
}
catch (Exception)
{
}
}
/// <inheritdoc />
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 (dispose)
{
_userManager.OnUserDeleted -= OnUserDeleted;
_userManager.OnUserUpdated -= OnUserUpdated;
_installationManager.PluginUninstalled -= OnPluginUninstalled;
_installationManager.PackageInstalling -= OnPackageInstalling;
_installationManager.PackageInstallationCancelled -= OnPackageInstallationCancelled;
_installationManager.PackageInstallationCompleted -= OnPackageInstallationCompleted;
_installationManager.PackageInstallationFailed -= OnPackageInstallationFailed;
_appHost.HasPendingRestartChanged -= OnHasPendingRestartChanged;
_taskManager.TaskCompleted -= OnTaskCompleted;
}
}
}
}

View File

@ -12,13 +12,13 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Emby.Server.Implementations.Services; using Emby.Server.Implementations.Services;
using Emby.Server.Implementations.SocketSharp; using Emby.Server.Implementations.SocketSharp;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Authentication; using MediaBrowser.Controller.Authentication;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Services; using MediaBrowser.Model.Services;

View File

@ -179,7 +179,7 @@ namespace Emby.Server.Implementations.HttpServer
return; return;
} }
WebSocketMessage<object> stub; WebSocketMessage<object>? stub;
try try
{ {
@ -209,6 +209,12 @@ namespace Emby.Server.Implementations.HttpServer
return; return;
} }
if (stub == null)
{
_logger.LogError("Error processing web socket message");
return;
}
// Tell the PipeReader how much of the buffer we have consumed // Tell the PipeReader how much of the buffer we have consumed
reader.AdvanceTo(buffer.End); reader.AdvanceTo(buffer.End);

View File

@ -771,7 +771,7 @@ namespace Emby.Server.Implementations.Library
if (folder.ParentId != rootFolder.Id) if (folder.ParentId != rootFolder.Id)
{ {
folder.ParentId = rootFolder.Id; folder.ParentId = rootFolder.Id;
folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); folder.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
} }
rootFolder.AddVirtualChild(folder); rootFolder.AddVirtualChild(folder);
@ -1868,7 +1868,8 @@ namespace Emby.Server.Implementations.Library
return image.Path != null && !image.IsLocalFile; return image.Path != null && !image.IsLocalFile;
} }
public void UpdateImages(BaseItem item, bool forceUpdate = false) /// <inheritdoc />
public async Task UpdateImagesAsync(BaseItem item, bool forceUpdate = false)
{ {
if (item == null) if (item == null)
{ {
@ -1891,7 +1892,7 @@ namespace Emby.Server.Implementations.Library
try try
{ {
var index = item.GetImageIndex(img); var index = item.GetImageIndex(img);
image = ConvertImageToLocal(item, img, index).ConfigureAwait(false).GetAwaiter().GetResult(); image = await ConvertImageToLocal(item, img, index).ConfigureAwait(false);
} }
catch (ArgumentException) catch (ArgumentException)
{ {
@ -1913,7 +1914,7 @@ namespace Emby.Server.Implementations.Library
} }
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Cannnot get image dimensions for {0}", image.Path); _logger.LogError(ex, "Cannot get image dimensions for {0}", image.Path);
image.Width = 0; image.Width = 0;
image.Height = 0; image.Height = 0;
continue; continue;
@ -1943,10 +1944,8 @@ namespace Emby.Server.Implementations.Library
RegisterItem(item); RegisterItem(item);
} }
/// <summary> /// <inheritdoc />
/// Updates the item. public async Task UpdateItemsAsync(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
/// </summary>
public void UpdateItems(IReadOnlyList<BaseItem> items, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{ {
foreach (var item in items) foreach (var item in items)
{ {
@ -1957,7 +1956,7 @@ namespace Emby.Server.Implementations.Library
item.DateLastSaved = DateTime.UtcNow; item.DateLastSaved = DateTime.UtcNow;
UpdateImages(item, updateReason >= ItemUpdateType.ImageUpdate); await UpdateImagesAsync(item, updateReason >= ItemUpdateType.ImageUpdate).ConfigureAwait(false);
} }
_itemRepository.SaveItems(items, cancellationToken); _itemRepository.SaveItems(items, cancellationToken);
@ -1991,17 +1990,9 @@ namespace Emby.Server.Implementations.Library
} }
} }
/// <summary> /// <inheritdoc />
/// Updates the item. public Task UpdateItemAsync(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
/// </summary> => UpdateItemsAsync(new[] { item }, parent, updateReason, cancellationToken);
/// <param name="item">The item.</param>
/// <param name="parent">The parent item.</param>
/// <param name="updateReason">The update reason.</param>
/// <param name="cancellationToken">The cancellation token.</param>
public void UpdateItem(BaseItem item, BaseItem parent, ItemUpdateType updateReason, CancellationToken cancellationToken)
{
UpdateItems(new[] { item }, parent, updateReason, cancellationToken);
}
/// <summary> /// <summary>
/// Reports the item removed. /// Reports the item removed.
@ -2233,7 +2224,7 @@ namespace Emby.Server.Implementations.Library
if (refresh) if (refresh)
{ {
item.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); item.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, CancellationToken.None).GetAwaiter().GetResult();
ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal); ProviderManager.QueueRefresh(item.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)), RefreshPriority.Normal);
} }
@ -2420,7 +2411,7 @@ namespace Emby.Server.Implementations.Library
if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
{ {
item.ViewType = viewType; item.ViewType = viewType;
item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).GetAwaiter().GetResult();
} }
var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval; var refresh = isNew || DateTime.UtcNow - item.DateLastRefreshed >= _viewRefreshInterval;
@ -2902,7 +2893,7 @@ namespace Emby.Server.Implementations.Library
await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false); await ProviderManager.SaveImage(item, url, image.Type, imageIndex, CancellationToken.None).ConfigureAwait(false);
item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
return item.GetImageInfo(image.Type, imageIndex); return item.GetImageInfo(image.Type, imageIndex);
} }
@ -2920,7 +2911,7 @@ namespace Emby.Server.Implementations.Library
// Remove this image to prevent it from retrying over and over // Remove this image to prevent it from retrying over and over
item.RemoveImage(image); item.RemoveImage(image);
item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
throw new InvalidOperationException(); throw new InvalidOperationException();
} }

View File

@ -13,6 +13,7 @@ using System.Threading.Tasks;
using System.Xml; using System.Xml;
using Emby.Server.Implementations.Library; using Emby.Server.Implementations.Library;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
@ -29,7 +30,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.MediaInfo;

View File

@ -5,8 +5,8 @@ using System.Collections.Concurrent;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using Jellyfin.Data.Events;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -10,6 +10,7 @@ using System.Threading.Tasks;
using Emby.Server.Implementations.Library; using Emby.Server.Implementations.Library;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
@ -24,7 +25,6 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Sorting; using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
using MediaBrowser.Model.LiveTv; using MediaBrowser.Model.LiveTv;
@ -422,7 +422,7 @@ namespace Emby.Server.Implementations.LiveTv
} }
} }
private LiveTvChannel GetChannel(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken) private async Task<LiveTvChannel> GetChannelAsync(ChannelInfo channelInfo, string serviceName, BaseItem parentFolder, CancellationToken cancellationToken)
{ {
var parentFolderId = parentFolder.Id; var parentFolderId = parentFolder.Id;
var isNew = false; var isNew = false;
@ -512,7 +512,7 @@ namespace Emby.Server.Implementations.LiveTv
} }
else if (forceUpdate) else if (forceUpdate)
{ {
_libraryManager.UpdateItem(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken); await _libraryManager.UpdateItemAsync(item, parentFolder, ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
} }
return item; return item;
@ -1129,7 +1129,7 @@ namespace Emby.Server.Implementations.LiveTv
try try
{ {
var item = GetChannel(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken); var item = await GetChannelAsync(channelInfo.Item2, channelInfo.Item1, parentFolder, cancellationToken).ConfigureAwait(false);
list.Add(item); list.Add(item);
} }
@ -1146,7 +1146,7 @@ namespace Emby.Server.Implementations.LiveTv
double percent = numComplete; double percent = numComplete;
percent /= allChannelsList.Count; percent /= allChannelsList.Count;
progress.Report(5 * percent + 10); progress.Report((5 * percent) + 10);
} }
progress.Report(15); progress.Report(15);
@ -1221,7 +1221,11 @@ namespace Emby.Server.Implementations.LiveTv
if (updatedPrograms.Count > 0) if (updatedPrograms.Count > 0)
{ {
_libraryManager.UpdateItems(updatedPrograms, currentChannel, ItemUpdateType.MetadataImport, cancellationToken); await _libraryManager.UpdateItemsAsync(
updatedPrograms,
currentChannel,
ItemUpdateType.MetadataImport,
cancellationToken).ConfigureAwait(false);
} }
currentChannel.IsMovie = isMovie; currentChannel.IsMovie = isMovie;
@ -1234,7 +1238,7 @@ namespace Emby.Server.Implementations.LiveTv
currentChannel.AddTag("Kids"); currentChannel.AddTag("Kids");
} }
currentChannel.UpdateToRepository(ItemUpdateType.MetadataImport, cancellationToken); await currentChannel.UpdateToRepositoryAsync(ItemUpdateType.MetadataImport, cancellationToken).ConfigureAwait(false);
await currentChannel.RefreshMetadata( await currentChannel.RefreshMetadata(
new MetadataRefreshOptions(new DirectoryService(_fileSystem)) new MetadataRefreshOptions(new DirectoryService(_fileSystem))
{ {

View File

@ -1,7 +1,7 @@
{ {
"Albums": "Album", "Albums": "Album",
"AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi", "AuthenticationSucceededWithUserName": "{0} berhasil diautentikasi",
"AppDeviceValues": "Aplikasi: {0}, Alat: {1}", "AppDeviceValues": "Aplikasi : {0}, Alat : {1}",
"LabelRunningTimeValue": "Waktu berjalan: {0}", "LabelRunningTimeValue": "Waktu berjalan: {0}",
"MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}", "MessageApplicationUpdatedTo": "Jellyfin Server sudah diperbarui ke {0}",
"MessageApplicationUpdated": "Jellyfin Server sudah diperbarui", "MessageApplicationUpdated": "Jellyfin Server sudah diperbarui",
@ -22,7 +22,7 @@
"HeaderContinueWatching": "Lanjutkan Menonton", "HeaderContinueWatching": "Lanjutkan Menonton",
"HeaderCameraUploads": "Unggahan Kamera", "HeaderCameraUploads": "Unggahan Kamera",
"HeaderAlbumArtists": "Album Artis", "HeaderAlbumArtists": "Album Artis",
"Genres": "Genre", "Genres": "Aliran",
"Folders": "Folder", "Folders": "Folder",
"Favorites": "Favorit", "Favorites": "Favorit",
"Collections": "Koleksi", "Collections": "Koleksi",

View File

@ -69,5 +69,8 @@
"AppDeviceValues": "App: {0}, อุปกรณ์: {1}", "AppDeviceValues": "App: {0}, อุปกรณ์: {1}",
"Albums": "อัลบั้ม", "Albums": "อัลบั้ม",
"ScheduledTaskStartedWithName": "{0} เริ่มต้น", "ScheduledTaskStartedWithName": "{0} เริ่มต้น",
"ScheduledTaskFailedWithName": "{0} ล้มเหลว" "ScheduledTaskFailedWithName": "{0} ล้มเหลว",
"Songs": "เพลง",
"Shows": "แสดง",
"ServerNameNeedsToBeRestarted": "{0} ต้องการรีสตาร์ท"
} }

View File

@ -152,10 +152,10 @@ namespace Emby.Server.Implementations.Playlists
if (options.ItemIdList.Length > 0) if (options.ItemIdList.Length > 0)
{ {
AddToPlaylistInternal(playlist.Id.ToString("N", CultureInfo.InvariantCulture), options.ItemIdList, user, new DtoOptions(false) await AddToPlaylistInternal(playlist.Id, options.ItemIdList, user, new DtoOptions(false)
{ {
EnableImages = true EnableImages = true
}); }).ConfigureAwait(false);
} }
return new PlaylistCreationResult(playlist.Id.ToString("N", CultureInfo.InvariantCulture)); return new PlaylistCreationResult(playlist.Id.ToString("N", CultureInfo.InvariantCulture));
@ -184,17 +184,17 @@ namespace Emby.Server.Implementations.Playlists
return Playlist.GetPlaylistItems(playlistMediaType, items, user, options); return Playlist.GetPlaylistItems(playlistMediaType, items, user, options);
} }
public void AddToPlaylist(string playlistId, ICollection<Guid> itemIds, Guid userId) public Task AddToPlaylistAsync(Guid playlistId, ICollection<Guid> itemIds, Guid userId)
{ {
var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId); var user = userId.Equals(Guid.Empty) ? null : _userManager.GetUserById(userId);
AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false) return AddToPlaylistInternal(playlistId, itemIds, user, new DtoOptions(false)
{ {
EnableImages = true EnableImages = true
}); });
} }
private void AddToPlaylistInternal(string playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options) private async Task AddToPlaylistInternal(Guid playlistId, ICollection<Guid> newItemIds, User user, DtoOptions options)
{ {
// Retrieve the existing playlist // Retrieve the existing playlist
var playlist = _libraryManager.GetItemById(playlistId) as Playlist var playlist = _libraryManager.GetItemById(playlistId) as Playlist
@ -238,7 +238,7 @@ namespace Emby.Server.Implementations.Playlists
// Update the playlist in the repository // Update the playlist in the repository
playlist.LinkedChildren = newLinkedChildren; playlist.LinkedChildren = newLinkedChildren;
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
// Update the playlist on disk // Update the playlist on disk
if (playlist.IsFile) if (playlist.IsFile)
@ -256,7 +256,7 @@ namespace Emby.Server.Implementations.Playlists
RefreshPriority.High); RefreshPriority.High);
} }
public void RemoveFromPlaylist(string playlistId, IEnumerable<string> entryIds) public async Task RemoveFromPlaylistAsync(string playlistId, IEnumerable<string> entryIds)
{ {
if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist)) if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
{ {
@ -273,7 +273,7 @@ namespace Emby.Server.Implementations.Playlists
.Select(i => i.Item1) .Select(i => i.Item1)
.ToArray(); .ToArray();
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (playlist.IsFile) if (playlist.IsFile)
{ {
@ -289,7 +289,7 @@ namespace Emby.Server.Implementations.Playlists
RefreshPriority.High); RefreshPriority.High);
} }
public void MoveItem(string playlistId, string entryId, int newIndex) public async Task MoveItemAsync(string playlistId, string entryId, int newIndex)
{ {
if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist)) if (!(_libraryManager.GetItemById(playlistId) is Playlist playlist))
{ {
@ -322,7 +322,7 @@ namespace Emby.Server.Implementations.Playlists
playlist.LinkedChildren = newList.ToArray(); playlist.LinkedChildren = newList.ToArray();
playlist.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await playlist.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (playlist.IsFile) if (playlist.IsFile)
{ {

View File

@ -6,10 +6,10 @@ using System.IO;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Progress; using MediaBrowser.Common.Progress;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -5,8 +5,8 @@ using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -9,6 +9,7 @@ using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Entities; using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Data.Events;
using MediaBrowser.Common.Events; using MediaBrowser.Common.Events;
using MediaBrowser.Common.Extensions; using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller; using MediaBrowser.Controller;
@ -17,6 +18,8 @@ using MediaBrowser.Controller.Devices;
using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Drawing;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Events;
using MediaBrowser.Controller.Events.Session;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Security; using MediaBrowser.Controller.Security;
@ -24,7 +27,6 @@ using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Devices; using MediaBrowser.Model.Devices;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Library; using MediaBrowser.Model.Library;
using MediaBrowser.Model.Querying; using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Session; using MediaBrowser.Model.Session;
@ -40,25 +42,16 @@ namespace Emby.Server.Implementations.Session
/// </summary> /// </summary>
public class SessionManager : ISessionManager, IDisposable public class SessionManager : ISessionManager, IDisposable
{ {
/// <summary>
/// The user data repository.
/// </summary>
private readonly IUserDataManager _userDataManager; private readonly IUserDataManager _userDataManager;
/// <summary>
/// The logger.
/// </summary>
private readonly ILogger<SessionManager> _logger; private readonly ILogger<SessionManager> _logger;
private readonly IEventManager _eventManager;
private readonly ILibraryManager _libraryManager; private readonly ILibraryManager _libraryManager;
private readonly IUserManager _userManager; private readonly IUserManager _userManager;
private readonly IMusicManager _musicManager; private readonly IMusicManager _musicManager;
private readonly IDtoService _dtoService; private readonly IDtoService _dtoService;
private readonly IImageProcessor _imageProcessor; private readonly IImageProcessor _imageProcessor;
private readonly IMediaSourceManager _mediaSourceManager; private readonly IMediaSourceManager _mediaSourceManager;
private readonly IServerApplicationHost _appHost; private readonly IServerApplicationHost _appHost;
private readonly IAuthenticationRepository _authRepo; private readonly IAuthenticationRepository _authRepo;
private readonly IDeviceManager _deviceManager; private readonly IDeviceManager _deviceManager;
@ -75,6 +68,7 @@ namespace Emby.Server.Implementations.Session
public SessionManager( public SessionManager(
ILogger<SessionManager> logger, ILogger<SessionManager> logger,
IEventManager eventManager,
IUserDataManager userDataManager, IUserDataManager userDataManager,
ILibraryManager libraryManager, ILibraryManager libraryManager,
IUserManager userManager, IUserManager userManager,
@ -87,6 +81,7 @@ namespace Emby.Server.Implementations.Session
IMediaSourceManager mediaSourceManager) IMediaSourceManager mediaSourceManager)
{ {
_logger = logger; _logger = logger;
_eventManager = eventManager;
_userDataManager = userDataManager; _userDataManager = userDataManager;
_libraryManager = libraryManager; _libraryManager = libraryManager;
_userManager = userManager; _userManager = userManager;
@ -209,6 +204,8 @@ namespace Emby.Server.Implementations.Session
} }
} }
_eventManager.Publish(new SessionStartedEventArgs(info));
EventHelper.QueueEventIfNotNull( EventHelper.QueueEventIfNotNull(
SessionStarted, SessionStarted,
this, this,
@ -230,6 +227,8 @@ namespace Emby.Server.Implementations.Session
}, },
_logger); _logger);
_eventManager.Publish(new SessionEndedEventArgs(info));
info.Dispose(); info.Dispose();
} }
@ -667,22 +666,26 @@ namespace Emby.Server.Implementations.Session
} }
} }
var eventArgs = new PlaybackProgressEventArgs
{
Item = libraryItem,
Users = users,
MediaSourceId = info.MediaSourceId,
MediaInfo = info.Item,
DeviceName = session.DeviceName,
ClientName = session.Client,
DeviceId = session.DeviceId,
Session = session
};
await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
// Nothing to save here // Nothing to save here
// Fire events to inform plugins // Fire events to inform plugins
EventHelper.QueueEventIfNotNull( EventHelper.QueueEventIfNotNull(
PlaybackStart, PlaybackStart,
this, this,
new PlaybackProgressEventArgs eventArgs,
{
Item = libraryItem,
Users = users,
MediaSourceId = info.MediaSourceId,
MediaInfo = info.Item,
DeviceName = session.DeviceName,
ClientName = session.Client,
DeviceId = session.DeviceId,
Session = session
},
_logger); _logger);
StartIdleCheckTimer(); StartIdleCheckTimer();
@ -750,23 +753,25 @@ namespace Emby.Server.Implementations.Session
} }
} }
PlaybackProgress?.Invoke( var eventArgs = new PlaybackProgressEventArgs
this, {
new PlaybackProgressEventArgs Item = libraryItem,
{ Users = users,
Item = libraryItem, PlaybackPositionTicks = session.PlayState.PositionTicks,
Users = users, MediaSourceId = session.PlayState.MediaSourceId,
PlaybackPositionTicks = session.PlayState.PositionTicks, MediaInfo = info.Item,
MediaSourceId = session.PlayState.MediaSourceId, DeviceName = session.DeviceName,
MediaInfo = info.Item, ClientName = session.Client,
DeviceName = session.DeviceName, DeviceId = session.DeviceId,
ClientName = session.Client, IsPaused = info.IsPaused,
DeviceId = session.DeviceId, PlaySessionId = info.PlaySessionId,
IsPaused = info.IsPaused, IsAutomated = isAutomated,
PlaySessionId = info.PlaySessionId, Session = session
IsAutomated = isAutomated, };
Session = session
}); await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
PlaybackProgress?.Invoke(this, eventArgs);
if (!isAutomated) if (!isAutomated)
{ {
@ -943,23 +948,23 @@ namespace Emby.Server.Implementations.Session
} }
} }
EventHelper.QueueEventIfNotNull( var eventArgs = new PlaybackStopEventArgs
PlaybackStopped, {
this, Item = libraryItem,
new PlaybackStopEventArgs Users = users,
{ PlaybackPositionTicks = info.PositionTicks,
Item = libraryItem, PlayedToCompletion = playedToCompletion,
Users = users, MediaSourceId = info.MediaSourceId,
PlaybackPositionTicks = info.PositionTicks, MediaInfo = info.Item,
PlayedToCompletion = playedToCompletion, DeviceName = session.DeviceName,
MediaSourceId = info.MediaSourceId, ClientName = session.Client,
MediaInfo = info.Item, DeviceId = session.DeviceId,
DeviceName = session.DeviceName, Session = session
ClientName = session.Client, };
DeviceId = session.DeviceId,
Session = session await _eventManager.PublishAsync(eventArgs).ConfigureAwait(false);
},
_logger); EventHelper.QueueEventIfNotNull(PlaybackStopped, this, eventArgs, _logger);
} }
private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed) private bool OnPlaybackStopped(User user, BaseItem item, long? positionTicks, bool playbackFailed)

View File

@ -4,9 +4,9 @@ using System.Linq;
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session; using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Net; using MediaBrowser.Model.Net;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -1,5 +1,6 @@
using System; using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Threading.Tasks;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions; using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers; using Jellyfin.Api.Helpers;
@ -51,7 +52,7 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="CollectionCreationOptions"/> with information about the new collection.</returns> /// <returns>A <see cref="CollectionCreationOptions"/> with information about the new collection.</returns>
[HttpPost] [HttpPost]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<CollectionCreationResult> CreateCollection( public async Task<ActionResult<CollectionCreationResult>> CreateCollection(
[FromQuery] string? name, [FromQuery] string? name,
[FromQuery] string? ids, [FromQuery] string? ids,
[FromQuery] Guid? parentId, [FromQuery] Guid? parentId,
@ -59,14 +60,14 @@ namespace Jellyfin.Api.Controllers
{ {
var userId = _authContext.GetAuthorizationInfo(Request).UserId; var userId = _authContext.GetAuthorizationInfo(Request).UserId;
var item = _collectionManager.CreateCollection(new CollectionCreationOptions var item = await _collectionManager.CreateCollectionAsync(new CollectionCreationOptions
{ {
IsLocked = isLocked, IsLocked = isLocked,
Name = name, Name = name,
ParentId = parentId, ParentId = parentId,
ItemIdList = RequestHelpers.Split(ids, ',', true), ItemIdList = RequestHelpers.Split(ids, ',', true),
UserIds = new[] { userId } UserIds = new[] { userId }
}); }).ConfigureAwait(false);
var dtoOptions = new DtoOptions().AddClientFields(Request); var dtoOptions = new DtoOptions().AddClientFields(Request);
@ -87,9 +88,9 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns> /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("{collectionId}/Items")] [HttpPost("{collectionId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) public async Task<ActionResult> AddToCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
{ {
_collectionManager.AddToCollection(collectionId, RequestHelpers.Split(itemIds, ',', true)); await _collectionManager.AddToCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(true);
return NoContent(); return NoContent();
} }
@ -102,9 +103,9 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns> /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpDelete("{collectionId}/Items")] [HttpDelete("{collectionId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds) public async Task<ActionResult> RemoveFromCollection([FromRoute] Guid collectionId, [FromQuery, Required] string? itemIds)
{ {
_collectionManager.RemoveFromCollection(collectionId, RequestHelpers.Split(itemIds, ',', true)); await _collectionManager.RemoveFromCollectionAsync(collectionId, RequestHelpers.GetGuids(itemIds)).ConfigureAwait(false);
return NoContent(); return NoContent();
} }
} }

View File

@ -117,7 +117,7 @@ namespace Jellyfin.Api.Controllers
[HttpPost("MediaEncoder/Path")] [HttpPost("MediaEncoder/Path")]
[Authorize(Policy = Policies.FirstTimeSetupOrElevated)] [Authorize(Policy = Policies.FirstTimeSetupOrElevated)]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult UpdateMediaEncoderPath([FromForm, Required] MediaEncoderPathDto mediaEncoderPath) public ActionResult UpdateMediaEncoderPath([FromBody, Required] MediaEncoderPathDto mediaEncoderPath)
{ {
_mediaEncoder.UpdateEncoderPath(mediaEncoderPath.Path, mediaEncoderPath.PathType); _mediaEncoder.UpdateEncoderPath(mediaEncoderPath.Path, mediaEncoderPath.PathType);
return NoContent(); return NoContent();

View File

@ -229,7 +229,7 @@ namespace Jellyfin.Api.Controllers
}); });
} }
private EventSubscriptionResponse ProcessEventRequest(IEventManager eventManager) private EventSubscriptionResponse ProcessEventRequest(IDlnaEventManager dlnaEventManager)
{ {
var subscriptionId = Request.Headers["SID"]; var subscriptionId = Request.Headers["SID"];
if (string.Equals(Request.Method, "subscribe", StringComparison.OrdinalIgnoreCase)) if (string.Equals(Request.Method, "subscribe", StringComparison.OrdinalIgnoreCase))
@ -240,17 +240,17 @@ namespace Jellyfin.Api.Controllers
if (string.IsNullOrEmpty(notificationType)) if (string.IsNullOrEmpty(notificationType))
{ {
return eventManager.RenewEventSubscription( return dlnaEventManager.RenewEventSubscription(
subscriptionId, subscriptionId,
notificationType, notificationType,
timeoutString, timeoutString,
callback); callback);
} }
return eventManager.CreateEventSubscription(notificationType, timeoutString, callback); return dlnaEventManager.CreateEventSubscription(notificationType, timeoutString, callback);
} }
return eventManager.CancelEventSubscription(subscriptionId); return dlnaEventManager.CancelEventSubscription(subscriptionId);
} }
} }
} }

View File

@ -1356,7 +1356,7 @@ namespace Jellyfin.Api.Controllers
return string.Format( return string.Format(
CultureInfo.InvariantCulture, CultureInfo.InvariantCulture,
"{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -f hls -max_delay 5000000 -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"", "{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -copyts -avoid_negative_ts disabled -max_muxing_queue_size 2048 -f hls -max_delay 5000000 -hls_time {6} -individual_header_trailer 0 -hls_segment_type {7} -start_number {8} -hls_segment_filename \"{9}\" -hls_playlist_type vod -hls_list_size 0 -y \"{10}\"",
inputModifier, inputModifier,
_encodingHelper.GetInputArgument(state, encodingOptions), _encodingHelper.GetInputArgument(state, encodingOptions),
threads, threads,

View File

@ -174,7 +174,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.RequiresElevation)] [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult DeleteItemImage( public async Task<ActionResult> DeleteItemImage(
[FromRoute] Guid itemId, [FromRoute] Guid itemId,
[FromRoute] ImageType imageType, [FromRoute] ImageType imageType,
[FromRoute] int? imageIndex = null) [FromRoute] int? imageIndex = null)
@ -185,7 +185,7 @@ namespace Jellyfin.Api.Controllers
return NotFound(); return NotFound();
} }
item.DeleteImage(imageType, imageIndex ?? 0); await item.DeleteImageAsync(imageType, imageIndex ?? 0).ConfigureAwait(false);
return NoContent(); return NoContent();
} }
@ -218,7 +218,7 @@ namespace Jellyfin.Api.Controllers
// Handle image/png; charset=utf-8 // Handle image/png; charset=utf-8
var mimeType = Request.ContentType.Split(';').FirstOrDefault(); var mimeType = Request.ContentType.Split(';').FirstOrDefault();
await _providerManager.SaveImage(item, Request.Body, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false); await _providerManager.SaveImage(item, Request.Body, mimeType, imageType, null, CancellationToken.None).ConfigureAwait(false);
item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
return NoContent(); return NoContent();
} }
@ -237,7 +237,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.RequiresElevation)] [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult UpdateItemImageIndex( public async Task<ActionResult> UpdateItemImageIndex(
[FromRoute] Guid itemId, [FromRoute] Guid itemId,
[FromRoute] ImageType imageType, [FromRoute] ImageType imageType,
[FromRoute] int imageIndex, [FromRoute] int imageIndex,
@ -249,7 +249,7 @@ namespace Jellyfin.Api.Controllers
return NotFound(); return NotFound();
} }
item.SwapImages(imageType, imageIndex, newIndex); await item.SwapImagesAsync(imageType, imageIndex, newIndex).ConfigureAwait(false);
return NoContent(); return NoContent();
} }
@ -264,7 +264,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.DefaultAuthorization)] [Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<IEnumerable<ImageInfo>> GetItemImageInfos([FromRoute] Guid itemId) public async Task<ActionResult<IEnumerable<ImageInfo>>> GetItemImageInfos([FromRoute] Guid itemId)
{ {
var item = _libraryManager.GetItemById(itemId); var item = _libraryManager.GetItemById(itemId);
if (item == null) if (item == null)
@ -281,7 +281,7 @@ namespace Jellyfin.Api.Controllers
return list; return list;
} }
_libraryManager.UpdateImages(item); // this makes sure dimensions and hashes are correct await _libraryManager.UpdateImagesAsync(item).ConfigureAwait(false); // this makes sure dimensions and hashes are correct
foreach (var image in itemImages) foreach (var image in itemImages)
{ {
@ -1281,9 +1281,9 @@ namespace Jellyfin.Api.Controllers
Response.Headers.Add(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", new CultureInfo("en-US", false))); Response.Headers.Add(HeaderNames.LastModified, dateImageModified.ToUniversalTime().ToString("ddd, dd MMM yyyy HH:mm:ss \"GMT\"", new CultureInfo("en-US", false)));
// if the image was not modified since "ifModifiedSinceHeader"-header, return a HTTP status code 304 not modified // if the image was not modified since "ifModifiedSinceHeader"-header, return a HTTP status code 304 not modified
if (!(dateImageModified > ifModifiedSinceHeader)) if (!(dateImageModified > ifModifiedSinceHeader) && cacheDuration.HasValue)
{ {
if (ifModifiedSinceHeader.Add(cacheDuration!.Value) < DateTime.UtcNow) if (ifModifiedSinceHeader.Add(cacheDuration.Value) < DateTime.UtcNow)
{ {
Response.StatusCode = StatusCodes.Status304NotModified; Response.StatusCode = StatusCodes.Status304NotModified;
return new ContentResult(); return new ContentResult();

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
@ -67,7 +68,7 @@ namespace Jellyfin.Api.Controllers
[HttpPost("Items/{itemId}")] [HttpPost("Items/{itemId}")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request) public async Task<ActionResult> UpdateItem([FromRoute] Guid itemId, [FromBody, Required] BaseItemDto request)
{ {
var item = _libraryManager.GetItemById(itemId); var item = _libraryManager.GetItemById(itemId);
if (item == null) if (item == null)
@ -101,7 +102,7 @@ namespace Jellyfin.Api.Controllers
item.OnMetadataChanged(); item.OnMetadataChanged();
item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
if (isLockedChanged && item.IsFolder) if (isLockedChanged && item.IsFolder)
{ {
@ -110,7 +111,7 @@ namespace Jellyfin.Api.Controllers
foreach (var child in folder.GetRecursiveChildren()) foreach (var child in folder.GetRecursiveChildren())
{ {
child.IsLocked = newLockData; child.IsLocked = newLockData;
child.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await child.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
} }
} }

View File

@ -4,10 +4,10 @@ using System.Linq;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions; using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers; using Jellyfin.Api.Helpers;
using Jellyfin.Data.Entities;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
@ -266,7 +266,9 @@ namespace Jellyfin.Api.Controllers
bool isInEnabledFolder = user!.GetPreference(PreferenceKind.EnabledFolders).Any(i => new Guid(i) == item.Id) bool isInEnabledFolder = user!.GetPreference(PreferenceKind.EnabledFolders).Any(i => new Guid(i) == item.Id)
// Assume all folders inside an EnabledChannel are enabled // Assume all folders inside an EnabledChannel are enabled
|| user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id); || user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.Id)
// Assume all items inside an EnabledChannel are enabled
|| user.GetPreference(PreferenceKind.EnabledChannels).Any(i => new Guid(i) == item.ChannelId);
var collectionFolders = _libraryManager.GetCollectionFolders(item); var collectionFolders = _libraryManager.GetCollectionFolders(item);
foreach (var collectionFolder in collectionFolders) foreach (var collectionFolder in collectionFolders)

View File

@ -19,6 +19,7 @@ using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
@ -35,8 +36,6 @@ using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using Book = MediaBrowser.Controller.Entities.Book; using Book = MediaBrowser.Controller.Entities.Book;
using Movie = Jellyfin.Data.Entities.Movie;
using MusicAlbum = Jellyfin.Data.Entities.MusicAlbum;
namespace Jellyfin.Api.Controllers namespace Jellyfin.Api.Controllers
{ {
@ -619,7 +618,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.Download)] [Authorize(Policy = Policies.Download)]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult GetDownload([FromRoute] Guid itemId) public async Task<ActionResult> GetDownload([FromRoute] Guid itemId)
{ {
var item = _libraryManager.GetItemById(itemId); var item = _libraryManager.GetItemById(itemId);
if (item == null) if (item == null)
@ -648,7 +647,7 @@ namespace Jellyfin.Api.Controllers
if (user != null) if (user != null)
{ {
LogDownload(item, user, auth); await LogDownloadAsync(item, user, auth).ConfigureAwait(false);
} }
var path = item.Path; var path = item.Path;
@ -861,17 +860,17 @@ namespace Jellyfin.Api.Controllers
: item; : item;
} }
private void LogDownload(BaseItem item, User user, AuthorizationInfo auth) private async Task LogDownloadAsync(BaseItem item, User user, AuthorizationInfo auth)
{ {
try try
{ {
_activityManager.Create(new ActivityLog( await _activityManager.CreateAsync(new ActivityLog(
string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name), string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Username, item.Name),
"UserDownloadingContent", "UserDownloadingContent",
auth.UserId) auth.UserId)
{ {
ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device), ShortOverview = string.Format(CultureInfo.InvariantCulture, _localization.GetLocalizedString("AppDeviceValues"), auth.Client, auth.Device),
}); }).ConfigureAwait(false);
} }
catch catch
{ {

View File

@ -76,7 +76,7 @@ namespace Jellyfin.Api.Controllers
[FromQuery] string? name, [FromQuery] string? name,
[FromQuery] string? collectionType, [FromQuery] string? collectionType,
[FromQuery] string[] paths, [FromQuery] string[] paths,
[FromBody] LibraryOptionsDto? libraryOptionsDto, [FromBody] AddVirtualFolderDto? libraryOptionsDto,
[FromQuery] bool refreshLibrary = false) [FromQuery] bool refreshLibrary = false)
{ {
var libraryOptions = libraryOptionsDto?.LibraryOptions ?? new LibraryOptions(); var libraryOptions = libraryOptionsDto?.LibraryOptions ?? new LibraryOptions();
@ -312,19 +312,17 @@ namespace Jellyfin.Api.Controllers
/// <summary> /// <summary>
/// Update library options. /// Update library options.
/// </summary> /// </summary>
/// <param name="id">The library name.</param> /// <param name="request">The library name and options.</param>
/// <param name="libraryOptions">The library options.</param>
/// <response code="204">Library updated.</response> /// <response code="204">Library updated.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns> /// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("LibraryOptions")] [HttpPost("LibraryOptions")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult UpdateLibraryOptions( public ActionResult UpdateLibraryOptions(
[FromQuery] string? id, [FromBody] UpdateLibraryOptionsDto request)
[FromBody] LibraryOptions? libraryOptions)
{ {
var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(id); var collectionFolder = (CollectionFolder)_libraryManager.GetItemById(request.Id);
collectionFolder.UpdateLibraryOptions(libraryOptions); collectionFolder.UpdateLibraryOptions(request.LibraryOptions);
return NoContent(); return NoContent();
} }
} }

View File

@ -269,9 +269,9 @@ namespace Jellyfin.Api.Controllers
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns> /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("LiveStreams/Close")] [HttpPost("LiveStreams/Close")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult CloseLiveStream([FromQuery, Required] string? liveStreamId) public async Task<ActionResult> CloseLiveStream([FromQuery, Required] string? liveStreamId)
{ {
_mediaSourceManager.CloseLiveStream(liveStreamId).GetAwaiter().GetResult(); await _mediaSourceManager.CloseLiveStream(liveStreamId).ConfigureAwait(false);
return NoContent(); return NoContent();
} }

View File

@ -10,6 +10,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Model.Dto; using MediaBrowser.Model.Dto;
@ -181,7 +182,7 @@ namespace Jellyfin.Api.Controllers
DtoOptions dtoOptions, DtoOptions dtoOptions,
RecommendationType type) RecommendationType type)
{ {
var itemTypes = new List<string> { nameof(MediaBrowser.Controller.Entities.Movies.Movie) }; var itemTypes = new List<string> { nameof(Movie) };
if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions) if (_serverConfigurationManager.Configuration.EnableExternalContentInSuggestions)
{ {
itemTypes.Add(nameof(Trailer)); itemTypes.Add(nameof(Trailer));

View File

@ -83,12 +83,12 @@ namespace Jellyfin.Api.Controllers
/// <returns>An <see cref="NoContentResult"/> on success.</returns> /// <returns>An <see cref="NoContentResult"/> on success.</returns>
[HttpPost("{playlistId}/Items")] [HttpPost("{playlistId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult AddToPlaylist( public async Task<ActionResult> AddToPlaylist(
[FromRoute] string? playlistId, [FromRoute] Guid playlistId,
[FromQuery] string? ids, [FromQuery] string? ids,
[FromQuery] Guid? userId) [FromQuery] Guid? userId)
{ {
_playlistManager.AddToPlaylist(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty); await _playlistManager.AddToPlaylistAsync(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty).ConfigureAwait(false);
return NoContent(); return NoContent();
} }
@ -102,12 +102,12 @@ namespace Jellyfin.Api.Controllers
/// <returns>An <see cref="NoContentResult"/> on success.</returns> /// <returns>An <see cref="NoContentResult"/> on success.</returns>
[HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")] [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult MoveItem( public async Task<ActionResult> MoveItem(
[FromRoute] string? playlistId, [FromRoute] string? playlistId,
[FromRoute] string? itemId, [FromRoute] string? itemId,
[FromRoute] int newIndex) [FromRoute] int newIndex)
{ {
_playlistManager.MoveItem(playlistId, itemId, newIndex); await _playlistManager.MoveItemAsync(playlistId, itemId, newIndex).ConfigureAwait(false);
return NoContent(); return NoContent();
} }
@ -120,9 +120,9 @@ namespace Jellyfin.Api.Controllers
/// <returns>An <see cref="NoContentResult"/> on success.</returns> /// <returns>An <see cref="NoContentResult"/> on success.</returns>
[HttpDelete("{playlistId}/Items")] [HttpDelete("{playlistId}/Items")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds) public async Task<ActionResult> RemoveFromPlaylist([FromRoute] string? playlistId, [FromQuery] string? entryIds)
{ {
_playlistManager.RemoveFromPlaylist(playlistId, RequestHelpers.Split(entryIds, ',', true)); await _playlistManager.RemoveFromPlaylistAsync(playlistId, RequestHelpers.Split(entryIds, ',', true)).ConfigureAwait(false);
return NoContent(); return NoContent();
} }

View File

@ -120,10 +120,14 @@ namespace Jellyfin.Api.Controllers
return NotFound(); return NotFound();
} }
var configuration = (BasePluginConfiguration)await JsonSerializer.DeserializeAsync(Request.Body, plugin.ConfigurationType, _serializerOptions) var configuration = (BasePluginConfiguration?)await JsonSerializer.DeserializeAsync(Request.Body, plugin.ConfigurationType, _serializerOptions)
.ConfigureAwait(false); .ConfigureAwait(false);
plugin.UpdateConfiguration(configuration); if (configuration != null)
{
plugin.UpdateConfiguration(configuration);
}
return NoContent(); return NoContent();
} }

View File

@ -221,7 +221,7 @@ namespace Jellyfin.Api.Controllers
await _providerManager.SaveImage(item, imageUrl, type, null, CancellationToken.None) await _providerManager.SaveImage(item, imageUrl, type, null, CancellationToken.None)
.ConfigureAwait(false); .ConfigureAwait(false);
item.UpdateToRepository(ItemUpdateType.ImageUpdate, CancellationToken.None); await item.UpdateToRepositoryAsync(ItemUpdateType.ImageUpdate, CancellationToken.None).ConfigureAwait(false);
return NoContent(); return NoContent();
} }

View File

@ -153,7 +153,6 @@ namespace Jellyfin.Api.Controllers
/// <param name="itemIds">The ids of the items to play, comma delimited.</param> /// <param name="itemIds">The ids of the items to play, comma delimited.</param>
/// <param name="startPositionTicks">The starting position of the first item.</param> /// <param name="startPositionTicks">The starting position of the first item.</param>
/// <param name="playCommand">The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.</param> /// <param name="playCommand">The type of play command to issue (PlayNow, PlayNext, PlayLast). Clients who have not yet implemented play next and play last may play now.</param>
/// <param name="playRequest">The <see cref="PlayRequest"/>.</param>
/// <response code="204">Instruction sent to session.</response> /// <response code="204">Instruction sent to session.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns> /// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Sessions/{sessionId}/Playing")] [HttpPost("Sessions/{sessionId}/Playing")]
@ -163,17 +162,14 @@ namespace Jellyfin.Api.Controllers
[FromRoute, Required] string? sessionId, [FromRoute, Required] string? sessionId,
[FromQuery] Guid[] itemIds, [FromQuery] Guid[] itemIds,
[FromQuery] long? startPositionTicks, [FromQuery] long? startPositionTicks,
[FromQuery] PlayCommand playCommand, [FromQuery] PlayCommand playCommand)
[FromBody, Required] PlayRequest playRequest)
{ {
if (playRequest == null) var playRequest = new PlayRequest
{ {
throw new ArgumentException("Request Body may not be null"); ItemIds = itemIds,
} StartPositionTicks = startPositionTicks,
PlayCommand = playCommand
playRequest.ItemIds = itemIds; };
playRequest.StartPositionTicks = startPositionTicks;
playRequest.PlayCommand = playCommand;
_sessionManager.SendPlayCommand( _sessionManager.SendPlayCommand(
RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id, RequestHelpers.GetSession(_sessionManager, _authContext, Request).Id,

View File

@ -1,3 +1,4 @@
using System.ComponentModel.DataAnnotations;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Api.Constants; using Jellyfin.Api.Constants;
@ -64,21 +65,16 @@ namespace Jellyfin.Api.Controllers
/// <summary> /// <summary>
/// Sets the initial startup wizard configuration. /// Sets the initial startup wizard configuration.
/// </summary> /// </summary>
/// <param name="uiCulture">The UI language culture.</param> /// <param name="startupConfiguration">The updated startup configuration.</param>
/// <param name="metadataCountryCode">The metadata country code.</param>
/// <param name="preferredMetadataLanguage">The preferred language for metadata.</param>
/// <response code="204">Configuration saved.</response> /// <response code="204">Configuration saved.</response>
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns> /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("Configuration")] [HttpPost("Configuration")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult UpdateInitialConfiguration( public ActionResult UpdateInitialConfiguration([FromBody, Required] StartupConfigurationDto startupConfiguration)
[FromForm] string? uiCulture,
[FromForm] string? metadataCountryCode,
[FromForm] string? preferredMetadataLanguage)
{ {
_config.Configuration.UICulture = uiCulture; _config.Configuration.UICulture = startupConfiguration.UICulture;
_config.Configuration.MetadataCountryCode = metadataCountryCode; _config.Configuration.MetadataCountryCode = startupConfiguration.MetadataCountryCode;
_config.Configuration.PreferredMetadataLanguage = preferredMetadataLanguage; _config.Configuration.PreferredMetadataLanguage = startupConfiguration.PreferredMetadataLanguage;
_config.SaveConfiguration(); _config.SaveConfiguration();
return NoContent(); return NoContent();
} }
@ -86,16 +82,15 @@ namespace Jellyfin.Api.Controllers
/// <summary> /// <summary>
/// Sets remote access and UPnP. /// Sets remote access and UPnP.
/// </summary> /// </summary>
/// <param name="enableRemoteAccess">Enable remote access.</param> /// <param name="startupRemoteAccessDto">The startup remote access dto.</param>
/// <param name="enableAutomaticPortMapping">Enable UPnP.</param>
/// <response code="204">Configuration saved.</response> /// <response code="204">Configuration saved.</response>
/// <returns>A <see cref="NoContentResult"/> indicating success.</returns> /// <returns>A <see cref="NoContentResult"/> indicating success.</returns>
[HttpPost("RemoteAccess")] [HttpPost("RemoteAccess")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult SetRemoteAccess([FromForm] bool enableRemoteAccess, [FromForm] bool enableAutomaticPortMapping) public ActionResult SetRemoteAccess([FromBody, Required] StartupRemoteAccessDto startupRemoteAccessDto)
{ {
_config.Configuration.EnableRemoteAccess = enableRemoteAccess; _config.Configuration.EnableRemoteAccess = startupRemoteAccessDto.EnableRemoteAccess;
_config.Configuration.EnableUPnP = enableAutomaticPortMapping; _config.Configuration.EnableUPnP = startupRemoteAccessDto.EnableAutomaticPortMapping;
_config.SaveConfiguration(); _config.SaveConfiguration();
return NoContent(); return NoContent();
} }
@ -131,7 +126,7 @@ namespace Jellyfin.Api.Controllers
/// </returns> /// </returns>
[HttpPost("User")] [HttpPost("User")]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> UpdateStartupUser([FromForm] StartupUserDto startupUserDto) public async Task<ActionResult> UpdateStartupUser([FromBody] StartupUserDto startupUserDto)
{ {
var user = _userManager.Users.First(); var user = _userManager.Users.First();

View File

@ -161,7 +161,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.RequiresElevation)] [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult DeleteAlternateSources([FromRoute] Guid itemId) public async Task<ActionResult> DeleteAlternateSources([FromRoute] Guid itemId)
{ {
var video = (Video)_libraryManager.GetItemById(itemId); var video = (Video)_libraryManager.GetItemById(itemId);
@ -180,12 +180,12 @@ namespace Jellyfin.Api.Controllers
link.SetPrimaryVersionId(null); link.SetPrimaryVersionId(null);
link.LinkedAlternateVersions = Array.Empty<LinkedChild>(); link.LinkedAlternateVersions = Array.Empty<LinkedChild>();
link.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await link.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
} }
video.LinkedAlternateVersions = Array.Empty<LinkedChild>(); video.LinkedAlternateVersions = Array.Empty<LinkedChild>();
video.SetPrimaryVersionId(null); video.SetPrimaryVersionId(null);
video.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await video.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
return NoContent(); return NoContent();
} }
@ -201,7 +201,7 @@ namespace Jellyfin.Api.Controllers
[Authorize(Policy = Policies.RequiresElevation)] [Authorize(Policy = Policies.RequiresElevation)]
[ProducesResponseType(StatusCodes.Status204NoContent)] [ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
public ActionResult MergeVersions([FromQuery, Required] string? itemIds) public async Task<ActionResult> MergeVersions([FromQuery, Required] string? itemIds)
{ {
var items = RequestHelpers.Split(itemIds, ',', true) var items = RequestHelpers.Split(itemIds, ',', true)
.Select(i => _libraryManager.GetItemById(i)) .Select(i => _libraryManager.GetItemById(i))
@ -239,7 +239,7 @@ namespace Jellyfin.Api.Controllers
{ {
item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture)); item.SetPrimaryVersionId(primaryVersion.Id.ToString("N", CultureInfo.InvariantCulture));
item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
list.Add(new LinkedChild list.Add(new LinkedChild
{ {
@ -258,12 +258,12 @@ namespace Jellyfin.Api.Controllers
if (item.LinkedAlternateVersions.Length > 0) if (item.LinkedAlternateVersions.Length > 0)
{ {
item.LinkedAlternateVersions = Array.Empty<LinkedChild>(); item.LinkedAlternateVersions = Array.Empty<LinkedChild>();
item.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await item.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
} }
} }
primaryVersion.LinkedAlternateVersions = list.ToArray(); primaryVersion.LinkedAlternateVersions = list.ToArray();
primaryVersion.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None); await primaryVersion.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
return NoContent(); return NoContent();
} }

View File

@ -127,7 +127,11 @@ namespace Jellyfin.Api.Helpers
{ {
// Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it // Since we're going to be setting properties on MediaSourceInfos that come out of _mediaSourceManager, we should clone it
// Should we move this directly into MediaSourceManager? // Should we move this directly into MediaSourceManager?
result.MediaSources = JsonSerializer.Deserialize<MediaSourceInfo[]>(JsonSerializer.SerializeToUtf8Bytes(mediaSources)); var mediaSourcesClone = JsonSerializer.Deserialize<MediaSourceInfo[]>(JsonSerializer.SerializeToUtf8Bytes(mediaSources));
if (mediaSourcesClone != null)
{
result.MediaSources = mediaSourcesClone;
}
result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture); result.PlaySessionId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);
} }

View File

@ -3,9 +3,9 @@
namespace Jellyfin.Api.Models.LibraryStructureDto namespace Jellyfin.Api.Models.LibraryStructureDto
{ {
/// <summary> /// <summary>
/// Library options dto. /// Add virtual folder dto.
/// </summary> /// </summary>
public class LibraryOptionsDto public class AddVirtualFolderDto
{ {
/// <summary> /// <summary>
/// Gets or sets library options. /// Gets or sets library options.

View File

@ -0,0 +1,21 @@
using System;
using MediaBrowser.Model.Configuration;
namespace Jellyfin.Api.Models.LibraryStructureDto
{
/// <summary>
/// Update library options dto.
/// </summary>
public class UpdateLibraryOptionsDto
{
/// <summary>
/// Gets or sets the library item id.
/// </summary>
public Guid Id { get; set; }
/// <summary>
/// Gets or sets library options.
/// </summary>
public LibraryOptions? LibraryOptions { get; set; }
}
}

View File

@ -0,0 +1,22 @@
using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Api.Models.StartupDtos
{
/// <summary>
/// Startup remote access dto.
/// </summary>
public class StartupRemoteAccessDto
{
/// <summary>
/// Gets or sets a value indicating whether enable remote access.
/// </summary>
[Required]
public bool EnableRemoteAccess { get; set; }
/// <summary>
/// Gets or sets a value indicating whether enable automatic port mapping.
/// </summary>
[Required]
public bool EnableAutomaticPortMapping { get; set; }
}
}

View File

@ -1,8 +1,8 @@
using System; using System;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Activity; using MediaBrowser.Model.Activity;
using MediaBrowser.Model.Events;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Jellyfin.Api.WebSocketListeners namespace Jellyfin.Api.WebSocketListeners

View File

@ -1,8 +1,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Jellyfin.Data.Events;
using MediaBrowser.Controller.Net; using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Events;
using MediaBrowser.Model.Tasks; using MediaBrowser.Model.Tasks;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;

View File

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;

View File

@ -1,6 +1,9 @@
#pragma warning disable CS1591
using System; using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace Jellyfin.Data.Entities namespace Jellyfin.Data.Entities
@ -8,7 +11,7 @@ namespace Jellyfin.Data.Entities
/// <summary> /// <summary>
/// An entity referencing an activity log entry. /// An entity referencing an activity log entry.
/// </summary> /// </summary>
public partial class ActivityLog : ISavingChanges public partial class ActivityLog : IHasConcurrencyToken
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ActivityLog"/> class. /// Initializes a new instance of the <see cref="ActivityLog"/> class.

View File

@ -1,208 +0,0 @@
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
*************************************************************************/
}
}

View File

@ -1,68 +0,0 @@
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; }
}
}

View File

@ -1,119 +0,0 @@
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; }
}
}

View File

@ -1,275 +0,0 @@
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
*************************************************************************/
}
}

View File

@ -1,121 +0,0 @@
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; }
}
}

View File

@ -1,154 +0,0 @@
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; }
}
}

View File

@ -1,157 +0,0 @@
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; }
}
}

View File

@ -1,230 +0,0 @@
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
*************************************************************************/
}
}

View File

@ -1,67 +0,0 @@
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; }
}
}

View File

@ -1,77 +0,0 @@
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
*************************************************************************/
}
}

View File

@ -1,114 +0,0 @@
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; }
}
}

View File

@ -1,192 +0,0 @@
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
*************************************************************************/
}
}

View File

@ -1,160 +0,0 @@
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
*************************************************************************/
}
}

View File

@ -1,16 +1,19 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using System.Linq; using System.Linq;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities namespace Jellyfin.Data.Entities
{ {
/// <summary> /// <summary>
/// An entity representing a group. /// An entity representing a group.
/// </summary> /// </summary>
public partial class Group : IHasPermissions, ISavingChanges public partial class Group : IHasPermissions, IHasConcurrencyToken
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="Group"/> class. /// Initializes a new instance of the <see cref="Group"/> class.

View File

@ -1,4 +1,6 @@
using System; #pragma warning disable CS1591
using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;

View File

@ -1,4 +1,6 @@
using System; #pragma warning disable CS1591
using System;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema; using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums; using Jellyfin.Data.Enums;

View File

@ -0,0 +1,81 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing artwork.
/// </summary>
public class Artwork : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Artwork"/> class.
/// </summary>
/// <param name="path">The path.</param>
/// <param name="kind">The kind of art.</param>
/// <param name="owner">The owner.</param>
public Artwork(string path, ArtKind kind, IHasArtwork owner)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
}
Path = path;
Kind = kind;
owner?.Artwork.Add(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="Artwork"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected Artwork()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the path.
/// </summary>
/// <remarks>
/// Required, Max length = 65535.
/// </remarks>
[Required]
[MaxLength(65535)]
[StringLength(65535)]
public string Path { get; set; }
/// <summary>
/// Gets or sets the kind of artwork.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
public ArtKind Kind { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a book.
/// </summary>
public class Book : LibraryItem, IHasReleases
{
/// <summary>
/// Initializes a new instance of the <see cref="Book"/> class.
/// </summary>
public Book()
{
BookMetadata = new HashSet<BookMetadata>();
Releases = new HashSet<Release>();
}
/// <summary>
/// Gets or sets a collection containing the metadata for this book.
/// </summary>
public virtual ICollection<BookMetadata> BookMetadata { get; protected set; }
/// <inheritdoc />
public virtual ICollection<Release> Releases { get; protected set; }
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity containing metadata for a book.
/// </summary>
public class BookMetadata : Metadata, IHasCompanies
{
/// <summary>
/// Initializes a new instance of the <see cref="BookMetadata"/> class.
/// </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="book">The book.</param>
public BookMetadata(string title, string language, Book book) : base(title, language)
{
if (book == null)
{
throw new ArgumentNullException(nameof(book));
}
book.BookMetadata.Add(this);
Publishers = new HashSet<Company>();
}
/// <summary>
/// Initializes a new instance of the <see cref="BookMetadata"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected BookMetadata()
{
}
/// <summary>
/// Gets or sets the ISBN.
/// </summary>
public long? Isbn { get; set; }
/// <summary>
/// Gets or sets a collection of the publishers for this book.
/// </summary>
public virtual ICollection<Company> Publishers { get; protected set; }
/// <inheritdoc />
[NotMapped]
public ICollection<Company> Companies => Publishers;
}
}

View File

@ -0,0 +1,102 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a chapter.
/// </summary>
public class Chapter : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Chapter"/> class.
/// </summary>
/// <param name="language">ISO-639-3 3-character language codes.</param>
/// <param name="startTime">The start time for this chapter.</param>
/// <param name="release">The release.</param>
public Chapter(string language, long startTime, Release release)
{
if (string.IsNullOrEmpty(language))
{
throw new ArgumentNullException(nameof(language));
}
Language = language;
StartTime = startTime;
if (release == null)
{
throw new ArgumentNullException(nameof(release));
}
release.Chapters.Add(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="Chapter"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected Chapter()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <remarks>
/// Max length = 1024.
/// </remarks>
[MaxLength(1024)]
[StringLength(1024)]
public string Name { get; set; }
/// <summary>
/// Gets or sets the language.
/// </summary>
/// <remarks>
/// Required, Min length = 3, Max length = 3
/// ISO-639-3 3-character language codes.
/// </remarks>
[Required]
[MinLength(3)]
[MaxLength(3)]
[StringLength(3)]
public string Language { get; set; }
/// <summary>
/// Gets or sets the start time.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
public long StartTime { get; set; }
/// <summary>
/// Gets or sets the end time.
/// </summary>
public long? EndTime { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; protected set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,55 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a collection.
/// </summary>
public class Collection : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Collection"/> class.
/// </summary>
public Collection()
{
Items = new HashSet<CollectionItem>();
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <remarks>
/// Max length = 1024.
/// </remarks>
[MaxLength(1024)]
[StringLength(1024)]
public string Name { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <summary>
/// Gets or sets a collection containing this collection's items.
/// </summary>
public virtual ICollection<CollectionItem> Items { get; protected set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,94 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a collection item.
/// </summary>
public class CollectionItem : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="CollectionItem"/> class.
/// </summary>
/// <param name="collection">The collection.</param>
/// <param name="previous">The previous item.</param>
/// <param name="next">The next item.</param>
public CollectionItem(Collection collection, CollectionItem previous, CollectionItem next)
{
if (collection == null)
{
throw new ArgumentNullException(nameof(collection));
}
collection.Items.Add(this);
if (next != null)
{
Next = next;
next.Previous = this;
}
if (previous != null)
{
Previous = previous;
previous.Next = this;
}
}
/// <summary>
/// Initializes a new instance of the <see cref="CollectionItem"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected CollectionItem()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <summary>
/// Gets or sets the library item.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
public virtual LibraryItem LibraryItem { get; set; }
/// <summary>
/// Gets or sets the next item in the collection.
/// </summary>
/// <remarks>
/// TODO check if this properly updated dependant and has the proper principal relationship
/// </remarks>
public virtual CollectionItem Next { get; set; }
/// <summary>
/// Gets or sets the previous item in the collection.
/// </summary>
/// <remarks>
/// TODO check if this properly updated dependant and has the proper principal relationship
/// </remarks>
public virtual CollectionItem Previous { get; set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a company.
/// </summary>
public class Company : IHasCompanies, IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Company"/> class.
/// </summary>
/// <param name="owner">The owner of this company.</param>
public Company(IHasCompanies owner)
{
owner?.Companies.Add(this);
CompanyMetadata = new HashSet<CompanyMetadata>();
}
/// <summary>
/// Initializes a new instance of the <see cref="Company"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected Company()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <summary>
/// Gets or sets a collection containing the metadata.
/// </summary>
public virtual ICollection<CompanyMetadata> CompanyMetadata { get; protected set; }
/// <summary>
/// Gets or sets a collection containing this company's child companies.
/// </summary>
public virtual ICollection<Company> ChildCompanies { get; protected set; }
/// <inheritdoc />
[NotMapped]
public ICollection<Company> Companies => ChildCompanies;
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,74 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity holding metadata for a <see cref="Company"/>.
/// </summary>
public class CompanyMetadata : Metadata
{
/// <summary>
/// Initializes a new instance of the <see cref="CompanyMetadata"/> class.
/// </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="company">The company.</param>
public CompanyMetadata(string title, string language, Company company) : base(title, language)
{
if (company == null)
{
throw new ArgumentNullException(nameof(company));
}
company.CompanyMetadata.Add(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="CompanyMetadata"/> class.
/// </summary>
protected CompanyMetadata()
{
}
/// <summary>
/// Gets or sets the description.
/// </summary>
/// <remarks>
/// Max length = 65535.
/// </remarks>
[MaxLength(65535)]
[StringLength(65535)]
public string Description { get; set; }
/// <summary>
/// Gets or sets the headquarters.
/// </summary>
/// <remarks>
/// Max length = 255.
/// </remarks>
[MaxLength(255)]
[StringLength(255)]
public string Headquarters { get; set; }
/// <summary>
/// Gets or sets the country code.
/// </summary>
/// <remarks>
/// Max length = 2.
/// </remarks>
[MaxLength(2)]
[StringLength(2)]
public string Country { get; set; }
/// <summary>
/// Gets or sets the homepage.
/// </summary>
/// <remarks>
/// Max length = 1024.
/// </remarks>
[MaxLength(1024)]
[StringLength(1024)]
public string Homepage { get; set; }
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a custom item.
/// </summary>
public class CustomItem : LibraryItem, IHasReleases
{
/// <summary>
/// Initializes a new instance of the <see cref="CustomItem"/> class.
/// </summary>
public CustomItem()
{
CustomItemMetadata = new HashSet<CustomItemMetadata>();
Releases = new HashSet<Release>();
}
/// <summary>
/// Gets or sets a collection containing the metadata for this item.
/// </summary>
public virtual ICollection<CustomItemMetadata> CustomItemMetadata { get; protected set; }
/// <inheritdoc />
public virtual ICollection<Release> Releases { get; protected set; }
}
}

View File

@ -0,0 +1,36 @@
using System;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity containing metadata for a custom item.
/// </summary>
public class CustomItemMetadata : Metadata
{
/// <summary>
/// Initializes a new instance of the <see cref="CustomItemMetadata"/> class.
/// </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="item">The item.</param>
public CustomItemMetadata(string title, string language, CustomItem item) : base(title, language)
{
if (item == null)
{
throw new ArgumentNullException(nameof(item));
}
item.CustomItemMetadata.Add(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomItemMetadata"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected CustomItemMetadata()
{
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing an episode.
/// </summary>
public class Episode : LibraryItem, IHasReleases
{
/// <summary>
/// Initializes a new instance of the <see cref="Episode"/> class.
/// </summary>
/// <param name="season">The season.</param>
public Episode(Season season)
{
if (season == null)
{
throw new ArgumentNullException(nameof(season));
}
season.Episodes.Add(this);
Releases = new HashSet<Release>();
EpisodeMetadata = new HashSet<EpisodeMetadata>();
}
/// <summary>
/// Initializes a new instance of the <see cref="Episode"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected Episode()
{
}
/// <summary>
/// Gets or sets the episode number.
/// </summary>
public int? EpisodeNumber { get; set; }
/// <inheritdoc />
public virtual ICollection<Release> Releases { get; protected set; }
/// <summary>
/// Gets or sets a collection containing the metadata for this episode.
/// </summary>
public virtual ICollection<EpisodeMetadata> EpisodeMetadata { get; protected set; }
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.ComponentModel.DataAnnotations;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity containing metadata for an <see cref="Episode"/>.
/// </summary>
public class EpisodeMetadata : Metadata
{
/// <summary>
/// Initializes a new instance of the <see cref="EpisodeMetadata"/> class.
/// </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="episode">The episode.</param>
public EpisodeMetadata(string title, string language, Episode episode) : base(title, language)
{
if (episode == null)
{
throw new ArgumentNullException(nameof(episode));
}
episode.EpisodeMetadata.Add(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="EpisodeMetadata"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected EpisodeMetadata()
{
}
/// <summary>
/// Gets or sets the outline.
/// </summary>
/// <remarks>
/// Max length = 1024.
/// </remarks>
[MaxLength(1024)]
[StringLength(1024)]
public string Outline { get; set; }
/// <summary>
/// Gets or sets the plot.
/// </summary>
/// <remarks>
/// Max length = 65535.
/// </remarks>
[MaxLength(65535)]
[StringLength(65535)]
public string Plot { get; set; }
/// <summary>
/// Gets or sets the tagline.
/// </summary>
/// <remarks>
/// Max length = 1024.
/// </remarks>
[MaxLength(1024)]
[StringLength(1024)]
public string Tagline { get; set; }
}
}

View File

@ -0,0 +1,75 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a genre.
/// </summary>
public class Genre : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Genre"/> class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="metadata">The metadata.</param>
public Genre(string name, Metadata metadata)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException(nameof(name));
}
Name = name;
if (metadata == null)
{
throw new ArgumentNullException(nameof(metadata));
}
metadata.Genres.Add(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="Genre"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected Genre()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <remarks>
/// Indexed, Required, Max length = 255.
/// </remarks>
[Required]
[MaxLength(255)]
[StringLength(255)]
public string Name { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; protected set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,76 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a library.
/// </summary>
public class Library : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Library"/> class.
/// </summary>
/// <param name="name">The name of the library.</param>
public Library(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException(nameof(name));
}
Name = name;
}
/// <summary>
/// Initializes a new instance of the <see cref="Library"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected Library()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <remarks>
/// Required, Max length = 128.
/// </remarks>
[Required]
[MaxLength(128)]
[StringLength(128)]
public string Name { get; set; }
/// <summary>
/// Gets or sets the root path of the library.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
[Required]
public string Path { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,63 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a library item.
/// </summary>
public abstract class LibraryItem : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="LibraryItem"/> class.
/// </summary>
/// <param name="library">The library of this item.</param>
protected LibraryItem(Library library)
{
DateAdded = DateTime.UtcNow;
Library = library;
}
/// <summary>
/// Initializes a new instance of the <see cref="LibraryItem"/> class.
/// </summary>
protected LibraryItem()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the date this library item was added.
/// </summary>
public DateTime DateAdded { get; protected set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; protected set; }
/// <summary>
/// Gets or sets the library of this item.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
[Required]
public virtual Library Library { get; set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Enums;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a file on disk.
/// </summary>
public class MediaFile : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="MediaFile"/> class.
/// </summary>
/// <param name="path">The path relative to the LibraryRoot.</param>
/// <param name="kind">The file kind.</param>
/// <param name="release">The release.</param>
public MediaFile(string path, MediaFileKind kind, Release release)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentNullException(nameof(path));
}
Path = path;
Kind = kind;
if (release == null)
{
throw new ArgumentNullException(nameof(release));
}
release.MediaFiles.Add(this);
MediaFileStreams = new HashSet<MediaFileStream>();
}
/// <summary>
/// Initializes a new instance of the <see cref="MediaFile"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected MediaFile()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the path relative to the library root.
/// </summary>
/// <remarks>
/// Required, Max length = 65535.
/// </remarks>
[Required]
[MaxLength(65535)]
[StringLength(65535)]
public string Path { get; set; }
/// <summary>
/// Gets or sets the kind of media file.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
public MediaFileKind Kind { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <summary>
/// Gets or sets a collection containing the streams in this file.
/// </summary>
public virtual ICollection<MediaFileStream> MediaFileStreams { get; protected set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a stream in a media file.
/// </summary>
public class MediaFileStream : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="MediaFileStream"/> class.
/// </summary>
/// <param name="streamNumber">The number of this stream.</param>
/// <param name="mediaFile">The media file.</param>
public MediaFileStream(int streamNumber, MediaFile mediaFile)
{
StreamNumber = streamNumber;
if (mediaFile == null)
{
throw new ArgumentNullException(nameof(mediaFile));
}
mediaFile.MediaFileStreams.Add(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="MediaFileStream"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected MediaFileStream()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the stream number.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
public int StreamNumber { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,165 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An abstract class that holds metadata.
/// </summary>
public abstract class Metadata : IHasArtwork, IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="Metadata"/> class.
/// </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)
{
if (string.IsNullOrEmpty(title))
{
throw new ArgumentNullException(nameof(title));
}
if (string.IsNullOrEmpty(language))
{
throw new ArgumentNullException(nameof(language));
}
Title = title;
Language = language;
DateAdded = DateTime.UtcNow;
DateModified = DateAdded;
PersonRoles = new HashSet<PersonRole>();
Genres = new HashSet<Genre>();
Artwork = new HashSet<Artwork>();
Ratings = new HashSet<Rating>();
Sources = new HashSet<MetadataProviderId>();
}
/// <summary>
/// Initializes a new instance of the <see cref="Metadata"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to being abstract.
/// </remarks>
protected Metadata()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the title.
/// </summary>
/// <remarks>
/// Required, Max length = 1024.
/// </remarks>
[Required]
[MaxLength(1024)]
[StringLength(1024)]
public string Title { get; set; }
/// <summary>
/// Gets or sets the original title.
/// </summary>
/// <remarks>
/// Max length = 1024.
/// </remarks>
[MaxLength(1024)]
[StringLength(1024)]
public string OriginalTitle { get; set; }
/// <summary>
/// Gets or sets the sort title.
/// </summary>
/// <remarks>
/// Max length = 1024.
/// </remarks>
[MaxLength(1024)]
[StringLength(1024)]
public string SortTitle { get; set; }
/// <summary>
/// Gets or sets the language.
/// </summary>
/// <remarks>
/// Required, Min length = 3, Max length = 3.
/// ISO-639-3 3-character language codes.
/// </remarks>
[Required]
[MinLength(3)]
[MaxLength(3)]
[StringLength(3)]
public string Language { get; set; }
/// <summary>
/// Gets or sets the release date.
/// </summary>
public DateTimeOffset? ReleaseDate { get; set; }
/// <summary>
/// Gets or sets the date added.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
public DateTime DateAdded { get; protected set; }
/// <summary>
/// Gets or sets the date modified.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
public DateTime DateModified { get; set; }
/// <summary>
/// Gets or sets the row version.
/// </summary>
/// <remarks>
/// Required, ConcurrencyToken.
/// </remarks>
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <summary>
/// Gets or sets a collection containing the person roles for this item.
/// </summary>
public virtual ICollection<PersonRole> PersonRoles { get; protected set; }
/// <summary>
/// Gets or sets a collection containing the generes for this item.
/// </summary>
public virtual ICollection<Genre> Genres { get; protected set; }
/// <inheritdoc />
public virtual ICollection<Artwork> Artwork { get; protected set; }
/// <summary>
/// Gets or sets a collection containing the ratings for this item.
/// </summary>
public virtual ICollection<Rating> Ratings { get; protected set; }
/// <summary>
/// Gets or sets a collection containing the metadata sources for this item.
/// </summary>
public virtual ICollection<MetadataProviderId> Sources { get; protected set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,67 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a metadata provider.
/// </summary>
public class MetadataProvider : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="MetadataProvider"/> class.
/// </summary>
/// <param name="name">The name of the metadata provider.</param>
public MetadataProvider(string name)
{
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException(nameof(name));
}
Name = name;
}
/// <summary>
/// Initializes a new instance of the <see cref="MetadataProvider"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected MetadataProvider()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the name.
/// </summary>
/// <remarks>
/// Required, Max length = 1024.
/// </remarks>
[Required]
[MaxLength(1024)]
[StringLength(1024)]
public string Name { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,83 @@
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a unique identifier for a metadata provider.
/// </summary>
public class MetadataProviderId : IHasConcurrencyToken
{
/// <summary>
/// Initializes a new instance of the <see cref="MetadataProviderId"/> class.
/// </summary>
/// <param name="providerId">The provider id.</param>
/// <param name="metadata">The metadata entity.</param>
public MetadataProviderId(string providerId, Metadata metadata)
{
if (string.IsNullOrEmpty(providerId))
{
throw new ArgumentNullException(nameof(providerId));
}
ProviderId = providerId;
if (metadata == null)
{
throw new ArgumentNullException(nameof(metadata));
}
metadata.Sources.Add(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="MetadataProviderId"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected MetadataProviderId()
{
}
/// <summary>
/// Gets or sets the id.
/// </summary>
/// <remarks>
/// Identity, Indexed, Required.
/// </remarks>
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; protected set; }
/// <summary>
/// Gets or sets the provider id.
/// </summary>
/// <remarks>
/// Required, Max length = 255.
/// </remarks>
[Required]
[MaxLength(255)]
[StringLength(255)]
public string ProviderId { get; set; }
/// <inheritdoc />
[ConcurrencyCheck]
public uint RowVersion { get; set; }
/// <summary>
/// Gets or sets the metadata provider.
/// </summary>
/// <remarks>
/// Required.
/// </remarks>
public virtual MetadataProvider MetadataProvider { get; set; }
/// <inheritdoc />
public void OnSavingChanges()
{
RowVersion++;
}
}
}

View File

@ -0,0 +1,28 @@
using System.Collections.Generic;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a movie.
/// </summary>
public class Movie : LibraryItem, IHasReleases
{
/// <summary>
/// Initializes a new instance of the <see cref="Movie"/> class.
/// </summary>
public Movie()
{
Releases = new HashSet<Release>();
MovieMetadata = new HashSet<MovieMetadata>();
}
/// <inheritdoc />
public virtual ICollection<Release> Releases { get; protected set; }
/// <summary>
/// Gets or sets a collection containing the metadata for this movie.
/// </summary>
public virtual ICollection<MovieMetadata> MovieMetadata { get; protected set; }
}
}

View File

@ -0,0 +1,85 @@
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using Jellyfin.Data.Interfaces;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity holding the metadata for a movie.
/// </summary>
public class MovieMetadata : Metadata, IHasCompanies
{
/// <summary>
/// Initializes a new instance of the <see cref="MovieMetadata"/> class.
/// </summary>
/// <param name="title">The title or name of the movie.</param>
/// <param name="language">ISO-639-3 3-character language codes.</param>
/// <param name="movie">The movie.</param>
public MovieMetadata(string title, string language, Movie movie) : base(title, language)
{
Studios = new HashSet<Company>();
movie.MovieMetadata.Add(this);
}
/// <summary>
/// Initializes a new instance of the <see cref="MovieMetadata"/> class.
/// </summary>
/// <remarks>
/// Default constructor. Protected due to required properties, but present because EF needs it.
/// </remarks>
protected MovieMetadata()
{
}
/// <summary>
/// Gets or sets the outline.
/// </summary>
/// <remarks>
/// Max length = 1024.
/// </remarks>
[MaxLength(1024)]
[StringLength(1024)]
public string Outline { get; set; }
/// <summary>
/// Gets or sets the tagline.
/// </summary>
/// <remarks>
/// Max length = 1024.
/// </remarks>
[MaxLength(1024)]
[StringLength(1024)]
public string Tagline { get; set; }
/// <summary>
/// Gets or sets the plot.
/// </summary>
/// <remarks>
/// Max length = 65535.
/// </remarks>
[MaxLength(65535)]
[StringLength(65535)]
public string Plot { get; set; }
/// <summary>
/// Gets or sets the country code.
/// </summary>
/// <remarks>
/// Max length = 2.
/// </remarks>
[MaxLength(2)]
[StringLength(2)]
public string Country { get; set; }
/// <summary>
/// Gets or sets the studios that produced this movie.
/// </summary>
public virtual ICollection<Company> Studios { get; protected set; }
/// <inheritdoc />
[NotMapped]
public ICollection<Company> Companies => Studios;
}
}

View File

@ -0,0 +1,29 @@
using System.Collections.Generic;
namespace Jellyfin.Data.Entities.Libraries
{
/// <summary>
/// An entity representing a music album.
/// </summary>
public class MusicAlbum : LibraryItem
{
/// <summary>
/// Initializes a new instance of the <see cref="MusicAlbum"/> class.
/// </summary>
public MusicAlbum()
{
MusicAlbumMetadata = new HashSet<MusicAlbumMetadata>();
Tracks = new HashSet<Track>();
}
/// <summary>
/// Gets or sets a collection containing the album metadata.
/// </summary>
public virtual ICollection<MusicAlbumMetadata> MusicAlbumMetadata { get; protected set; }
/// <summary>
/// Gets or sets a collection containing the tracks.
/// </summary>
public virtual ICollection<Track> Tracks { get; protected set; }
}
}

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