mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-04-24 14:08:44 -04:00
commit
b4fdfb562d
30 changed files with 414 additions and 579 deletions
|
@ -932,13 +932,7 @@ namespace Emby.Dlna.Didl
|
||||||
|
|
||||||
private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
|
private void AddCover(BaseItem item, BaseItem context, StubType? stubType, XmlWriter writer)
|
||||||
{
|
{
|
||||||
ImageDownloadInfo imageInfo = null;
|
ImageDownloadInfo imageInfo = GetImageInfo(item);;
|
||||||
|
|
||||||
// Finally, just use the image from the item
|
|
||||||
if (imageInfo == null)
|
|
||||||
{
|
|
||||||
imageInfo = GetImageInfo(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (imageInfo == null)
|
if (imageInfo == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -270,17 +270,10 @@ namespace Emby.Drawing
|
||||||
// create the bitmap
|
// create the bitmap
|
||||||
var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
|
var bitmap = new SKBitmap(codec.Info.Width, codec.Info.Height, !requiresTransparencyHack);
|
||||||
|
|
||||||
if (bitmap != null)
|
// decode
|
||||||
{
|
codec.GetPixels(bitmap.Info, bitmap.GetPixels());
|
||||||
// decode
|
|
||||||
codec.GetPixels(bitmap.Info, bitmap.GetPixels());
|
|
||||||
|
|
||||||
origin = codec.EncodedOrigin;
|
origin = codec.EncodedOrigin;
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
origin = GetSKEncodedOrigin(orientation);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bitmap;
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,8 +30,11 @@ namespace Emby.Naming.AudioBook
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(path));
|
throw new ArgumentNullException(nameof(path));
|
||||||
}
|
}
|
||||||
if (IsDirectory)
|
|
||||||
|
if (IsDirectory) // TODO
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var extension = Path.GetExtension(path) ?? string.Empty;
|
var extension = Path.GetExtension(path) ?? string.Empty;
|
||||||
// Check supported extensions
|
// Check supported extensions
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Plugins;
|
using MediaBrowser.Common.Plugins;
|
||||||
using MediaBrowser.Common.Updates;
|
using MediaBrowser.Common.Updates;
|
||||||
|
@ -92,18 +93,18 @@ namespace Emby.Server.Implementations.Activity
|
||||||
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
|
_appHost.ApplicationUpdated += _appHost_ApplicationUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
|
async void _deviceManager_CameraImageUploaded(object sender, GenericEventArgs<CameraImageUploadInfo> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("CameraImageUploadedFrom"), e.Argument.Device.Name),
|
Name = string.Format(_localization.GetLocalizedString("CameraImageUploadedFrom"), e.Argument.Device.Name),
|
||||||
Type = NotificationType.CameraImageUploaded.ToString()
|
Type = NotificationType.CameraImageUploaded.ToString()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _userManager_UserLockedOut(object sender, GenericEventArgs<User> e)
|
async void _userManager_UserLockedOut(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserLockedOutWithName"), e.Argument.Name),
|
||||||
Type = NotificationType.UserLockedOut.ToString(),
|
Type = NotificationType.UserLockedOut.ToString(),
|
||||||
|
@ -111,9 +112,9 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
|
async void _subManager_SubtitleDownloadFailure(object sender, SubtitleDownloadFailureEventArgs e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)),
|
Name = string.Format(_localization.GetLocalizedString("SubtitleDownloadFailureFromForItem"), e.Provider, Notifications.Notifications.GetItemName(e.Item)),
|
||||||
Type = "SubtitleDownloadFailure",
|
Type = "SubtitleDownloadFailure",
|
||||||
|
@ -122,7 +123,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
|
async void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
|
||||||
{
|
{
|
||||||
var item = e.MediaInfo;
|
var item = e.MediaInfo;
|
||||||
|
|
||||||
|
@ -145,7 +146,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
|
|
||||||
var user = e.Users.First();
|
var user = e.Users.First();
|
||||||
|
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
|
Name = string.Format(_localization.GetLocalizedString("UserStoppedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
|
||||||
Type = GetPlaybackStoppedNotificationType(item.MediaType),
|
Type = GetPlaybackStoppedNotificationType(item.MediaType),
|
||||||
|
@ -153,7 +154,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
|
async void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
|
||||||
{
|
{
|
||||||
var item = e.MediaInfo;
|
var item = e.MediaInfo;
|
||||||
|
|
||||||
|
@ -176,7 +177,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
|
|
||||||
var user = e.Users.First();
|
var user = e.Users.First();
|
||||||
|
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
|
Name = string.Format(_localization.GetLocalizedString("UserStartedPlayingItemWithValues"), user.Name, GetItemName(item), e.DeviceName),
|
||||||
Type = GetPlaybackNotificationType(item.MediaType),
|
Type = GetPlaybackNotificationType(item.MediaType),
|
||||||
|
@ -237,7 +238,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sessionManager_SessionEnded(object sender, SessionEventArgs e)
|
async void _sessionManager_SessionEnded(object sender, SessionEventArgs e)
|
||||||
{
|
{
|
||||||
string name;
|
string name;
|
||||||
var session = e.SessionInfo;
|
var session = e.SessionInfo;
|
||||||
|
@ -254,7 +255,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
name = string.Format(_localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, session.DeviceName);
|
name = string.Format(_localization.GetLocalizedString("UserOfflineFromDevice"), session.UserName, session.DeviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
Type = "SessionEnded",
|
Type = "SessionEnded",
|
||||||
|
@ -263,11 +264,11 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> e)
|
async void _sessionManager_AuthenticationSucceeded(object sender, GenericEventArgs<AuthenticationResult> e)
|
||||||
{
|
{
|
||||||
var user = e.Argument.User;
|
var user = e.Argument.User;
|
||||||
|
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name),
|
Name = string.Format(_localization.GetLocalizedString("AuthenticationSucceededWithUserName"), user.Name),
|
||||||
Type = "AuthenticationSucceeded",
|
Type = "AuthenticationSucceeded",
|
||||||
|
@ -276,9 +277,9 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sessionManager_AuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
|
async void _sessionManager_AuthenticationFailed(object sender, GenericEventArgs<AuthenticationRequest> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username),
|
Name = string.Format(_localization.GetLocalizedString("FailedLoginAttemptWithUserName"), e.Argument.Username),
|
||||||
Type = "AuthenticationFailed",
|
Type = "AuthenticationFailed",
|
||||||
|
@ -287,9 +288,9 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _appHost_ApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e)
|
async void _appHost_ApplicationUpdated(object sender, GenericEventArgs<PackageVersionInfo> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("MessageApplicationUpdatedTo"), e.Argument.versionStr),
|
Name = string.Format(_localization.GetLocalizedString("MessageApplicationUpdatedTo"), e.Argument.versionStr),
|
||||||
Type = NotificationType.ApplicationUpdateInstalled.ToString(),
|
Type = NotificationType.ApplicationUpdateInstalled.ToString(),
|
||||||
|
@ -297,27 +298,27 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
|
async void _config_NamedConfigurationUpdated(object sender, ConfigurationUpdateEventArgs e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("MessageNamedServerConfigurationUpdatedWithValue"), e.Key),
|
Name = string.Format(_localization.GetLocalizedString("MessageNamedServerConfigurationUpdatedWithValue"), e.Key),
|
||||||
Type = "NamedConfigurationUpdated"
|
Type = "NamedConfigurationUpdated"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _config_ConfigurationUpdated(object sender, EventArgs e)
|
async void _config_ConfigurationUpdated(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = _localization.GetLocalizedString("MessageServerConfigurationUpdated"),
|
Name = _localization.GetLocalizedString("MessageServerConfigurationUpdated"),
|
||||||
Type = "ServerConfigurationUpdated"
|
Type = "ServerConfigurationUpdated"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
|
async void _userManager_UserPolicyUpdated(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserPolicyUpdatedWithName"), e.Argument.Name),
|
||||||
Type = "UserPolicyUpdated",
|
Type = "UserPolicyUpdated",
|
||||||
|
@ -325,18 +326,18 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _userManager_UserDeleted(object sender, GenericEventArgs<User> e)
|
async void _userManager_UserDeleted(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserDeletedWithName"), e.Argument.Name),
|
||||||
Type = "UserDeleted"
|
Type = "UserDeleted"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _userManager_UserPasswordChanged(object sender, GenericEventArgs<User> e)
|
async void _userManager_UserPasswordChanged(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserPasswordChangedWithName"), e.Argument.Name),
|
||||||
Type = "UserPasswordChanged",
|
Type = "UserPasswordChanged",
|
||||||
|
@ -344,9 +345,9 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _userManager_UserCreated(object sender, GenericEventArgs<User> e)
|
async void _userManager_UserCreated(object sender, GenericEventArgs<User> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserCreatedWithName"), e.Argument.Name),
|
||||||
Type = "UserCreated",
|
Type = "UserCreated",
|
||||||
|
@ -354,9 +355,9 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _subManager_SubtitlesDownloaded(object sender, SubtitleDownloadEventArgs e)
|
async void _subManager_SubtitlesDownloaded(object sender, SubtitleDownloadEventArgs e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("SubtitlesDownloadedForItem"), Notifications.Notifications.GetItemName(e.Item)),
|
Name = string.Format(_localization.GetLocalizedString("SubtitlesDownloadedForItem"), Notifications.Notifications.GetItemName(e.Item)),
|
||||||
Type = "SubtitlesDownloaded",
|
Type = "SubtitlesDownloaded",
|
||||||
|
@ -365,7 +366,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _sessionManager_SessionStarted(object sender, SessionEventArgs e)
|
async void _sessionManager_SessionStarted(object sender, SessionEventArgs e)
|
||||||
{
|
{
|
||||||
string name;
|
string name;
|
||||||
var session = e.SessionInfo;
|
var session = e.SessionInfo;
|
||||||
|
@ -382,7 +383,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
name = string.Format(_localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, session.DeviceName);
|
name = string.Format(_localization.GetLocalizedString("UserOnlineFromDevice"), session.UserName, session.DeviceName);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = name,
|
Name = name,
|
||||||
Type = "SessionStarted",
|
Type = "SessionStarted",
|
||||||
|
@ -391,9 +392,9 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _installationManager_PluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e)
|
async void _installationManager_PluginUpdated(object sender, GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name),
|
Name = string.Format(_localization.GetLocalizedString("PluginUpdatedWithName"), e.Argument.Item1.Name),
|
||||||
Type = NotificationType.PluginUpdateInstalled.ToString(),
|
Type = NotificationType.PluginUpdateInstalled.ToString(),
|
||||||
|
@ -402,18 +403,18 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _installationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
|
async void _installationManager_PluginUninstalled(object sender, GenericEventArgs<IPlugin> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name),
|
Name = string.Format(_localization.GetLocalizedString("PluginUninstalledWithName"), e.Argument.Name),
|
||||||
Type = NotificationType.PluginUninstalled.ToString()
|
Type = NotificationType.PluginUninstalled.ToString()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _installationManager_PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
|
async void _installationManager_PluginInstalled(object sender, GenericEventArgs<PackageVersionInfo> e)
|
||||||
{
|
{
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name),
|
Name = string.Format(_localization.GetLocalizedString("PluginInstalledWithName"), e.Argument.name),
|
||||||
Type = NotificationType.PluginInstalled.ToString(),
|
Type = NotificationType.PluginInstalled.ToString(),
|
||||||
|
@ -421,11 +422,11 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
|
async void _installationManager_PackageInstallationFailed(object sender, InstallationFailedEventArgs e)
|
||||||
{
|
{
|
||||||
var installationInfo = e.InstallationInfo;
|
var installationInfo = e.InstallationInfo;
|
||||||
|
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name),
|
Name = string.Format(_localization.GetLocalizedString("NameInstallFailed"), installationInfo.Name),
|
||||||
Type = NotificationType.InstallationFailed.ToString(),
|
Type = NotificationType.InstallationFailed.ToString(),
|
||||||
|
@ -434,7 +435,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
|
async void _taskManager_TaskCompleted(object sender, TaskCompletionEventArgs e)
|
||||||
{
|
{
|
||||||
var result = e.Result;
|
var result = e.Result;
|
||||||
var task = e.Task;
|
var task = e.Task;
|
||||||
|
@ -461,7 +462,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
vals.Add(e.Result.LongErrorMessage);
|
vals.Add(e.Result.LongErrorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateLogEntry(new ActivityLogEntry
|
await CreateLogEntry(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
|
Name = string.Format(_localization.GetLocalizedString("ScheduledTaskFailedWithName"), task.Name),
|
||||||
Type = NotificationType.TaskFailed.ToString(),
|
Type = NotificationType.TaskFailed.ToString(),
|
||||||
|
@ -472,11 +473,11 @@ namespace Emby.Server.Implementations.Activity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateLogEntry(ActivityLogEntry entry)
|
private async Task CreateLogEntry(ActivityLogEntry entry)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_activityManager.Create(entry);
|
await _activityManager.CreateAsync(entry);
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Model.Activity;
|
using MediaBrowser.Model.Activity;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
using MediaBrowser.Model.Querying;
|
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Activity
|
namespace Emby.Server.Implementations.Activity
|
||||||
|
@ -26,20 +27,38 @@ namespace Emby.Server.Implementations.Activity
|
||||||
_userManager = userManager;
|
_userManager = userManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Create(ActivityLogEntry entry)
|
public async Task CreateAsync(ActivityLogEntry entry)
|
||||||
{
|
{
|
||||||
entry.Date = DateTime.UtcNow;
|
entry.Date = DateTime.UtcNow;
|
||||||
|
|
||||||
_repo.Create(entry);
|
await _repo.CreateAsync(entry);
|
||||||
|
|
||||||
EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(entry));
|
EntryCreated?.Invoke(this, new GenericEventArgs<ActivityLogEntry>(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
public IEnumerable<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
||||||
{
|
{
|
||||||
var result = _repo.GetActivityLogEntries(minDate, hasUserId, startIndex, limit);
|
var result = _repo.GetActivityLogEntries();
|
||||||
|
|
||||||
foreach (var item in result.Items.Where(i => !i.UserId.Equals(Guid.Empty)))
|
if (minDate.HasValue)
|
||||||
|
{
|
||||||
|
result = result.Where(x => x.Date >= minDate.Value);
|
||||||
|
}
|
||||||
|
if (hasUserId.HasValue)
|
||||||
|
{
|
||||||
|
result = result.Where(x => x.UserId != null && x.UserId != Guid.Empty);
|
||||||
|
}
|
||||||
|
if (startIndex.HasValue)
|
||||||
|
{
|
||||||
|
result = result.Where(x => x.Id >= startIndex.Value);
|
||||||
|
}
|
||||||
|
if (limit.HasValue)
|
||||||
|
{
|
||||||
|
result = result.Take(limit.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add images for each user
|
||||||
|
foreach (var item in result)
|
||||||
{
|
{
|
||||||
var user = _userManager.GetUserById(item.UserId);
|
var user = _userManager.GetUserById(item.UserId);
|
||||||
|
|
||||||
|
@ -50,12 +69,7 @@ namespace Emby.Server.Implementations.Activity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result.AsEnumerable();
|
||||||
}
|
|
||||||
|
|
||||||
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit)
|
|
||||||
{
|
|
||||||
return GetActivityLogEntries(minDate, null, startIndex, limit);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,310 +1,37 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Emby.Server.Implementations.Data;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Controller;
|
|
||||||
using MediaBrowser.Model.Activity;
|
using MediaBrowser.Model.Activity;
|
||||||
using MediaBrowser.Model.IO;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using MediaBrowser.Model.Querying;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using SQLitePCL.pretty;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.Activity
|
namespace Emby.Server.Implementations.Activity
|
||||||
{
|
{
|
||||||
public class ActivityRepository : BaseSqliteRepository, IActivityRepository
|
public class ActivityRepository : DbContext, IActivityRepository
|
||||||
{
|
{
|
||||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
protected string _dataDirPath;
|
||||||
protected IFileSystem FileSystem { get; private set; }
|
|
||||||
|
|
||||||
public ActivityRepository(ILoggerFactory loggerFactory, IServerApplicationPaths appPaths, IFileSystem fileSystem)
|
public DbSet<ActivityLogEntry> ActivityLogs { get; set; }
|
||||||
: base(loggerFactory.CreateLogger(nameof(ActivityRepository)))
|
|
||||||
|
public ActivityRepository(string dataDirPath)
|
||||||
{
|
{
|
||||||
DbFilePath = Path.Combine(appPaths.DataPath, "activitylog.db");
|
_dataDirPath = dataDirPath;
|
||||||
FileSystem = fileSystem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Initialize()
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
||||||
{
|
{
|
||||||
try
|
// Ensure the dir exists
|
||||||
{
|
Directory.CreateDirectory(_dataDirPath);
|
||||||
InitializeInternal();
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError(ex, "Error loading database file. Will reset and retry.");
|
|
||||||
|
|
||||||
FileSystem.DeleteFile(DbFilePath);
|
optionsBuilder.UseSqlite($"Filename={Path.Combine(_dataDirPath, "activitylog.sqlite.db")}");
|
||||||
|
|
||||||
InitializeInternal();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeInternal()
|
public async Task CreateAsync(ActivityLogEntry entry)
|
||||||
{
|
{
|
||||||
using (var connection = CreateConnection())
|
await ActivityLogs.AddAsync(entry);
|
||||||
{
|
await SaveChangesAsync();
|
||||||
RunDefaultInitialization(connection);
|
|
||||||
|
|
||||||
connection.RunQueries(new[]
|
|
||||||
{
|
|
||||||
"create table if not exists ActivityLog (Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Overview TEXT, ShortOverview TEXT, Type TEXT NOT NULL, ItemId TEXT, UserId TEXT, DateCreated DATETIME NOT NULL, LogSeverity TEXT NOT NULL)",
|
|
||||||
"drop index if exists idx_ActivityLogEntries"
|
|
||||||
});
|
|
||||||
|
|
||||||
TryMigrate(connection);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void TryMigrate(ManagedConnection connection)
|
public IQueryable<ActivityLogEntry> GetActivityLogEntries()
|
||||||
{
|
=> ActivityLogs;
|
||||||
try
|
|
||||||
{
|
|
||||||
if (TableExists(connection, "ActivityLogEntries"))
|
|
||||||
{
|
|
||||||
connection.RunQueries(new[]
|
|
||||||
{
|
|
||||||
"INSERT INTO ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) SELECT Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity FROM ActivityLogEntries",
|
|
||||||
"drop table if exists ActivityLogEntries"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError(ex, "Error migrating activity log database");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string BaseActivitySelectText = "select Id, Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity from ActivityLog";
|
|
||||||
|
|
||||||
public void Create(ActivityLogEntry entry)
|
|
||||||
{
|
|
||||||
if (entry == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (WriteLock.Write())
|
|
||||||
using (var connection = CreateConnection())
|
|
||||||
{
|
|
||||||
connection.RunInTransaction(db =>
|
|
||||||
{
|
|
||||||
using (var statement = db.PrepareStatement("insert into ActivityLog (Name, Overview, ShortOverview, Type, ItemId, UserId, DateCreated, LogSeverity) values (@Name, @Overview, @ShortOverview, @Type, @ItemId, @UserId, @DateCreated, @LogSeverity)"))
|
|
||||||
{
|
|
||||||
statement.TryBind("@Name", entry.Name);
|
|
||||||
|
|
||||||
statement.TryBind("@Overview", entry.Overview);
|
|
||||||
statement.TryBind("@ShortOverview", entry.ShortOverview);
|
|
||||||
statement.TryBind("@Type", entry.Type);
|
|
||||||
statement.TryBind("@ItemId", entry.ItemId);
|
|
||||||
|
|
||||||
if (entry.UserId.Equals(Guid.Empty))
|
|
||||||
{
|
|
||||||
statement.TryBindNull("@UserId");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
statement.TryBind("@UserId", entry.UserId.ToString("N"));
|
|
||||||
}
|
|
||||||
|
|
||||||
statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
|
|
||||||
statement.TryBind("@LogSeverity", entry.Severity.ToString());
|
|
||||||
|
|
||||||
statement.MoveNext();
|
|
||||||
}
|
|
||||||
}, TransactionMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update(ActivityLogEntry entry)
|
|
||||||
{
|
|
||||||
if (entry == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(entry));
|
|
||||||
}
|
|
||||||
|
|
||||||
using (WriteLock.Write())
|
|
||||||
using (var connection = CreateConnection())
|
|
||||||
{
|
|
||||||
connection.RunInTransaction(db =>
|
|
||||||
{
|
|
||||||
using (var statement = db.PrepareStatement("Update ActivityLog set Name=@Name,Overview=@Overview,ShortOverview=@ShortOverview,Type=@Type,ItemId=@ItemId,UserId=@UserId,DateCreated=@DateCreated,LogSeverity=@LogSeverity where Id=@Id"))
|
|
||||||
{
|
|
||||||
statement.TryBind("@Id", entry.Id);
|
|
||||||
|
|
||||||
statement.TryBind("@Name", entry.Name);
|
|
||||||
statement.TryBind("@Overview", entry.Overview);
|
|
||||||
statement.TryBind("@ShortOverview", entry.ShortOverview);
|
|
||||||
statement.TryBind("@Type", entry.Type);
|
|
||||||
statement.TryBind("@ItemId", entry.ItemId);
|
|
||||||
|
|
||||||
if (entry.UserId.Equals(Guid.Empty))
|
|
||||||
{
|
|
||||||
statement.TryBindNull("@UserId");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
statement.TryBind("@UserId", entry.UserId.ToString("N"));
|
|
||||||
}
|
|
||||||
|
|
||||||
statement.TryBind("@DateCreated", entry.Date.ToDateTimeParamValue());
|
|
||||||
statement.TryBind("@LogSeverity", entry.Severity.ToString());
|
|
||||||
|
|
||||||
statement.MoveNext();
|
|
||||||
}
|
|
||||||
}, TransactionMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? startIndex, int? limit)
|
|
||||||
{
|
|
||||||
using (WriteLock.Read())
|
|
||||||
using (var connection = CreateConnection(true))
|
|
||||||
{
|
|
||||||
var commandText = BaseActivitySelectText;
|
|
||||||
var whereClauses = new List<string>();
|
|
||||||
|
|
||||||
if (minDate.HasValue)
|
|
||||||
{
|
|
||||||
whereClauses.Add("DateCreated>=@DateCreated");
|
|
||||||
}
|
|
||||||
if (hasUserId.HasValue)
|
|
||||||
{
|
|
||||||
if (hasUserId.Value)
|
|
||||||
{
|
|
||||||
whereClauses.Add("UserId not null");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
whereClauses.Add("UserId is null");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var whereTextWithoutPaging = whereClauses.Count == 0 ?
|
|
||||||
string.Empty :
|
|
||||||
" where " + string.Join(" AND ", whereClauses.ToArray());
|
|
||||||
|
|
||||||
if (startIndex.HasValue && startIndex.Value > 0)
|
|
||||||
{
|
|
||||||
var pagingWhereText = whereClauses.Count == 0 ?
|
|
||||||
string.Empty :
|
|
||||||
" where " + string.Join(" AND ", whereClauses.ToArray());
|
|
||||||
|
|
||||||
whereClauses.Add(string.Format("Id NOT IN (SELECT Id FROM ActivityLog {0} ORDER BY DateCreated DESC LIMIT {1})",
|
|
||||||
pagingWhereText,
|
|
||||||
startIndex.Value.ToString(_usCulture)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var whereText = whereClauses.Count == 0 ?
|
|
||||||
string.Empty :
|
|
||||||
" where " + string.Join(" AND ", whereClauses.ToArray());
|
|
||||||
|
|
||||||
commandText += whereText;
|
|
||||||
|
|
||||||
commandText += " ORDER BY DateCreated DESC";
|
|
||||||
|
|
||||||
if (limit.HasValue)
|
|
||||||
{
|
|
||||||
commandText += " LIMIT " + limit.Value.ToString(_usCulture);
|
|
||||||
}
|
|
||||||
|
|
||||||
var statementTexts = new List<string>();
|
|
||||||
statementTexts.Add(commandText);
|
|
||||||
statementTexts.Add("select count (Id) from ActivityLog" + whereTextWithoutPaging);
|
|
||||||
|
|
||||||
return connection.RunInTransaction(db =>
|
|
||||||
{
|
|
||||||
var list = new List<ActivityLogEntry>();
|
|
||||||
var result = new QueryResult<ActivityLogEntry>();
|
|
||||||
|
|
||||||
var statements = PrepareAllSafe(db, statementTexts).ToList();
|
|
||||||
|
|
||||||
using (var statement = statements[0])
|
|
||||||
{
|
|
||||||
if (minDate.HasValue)
|
|
||||||
{
|
|
||||||
statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var row in statement.ExecuteQuery())
|
|
||||||
{
|
|
||||||
list.Add(GetEntry(row));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
using (var statement = statements[1])
|
|
||||||
{
|
|
||||||
if (minDate.HasValue)
|
|
||||||
{
|
|
||||||
statement.TryBind("@DateCreated", minDate.Value.ToDateTimeParamValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
result.TotalRecordCount = statement.ExecuteQuery().SelectScalarInt().First();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.Items = list.ToArray();
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}, ReadTransactionMode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ActivityLogEntry GetEntry(IReadOnlyList<IResultSetValue> reader)
|
|
||||||
{
|
|
||||||
var index = 0;
|
|
||||||
|
|
||||||
var info = new ActivityLogEntry
|
|
||||||
{
|
|
||||||
Id = reader[index].ToInt64()
|
|
||||||
};
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.Name = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.Overview = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.ShortOverview = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.Type = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.ItemId = reader[index].ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.UserId = new Guid(reader[index].ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
index++;
|
|
||||||
info.Date = reader[index].ReadDateTime();
|
|
||||||
|
|
||||||
index++;
|
|
||||||
if (reader[index].SQLiteType != SQLiteType.Null)
|
|
||||||
{
|
|
||||||
info.Severity = (LogLevel)Enum.Parse(typeof(LogLevel), reader[index].ToString(), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -709,7 +709,7 @@ namespace Emby.Server.Implementations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Init()
|
public async Task InitAsync()
|
||||||
{
|
{
|
||||||
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
|
HttpPort = ServerConfigurationManager.Configuration.HttpServerPortNumber;
|
||||||
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
|
HttpsPort = ServerConfigurationManager.Configuration.HttpsPortNumber;
|
||||||
|
@ -739,7 +739,7 @@ namespace Emby.Server.Implementations
|
||||||
|
|
||||||
SetHttpLimit();
|
SetHttpLimit();
|
||||||
|
|
||||||
RegisterResources();
|
await RegisterResourcesAsync();
|
||||||
|
|
||||||
FindParts();
|
FindParts();
|
||||||
}
|
}
|
||||||
|
@ -754,7 +754,7 @@ namespace Emby.Server.Implementations
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Registers resources that classes will depend on
|
/// Registers resources that classes will depend on
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected void RegisterResources()
|
protected async Task RegisterResourcesAsync()
|
||||||
{
|
{
|
||||||
RegisterSingleInstance(ConfigurationManager);
|
RegisterSingleInstance(ConfigurationManager);
|
||||||
RegisterSingleInstance<IApplicationHost>(this);
|
RegisterSingleInstance<IApplicationHost>(this);
|
||||||
|
@ -931,7 +931,7 @@ namespace Emby.Server.Implementations
|
||||||
EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
|
EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
|
||||||
RegisterSingleInstance(EncodingManager);
|
RegisterSingleInstance(EncodingManager);
|
||||||
|
|
||||||
var activityLogRepo = GetActivityLogRepository();
|
var activityLogRepo = await GetActivityLogRepositoryAsync();
|
||||||
RegisterSingleInstance(activityLogRepo);
|
RegisterSingleInstance(activityLogRepo);
|
||||||
RegisterSingleInstance<IActivityManager>(new ActivityManager(LoggerFactory, activityLogRepo, UserManager));
|
RegisterSingleInstance<IActivityManager>(new ActivityManager(LoggerFactory, activityLogRepo, UserManager));
|
||||||
|
|
||||||
|
@ -1146,11 +1146,11 @@ namespace Emby.Server.Implementations
|
||||||
return repo;
|
return repo;
|
||||||
}
|
}
|
||||||
|
|
||||||
private IActivityRepository GetActivityLogRepository()
|
private async Task<IActivityRepository> GetActivityLogRepositoryAsync()
|
||||||
{
|
{
|
||||||
var repo = new ActivityRepository(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager);
|
var repo = new ActivityRepository(ServerConfigurationManager.ApplicationPaths.DataPath);
|
||||||
|
|
||||||
repo.Initialize();
|
await repo.Database.EnsureCreatedAsync();
|
||||||
|
|
||||||
return repo;
|
return repo;
|
||||||
}
|
}
|
||||||
|
|
|
@ -681,22 +681,17 @@ namespace Emby.Server.Implementations.Channels
|
||||||
// Find the corresponding channel provider plugin
|
// Find the corresponding channel provider plugin
|
||||||
var channelProvider = GetChannelProvider(channel);
|
var channelProvider = GetChannelProvider(channel);
|
||||||
|
|
||||||
var user = query.User;
|
var parentItem = query.ParentId == Guid.Empty ? channel : _libraryManager.GetItemById(query.ParentId);
|
||||||
|
|
||||||
ChannelItemSortField? sortField = null;
|
|
||||||
var sortDescending = false;
|
|
||||||
|
|
||||||
var parentItem = !query.ParentId.Equals(Guid.Empty) ? _libraryManager.GetItemById(query.ParentId) : channel;
|
|
||||||
|
|
||||||
var itemsResult = await GetChannelItems(channelProvider,
|
var itemsResult = await GetChannelItems(channelProvider,
|
||||||
user,
|
query.User,
|
||||||
parentItem is Channel ? null : parentItem.ExternalId,
|
parentItem is Channel ? null : parentItem.ExternalId,
|
||||||
sortField,
|
null,
|
||||||
sortDescending,
|
false,
|
||||||
cancellationToken)
|
cancellationToken)
|
||||||
.ConfigureAwait(false);
|
.ConfigureAwait(false);
|
||||||
|
|
||||||
if (query.ParentId.Equals(Guid.Empty))
|
if (query.ParentId == Guid.Empty)
|
||||||
{
|
{
|
||||||
query.Parent = channel;
|
query.Parent = channel;
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0" />
|
||||||
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
|
<PackageReference Include="ServiceStack.Text.Core" Version="5.4.0" />
|
||||||
<PackageReference Include="sharpcompress" Version="0.22.0" />
|
<PackageReference Include="sharpcompress" Version="0.22.0" />
|
||||||
<PackageReference Include="SimpleInjector" Version="4.4.2" />
|
<PackageReference Include="SimpleInjector" Version="4.4.2" />
|
||||||
|
|
|
@ -21,7 +21,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
private long RangeStart { get; set; }
|
private long RangeStart { get; set; }
|
||||||
private long RangeEnd { get; set; }
|
private long RangeEnd { get; set; }
|
||||||
private long RangeLength { get; set; }
|
private long RangeLength { get; set; }
|
||||||
private long TotalContentLength { get; set; }
|
public long TotalContentLength { get; set; }
|
||||||
|
|
||||||
public Action OnComplete { get; set; }
|
public Action OnComplete { get; set; }
|
||||||
public Action OnError { get; set; }
|
public Action OnError { get; set; }
|
||||||
|
|
|
@ -5,6 +5,7 @@ using System.IO;
|
||||||
using System.IO.Compression;
|
using System.IO.Compression;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
using System.Runtime.Serialization;
|
using System.Runtime.Serialization;
|
||||||
|
using System.Security.Cryptography;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Xml;
|
using System.Xml;
|
||||||
|
@ -423,13 +424,11 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
|
private object GetCachedResult(IRequest requestContext, IDictionary<string, string> responseHeaders, Guid cacheKey, string cacheKeyString, DateTime? lastDateModified, TimeSpan? cacheDuration, string contentType)
|
||||||
{
|
{
|
||||||
responseHeaders["ETag"] = string.Format("\"{0}\"", cacheKeyString);
|
|
||||||
|
|
||||||
bool noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
|
bool noCache = (requestContext.Headers.Get("Cache-Control") ?? string.Empty).IndexOf("no-cache", StringComparison.OrdinalIgnoreCase) != -1;
|
||||||
|
|
||||||
if (!noCache)
|
if (!noCache)
|
||||||
{
|
{
|
||||||
if (IsNotModified(requestContext, cacheKey, lastDateModified, cacheDuration))
|
if (IsNotModified(requestContext, cacheKey))
|
||||||
{
|
{
|
||||||
AddAgeHeader(responseHeaders, lastDateModified);
|
AddAgeHeader(responseHeaders, lastDateModified);
|
||||||
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
|
AddExpiresHeader(responseHeaders, cacheKeyString, cacheDuration);
|
||||||
|
@ -442,7 +441,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AddCachingHeaders(responseHeaders, cacheKeyString, lastDateModified, cacheDuration);
|
AddCachingHeaders(responseHeaders, cacheKeyString, cacheDuration);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -532,10 +531,11 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
|
|
||||||
public async Task<object> GetStaticResult(IRequest requestContext, StaticResultOptions options)
|
public async Task<object> GetStaticResult(IRequest requestContext, StaticResultOptions options)
|
||||||
{
|
{
|
||||||
var cacheKey = options.CacheKey;
|
|
||||||
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
options.ResponseHeaders = options.ResponseHeaders ?? new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
|
||||||
var contentType = options.ContentType;
|
|
||||||
|
|
||||||
|
var contentType = options.ContentType;
|
||||||
|
var etag = requestContext.Headers.Get("If-None-Match");
|
||||||
|
var cacheKey = etag != null ? new Guid(etag.Trim('\"')) : Guid.Empty;
|
||||||
if (!cacheKey.Equals(Guid.Empty))
|
if (!cacheKey.Equals(Guid.Empty))
|
||||||
{
|
{
|
||||||
var key = cacheKey.ToString("N");
|
var key = cacheKey.ToString("N");
|
||||||
|
@ -554,8 +554,6 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
var factoryFn = options.ContentFactory;
|
var factoryFn = options.ContentFactory;
|
||||||
var responseHeaders = options.ResponseHeaders;
|
var responseHeaders = options.ResponseHeaders;
|
||||||
|
|
||||||
//var requestedCompressionType = GetCompressionType(requestContext);
|
|
||||||
|
|
||||||
var rangeHeader = requestContext.Headers.Get("Range");
|
var rangeHeader = requestContext.Headers.Get("Range");
|
||||||
|
|
||||||
if (!isHeadRequest && !string.IsNullOrEmpty(options.Path))
|
if (!isHeadRequest && !string.IsNullOrEmpty(options.Path))
|
||||||
|
@ -568,10 +566,21 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
};
|
};
|
||||||
|
|
||||||
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
|
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
|
||||||
|
// Generate an ETag based on identifying information - TODO read contents from filesystem instead?
|
||||||
|
var responseId = $"{hasHeaders.ContentType}{options.Path}{hasHeaders.TotalContentLength}";
|
||||||
|
var hashedId = MD5.Create().ComputeHash(Encoding.Default.GetBytes(responseId));
|
||||||
|
hasHeaders.Headers["ETag"] = new Guid(hashedId).ToString("N");
|
||||||
|
|
||||||
return hasHeaders;
|
return hasHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
var stream = await factoryFn().ConfigureAwait(false);
|
var stream = await factoryFn().ConfigureAwait(false);
|
||||||
|
// Generate an etag based on stream content
|
||||||
|
var streamHash = MD5.Create().ComputeHash(stream);
|
||||||
|
var newEtag = new Guid(streamHash).ToString("N");
|
||||||
|
|
||||||
|
// reset position so the response can re-use it -- TODO is this ok?
|
||||||
|
stream.Position = 0;
|
||||||
|
|
||||||
var totalContentLength = options.ContentLength;
|
var totalContentLength = options.ContentLength;
|
||||||
if (!totalContentLength.HasValue)
|
if (!totalContentLength.HasValue)
|
||||||
|
@ -594,6 +603,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
};
|
};
|
||||||
|
|
||||||
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
|
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
|
||||||
|
hasHeaders.Headers["ETag"] = newEtag;
|
||||||
return hasHeaders;
|
return hasHeaders;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -618,6 +628,7 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
};
|
};
|
||||||
|
|
||||||
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
|
AddResponseHeaders(hasHeaders, options.ResponseHeaders);
|
||||||
|
hasHeaders.Headers["ETag"] = newEtag;
|
||||||
return hasHeaders;
|
return hasHeaders;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -630,16 +641,8 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds the caching responseHeaders.
|
/// Adds the caching responseHeaders.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
|
private void AddCachingHeaders(IDictionary<string, string> responseHeaders, string cacheKey, TimeSpan? cacheDuration)
|
||||||
{
|
{
|
||||||
// Don't specify both last modified and Etag, unless caching unconditionally. They are redundant
|
|
||||||
// https://developers.google.com/speed/docs/best-practices/caching#LeverageBrowserCaching
|
|
||||||
if (lastDateModified.HasValue && (string.IsNullOrEmpty(cacheKey) || cacheDuration.HasValue))
|
|
||||||
{
|
|
||||||
AddAgeHeader(responseHeaders, lastDateModified);
|
|
||||||
responseHeaders["Last-Modified"] = lastDateModified.Value.ToString("r");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cacheDuration.HasValue)
|
if (cacheDuration.HasValue)
|
||||||
{
|
{
|
||||||
responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
|
responseHeaders["Cache-Control"] = "public, max-age=" + Convert.ToInt32(cacheDuration.Value.TotalSeconds);
|
||||||
|
@ -692,28 +695,15 @@ namespace Emby.Server.Implementations.HttpServer
|
||||||
/// <param name="lastDateModified">The last date modified.</param>
|
/// <param name="lastDateModified">The last date modified.</param>
|
||||||
/// <param name="cacheDuration">Duration of the cache.</param>
|
/// <param name="cacheDuration">Duration of the cache.</param>
|
||||||
/// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns>
|
/// <returns><c>true</c> if [is not modified] [the specified cache key]; otherwise, <c>false</c>.</returns>
|
||||||
private bool IsNotModified(IRequest requestContext, Guid cacheKey, DateTime? lastDateModified, TimeSpan? cacheDuration)
|
private bool IsNotModified(IRequest requestContext, Guid cacheKey)
|
||||||
{
|
{
|
||||||
//var isNotModified = true;
|
|
||||||
|
|
||||||
var ifModifiedSinceHeader = requestContext.Headers.Get("If-Modified-Since");
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(ifModifiedSinceHeader)
|
|
||||||
&& DateTime.TryParse(ifModifiedSinceHeader, out DateTime ifModifiedSince)
|
|
||||||
&& IsNotModified(ifModifiedSince.ToUniversalTime(), cacheDuration, lastDateModified))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match");
|
var ifNoneMatchHeader = requestContext.Headers.Get("If-None-Match");
|
||||||
|
|
||||||
bool hasCacheKey = !cacheKey.Equals(Guid.Empty);
|
bool hasCacheKey = !cacheKey.Equals(Guid.Empty);
|
||||||
|
|
||||||
// Validate If-None-Match
|
// Validate If-None-Match
|
||||||
if ((hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader)))
|
if (hasCacheKey && !string.IsNullOrEmpty(ifNoneMatchHeader))
|
||||||
{
|
{
|
||||||
ifNoneMatchHeader = (ifNoneMatchHeader ?? string.Empty).Trim('\"');
|
|
||||||
|
|
||||||
if (Guid.TryParse(ifNoneMatchHeader, out var ifNoneMatch)
|
if (Guid.TryParse(ifNoneMatchHeader, out var ifNoneMatch)
|
||||||
&& cacheKey.Equals(ifNoneMatch))
|
&& cacheKey.Equals(ifNoneMatch))
|
||||||
{
|
{
|
||||||
|
|
|
@ -469,7 +469,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Don't hardcode this
|
// TODO: Don't hardcode this
|
||||||
var isAudio = false;
|
const bool isAudio = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -480,9 +480,11 @@ namespace Emby.Server.Implementations.Library
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// hack - these two values were taken from LiveTVMediaSourceProvider
|
// hack - these two values were taken from LiveTVMediaSourceProvider
|
||||||
var cacheKey = request.OpenToken;
|
string cacheKey = request.OpenToken;
|
||||||
|
|
||||||
await new LiveStreamHelper(_mediaEncoder(), _logger, _jsonSerializer, _appPaths).AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken).ConfigureAwait(false);
|
await new LiveStreamHelper(_mediaEncoder(), _logger, _jsonSerializer, _appPaths)
|
||||||
|
.AddMediaInfoWithProbe(mediaSource, isAudio, cacheKey, true, cancellationToken)
|
||||||
|
.ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
@ -491,6 +493,7 @@ namespace Emby.Server.Implementations.Library
|
||||||
AddMediaInfo(mediaSource, isAudio);
|
AddMediaInfo(mediaSource, isAudio);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: @bond Fix
|
||||||
var json = _jsonSerializer.SerializeToString(mediaSource);
|
var json = _jsonSerializer.SerializeToString(mediaSource);
|
||||||
_logger.LogInformation("Live stream opened: " + json);
|
_logger.LogInformation("Live stream opened: " + json);
|
||||||
var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
|
var clone = _jsonSerializer.DeserializeFromString<MediaSourceInfo>(json);
|
||||||
|
|
|
@ -86,12 +86,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||||
.Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
|
.Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (isBooksCollectionType)
|
return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
|
||||||
{
|
|
||||||
return FindAudio<AudioBook>(args, args.Path, args.Parent, files, args.DirectoryService, collectionType, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LibraryManager.IsAudioFile(args.Path, libraryOptions))
|
if (LibraryManager.IsAudioFile(args.Path, libraryOptions))
|
||||||
|
@ -145,36 +140,19 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||||
private T FindAudio<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName)
|
private T FindAudio<T>(ItemResolveArgs args, string path, Folder parent, List<FileSystemMetadata> fileSystemEntries, IDirectoryService directoryService, string collectionType, bool parseName)
|
||||||
where T : MediaBrowser.Controller.Entities.Audio.Audio, new()
|
where T : MediaBrowser.Controller.Entities.Audio.Audio, new()
|
||||||
{
|
{
|
||||||
var multiDiscFolders = new List<FileSystemMetadata>();
|
|
||||||
|
|
||||||
var libraryOptions = args.GetLibraryOptions();
|
|
||||||
var filesFromOtherItems = new List<FileSystemMetadata>();
|
|
||||||
|
|
||||||
// TODO: Allow GetMultiDiscMovie in here
|
// TODO: Allow GetMultiDiscMovie in here
|
||||||
var supportsMultiVersion = false;
|
const bool supportsMultiVersion = false;
|
||||||
|
|
||||||
var result = ResolveMultipleAudio<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ??
|
var result = ResolveMultipleAudio<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ??
|
||||||
new MultiItemResolverResult();
|
new MultiItemResolverResult();
|
||||||
|
|
||||||
if (result.Items.Count == 1)
|
if (result.Items.Count == 1)
|
||||||
{
|
{
|
||||||
var videoPath = result.Items[0].Path;
|
|
||||||
|
|
||||||
// If we were supporting this we'd be checking filesFromOtherItems
|
// If we were supporting this we'd be checking filesFromOtherItems
|
||||||
var hasOtherItems = false;
|
var item = (T)result.Items[0];
|
||||||
|
item.IsInMixedFolder = false;
|
||||||
if (!hasOtherItems)
|
item.Name = Path.GetFileName(item.ContainingFolderPath);
|
||||||
{
|
return item;
|
||||||
var item = (T)result.Items[0];
|
|
||||||
item.IsInMixedFolder = false;
|
|
||||||
item.Name = Path.GetFileName(item.ContainingFolderPath);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result.Items.Count == 0 && multiDiscFolders.Count > 0)
|
|
||||||
{
|
|
||||||
//return GetMultiDiscAudio<T>(multiDiscFolders, directoryService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -194,11 +172,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio
|
||||||
{
|
{
|
||||||
leftOver.Add(child);
|
leftOver.Add(child);
|
||||||
}
|
}
|
||||||
else if (IsIgnored(child.Name))
|
else if (!IsIgnored(child.Name))
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
files.Add(child);
|
files.Add(child);
|
||||||
}
|
}
|
||||||
|
|
|
@ -410,7 +410,7 @@ namespace Emby.Server.Implementations.Library.Resolvers.Movies
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Allow GetMultiDiscMovie in here
|
// TODO: Allow GetMultiDiscMovie in here
|
||||||
var supportsMultiVersion = true;
|
const bool supportsMultiVersion = true;
|
||||||
|
|
||||||
var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ??
|
var result = ResolveVideos<T>(parent, fileSystemEntries, directoryService, supportsMultiVersion, collectionType, parseName) ??
|
||||||
new MultiItemResolverResult();
|
new MultiItemResolverResult();
|
||||||
|
|
|
@ -153,16 +153,8 @@ namespace Emby.Server.Implementations.Library.Resolvers.TV
|
||||||
var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
|
var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
|
||||||
|
|
||||||
var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions);
|
var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions);
|
||||||
bool? isNamed = null;
|
|
||||||
bool? isOptimistic = null;
|
|
||||||
|
|
||||||
if (!isTvContentType)
|
var episodeInfo = episodeResolver.Resolve(fullName, false, true, false, fillExtendedInfo: false);
|
||||||
{
|
|
||||||
isNamed = true;
|
|
||||||
isOptimistic = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var episodeInfo = episodeResolver.Resolve(fullName, false, isNamed, isOptimistic, fillExtendedInfo: false);
|
|
||||||
if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue)
|
if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -322,17 +322,14 @@ namespace Emby.Server.Implementations.Library
|
||||||
throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
|
throw new SecurityException(string.Format("The {0} account is currently disabled. Please consult with your administrator.", user.Name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user != null)
|
if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
|
||||||
{
|
{
|
||||||
if (!user.Policy.EnableRemoteAccess && !_networkManager.IsInLocalNetwork(remoteEndPoint))
|
throw new SecurityException("Forbidden.");
|
||||||
{
|
}
|
||||||
throw new SecurityException("Forbidden.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!user.IsParentalScheduleAllowed())
|
if (!user.IsParentalScheduleAllowed())
|
||||||
{
|
{
|
||||||
throw new SecurityException("User is not allowed access at this time.");
|
throw new SecurityException("User is not allowed access at this time.");
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update LastActivityDate and LastLoginDate, then save
|
// Update LastActivityDate and LastLoginDate, then save
|
||||||
|
@ -463,26 +460,26 @@ namespace Emby.Server.Implementations.Library
|
||||||
{
|
{
|
||||||
user.Policy.InvalidLoginAttemptCount = newValue;
|
user.Policy.InvalidLoginAttemptCount = newValue;
|
||||||
|
|
||||||
var maxCount = user.Policy.IsAdministrator ?
|
var maxCount = user.Policy.IsAdministrator ? 3 : 5;
|
||||||
3 :
|
|
||||||
5;
|
|
||||||
|
|
||||||
|
// TODO: Fix
|
||||||
|
/*
|
||||||
var fireLockout = false;
|
var fireLockout = false;
|
||||||
|
|
||||||
if (newValue >= maxCount)
|
if (newValue >= maxCount)
|
||||||
{
|
{
|
||||||
//_logger.LogDebug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue.ToString(CultureInfo.InvariantCulture));
|
_logger.LogDebug("Disabling user {0} due to {1} unsuccessful login attempts.", user.Name, newValue.ToString(CultureInfo.InvariantCulture));
|
||||||
//user.Policy.IsDisabled = true;
|
user.Policy.IsDisabled = true;
|
||||||
|
|
||||||
//fireLockout = true;
|
fireLockout = true;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
UpdateUserPolicy(user, user.Policy, false);
|
UpdateUserPolicy(user, user.Policy, false);
|
||||||
|
|
||||||
if (fireLockout)
|
/* if (fireLockout)
|
||||||
{
|
{
|
||||||
UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
|
UserLockedOut?.Invoke(this, new GenericEventArgs<User>(user));
|
||||||
}
|
}*/
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1133,8 +1133,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
IgnoreIndex = true
|
IgnoreIndex = true
|
||||||
};
|
};
|
||||||
|
|
||||||
var isAudio = false;
|
await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _config.CommonApplicationPaths)
|
||||||
await new LiveStreamHelper(_mediaEncoder, _logger, _jsonSerializer, _config.CommonApplicationPaths).AddMediaInfoWithProbe(stream, isAudio, false, cancellationToken).ConfigureAwait(false);
|
.AddMediaInfoWithProbe(stream, false, false, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
return new List<MediaSourceInfo>
|
return new List<MediaSourceInfo>
|
||||||
{
|
{
|
||||||
|
@ -1149,12 +1149,12 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
|
|
||||||
public Task RecordLiveStream(string id, CancellationToken cancellationToken)
|
public Task RecordLiveStream(string id, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task ResetTuner(string id, CancellationToken cancellationToken)
|
public Task ResetTuner(string id, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
return Task.FromResult(0);
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
async void _timerProvider_TimerFired(object sender, GenericEventArgs<TimerInfo> e)
|
async void _timerProvider_TimerFired(object sender, GenericEventArgs<TimerInfo> e)
|
||||||
|
|
|
@ -175,12 +175,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
|
||||||
}
|
}
|
||||||
|
|
||||||
var videoStream = mediaSource.VideoStream;
|
var videoStream = mediaSource.VideoStream;
|
||||||
string videoDecoder = null;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(videoDecoder))
|
|
||||||
{
|
|
||||||
inputModifier += " " + videoDecoder;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mediaSource.ReadAtNativeFramerate)
|
if (mediaSource.ReadAtNativeFramerate)
|
||||||
{
|
{
|
||||||
|
|
|
@ -354,10 +354,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
int? height = null;
|
int? height = null;
|
||||||
bool isInterlaced = true;
|
bool isInterlaced = true;
|
||||||
string videoCodec = null;
|
string videoCodec = null;
|
||||||
string audioCodec = null;
|
|
||||||
|
|
||||||
int? videoBitrate = null;
|
int? videoBitrate = null;
|
||||||
int? audioBitrate = null;
|
|
||||||
|
|
||||||
var isHd = channelInfo.IsHD ?? true;
|
var isHd = channelInfo.IsHD ?? true;
|
||||||
|
|
||||||
|
@ -427,20 +425,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (channelInfo != null)
|
if (string.IsNullOrWhiteSpace(videoCodec))
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(videoCodec))
|
videoCodec = channelInfo.VideoCodec;
|
||||||
{
|
|
||||||
videoCodec = channelInfo.VideoCodec;
|
|
||||||
}
|
|
||||||
audioCodec = channelInfo.AudioCodec;
|
|
||||||
|
|
||||||
if (!videoBitrate.HasValue)
|
|
||||||
{
|
|
||||||
videoBitrate = isHd ? 15000000 : 2000000;
|
|
||||||
}
|
|
||||||
audioBitrate = isHd ? 448000 : 192000;
|
|
||||||
}
|
}
|
||||||
|
string audioCodec = channelInfo.AudioCodec;
|
||||||
|
|
||||||
|
if (!videoBitrate.HasValue)
|
||||||
|
{
|
||||||
|
videoBitrate = isHd ? 15000000 : 2000000;
|
||||||
|
}
|
||||||
|
int? audioBitrate = isHd ? 448000 : 192000;
|
||||||
|
|
||||||
// normalize
|
// normalize
|
||||||
if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
|
@ -99,7 +99,7 @@ namespace Jellyfin.Server
|
||||||
new SystemEvents(),
|
new SystemEvents(),
|
||||||
new NetworkManager(_loggerFactory, environmentInfo)))
|
new NetworkManager(_loggerFactory, environmentInfo)))
|
||||||
{
|
{
|
||||||
appHost.Init();
|
await appHost.InitAsync();
|
||||||
|
|
||||||
appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager);
|
appHost.ImageProcessor.ImageEncoder = GetImageEncoder(fileSystem, appPaths, appHost.LocalizationManager);
|
||||||
|
|
||||||
|
@ -108,7 +108,6 @@ namespace Jellyfin.Server
|
||||||
await appHost.RunStartupTasks();
|
await appHost.RunStartupTasks();
|
||||||
|
|
||||||
// TODO: read input for a stop command
|
// TODO: read input for a stop command
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Block main thread until shutdown
|
// Block main thread until shutdown
|
||||||
|
@ -167,7 +166,6 @@ namespace Jellyfin.Server
|
||||||
{
|
{
|
||||||
Directory.CreateDirectory(programDataPath);
|
Directory.CreateDirectory(programDataPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");
|
string configDir = Environment.GetEnvironmentVariable("JELLYFIN_CONFIG_DIR");
|
||||||
if (string.IsNullOrEmpty(configDir))
|
if (string.IsNullOrEmpty(configDir))
|
||||||
{
|
{
|
||||||
|
@ -224,7 +222,7 @@ namespace Jellyfin.Server
|
||||||
.GetManifestResourceStream("Jellyfin.Server.Resources.Configuration.logging.json"))
|
.GetManifestResourceStream("Jellyfin.Server.Resources.Configuration.logging.json"))
|
||||||
using (Stream fstr = File.Open(configPath, FileMode.CreateNew))
|
using (Stream fstr = File.Open(configPath, FileMode.CreateNew))
|
||||||
{
|
{
|
||||||
await rscstr.CopyToAsync(fstr);
|
await rscstr.CopyToAsync(fstr).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var configuration = new ConfigurationBuilder()
|
var configuration = new ConfigurationBuilder()
|
||||||
|
@ -334,11 +332,9 @@ namespace Jellyfin.Server
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
commandLineArgsString = string.Join(" ",
|
commandLineArgsString = string.Join(
|
||||||
Environment.GetCommandLineArgs()
|
" ",
|
||||||
.Skip(1)
|
Environment.GetCommandLineArgs().Skip(1).Select(NormalizeCommandLineArgument));
|
||||||
.Select(NormalizeCommandLineArgument)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogInformation("Executable: {0}", module);
|
_logger.LogInformation("Executable: {0}", module);
|
||||||
|
|
|
@ -15,19 +15,27 @@ namespace Jellyfin.SocketSharp
|
||||||
{
|
{
|
||||||
int ap = header.IndexOf(attr);
|
int ap = header.IndexOf(attr);
|
||||||
if (ap == -1)
|
if (ap == -1)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
ap += attr.Length;
|
ap += attr.Length;
|
||||||
if (ap >= header.Length)
|
if (ap >= header.Length)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
char ending = header[ap];
|
char ending = header[ap];
|
||||||
if (ending != '"')
|
if (ending != '"')
|
||||||
|
{
|
||||||
ending = ' ';
|
ending = ' ';
|
||||||
|
}
|
||||||
|
|
||||||
int end = header.IndexOf(ending, ap + 1);
|
int end = header.IndexOf(ending, ap + 1);
|
||||||
if (end == -1)
|
if (end == -1)
|
||||||
|
{
|
||||||
return ending == '"' ? null : header.Substring(ap);
|
return ending == '"' ? null : header.Substring(ap);
|
||||||
|
}
|
||||||
|
|
||||||
return header.Substring(ap + 1, end - ap - 1);
|
return header.Substring(ap + 1, end - ap - 1);
|
||||||
}
|
}
|
||||||
|
@ -36,7 +44,9 @@ namespace Jellyfin.SocketSharp
|
||||||
{
|
{
|
||||||
string boundary = GetParameter(ContentType, "; boundary=");
|
string boundary = GetParameter(ContentType, "; boundary=");
|
||||||
if (boundary == null)
|
if (boundary == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
using (var requestStream = InputStream)
|
using (var requestStream = InputStream)
|
||||||
{
|
{
|
||||||
|
@ -124,7 +134,9 @@ namespace Jellyfin.SocketSharp
|
||||||
{
|
{
|
||||||
string v = "\"" + value + "\"";
|
string v = "\"" + value + "\"";
|
||||||
if (v.Length > 20)
|
if (v.Length > 20)
|
||||||
|
{
|
||||||
v = v.Substring(0, 16) + "...\"";
|
v = v.Substring(0, 16) + "...\"";
|
||||||
|
}
|
||||||
|
|
||||||
string msg = string.Format("A potentially dangerous Request.{0} value was " +
|
string msg = string.Format("A potentially dangerous Request.{0} value was " +
|
||||||
"detected from the client ({1}={2}).", name, key, v);
|
"detected from the client ({1}={2}).", name, key, v);
|
||||||
|
@ -135,21 +147,23 @@ namespace Jellyfin.SocketSharp
|
||||||
static void ValidateNameValueCollection(string name, QueryParamCollection coll)
|
static void ValidateNameValueCollection(string name, QueryParamCollection coll)
|
||||||
{
|
{
|
||||||
if (coll == null)
|
if (coll == null)
|
||||||
|
{
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var pair in coll)
|
foreach (var pair in coll)
|
||||||
{
|
{
|
||||||
var key = pair.Name;
|
var key = pair.Name;
|
||||||
var val = pair.Value;
|
var val = pair.Value;
|
||||||
if (val != null && val.Length > 0 && IsInvalidString(val))
|
if (val != null && val.Length > 0 && IsInvalidString(val))
|
||||||
|
{
|
||||||
ThrowValidationException(name, key, val);
|
ThrowValidationException(name, key, val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static bool IsInvalidString(string val)
|
internal static bool IsInvalidString(string val)
|
||||||
{
|
=> IsInvalidString(val, out var validationFailureIndex);
|
||||||
return IsInvalidString(val, out var validationFailureIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool IsInvalidString(string val, out int validationFailureIndex)
|
internal static bool IsInvalidString(string val, out int validationFailureIndex)
|
||||||
{
|
{
|
||||||
|
@ -157,7 +171,9 @@ namespace Jellyfin.SocketSharp
|
||||||
|
|
||||||
int len = val.Length;
|
int len = val.Length;
|
||||||
if (len < 2)
|
if (len < 2)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
char current = val[0];
|
char current = val[0];
|
||||||
for (int idx = 1; idx < len; idx++)
|
for (int idx = 1; idx < len; idx++)
|
||||||
|
@ -195,10 +211,15 @@ namespace Jellyfin.SocketSharp
|
||||||
|
|
||||||
bool IsContentType(string ct, bool starts_with)
|
bool IsContentType(string ct, bool starts_with)
|
||||||
{
|
{
|
||||||
if (ct == null || ContentType == null) return false;
|
if (ct == null || ContentType == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (starts_with)
|
if (starts_with)
|
||||||
|
{
|
||||||
return StrUtils.StartsWith(ContentType, ct, true);
|
return StrUtils.StartsWith(ContentType, ct, true);
|
||||||
|
}
|
||||||
|
|
||||||
return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase);
|
return string.Equals(ContentType, ct, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
@ -231,7 +252,9 @@ namespace Jellyfin.SocketSharp
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
value.Append((char)c);
|
value.Append((char)c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
{
|
{
|
||||||
|
@ -240,22 +263,26 @@ namespace Jellyfin.SocketSharp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (c == '&')
|
else if (c == '&')
|
||||||
|
{
|
||||||
AddRawKeyValue(form, key, value);
|
AddRawKeyValue(form, key, value);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
key.Append((char)c);
|
key.Append((char)c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
|
{
|
||||||
AddRawKeyValue(form, key, value);
|
AddRawKeyValue(form, key, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddRawKeyValue(WebROCollection form, StringBuilder key, StringBuilder value)
|
static void AddRawKeyValue(WebROCollection form, StringBuilder key, StringBuilder value)
|
||||||
{
|
{
|
||||||
string decodedKey = WebUtility.UrlDecode(key.ToString());
|
form.Add(WebUtility.UrlDecode(key.ToString()), WebUtility.UrlDecode(value.ToString()));
|
||||||
form.Add(decodedKey,
|
|
||||||
WebUtility.UrlDecode(value.ToString()));
|
|
||||||
|
|
||||||
key.Length = 0;
|
key.Length = 0;
|
||||||
value.Length = 0;
|
value.Length = 0;
|
||||||
|
@ -271,7 +298,9 @@ namespace Jellyfin.SocketSharp
|
||||||
foreach (var pair in this)
|
foreach (var pair in this)
|
||||||
{
|
{
|
||||||
if (result.Length > 0)
|
if (result.Length > 0)
|
||||||
|
{
|
||||||
result.Append('&');
|
result.Append('&');
|
||||||
|
}
|
||||||
|
|
||||||
var key = pair.Name;
|
var key = pair.Name;
|
||||||
if (key != null && key.Length > 0)
|
if (key != null && key.Length > 0)
|
||||||
|
@ -314,33 +343,52 @@ namespace Jellyfin.SocketSharp
|
||||||
public override int Read(byte[] buffer, int dest_offset, int count)
|
public override int Read(byte[] buffer, int dest_offset, int count)
|
||||||
{
|
{
|
||||||
if (buffer == null)
|
if (buffer == null)
|
||||||
|
{
|
||||||
throw new ArgumentNullException(nameof(buffer));
|
throw new ArgumentNullException(nameof(buffer));
|
||||||
|
}
|
||||||
|
|
||||||
if (dest_offset < 0)
|
if (dest_offset < 0)
|
||||||
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(dest_offset), "< 0");
|
throw new ArgumentOutOfRangeException(nameof(dest_offset), "< 0");
|
||||||
|
}
|
||||||
|
|
||||||
if (count < 0)
|
if (count < 0)
|
||||||
|
{
|
||||||
throw new ArgumentOutOfRangeException(nameof(count), "< 0");
|
throw new ArgumentOutOfRangeException(nameof(count), "< 0");
|
||||||
|
}
|
||||||
|
|
||||||
int len = buffer.Length;
|
int len = buffer.Length;
|
||||||
if (dest_offset > len)
|
if (dest_offset > len)
|
||||||
|
{
|
||||||
throw new ArgumentException("destination offset is beyond array size");
|
throw new ArgumentException("destination offset is beyond array size");
|
||||||
|
}
|
||||||
|
|
||||||
// reordered to avoid possible integer overflow
|
// reordered to avoid possible integer overflow
|
||||||
if (dest_offset > len - count)
|
if (dest_offset > len - count)
|
||||||
|
{
|
||||||
throw new ArgumentException("Reading would overrun buffer");
|
throw new ArgumentException("Reading would overrun buffer");
|
||||||
|
}
|
||||||
|
|
||||||
if (count > end - position)
|
if (count > end - position)
|
||||||
|
{
|
||||||
count = (int)(end - position);
|
count = (int)(end - position);
|
||||||
|
}
|
||||||
|
|
||||||
if (count <= 0)
|
if (count <= 0)
|
||||||
|
{
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
s.Position = position;
|
s.Position = position;
|
||||||
int result = s.Read(buffer, dest_offset, count);
|
int result = s.Read(buffer, dest_offset, count);
|
||||||
if (result > 0)
|
if (result > 0)
|
||||||
|
{
|
||||||
position += result;
|
position += result;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
position = end;
|
position = end;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -348,14 +396,20 @@ namespace Jellyfin.SocketSharp
|
||||||
public override int ReadByte()
|
public override int ReadByte()
|
||||||
{
|
{
|
||||||
if (position >= end)
|
if (position >= end)
|
||||||
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
s.Position = position;
|
s.Position = position;
|
||||||
int result = s.ReadByte();
|
int result = s.ReadByte();
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
|
{
|
||||||
position = end;
|
position = end;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
position++;
|
position++;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -380,7 +434,9 @@ namespace Jellyfin.SocketSharp
|
||||||
|
|
||||||
long virt = real - offset;
|
long virt = real - offset;
|
||||||
if (virt < 0 || virt > Length)
|
if (virt < 0 || virt > Length)
|
||||||
|
{
|
||||||
throw new ArgumentException();
|
throw new ArgumentException();
|
||||||
|
}
|
||||||
|
|
||||||
position = s.Seek(real, SeekOrigin.Begin);
|
position = s.Seek(real, SeekOrigin.Begin);
|
||||||
return position;
|
return position;
|
||||||
|
@ -410,7 +466,9 @@ namespace Jellyfin.SocketSharp
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (value > Length)
|
if (value > Length)
|
||||||
throw new ArgumentOutOfRangeException();
|
{
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(value));
|
||||||
|
}
|
||||||
|
|
||||||
position = Seek(value, SeekOrigin.Begin);
|
position = Seek(value, SeekOrigin.Begin);
|
||||||
}
|
}
|
||||||
|
@ -438,7 +496,7 @@ namespace Jellyfin.SocketSharp
|
||||||
public static readonly CultureInfo InvariantCulture = CultureInfo.InvariantCulture;
|
public static readonly CultureInfo InvariantCulture = CultureInfo.InvariantCulture;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal sealed class StrUtils
|
internal static class StrUtils
|
||||||
{
|
{
|
||||||
public static bool StartsWith(string str1, string str2, bool ignore_case)
|
public static bool StartsWith(string str1, string str2, bool ignore_case)
|
||||||
{
|
{
|
||||||
|
@ -455,11 +513,15 @@ namespace Jellyfin.SocketSharp
|
||||||
{
|
{
|
||||||
int l2 = str2.Length;
|
int l2 = str2.Length;
|
||||||
if (l2 == 0)
|
if (l2 == 0)
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int l1 = str1.Length;
|
int l1 = str1.Length;
|
||||||
if (l2 > l1)
|
if (l2 > l1)
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
|
var comparison = ignore_case ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
|
||||||
return str1.IndexOf(str2, comparison) == str1.Length - str2.Length - 1;
|
return str1.IndexOf(str2, comparison) == str1.Length - str2.Length - 1;
|
||||||
|
@ -493,7 +555,7 @@ namespace Jellyfin.SocketSharp
|
||||||
Encoding encoding;
|
Encoding encoding;
|
||||||
StringBuilder sb;
|
StringBuilder sb;
|
||||||
|
|
||||||
const byte HYPHEN = (byte)'-', LF = (byte)'\n', CR = (byte)'\r';
|
const byte LF = (byte)'\n', CR = (byte)'\r';
|
||||||
|
|
||||||
// See RFC 2046
|
// See RFC 2046
|
||||||
// In the case of multipart entities, in which one or more different
|
// In the case of multipart entities, in which one or more different
|
||||||
|
@ -520,7 +582,7 @@ namespace Jellyfin.SocketSharp
|
||||||
sb = new StringBuilder();
|
sb = new StringBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
string ReadLine()
|
private string ReadLine()
|
||||||
{
|
{
|
||||||
// CRLF or LF are ok as line endings.
|
// CRLF or LF are ok as line endings.
|
||||||
bool got_cr = false;
|
bool got_cr = false;
|
||||||
|
@ -543,58 +605,86 @@ namespace Jellyfin.SocketSharp
|
||||||
}
|
}
|
||||||
|
|
||||||
if (got_cr)
|
if (got_cr)
|
||||||
|
{
|
||||||
sb.Length--;
|
sb.Length--;
|
||||||
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static string GetContentDispositionAttribute(string l, string name)
|
private static string GetContentDispositionAttribute(string l, string name)
|
||||||
{
|
{
|
||||||
int idx = l.IndexOf(name + "=\"");
|
int idx = l.IndexOf(name + "=\"");
|
||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
int begin = idx + name.Length + "=\"".Length;
|
int begin = idx + name.Length + "=\"".Length;
|
||||||
int end = l.IndexOf('"', begin);
|
int end = l.IndexOf('"', begin);
|
||||||
if (end < 0)
|
if (end < 0)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (begin == end)
|
if (begin == end)
|
||||||
return "";
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
return l.Substring(begin, end - begin);
|
return l.Substring(begin, end - begin);
|
||||||
}
|
}
|
||||||
|
|
||||||
string GetContentDispositionAttributeWithEncoding(string l, string name)
|
private string GetContentDispositionAttributeWithEncoding(string l, string name)
|
||||||
{
|
{
|
||||||
int idx = l.IndexOf(name + "=\"");
|
int idx = l.IndexOf(name + "=\"");
|
||||||
if (idx < 0)
|
if (idx < 0)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
int begin = idx + name.Length + "=\"".Length;
|
int begin = idx + name.Length + "=\"".Length;
|
||||||
int end = l.IndexOf('"', begin);
|
int end = l.IndexOf('"', begin);
|
||||||
if (end < 0)
|
if (end < 0)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (begin == end)
|
if (begin == end)
|
||||||
return "";
|
{
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
string temp = l.Substring(begin, end - begin);
|
string temp = l.Substring(begin, end - begin);
|
||||||
byte[] source = new byte[temp.Length];
|
byte[] source = new byte[temp.Length];
|
||||||
for (int i = temp.Length - 1; i >= 0; i--)
|
for (int i = temp.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
source[i] = (byte)temp[i];
|
source[i] = (byte)temp[i];
|
||||||
|
}
|
||||||
|
|
||||||
return encoding.GetString(source, 0, source.Length);
|
return encoding.GetString(source, 0, source.Length);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadBoundary()
|
private bool ReadBoundary()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
string line = ReadLine();
|
string line = ReadLine();
|
||||||
while (line == "")
|
while (line == string.Empty)
|
||||||
|
{
|
||||||
line = ReadLine();
|
line = ReadLine();
|
||||||
|
}
|
||||||
|
|
||||||
if (line[0] != '-' || line[1] != '-')
|
if (line[0] != '-' || line[1] != '-')
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!StrUtils.EndsWith(line, boundary, false))
|
if (!StrUtils.EndsWith(line, boundary, false))
|
||||||
|
{
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
@ -603,25 +693,31 @@ namespace Jellyfin.SocketSharp
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string ReadHeaders()
|
private string ReadHeaders()
|
||||||
{
|
{
|
||||||
string s = ReadLine();
|
string s = ReadLine();
|
||||||
if (s == "")
|
if (s.Length == 0)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompareBytes(byte[] orig, byte[] other)
|
private static bool CompareBytes(byte[] orig, byte[] other)
|
||||||
{
|
{
|
||||||
for (int i = orig.Length - 1; i >= 0; i--)
|
for (int i = orig.Length - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
if (orig[i] != other[i])
|
if (orig[i] != other[i])
|
||||||
|
{
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
long MoveToNextBoundary()
|
private long MoveToNextBoundary()
|
||||||
{
|
{
|
||||||
long retval = 0;
|
long retval = 0;
|
||||||
bool got_cr = false;
|
bool got_cr = false;
|
||||||
|
@ -631,13 +727,18 @@ namespace Jellyfin.SocketSharp
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (state == 0 && c == LF)
|
if (state == 0 && c == LF)
|
||||||
{
|
{
|
||||||
retval = data.Position - 1;
|
retval = data.Position - 1;
|
||||||
if (got_cr)
|
if (got_cr)
|
||||||
|
{
|
||||||
retval--;
|
retval--;
|
||||||
|
}
|
||||||
|
|
||||||
state = 1;
|
state = 1;
|
||||||
c = data.ReadByte();
|
c = data.ReadByte();
|
||||||
}
|
}
|
||||||
|
@ -650,7 +751,9 @@ namespace Jellyfin.SocketSharp
|
||||||
{
|
{
|
||||||
c = data.ReadByte();
|
c = data.ReadByte();
|
||||||
if (c == -1)
|
if (c == -1)
|
||||||
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (c != '-')
|
if (c != '-')
|
||||||
{
|
{
|
||||||
|
@ -662,7 +765,9 @@ namespace Jellyfin.SocketSharp
|
||||||
int nread = data.Read(buffer, 0, buffer.Length);
|
int nread = data.Read(buffer, 0, buffer.Length);
|
||||||
int bl = buffer.Length;
|
int bl = buffer.Length;
|
||||||
if (nread != bl)
|
if (nread != bl)
|
||||||
|
{
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
if (!CompareBytes(boundary_bytes, buffer))
|
if (!CompareBytes(boundary_bytes, buffer))
|
||||||
{
|
{
|
||||||
|
@ -673,6 +778,7 @@ namespace Jellyfin.SocketSharp
|
||||||
data.Position++;
|
data.Position++;
|
||||||
got_cr = false;
|
got_cr = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = data.ReadByte();
|
c = data.ReadByte();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -690,12 +796,16 @@ namespace Jellyfin.SocketSharp
|
||||||
data.Position++;
|
data.Position++;
|
||||||
got_cr = false;
|
got_cr = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
c = data.ReadByte();
|
c = data.ReadByte();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
data.Position = retval + 2;
|
data.Position = retval + 2;
|
||||||
if (got_cr)
|
if (got_cr)
|
||||||
|
{
|
||||||
data.Position++;
|
data.Position++;
|
||||||
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -711,7 +821,9 @@ namespace Jellyfin.SocketSharp
|
||||||
public Element ReadNextElement()
|
public Element ReadNextElement()
|
||||||
{
|
{
|
||||||
if (at_eof || ReadBoundary())
|
if (at_eof || ReadBoundary())
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var elem = new Element();
|
var elem = new Element();
|
||||||
string header;
|
string header;
|
||||||
|
@ -734,19 +846,27 @@ namespace Jellyfin.SocketSharp
|
||||||
elem.Start = start;
|
elem.Start = start;
|
||||||
long pos = MoveToNextBoundary();
|
long pos = MoveToNextBoundary();
|
||||||
if (pos == -1)
|
if (pos == -1)
|
||||||
|
{
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
elem.Length = pos - start;
|
elem.Length = pos - start;
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
static string StripPath(string path)
|
private static string StripPath(string path)
|
||||||
{
|
{
|
||||||
if (path == null || path.Length == 0)
|
if (path == null || path.Length == 0)
|
||||||
|
{
|
||||||
return path;
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
if (path.IndexOf(":\\") != 1 && !path.StartsWith("\\\\"))
|
if (path.IndexOf(":\\", StringComparison.Ordinal) != 1
|
||||||
|
&& !path.StartsWith("\\\\", StringComparison.Ordinal))
|
||||||
|
{
|
||||||
return path;
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
return path.Substring(path.LastIndexOf('\\') + 1);
|
return path.Substring(path.LastIndexOf('\\') + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,15 +83,15 @@ namespace Jellyfin.SocketSharp
|
||||||
|
|
||||||
private void ProcessContext(HttpListenerContext context)
|
private void ProcessContext(HttpListenerContext context)
|
||||||
{
|
{
|
||||||
//InitTask(context, _disposeCancellationToken);
|
var _ = Task.Run(async () => await InitTask(context, _disposeCancellationToken));
|
||||||
Task.Run(() => InitTask(context, _disposeCancellationToken));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LogRequest(ILogger logger, HttpListenerRequest request)
|
private static void LogRequest(ILogger logger, HttpListenerRequest request)
|
||||||
{
|
{
|
||||||
var url = request.Url.ToString();
|
var url = request.Url.ToString();
|
||||||
|
|
||||||
logger.LogInformation("{0} {1}. UserAgent: {2}", request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
|
logger.LogInformation("{0} {1}. UserAgent: {2}",
|
||||||
|
request.IsWebSocketRequest ? "WS" : "HTTP " + request.HttpMethod, url, request.UserAgent ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken)
|
private Task InitTask(HttpListenerContext context, CancellationToken cancellationToken)
|
||||||
|
@ -196,7 +196,7 @@ namespace Jellyfin.SocketSharp
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ctx.Response.StatusCode = 200;
|
ctx.Response.StatusCode = statusCode;
|
||||||
ctx.Response.Close();
|
ctx.Response.Close();
|
||||||
}
|
}
|
||||||
catch (ObjectDisposedException)
|
catch (ObjectDisposedException)
|
||||||
|
|
|
@ -242,7 +242,6 @@ namespace Jellyfin.SocketSharp
|
||||||
return request.ContentType.StartsWith(contentType, StringComparison.OrdinalIgnoreCase);
|
return request.ContentType.StartsWith(contentType, StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
public const string Xml = "application/xml";
|
|
||||||
private static string GetQueryStringContentType(IRequest httpReq)
|
private static string GetQueryStringContentType(IRequest httpReq)
|
||||||
{
|
{
|
||||||
var format = httpReq.QueryString["format"];
|
var format = httpReq.QueryString["format"];
|
||||||
|
@ -250,22 +249,40 @@ namespace Jellyfin.SocketSharp
|
||||||
{
|
{
|
||||||
const int formatMaxLength = 4;
|
const int formatMaxLength = 4;
|
||||||
var pi = httpReq.PathInfo;
|
var pi = httpReq.PathInfo;
|
||||||
if (pi == null || pi.Length <= formatMaxLength) return null;
|
if (pi == null || pi.Length <= formatMaxLength)
|
||||||
if (pi[0] == '/') pi = pi.Substring(1);
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (pi[0] == '/')
|
||||||
|
{
|
||||||
|
pi = pi.Substring(1);
|
||||||
|
}
|
||||||
format = LeftPart(pi, '/');
|
format = LeftPart(pi, '/');
|
||||||
if (format.Length > formatMaxLength) return null;
|
if (format.Length > formatMaxLength)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
format = LeftPart(format, '.').ToLower();
|
format = LeftPart(format, '.').ToLower();
|
||||||
if (format.Contains("json")) return "application/json";
|
if (format.Contains("json", StringComparison.OrdinalIgnoreCase))
|
||||||
if (format.Contains("xml")) return Xml;
|
{
|
||||||
|
return "application/json";
|
||||||
|
}
|
||||||
|
if (format.Contains("xml", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "application/xml";
|
||||||
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string LeftPart(string strVal, char needle)
|
public static string LeftPart(string strVal, char needle)
|
||||||
{
|
{
|
||||||
if (strVal == null) return null;
|
if (strVal == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
var pos = strVal.IndexOf(needle);
|
var pos = strVal.IndexOf(needle);
|
||||||
return pos == -1
|
return pos == -1
|
||||||
? strVal
|
? strVal
|
||||||
|
@ -283,14 +300,14 @@ namespace Jellyfin.SocketSharp
|
||||||
{
|
{
|
||||||
var mode = HandlerFactoryPath;
|
var mode = HandlerFactoryPath;
|
||||||
|
|
||||||
var pos = request.RawUrl.IndexOf("?");
|
var pos = request.RawUrl.IndexOf("?", StringComparison.Ordinal);
|
||||||
if (pos != -1)
|
if (pos != -1)
|
||||||
{
|
{
|
||||||
var path = request.RawUrl.Substring(0, pos);
|
var path = request.RawUrl.Substring(0, pos);
|
||||||
this.pathInfo = GetPathInfo(
|
this.pathInfo = GetPathInfo(
|
||||||
path,
|
path,
|
||||||
mode,
|
mode,
|
||||||
mode ?? "");
|
mode ?? string.Empty);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -307,18 +324,27 @@ namespace Jellyfin.SocketSharp
|
||||||
private static string GetPathInfo(string fullPath, string mode, string appPath)
|
private static string GetPathInfo(string fullPath, string mode, string appPath)
|
||||||
{
|
{
|
||||||
var pathInfo = ResolvePathInfoFromMappedPath(fullPath, mode);
|
var pathInfo = ResolvePathInfoFromMappedPath(fullPath, mode);
|
||||||
if (!string.IsNullOrEmpty(pathInfo)) return pathInfo;
|
if (!string.IsNullOrEmpty(pathInfo))
|
||||||
|
{
|
||||||
|
return pathInfo;
|
||||||
|
}
|
||||||
|
|
||||||
//Wildcard mode relies on this to work out the handlerPath
|
//Wildcard mode relies on this to work out the handlerPath
|
||||||
pathInfo = ResolvePathInfoFromMappedPath(fullPath, appPath);
|
pathInfo = ResolvePathInfoFromMappedPath(fullPath, appPath);
|
||||||
if (!string.IsNullOrEmpty(pathInfo)) return pathInfo;
|
if (!string.IsNullOrEmpty(pathInfo))
|
||||||
|
{
|
||||||
|
return pathInfo;
|
||||||
|
}
|
||||||
|
|
||||||
return fullPath;
|
return fullPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string ResolvePathInfoFromMappedPath(string fullPath, string mappedPathRoot)
|
private static string ResolvePathInfoFromMappedPath(string fullPath, string mappedPathRoot)
|
||||||
{
|
{
|
||||||
if (mappedPathRoot == null) return null;
|
if (mappedPathRoot == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var sbPathInfo = new StringBuilder();
|
var sbPathInfo = new StringBuilder();
|
||||||
var fullPathParts = fullPath.Split('/');
|
var fullPathParts = fullPath.Split('/');
|
||||||
|
@ -345,7 +371,10 @@ namespace Jellyfin.SocketSharp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!pathRootFound) return null;
|
if (!pathRootFound)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
var path = sbPathInfo.ToString();
|
var path = sbPathInfo.ToString();
|
||||||
return path.Length > 1 ? path.TrimEnd('/') : "/";
|
return path.Length > 1 ? path.TrimEnd('/') : "/";
|
||||||
|
@ -400,7 +429,10 @@ namespace Jellyfin.SocketSharp
|
||||||
public static Encoding GetEncoding(string contentTypeHeader)
|
public static Encoding GetEncoding(string contentTypeHeader)
|
||||||
{
|
{
|
||||||
var param = GetParameter(contentTypeHeader, "charset=");
|
var param = GetParameter(contentTypeHeader, "charset=");
|
||||||
if (param == null) return null;
|
if (param == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return Encoding.GetEncoding(param);
|
return Encoding.GetEncoding(param);
|
||||||
|
@ -423,7 +455,9 @@ namespace Jellyfin.SocketSharp
|
||||||
if (httpFiles == null)
|
if (httpFiles == null)
|
||||||
{
|
{
|
||||||
if (files == null)
|
if (files == null)
|
||||||
return httpFiles = new IHttpFile[0];
|
{
|
||||||
|
return httpFiles = Array.Empty<IHttpFile>();
|
||||||
|
}
|
||||||
|
|
||||||
httpFiles = new IHttpFile[files.Count];
|
httpFiles = new IHttpFile[files.Count];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
|
@ -839,7 +839,7 @@ namespace MediaBrowser.Api.Library
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
_activityManager.Create(new ActivityLogEntry
|
_activityManager.CreateAsync(new ActivityLogEntry
|
||||||
{
|
{
|
||||||
Name = string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name),
|
Name = string.Format(_localization.GetLocalizedString("UserDownloadingItemWithValues"), user.Name, item.Name),
|
||||||
Type = "UserDownloadingContent",
|
Type = "UserDownloadingContent",
|
||||||
|
|
|
@ -131,7 +131,7 @@ namespace MediaBrowser.Common
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Inits this instance.
|
/// Inits this instance.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Init();
|
Task InitAsync();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the instance.
|
/// Creates the instance.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Model.Events;
|
using MediaBrowser.Model.Events;
|
||||||
using MediaBrowser.Model.Querying;
|
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Activity
|
namespace MediaBrowser.Model.Activity
|
||||||
{
|
{
|
||||||
|
@ -8,10 +9,8 @@ namespace MediaBrowser.Model.Activity
|
||||||
{
|
{
|
||||||
event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
|
event EventHandler<GenericEventArgs<ActivityLogEntry>> EntryCreated;
|
||||||
|
|
||||||
void Create(ActivityLogEntry entry);
|
Task CreateAsync(ActivityLogEntry entry);
|
||||||
|
|
||||||
QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, int? startIndex, int? limit);
|
IEnumerable<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y);
|
||||||
|
|
||||||
QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? hasUserId, int? x, int? y);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
using System;
|
using System.Linq;
|
||||||
using MediaBrowser.Model.Querying;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.Activity
|
namespace MediaBrowser.Model.Activity
|
||||||
{
|
{
|
||||||
public interface IActivityRepository
|
public interface IActivityRepository
|
||||||
{
|
{
|
||||||
void Create(ActivityLogEntry entry);
|
Task CreateAsync(ActivityLogEntry entry);
|
||||||
|
|
||||||
QueryResult<ActivityLogEntry> GetActivityLogEntries(DateTime? minDate, bool? z, int? startIndex, int? limit);
|
IQueryable<ActivityLogEntry> GetActivityLogEntries();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,8 +23,7 @@ get_version()
|
||||||
(
|
(
|
||||||
local ROOT=${1-$DEFAULT_ROOT}
|
local ROOT=${1-$DEFAULT_ROOT}
|
||||||
grep "AssemblyVersion" ${ROOT}/SharedVersion.cs \
|
grep "AssemblyVersion" ${ROOT}/SharedVersion.cs \
|
||||||
| sed -E 's/\[assembly: ?AssemblyVersion\("([0-9\.]+)"\)\]/\1/' \
|
| sed -E 's/\[assembly: ?AssemblyVersion\("([0-9\.]+)"\)\]/\1/'
|
||||||
| sed -E 's/.0$//'
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Run a build
|
# Run a build
|
||||||
|
|
|
@ -4,4 +4,15 @@ source ../common.build.sh
|
||||||
|
|
||||||
VERSION=`get_version ../..`
|
VERSION=`get_version ../..`
|
||||||
|
|
||||||
clean_jellyfin ../.. Release `pwd`/dist/jellyfin_${VERSION}
|
package_temporary_dir="`pwd`/pkg-dist-tmp"
|
||||||
|
pkg_src_dir="`pwd`/pkg-src"
|
||||||
|
image_name="jellyfin-rpmbuild"
|
||||||
|
docker_sudo=""
|
||||||
|
if ! $(id -Gn | grep -q 'docker') && [ ! ${EUID:-1000} -eq 0 ] && \
|
||||||
|
[ ! $USER == "root" ] && ! $(echo "$OSTYPE" | grep -q "darwin"); then
|
||||||
|
docker_sudo=sudo
|
||||||
|
fi
|
||||||
|
|
||||||
|
$docker_sudo docker image rm $image_name --force
|
||||||
|
rm -rf "$package_temporary_dir"
|
||||||
|
rm -rf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz"
|
||||||
|
|
|
@ -18,10 +18,15 @@ output_dir="`pwd`/pkg-dist"
|
||||||
pkg_src_dir="`pwd`/pkg-src"
|
pkg_src_dir="`pwd`/pkg-src"
|
||||||
current_user="`whoami`"
|
current_user="`whoami`"
|
||||||
image_name="jellyfin-rpmbuild"
|
image_name="jellyfin-rpmbuild"
|
||||||
|
docker_sudo=""
|
||||||
|
if ! $(id -Gn | grep -q 'docker') && [ ! ${EUID:-1000} -eq 0 ] && \
|
||||||
|
[ ! $USER == "root" ] && ! $(echo "$OSTYPE" | grep -q "darwin"); then
|
||||||
|
docker_sudo=sudo
|
||||||
|
fi
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
set +o errexit
|
set +o errexit
|
||||||
docker image rm $image_name --force
|
$docker_sudo docker image rm $image_name --force
|
||||||
rm -rf "$package_temporary_dir"
|
rm -rf "$package_temporary_dir"
|
||||||
rm -rf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz"
|
rm -rf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz"
|
||||||
}
|
}
|
||||||
|
@ -30,7 +35,7 @@ GNU_TAR=1
|
||||||
mkdir -p "$package_temporary_dir"
|
mkdir -p "$package_temporary_dir"
|
||||||
echo "Bundling all sources for RPM build."
|
echo "Bundling all sources for RPM build."
|
||||||
tar \
|
tar \
|
||||||
--transform "s,^\.,jellyfin-${VERSION}" \
|
--transform "s,^\.,jellyfin-${VERSION}," \
|
||||||
--exclude='.git*' \
|
--exclude='.git*' \
|
||||||
--exclude='**/.git' \
|
--exclude='**/.git' \
|
||||||
--exclude='**/.hg' \
|
--exclude='**/.hg' \
|
||||||
|
@ -42,10 +47,8 @@ tar \
|
||||||
--exclude='**/.nuget' \
|
--exclude='**/.nuget' \
|
||||||
--exclude='*.deb' \
|
--exclude='*.deb' \
|
||||||
--exclude='*.rpm' \
|
--exclude='*.rpm' \
|
||||||
-Jcvf \
|
-zcf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" \
|
||||||
"$package_temporary_dir/jellyfin-${VERSION}.tar.xz" \
|
-C "../.." ./ || GNU_TAR=0
|
||||||
-C "../.." \
|
|
||||||
./ || true && GNU_TAR=0
|
|
||||||
|
|
||||||
if [ $GNU_TAR -eq 0 ]; then
|
if [ $GNU_TAR -eq 0 ]; then
|
||||||
echo "The installed tar binary did not support --transform. Using workaround."
|
echo "The installed tar binary did not support --transform. Using workaround."
|
||||||
|
@ -75,9 +78,9 @@ if [ $GNU_TAR -eq 0 ]; then
|
||||||
tar -zcf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" -C "$package_temporary_dir" "jellyfin-${VERSION}"
|
tar -zcf "$pkg_src_dir/jellyfin-${VERSION}.tar.gz" -C "$package_temporary_dir" "jellyfin-${VERSION}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
docker build ../.. -t "$image_name" -f ./Dockerfile
|
$docker_sudo docker build ../.. -t "$image_name" -f ./Dockerfile
|
||||||
mkdir -p "$output_dir"
|
mkdir -p "$output_dir"
|
||||||
docker run --rm -v "$package_temporary_dir:/temp" "$image_name" sh -c 'find /build/rpmbuild -maxdepth 3 -type f -name "jellyfin*.rpm" -exec mv {} /temp \;'
|
$docker_sudo docker run --rm -v "$package_temporary_dir:/temp" "$image_name" sh -c 'find /build/rpmbuild -maxdepth 3 -type f -name "jellyfin*.rpm" -exec mv {} /temp \;'
|
||||||
chown -R "$current_user" "$package_temporary_dir" \
|
chown -R "$current_user" "$package_temporary_dir" \
|
||||||
|| sudo chown -R "$current_user" "$package_temporary_dir"
|
|| sudo chown -R "$current_user" "$package_temporary_dir"
|
||||||
mv "$package_temporary_dir"/*.rpm "$output_dir"
|
mv "$package_temporary_dir"/*.rpm "$output_dir"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue