add library to front page
This commit is contained in:
parent
d4d10f6e43
commit
0eaba37c11
|
@ -80,5 +80,13 @@ namespace MediaBrowser.Api.ScheduledTasks
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool dispose)
|
||||||
|
{
|
||||||
|
TaskManager.TaskExecuting -= TaskManager_TaskExecuting;
|
||||||
|
TaskManager.TaskCompleted -= TaskManager_TaskCompleted;
|
||||||
|
|
||||||
|
base.Dispose(dispose);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
using MediaBrowser.Common.Net;
|
using MediaBrowser.Common.Net;
|
||||||
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.Session;
|
using MediaBrowser.Controller.Session;
|
||||||
using MediaBrowser.Model.Logging;
|
using MediaBrowser.Model.Logging;
|
||||||
using MediaBrowser.Model.Session;
|
using MediaBrowser.Model.Session;
|
||||||
|
@ -36,6 +37,49 @@ namespace MediaBrowser.Api.WebSocket
|
||||||
: base(logger)
|
: base(logger)
|
||||||
{
|
{
|
||||||
_sessionManager = sessionManager;
|
_sessionManager = sessionManager;
|
||||||
|
|
||||||
|
_sessionManager.SessionStarted += _sessionManager_SessionStarted;
|
||||||
|
_sessionManager.SessionEnded += _sessionManager_SessionEnded;
|
||||||
|
_sessionManager.PlaybackStart += _sessionManager_PlaybackStart;
|
||||||
|
_sessionManager.PlaybackStopped += _sessionManager_PlaybackStopped;
|
||||||
|
_sessionManager.PlaybackProgress += _sessionManager_PlaybackProgress;
|
||||||
|
_sessionManager.CapabilitiesChanged += _sessionManager_CapabilitiesChanged;
|
||||||
|
_sessionManager.SessionActivity += _sessionManager_SessionActivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sessionManager_SessionActivity(object sender, SessionEventArgs e)
|
||||||
|
{
|
||||||
|
SendData(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sessionManager_CapabilitiesChanged(object sender, SessionEventArgs e)
|
||||||
|
{
|
||||||
|
SendData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sessionManager_PlaybackProgress(object sender, PlaybackProgressEventArgs e)
|
||||||
|
{
|
||||||
|
SendData(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sessionManager_PlaybackStopped(object sender, PlaybackStopEventArgs e)
|
||||||
|
{
|
||||||
|
SendData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sessionManager_PlaybackStart(object sender, PlaybackProgressEventArgs e)
|
||||||
|
{
|
||||||
|
SendData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sessionManager_SessionEnded(object sender, SessionEventArgs e)
|
||||||
|
{
|
||||||
|
SendData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _sessionManager_SessionStarted(object sender, SessionEventArgs e)
|
||||||
|
{
|
||||||
|
SendData(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -47,5 +91,26 @@ namespace MediaBrowser.Api.WebSocket
|
||||||
{
|
{
|
||||||
return Task.FromResult(_sessionManager.Sessions.Where(i => i.IsActive).Select(_sessionManager.GetSessionInfoDto));
|
return Task.FromResult(_sessionManager.Sessions.Where(i => i.IsActive).Select(_sessionManager.GetSessionInfoDto));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override bool SendOnTimer
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool dispose)
|
||||||
|
{
|
||||||
|
_sessionManager.SessionStarted -= _sessionManager_SessionStarted;
|
||||||
|
_sessionManager.SessionEnded -= _sessionManager_SessionEnded;
|
||||||
|
_sessionManager.PlaybackStart -= _sessionManager_PlaybackStart;
|
||||||
|
_sessionManager.PlaybackStopped -= _sessionManager_PlaybackStopped;
|
||||||
|
_sessionManager.PlaybackProgress -= _sessionManager_PlaybackProgress;
|
||||||
|
_sessionManager.CapabilitiesChanged -= _sessionManager_CapabilitiesChanged;
|
||||||
|
_sessionManager.SessionActivity -= _sessionManager_SessionActivity;
|
||||||
|
|
||||||
|
base.Dispose(dispose);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -765,7 +765,7 @@ namespace MediaBrowser.Common.Implementations
|
||||||
{
|
{
|
||||||
Logger.Info("Application has been updated to version {0}", package.versionStr);
|
Logger.Info("Application has been updated to version {0}", package.versionStr);
|
||||||
|
|
||||||
EventHelper.QueueEventIfNotNull(ApplicationUpdated, this, new GenericEventArgs<PackageVersionInfo>
|
EventHelper.FireEventIfNotNull(ApplicationUpdated, this, new GenericEventArgs<PackageVersionInfo>
|
||||||
{
|
{
|
||||||
Argument = package
|
Argument = package
|
||||||
|
|
||||||
|
|
|
@ -192,7 +192,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||||
/// <param name="task">The task.</param>
|
/// <param name="task">The task.</param>
|
||||||
internal void OnTaskExecuting(IScheduledTaskWorker task)
|
internal void OnTaskExecuting(IScheduledTaskWorker task)
|
||||||
{
|
{
|
||||||
EventHelper.QueueEventIfNotNull(TaskExecuting, this, new GenericEventArgs<IScheduledTaskWorker>
|
EventHelper.FireEventIfNotNull(TaskExecuting, this, new GenericEventArgs<IScheduledTaskWorker>
|
||||||
{
|
{
|
||||||
Argument = task
|
Argument = task
|
||||||
|
|
||||||
|
@ -206,7 +206,7 @@ namespace MediaBrowser.Common.Implementations.ScheduledTasks
|
||||||
/// <param name="result">The result.</param>
|
/// <param name="result">The result.</param>
|
||||||
internal void OnTaskCompleted(IScheduledTaskWorker task, TaskResult result)
|
internal void OnTaskCompleted(IScheduledTaskWorker task, TaskResult result)
|
||||||
{
|
{
|
||||||
EventHelper.QueueEventIfNotNull(TaskCompleted, task, new TaskCompletionEventArgs
|
EventHelper.FireEventIfNotNull(TaskCompleted, task, new TaskCompletionEventArgs
|
||||||
{
|
{
|
||||||
Result = result,
|
Result = result,
|
||||||
Task = task
|
Task = task
|
||||||
|
|
|
@ -52,7 +52,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
||||||
/// <param name="plugin">The plugin.</param>
|
/// <param name="plugin">The plugin.</param>
|
||||||
private void OnPluginUninstalled(IPlugin plugin)
|
private void OnPluginUninstalled(IPlugin plugin)
|
||||||
{
|
{
|
||||||
EventHelper.QueueEventIfNotNull(PluginUninstalled, this, new GenericEventArgs<IPlugin> { Argument = plugin }, _logger);
|
EventHelper.FireEventIfNotNull(PluginUninstalled, this, new GenericEventArgs<IPlugin> { Argument = plugin }, _logger);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
||||||
{
|
{
|
||||||
_logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.version, newVersion.classification);
|
_logger.Info("Plugin updated: {0} {1} {2}", newVersion.name, newVersion.version, newVersion.classification);
|
||||||
|
|
||||||
EventHelper.QueueEventIfNotNull(PluginUpdated, this, new GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> { Argument = new Tuple<IPlugin, PackageVersionInfo>(plugin, newVersion) }, _logger);
|
EventHelper.FireEventIfNotNull(PluginUpdated, this, new GenericEventArgs<Tuple<IPlugin, PackageVersionInfo>> { Argument = new Tuple<IPlugin, PackageVersionInfo>(plugin, newVersion) }, _logger);
|
||||||
|
|
||||||
_applicationHost.NotifyPendingRestart();
|
_applicationHost.NotifyPendingRestart();
|
||||||
}
|
}
|
||||||
|
@ -89,7 +89,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
||||||
{
|
{
|
||||||
_logger.Info("New plugin installed: {0} {1} {2}", package.name, package.version, package.classification);
|
_logger.Info("New plugin installed: {0} {1} {2}", package.name, package.version, package.classification);
|
||||||
|
|
||||||
EventHelper.QueueEventIfNotNull(PluginInstalled, this, new GenericEventArgs<PackageVersionInfo> { Argument = package }, _logger);
|
EventHelper.FireEventIfNotNull(PluginInstalled, this, new GenericEventArgs<PackageVersionInfo> { Argument = package }, _logger);
|
||||||
|
|
||||||
_applicationHost.NotifyPendingRestart();
|
_applicationHost.NotifyPendingRestart();
|
||||||
}
|
}
|
||||||
|
@ -403,7 +403,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
||||||
PackageVersionInfo = package
|
PackageVersionInfo = package
|
||||||
};
|
};
|
||||||
|
|
||||||
EventHelper.QueueEventIfNotNull(PackageInstalling, this, installationEventArgs, _logger);
|
EventHelper.FireEventIfNotNull(PackageInstalling, this, installationEventArgs, _logger);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -418,7 +418,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
||||||
|
|
||||||
CompletedInstallations.Add(installationInfo);
|
CompletedInstallations.Add(installationInfo);
|
||||||
|
|
||||||
EventHelper.QueueEventIfNotNull(PackageInstallationCompleted, this, installationEventArgs, _logger);
|
EventHelper.FireEventIfNotNull(PackageInstallationCompleted, this, installationEventArgs, _logger);
|
||||||
}
|
}
|
||||||
catch (OperationCanceledException)
|
catch (OperationCanceledException)
|
||||||
{
|
{
|
||||||
|
@ -429,7 +429,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
||||||
|
|
||||||
_logger.Info("Package installation cancelled: {0} {1}", package.name, package.versionStr);
|
_logger.Info("Package installation cancelled: {0} {1}", package.name, package.versionStr);
|
||||||
|
|
||||||
EventHelper.QueueEventIfNotNull(PackageInstallationCancelled, this, installationEventArgs, _logger);
|
EventHelper.FireEventIfNotNull(PackageInstallationCancelled, this, installationEventArgs, _logger);
|
||||||
|
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -442,7 +442,7 @@ namespace MediaBrowser.Common.Implementations.Updates
|
||||||
CurrentInstallations.Remove(tuple);
|
CurrentInstallations.Remove(tuple);
|
||||||
}
|
}
|
||||||
|
|
||||||
EventHelper.QueueEventIfNotNull(PackageInstallationFailed, this, new InstallationFailedEventArgs
|
EventHelper.FireEventIfNotNull(PackageInstallationFailed, this, new InstallationFailedEventArgs
|
||||||
{
|
{
|
||||||
InstallationInfo = installationInfo,
|
InstallationInfo = installationInfo,
|
||||||
Exception = ex
|
Exception = ex
|
||||||
|
|
|
@ -1422,22 +1422,34 @@ namespace MediaBrowser.Controller.Entities
|
||||||
throw new ArgumentException("Cannot call AddImages with chapter images");
|
throw new ArgumentException("Cannot call AddImages with chapter images");
|
||||||
}
|
}
|
||||||
|
|
||||||
var existingImagePaths = GetImages(imageType)
|
var existingImages = GetImages(imageType)
|
||||||
.Select(i => i.Path)
|
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var newImages = images
|
var newImageList = new List<FileSystemInfo>();
|
||||||
.Where(i => !existingImagePaths.Contains(i.FullName, StringComparer.OrdinalIgnoreCase))
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
ImageInfos.AddRange(newImages.Select(i => new ItemImageInfo
|
foreach (var newImage in images)
|
||||||
|
{
|
||||||
|
var existing = existingImages
|
||||||
|
.FirstOrDefault(i => string.Equals(i.Path, newImage.FullName, StringComparison.OrdinalIgnoreCase));
|
||||||
|
|
||||||
|
if (existing == null)
|
||||||
|
{
|
||||||
|
newImageList.Add(newImage);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
existing.DateModified = FileSystem.GetLastWriteTimeUtc(newImage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImageInfos.AddRange(newImageList.Select(i => new ItemImageInfo
|
||||||
{
|
{
|
||||||
Path = i.FullName,
|
Path = i.FullName,
|
||||||
Type = imageType,
|
Type = imageType,
|
||||||
DateModified = FileSystem.GetLastWriteTimeUtc(i)
|
DateModified = FileSystem.GetLastWriteTimeUtc(i)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return newImages.Count > 0;
|
return newImageList.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -38,6 +38,13 @@ namespace MediaBrowser.Controller.Session
|
||||||
/// Occurs when [session ended].
|
/// Occurs when [session ended].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event EventHandler<SessionEventArgs> SessionEnded;
|
event EventHandler<SessionEventArgs> SessionEnded;
|
||||||
|
|
||||||
|
event EventHandler<SessionEventArgs> SessionActivity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Occurs when [capabilities changed].
|
||||||
|
/// </summary>
|
||||||
|
event EventHandler<SessionEventArgs> CapabilitiesChanged;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the sessions.
|
/// Gets the sessions.
|
||||||
|
|
|
@ -269,8 +269,8 @@ namespace MediaBrowser.Model.Dlna
|
||||||
playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',')[0];
|
playlistItem.AudioCodec = transcodingProfile.AudioCodec.Split(',')[0];
|
||||||
playlistItem.VideoCodec = transcodingProfile.VideoCodec;
|
playlistItem.VideoCodec = transcodingProfile.VideoCodec;
|
||||||
playlistItem.Protocol = transcodingProfile.Protocol;
|
playlistItem.Protocol = transcodingProfile.Protocol;
|
||||||
playlistItem.AudioStreamIndex = options.AudioStreamIndex;
|
playlistItem.AudioStreamIndex = options.AudioStreamIndex ?? item.DefaultAudioStreamIndex;
|
||||||
playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex;
|
playlistItem.SubtitleStreamIndex = options.SubtitleStreamIndex ?? item.DefaultSubtitleStreamIndex;
|
||||||
|
|
||||||
List<ProfileCondition> videoTranscodingConditions = new List<ProfileCondition>();
|
List<ProfileCondition> videoTranscodingConditions = new List<ProfileCondition>();
|
||||||
foreach (CodecProfile i in options.Profile.CodecProfiles)
|
foreach (CodecProfile i in options.Profile.CodecProfiles)
|
||||||
|
|
|
@ -267,40 +267,55 @@ namespace MediaBrowser.Providers.Manager
|
||||||
{
|
{
|
||||||
var currentImage = item.GetImageInfo(type, 0);
|
var currentImage = item.GetImageInfo(type, 0);
|
||||||
|
|
||||||
if (currentImage == null || !string.Equals(currentImage.Path, image.FileInfo.FullName, StringComparison.OrdinalIgnoreCase))
|
if (currentImage == null)
|
||||||
{
|
{
|
||||||
item.SetImagePath(type, image.FileInfo);
|
item.SetImagePath(type, image.FileInfo);
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
else if (!string.Equals(currentImage.Path, image.FileInfo.FullName,
|
||||||
|
StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
item.SetImagePath(type, image.FileInfo);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentImage.DateModified = _fileSystem.GetLastWriteTimeUtc(image.FileInfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var backdrops = images.Where(i => i.Type == ImageType.Backdrop).ToList();
|
if (UpdateMultiImages(item, images, ImageType.Backdrop))
|
||||||
if (backdrops.Count > 0)
|
|
||||||
{
|
{
|
||||||
var foundImages = images.Where(i => i.Type == ImageType.Backdrop)
|
changed = true;
|
||||||
.Select(i => i.FileInfo)
|
|
||||||
.ToList();
|
|
||||||
|
|
||||||
if (foundImages.Count > 0)
|
|
||||||
{
|
|
||||||
if (item.AddImages(ImageType.Backdrop, foundImages))
|
|
||||||
{
|
|
||||||
changed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var hasScreenshots = item as IHasScreenshots;
|
var hasScreenshots = item as IHasScreenshots;
|
||||||
if (hasScreenshots != null)
|
if (hasScreenshots != null)
|
||||||
{
|
{
|
||||||
var foundImages = images.Where(i => i.Type == ImageType.Screenshot)
|
if (UpdateMultiImages(item, images, ImageType.Screenshot))
|
||||||
|
{
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool UpdateMultiImages(IHasImages item, List<LocalImageInfo> images, ImageType type)
|
||||||
|
{
|
||||||
|
var changed = false;
|
||||||
|
|
||||||
|
var backdrops = images.Where(i => i.Type == type).ToList();
|
||||||
|
if (backdrops.Count > 0)
|
||||||
|
{
|
||||||
|
var foundImages = images.Where(i => i.Type == type)
|
||||||
.Select(i => i.FileInfo)
|
.Select(i => i.FileInfo)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
if (foundImages.Count > 0)
|
if (foundImages.Count > 0)
|
||||||
{
|
{
|
||||||
if (item.AddImages(ImageType.Screenshot, foundImages))
|
if (item.AddImages(type, foundImages))
|
||||||
{
|
{
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||||
/// <param name="user">The user.</param>
|
/// <param name="user">The user.</param>
|
||||||
private void OnUserUpdated(User user)
|
private void OnUserUpdated(User user)
|
||||||
{
|
{
|
||||||
EventHelper.QueueEventIfNotNull(UserUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
|
EventHelper.FireEventIfNotNull(UserUpdated, this, new GenericEventArgs<User> { Argument = user }, _logger);
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,9 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
public event EventHandler<PlaybackStopEventArgs> PlaybackStopped;
|
public event EventHandler<PlaybackStopEventArgs> PlaybackStopped;
|
||||||
|
|
||||||
public event EventHandler<SessionEventArgs> SessionStarted;
|
public event EventHandler<SessionEventArgs> SessionStarted;
|
||||||
|
public event EventHandler<SessionEventArgs> CapabilitiesChanged;
|
||||||
public event EventHandler<SessionEventArgs> SessionEnded;
|
public event EventHandler<SessionEventArgs> SessionEnded;
|
||||||
|
public event EventHandler<SessionEventArgs> SessionActivity;
|
||||||
|
|
||||||
private IEnumerable<ISessionControllerFactory> _sessionFactories = new List<ISessionControllerFactory>();
|
private IEnumerable<ISessionControllerFactory> _sessionFactories = new List<ISessionControllerFactory>();
|
||||||
|
|
||||||
|
@ -224,10 +225,16 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
{
|
{
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Save this directly. No need to fire off all the events for this.
|
// Save this directly. No need to fire off all the events for this.
|
||||||
await _userRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
|
await _userRepository.SaveUser(user, CancellationToken.None).ConfigureAwait(false);
|
||||||
|
|
||||||
|
EventHelper.FireEventIfNotNull(SessionActivity, this, new SessionEventArgs
|
||||||
|
{
|
||||||
|
SessionInfo = session
|
||||||
|
|
||||||
|
}, _logger);
|
||||||
|
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -517,7 +524,7 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EventHelper.QueueEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs
|
EventHelper.FireEventIfNotNull(PlaybackProgress, this, new PlaybackProgressEventArgs
|
||||||
{
|
{
|
||||||
Item = libraryItem,
|
Item = libraryItem,
|
||||||
Users = users,
|
Users = users,
|
||||||
|
@ -1127,6 +1134,12 @@ namespace MediaBrowser.Server.Implementations.Session
|
||||||
|
|
||||||
session.PlayableMediaTypes = capabilities.PlayableMediaTypes;
|
session.PlayableMediaTypes = capabilities.PlayableMediaTypes;
|
||||||
session.SupportedCommands = capabilities.SupportedCommands;
|
session.SupportedCommands = capabilities.SupportedCommands;
|
||||||
|
|
||||||
|
EventHelper.FireEventIfNotNull(CapabilitiesChanged, this, new SessionEventArgs
|
||||||
|
{
|
||||||
|
SessionInfo = session
|
||||||
|
|
||||||
|
}, _logger);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SessionInfoDto GetSessionInfoDto(SessionInfo session)
|
public SessionInfoDto GetSessionInfoDto(SessionInfo session)
|
||||||
|
|
Loading…
Reference in New Issue
Block a user