mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-07-09 03:04:24 -04:00
fixed issue of not seeing network shares
This commit is contained in:
parent
5843c0936c
commit
35d9b29c97
@ -207,11 +207,6 @@ namespace MediaBrowser.Api
|
|||||||
{
|
{
|
||||||
var entries = new DirectoryInfo(request.Path).EnumerateFileSystemInfos().Where(i =>
|
var entries = new DirectoryInfo(request.Path).EnumerateFileSystemInfos().Where(i =>
|
||||||
{
|
{
|
||||||
if (i.Attributes.HasFlag(FileAttributes.System))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!request.IncludeHidden && i.Attributes.HasFlag(FileAttributes.Hidden))
|
if (!request.IncludeHidden && i.Attributes.HasFlag(FileAttributes.Hidden))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
@ -73,23 +73,23 @@ namespace MediaBrowser.Controller.Drawing
|
|||||||
{
|
{
|
||||||
// http://msdn.microsoft.com/en-us/library/system.drawing.graphics.fromimage.aspx
|
// http://msdn.microsoft.com/en-us/library/system.drawing.graphics.fromimage.aspx
|
||||||
|
|
||||||
if (format.HasFlag(PixelFormat.Indexed))
|
if (format == PixelFormat.Indexed)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (format.HasFlag(PixelFormat.Undefined))
|
if (format == PixelFormat.Undefined)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (format.HasFlag(PixelFormat.DontCare))
|
if (format == PixelFormat.DontCare)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (format.HasFlag(PixelFormat.Format16bppArgb1555))
|
if (format == PixelFormat.Format16bppArgb1555)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (format.HasFlag(PixelFormat.Format16bppGrayScale))
|
if (format == PixelFormat.Format16bppGrayScale)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -160,7 +160,7 @@ namespace MediaBrowser.Controller.Drawing
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here
|
// Graphics.FromImage will throw an exception if the PixelFormat is Indexed, so we need to handle that here
|
||||||
var thumbnail = bmp.PixelFormat.HasFlag(PixelFormat.Indexed) ? new Bitmap(croppedWidth, croppedHeight) : new Bitmap(croppedWidth, croppedHeight, bmp.PixelFormat);
|
var thumbnail = bmp.PixelFormat == PixelFormat.Indexed ? new Bitmap(croppedWidth, croppedHeight) : new Bitmap(croppedWidth, croppedHeight, bmp.PixelFormat);
|
||||||
|
|
||||||
// Preserve the original resolution
|
// Preserve the original resolution
|
||||||
thumbnail.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
|
thumbnail.SetResolution(bmp.HorizontalResolution, bmp.VerticalResolution);
|
||||||
|
@ -279,7 +279,7 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
// Record the name of each file
|
// Record the name of each file
|
||||||
// Need to sort these because accoring to msdn docs, our i/o methods are not guaranteed in any order
|
// Need to sort these because accoring to msdn docs, our i/o methods are not guaranteed in any order
|
||||||
foreach (var file in ResolveArgs.FileSystemChildren
|
foreach (var file in ResolveArgs.FileSystemChildren
|
||||||
.Where(i => !i.Attributes.HasFlag(FileAttributes.System))
|
.Where(i => (i.Attributes & FileAttributes.System) != FileAttributes.System)
|
||||||
.OrderBy(f => f.Name))
|
.OrderBy(f => f.Name))
|
||||||
{
|
{
|
||||||
sb.Append(file.Name);
|
sb.Append(file.Name);
|
||||||
@ -363,12 +363,15 @@ namespace MediaBrowser.Controller.Entities
|
|||||||
return new ItemResolveArgs(ConfigurationManager.ApplicationPaths);
|
return new ItemResolveArgs(ConfigurationManager.ApplicationPaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isDirectory = false;
|
||||||
|
|
||||||
if (UseParentPathToCreateResolveArgs)
|
if (UseParentPathToCreateResolveArgs)
|
||||||
{
|
{
|
||||||
path = System.IO.Path.GetDirectoryName(path);
|
path = System.IO.Path.GetDirectoryName(path);
|
||||||
|
isDirectory = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
pathInfo = pathInfo ?? FileSystem.GetFileSystemInfo(path);
|
pathInfo = pathInfo ?? (isDirectory ? new DirectoryInfo(path) : FileSystem.GetFileSystemInfo(path));
|
||||||
|
|
||||||
if (pathInfo == null || !pathInfo.Exists)
|
if (pathInfo == null || !pathInfo.Exists)
|
||||||
{
|
{
|
||||||
|
@ -35,7 +35,7 @@ namespace MediaBrowser.Controller.IO
|
|||||||
|
|
||||||
foreach (var entry in entries)
|
foreach (var entry in entries)
|
||||||
{
|
{
|
||||||
var isDirectory = entry.Attributes.HasFlag(FileAttributes.Directory);
|
var isDirectory = (entry.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
|
||||||
|
|
||||||
if (resolveShortcuts && FileSystem.IsShortcut(entry.FullName))
|
if (resolveShortcuts && FileSystem.IsShortcut(entry.FullName))
|
||||||
{
|
{
|
||||||
|
@ -216,103 +216,6 @@ namespace MediaBrowser.Controller.IO
|
|||||||
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.MAX_ALTERNATE)]
|
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.MAX_ALTERNATE)]
|
||||||
public string cAlternate;
|
public string cAlternate;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether this instance is hidden.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if this instance is hidden; otherwise, <c>false</c>.</value>
|
|
||||||
public bool IsHidden
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return dwFileAttributes.HasFlag(FileAttributes.Hidden);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether this instance is system file.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if this instance is system file; otherwise, <c>false</c>.</value>
|
|
||||||
public bool IsSystemFile
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return dwFileAttributes.HasFlag(FileAttributes.System);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether this instance is directory.
|
|
||||||
/// </summary>
|
|
||||||
/// <value><c>true</c> if this instance is directory; otherwise, <c>false</c>.</value>
|
|
||||||
public bool IsDirectory
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return dwFileAttributes.HasFlag(FileAttributes.Directory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the creation time UTC.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The creation time UTC.</value>
|
|
||||||
public DateTime CreationTimeUtc
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return ParseFileTime(ftCreationTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the last access time UTC.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The last access time UTC.</value>
|
|
||||||
public DateTime LastAccessTimeUtc
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return ParseFileTime(ftLastAccessTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the last write time UTC.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The last write time UTC.</value>
|
|
||||||
public DateTime LastWriteTimeUtc
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
return ParseFileTime(ftLastWriteTime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Parses the file time.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="filetime">The filetime.</param>
|
|
||||||
/// <returns>DateTime.</returns>
|
|
||||||
private DateTime ParseFileTime(FILETIME filetime)
|
|
||||||
{
|
|
||||||
long highBits = filetime.dwHighDateTime;
|
|
||||||
highBits = highBits << 32;
|
|
||||||
|
|
||||||
var val = highBits + (long) filetime.dwLowDateTime;
|
|
||||||
|
|
||||||
if (val < 0L)
|
|
||||||
{
|
|
||||||
return DateTime.MinValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (val > 2650467743999999999L)
|
|
||||||
{
|
|
||||||
return DateTime.MaxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DateTime.FromFileTimeUtc(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the path.
|
/// Gets or sets the path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -26,12 +26,11 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a path into a BaseItem
|
/// Resolves a path into a BaseItem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="parent">The parent.</param>
|
|
||||||
/// <param name="fileInfo">The file info.</param>
|
/// <param name="fileInfo">The file info.</param>
|
||||||
|
/// <param name="parent">The parent.</param>
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
/// <exception cref="System.ArgumentNullException"></exception>
|
/// <exception cref="System.ArgumentNullException"></exception>
|
||||||
BaseItem ResolvePath(string path, Folder parent = null, FileSystemInfo fileInfo = null);
|
BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a set of files into a list of BaseItem
|
/// Resolves a set of files into a list of BaseItem
|
||||||
|
@ -68,7 +68,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return FileInfo.Attributes.HasFlag(FileAttributes.Directory);
|
return (FileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,7 +80,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return FileInfo.Attributes.HasFlag(FileAttributes.Hidden);
|
return (FileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return FileInfo.Attributes.HasFlag(FileAttributes.System);
|
return (FileInfo.Attributes & FileAttributes.System) == FileAttributes.System;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -213,7 +213,7 @@ namespace MediaBrowser.Controller.Library
|
|||||||
/// <exception cref="System.IO.FileNotFoundException"></exception>
|
/// <exception cref="System.IO.FileNotFoundException"></exception>
|
||||||
public void AddMetadataFile(string path)
|
public void AddMetadataFile(string path)
|
||||||
{
|
{
|
||||||
var file = FileSystem.GetFileSystemInfo(path);
|
var file = new FileInfo(path);
|
||||||
|
|
||||||
if (!file.Exists)
|
if (!file.Exists)
|
||||||
{
|
{
|
||||||
|
@ -197,12 +197,17 @@ namespace MediaBrowser.Controller.Library
|
|||||||
{
|
{
|
||||||
var attributes = child.Attributes;
|
var attributes = child.Attributes;
|
||||||
|
|
||||||
if (attributes.HasFlag(FileAttributes.Hidden) || attributes.HasFlag(FileAttributes.System))
|
if ((attributes & FileAttributes.Hidden) == FileAttributes.Hidden)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attributes.HasFlag(FileAttributes.Directory))
|
if ((attributes & FileAttributes.System) == FileAttributes.System)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((attributes & FileAttributes.Directory) == FileAttributes.Directory)
|
||||||
{
|
{
|
||||||
if (IsSeasonFolder(child.FullName))
|
if (IsSeasonFolder(child.FullName))
|
||||||
{
|
{
|
||||||
|
@ -96,7 +96,7 @@ namespace MediaBrowser.Controller.Resolvers
|
|||||||
}
|
}
|
||||||
|
|
||||||
// See if a different path came out of the resolver than what went in
|
// See if a different path came out of the resolver than what went in
|
||||||
if (!args.Path.Equals(item.Path, StringComparison.OrdinalIgnoreCase))
|
if (!string.Equals(args.Path, item.Path, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
var childData = args.IsDirectory ? args.GetFileSystemEntryByPath(item.Path) : null;
|
var childData = args.IsDirectory ? args.GetFileSystemEntryByPath(item.Path) : null;
|
||||||
|
|
||||||
|
@ -47,20 +47,30 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
var parentFolderName = Path.GetFileName(Path.GetDirectoryName(args.Path));
|
var parentFolderName = Path.GetFileName(Path.GetDirectoryName(args.Path));
|
||||||
|
|
||||||
if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase) || string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drives will sometimes be hidden
|
// Drives will sometimes be hidden
|
||||||
if (args.Path.EndsWith(":\\", StringComparison.OrdinalIgnoreCase))
|
if (args.Path.EndsWith(Path.VolumeSeparatorChar + "\\", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
if (new DriveInfo(args.Path).IsReady)
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shares will sometimes be hidden
|
||||||
|
if (args.Path.StartsWith("\\", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// Look for a share, e.g. \\server\movies
|
||||||
|
// Is there a better way to detect if a path is a share without using native code?
|
||||||
|
if (args.Path.Substring(2).Split(Path.DirectorySeparatorChar).Length == 2)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Error("Operating system reports drive is not ready: {0}", args.Path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -446,24 +446,21 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Resolves a path into a BaseItem
|
/// Resolves a path into a BaseItem
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path.</param>
|
|
||||||
/// <param name="parent">The parent.</param>
|
|
||||||
/// <param name="fileInfo">The file info.</param>
|
/// <param name="fileInfo">The file info.</param>
|
||||||
|
/// <param name="parent">The parent.</param>
|
||||||
/// <returns>BaseItem.</returns>
|
/// <returns>BaseItem.</returns>
|
||||||
/// <exception cref="System.ArgumentNullException"></exception>
|
/// <exception cref="System.ArgumentNullException"></exception>
|
||||||
public BaseItem ResolvePath(string path, Folder parent = null, FileSystemInfo fileInfo = null)
|
public BaseItem ResolvePath(FileSystemInfo fileInfo, Folder parent = null)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(path))
|
if (fileInfo == null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException();
|
throw new ArgumentNullException("fileInfo");
|
||||||
}
|
}
|
||||||
|
|
||||||
fileInfo = fileInfo ?? FileSystem.GetFileSystemInfo(path);
|
|
||||||
|
|
||||||
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths)
|
var args = new ItemResolveArgs(ConfigurationManager.ApplicationPaths)
|
||||||
{
|
{
|
||||||
Parent = parent,
|
Parent = parent,
|
||||||
Path = path,
|
Path = fileInfo.FullName,
|
||||||
FileInfo = fileInfo
|
FileInfo = fileInfo
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -534,7 +531,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var item = ResolvePath(f.FullName, parent, f) as T;
|
var item = ResolvePath(f, parent) as T;
|
||||||
|
|
||||||
if (item != null)
|
if (item != null)
|
||||||
{
|
{
|
||||||
@ -567,7 +564,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
Directory.CreateDirectory(rootFolderPath);
|
Directory.CreateDirectory(rootFolderPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
var rootFolder = RetrieveItem(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(rootFolderPath);
|
var rootFolder = RetrieveItem(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath));
|
||||||
|
|
||||||
// Add in the plug-in folders
|
// Add in the plug-in folders
|
||||||
foreach (var child in PluginFolderCreators)
|
foreach (var child in PluginFolderCreators)
|
||||||
@ -585,7 +582,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
/// <returns>UserRootFolder.</returns>
|
/// <returns>UserRootFolder.</returns>
|
||||||
public UserRootFolder GetUserRootFolder(string userRootPath)
|
public UserRootFolder GetUserRootFolder(string userRootPath)
|
||||||
{
|
{
|
||||||
return _userRootFolders.GetOrAdd(userRootPath, key => RetrieveItem(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ?? (UserRootFolder)ResolvePath(userRootPath));
|
return _userRootFolders.GetOrAdd(userRootPath, key => RetrieveItem(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ?? (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -56,7 +56,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||||||
if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path))
|
if (string.IsNullOrEmpty(item.Name) && !string.IsNullOrEmpty(item.Path))
|
||||||
{
|
{
|
||||||
//we use our resolve args name here to get the name of the containg folder, not actual video file
|
//we use our resolve args name here to get the name of the containg folder, not actual video file
|
||||||
item.Name = GetMBName(item.ResolveArgs.FileInfo.Name, item.ResolveArgs.FileInfo.Attributes.HasFlag(FileAttributes.Directory));
|
item.Name = GetMBName(item.ResolveArgs.FileInfo.Name, (item.ResolveArgs.FileInfo.Attributes & FileAttributes.Directory) == FileAttributes.Directory);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +40,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If we contain an album assume we are an artist folder
|
// If we contain an album assume we are an artist folder
|
||||||
return args.FileSystemChildren.Where(i => i.Attributes.HasFlag(FileAttributes.Directory)).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName)) ? new MusicArtist() : null;
|
return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName)) ? new MusicArtist() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -135,7 +135,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
|||||||
// Loop through each child file/folder and see if we find a video
|
// Loop through each child file/folder and see if we find a video
|
||||||
foreach (var child in args.FileSystemChildren)
|
foreach (var child in args.FileSystemChildren)
|
||||||
{
|
{
|
||||||
if (child.Attributes.HasFlag(FileAttributes.Directory))
|
if ((child.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
|
||||||
{
|
{
|
||||||
if (IsDvdDirectory(child.Name))
|
if (IsDvdDirectory(child.Name))
|
||||||
{
|
{
|
||||||
|
Loading…
x
Reference in New Issue
Block a user