add options for media in archives
This commit is contained in:
parent
0024aa44ee
commit
51b5e379d5
|
@ -186,9 +186,9 @@ namespace MediaBrowser.Api
|
|||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPackages request)
|
||||
public async Task<object> Get(GetPackages request)
|
||||
{
|
||||
var packages = _installationManager.GetAvailablePackages(CancellationToken.None, request.PackageType, _appHost.ApplicationVersion).Result;
|
||||
var packages = await _installationManager.GetAvailablePackages(CancellationToken.None, request.PackageType, _appHost.ApplicationVersion).ConfigureAwait(false);
|
||||
|
||||
if (!string.IsNullOrEmpty(request.TargetSystems))
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using MediaBrowser.Common;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Common.Security;
|
||||
using MediaBrowser.Common.Updates;
|
||||
|
@ -13,6 +14,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Api
|
||||
{
|
||||
|
@ -155,10 +157,31 @@ namespace MediaBrowser.Api
|
|||
/// </summary>
|
||||
/// <param name="request">The request.</param>
|
||||
/// <returns>System.Object.</returns>
|
||||
public object Get(GetPlugins request)
|
||||
public async Task<object> Get(GetPlugins request)
|
||||
{
|
||||
var result = _appHost.Plugins.OrderBy(p => p.Name).Select(p => p.GetPluginInfo()).ToList();
|
||||
|
||||
// Don't fail just on account of image url's
|
||||
try
|
||||
{
|
||||
var packages = (await _installationManager.GetAvailablePackagesWithoutRegistrationInfo(CancellationToken.None))
|
||||
.ToList();
|
||||
|
||||
foreach (var plugin in result)
|
||||
{
|
||||
var pkg = packages.FirstOrDefault(i => !string.IsNullOrWhiteSpace(i.guid) && new Guid(plugin.Id).Equals(new Guid(i.guid)));
|
||||
|
||||
if (pkg != null)
|
||||
{
|
||||
plugin.ImageUrl = pkg.thumbImage;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return ToOptimizedSerializedResultUsingCache(result);
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,14 @@ namespace MediaBrowser.LocalMetadata
|
|||
}
|
||||
}
|
||||
|
||||
public int Order
|
||||
{
|
||||
get
|
||||
{
|
||||
// After Nfo
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class XmlProviderUtils
|
||||
|
|
|
@ -175,6 +175,9 @@ namespace MediaBrowser.Model.Configuration
|
|||
|
||||
public NameValuePair[] ContentTypes { get; set; }
|
||||
|
||||
public bool EnableAudioArchiveFiles { get; set; }
|
||||
public bool EnableVideoArchiveFiles { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
|
||||
/// </summary>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using MediaBrowser.Model.Updates;
|
||||
using System;
|
||||
using System;
|
||||
|
||||
namespace MediaBrowser.Model.Plugins
|
||||
{
|
||||
|
@ -49,5 +48,10 @@ namespace MediaBrowser.Model.Plugins
|
|||
/// </summary>
|
||||
/// <value>The unique id.</value>
|
||||
public string Id { get; set; }
|
||||
/// <summary>
|
||||
/// Gets or sets the image URL.
|
||||
/// </summary>
|
||||
/// <value>The image URL.</value>
|
||||
public string ImageUrl { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ using System.IO;
|
|||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Server.Implementations.Library;
|
||||
|
||||
namespace MediaBrowser.Server.Implementations.FileOrganization
|
||||
{
|
||||
|
@ -55,7 +56,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization
|
|||
FileSize = new FileInfo(path).Length
|
||||
};
|
||||
|
||||
var resolver = new Naming.TV.EpisodeResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var namingOptions = ((LibraryManager) _libraryManager).GetNamingOptions();
|
||||
var resolver = new Naming.TV.EpisodeResolver(namingOptions, new Naming.Logging.NullLogger());
|
||||
|
||||
var episodeInfo = resolver.Resolve(path, FileInfoType.File) ??
|
||||
new Naming.TV.EpisodeInfo();
|
||||
|
|
|
@ -1754,24 +1754,24 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
|
||||
public bool IsVideoFile(string path)
|
||||
{
|
||||
var resolver = new VideoResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var resolver = new VideoResolver(GetNamingOptions(), new Naming.Logging.NullLogger());
|
||||
return resolver.IsVideoFile(path);
|
||||
}
|
||||
|
||||
public bool IsAudioFile(string path)
|
||||
{
|
||||
var parser = new AudioFileParser(new ExtendedNamingOptions());
|
||||
var parser = new AudioFileParser(GetNamingOptions());
|
||||
return parser.IsAudioFile(path);
|
||||
}
|
||||
|
||||
public int? GetSeasonNumberFromPath(string path)
|
||||
{
|
||||
return new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(path, true, true).SeasonNumber;
|
||||
return new SeasonPathParser(GetNamingOptions(), new RegexProvider()).Parse(path, true, true).SeasonNumber;
|
||||
}
|
||||
|
||||
public bool FillMissingEpisodeNumbersFromPath(Episode episode)
|
||||
{
|
||||
var resolver = new EpisodeResolver(new ExtendedNamingOptions(),
|
||||
var resolver = new EpisodeResolver(GetNamingOptions(),
|
||||
new Naming.Logging.NullLogger());
|
||||
|
||||
var fileType = episode.VideoType == VideoType.BluRay || episode.VideoType == VideoType.Dvd || episode.VideoType == VideoType.HdDvd ?
|
||||
|
@ -1889,9 +1889,28 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
return changed;
|
||||
}
|
||||
|
||||
public NamingOptions GetNamingOptions()
|
||||
{
|
||||
var options = new ExtendedNamingOptions();
|
||||
|
||||
if (!ConfigurationManager.Configuration.EnableAudioArchiveFiles)
|
||||
{
|
||||
options.AudioFileExtensions.Remove(".rar");
|
||||
options.AudioFileExtensions.Remove(".zip");
|
||||
}
|
||||
|
||||
if (!ConfigurationManager.Configuration.EnableVideoArchiveFiles)
|
||||
{
|
||||
options.VideoFileExtensions.Remove(".rar");
|
||||
options.VideoFileExtensions.Remove(".zip");
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
public ItemLookupInfo ParseName(string name)
|
||||
{
|
||||
var resolver = new VideoResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var resolver = new VideoResolver(GetNamingOptions(), new Naming.Logging.NullLogger());
|
||||
|
||||
var result = resolver.CleanDateTime(name);
|
||||
var cleanName = resolver.CleanString(result.Name);
|
||||
|
@ -1910,7 +1929,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
|
||||
.ToList();
|
||||
|
||||
var videoListResolver = new VideoListResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var videoListResolver = new VideoListResolver(GetNamingOptions(), new Naming.Logging.NullLogger());
|
||||
|
||||
var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new PortableFileInfo
|
||||
{
|
||||
|
@ -1963,7 +1982,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
.SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly))
|
||||
.ToList();
|
||||
|
||||
var videoListResolver = new VideoListResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var videoListResolver = new VideoListResolver(GetNamingOptions(), new Naming.Logging.NullLogger());
|
||||
|
||||
var videos = videoListResolver.Resolve(fileSystemChildren.Select(i => new PortableFileInfo
|
||||
{
|
||||
|
@ -2001,7 +2020,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
|||
|
||||
private void SetExtraTypeFromFilename(Video item)
|
||||
{
|
||||
var resolver = new ExtraResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger(), new RegexProvider());
|
||||
var resolver = new ExtraResolver(GetNamingOptions(), new Naming.Logging.NullLogger(), new RegexProvider());
|
||||
|
||||
var result = resolver.GetExtraInfo(item.Path);
|
||||
|
||||
|
|
|
@ -74,14 +74,10 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
|||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="directoryService">The directory service.</param>
|
||||
/// <param name="logger">The logger.</param>
|
||||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <returns><c>true</c> if [is music album] [the specified data]; otherwise, <c>false</c>.</returns>
|
||||
public static bool IsMusicAlbum(string path, IDirectoryService directoryService, ILogger logger, IFileSystem fileSystem,
|
||||
ILibraryManager libraryManager)
|
||||
public bool IsMusicAlbum(string path, IDirectoryService directoryService)
|
||||
{
|
||||
return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, logger, fileSystem, libraryManager);
|
||||
return ContainsMusic(directoryService.GetFileSystemEntries(path), true, directoryService, _logger, _fileSystem, _libraryManager);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -111,7 +107,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
|||
/// <param name="fileSystem">The file system.</param>
|
||||
/// <param name="libraryManager">The library manager.</param>
|
||||
/// <returns><c>true</c> if the specified list contains music; otherwise, <c>false</c>.</returns>
|
||||
private static bool ContainsMusic(IEnumerable<FileSystemInfo> list,
|
||||
private bool ContainsMusic(IEnumerable<FileSystemInfo> list,
|
||||
bool allowSubfolders,
|
||||
IDirectoryService directoryService,
|
||||
ILogger logger,
|
||||
|
@ -169,9 +165,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
|||
return discSubfolderCount > 0;
|
||||
}
|
||||
|
||||
private static bool IsMultiDiscFolder(string path)
|
||||
private bool IsMultiDiscFolder(string path)
|
||||
{
|
||||
var parser = new AlbumParser(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
|
||||
|
||||
var parser = new AlbumParser(namingOptions, new Naming.Logging.NullLogger());
|
||||
var result = parser.ParseMultiPart(path);
|
||||
|
||||
return result.IsMultiPart;
|
||||
|
|
|
@ -70,9 +70,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Audio
|
|||
}
|
||||
|
||||
var directoryService = args.DirectoryService;
|
||||
|
||||
|
||||
var albumResolver = new MusicAlbumResolver(_logger, _fileSystem, _libraryManager);
|
||||
|
||||
// If we contain an album assume we are an artist folder
|
||||
return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => MusicAlbumResolver.IsMusicAlbum(i.FullName, directoryService, _logger, _fileSystem, _libraryManager)) ? new MusicArtist() : null;
|
||||
return args.FileSystemChildren.Where(i => (i.Attributes & FileAttributes.Directory) == FileAttributes.Directory).Any(i => albumResolver.IsMusicAlbum(i.FullName, directoryService)) ? new MusicArtist() : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -44,8 +44,10 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
|
|||
protected TVideoType ResolveVideo<TVideoType>(ItemResolveArgs args, bool parseName)
|
||||
where TVideoType : Video, new()
|
||||
{
|
||||
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
|
||||
|
||||
// If the path is a file check for a matching extensions
|
||||
var parser = new Naming.Video.VideoResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var parser = new Naming.Video.VideoResolver(namingOptions, new Naming.Logging.NullLogger());
|
||||
|
||||
if (args.IsDirectory)
|
||||
{
|
||||
|
@ -229,7 +231,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
|
|||
|
||||
protected void Set3DFormat(Video video)
|
||||
{
|
||||
var resolver = new Format3DParser(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
|
||||
|
||||
var resolver = new Format3DParser(namingOptions, new Naming.Logging.NullLogger());
|
||||
var result = resolver.Parse(video.Path);
|
||||
|
||||
Set3DFormat(video, result.Is3D, result.Format3D);
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Common.Extensions;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Controller.Resolvers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Naming.Common;
|
||||
using MediaBrowser.Naming.IO;
|
||||
using MediaBrowser.Naming.Video;
|
||||
using System;
|
||||
|
@ -68,6 +69,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
|||
return ResolveVideos<Video>(parent, files, directoryService, collectionType, false);
|
||||
}
|
||||
|
||||
if (parent is Series || parent.Parents.OfType<Series>().Any())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return ResolveVideos<Movie>(parent, files, directoryService, collectionType, false);
|
||||
}
|
||||
|
||||
|
@ -92,6 +98,10 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
|||
if ((child.Attributes & FileAttributes.Directory) == FileAttributes.Directory)
|
||||
{
|
||||
leftOver.Add(child);
|
||||
}
|
||||
else if (IsIgnored(child.Name))
|
||||
{
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -99,7 +109,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
|||
}
|
||||
}
|
||||
|
||||
var resolver = new VideoListResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
|
||||
|
||||
var resolver = new VideoListResolver(namingOptions, new Naming.Logging.NullLogger());
|
||||
var resolverResult = resolver.Resolve(files.Select(i => new PortableFileInfo
|
||||
{
|
||||
FullName = i.FullName,
|
||||
|
@ -173,6 +185,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
|||
return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
|
||||
}
|
||||
|
||||
if (args.HasParent<Series>())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
|
||||
}
|
||||
|
||||
|
@ -209,7 +226,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
|||
}
|
||||
else if (string.IsNullOrEmpty(collectionType))
|
||||
{
|
||||
item = ResolveVideo<Movie>(args, true);
|
||||
if (args.HasParent<Series>())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
item = ResolveVideo<Video>(args, false);
|
||||
}
|
||||
|
||||
if (item != null)
|
||||
|
@ -220,6 +242,22 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
|||
return item;
|
||||
}
|
||||
|
||||
private bool IsIgnored(string filename)
|
||||
{
|
||||
// Ignore samples
|
||||
var sampleFilename = " " + filename.Replace(".", " ", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("-", " ", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("_", " ", StringComparison.OrdinalIgnoreCase)
|
||||
.Replace("!", " ", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (sampleFilename.IndexOf(" sample ", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the initial item values.
|
||||
/// </summary>
|
||||
|
@ -397,7 +435,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
|
|||
return null;
|
||||
}
|
||||
|
||||
var resolver = new StackResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var namingOptions = ((LibraryManager)LibraryManager).GetNamingOptions();
|
||||
var resolver = new StackResolver(namingOptions, new Naming.Logging.NullLogger());
|
||||
|
||||
var result = resolver.ResolveDirectories(folderPaths);
|
||||
|
||||
|
|
|
@ -16,13 +16,16 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
|
|||
/// </summary>
|
||||
private readonly IServerConfigurationManager _config;
|
||||
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SeasonResolver"/> class.
|
||||
/// </summary>
|
||||
/// <param name="config">The config.</param>
|
||||
public SeasonResolver(IServerConfigurationManager config)
|
||||
public SeasonResolver(IServerConfigurationManager config, ILibraryManager libraryManager)
|
||||
{
|
||||
_config = config;
|
||||
_libraryManager = libraryManager;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -34,9 +37,11 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
|
|||
{
|
||||
if (args.Parent is Series && args.IsDirectory)
|
||||
{
|
||||
var namingOptions = ((LibraryManager)_libraryManager).GetNamingOptions();
|
||||
|
||||
var season = new Season
|
||||
{
|
||||
IndexNumber = new SeasonPathParser(new ExtendedNamingOptions(), new RegexProvider()).Parse(args.Path, true, true).SeasonNumber
|
||||
IndexNumber = new SeasonPathParser(namingOptions, new RegexProvider()).Parse(args.Path, true, true).SeasonNumber
|
||||
};
|
||||
|
||||
if (season.IndexNumber.HasValue && season.IndexNumber.Value == 0)
|
||||
|
|
|
@ -141,7 +141,9 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
|
|||
return true;
|
||||
}
|
||||
|
||||
var episodeResolver = new Naming.TV.EpisodeResolver(new ExtendedNamingOptions(), new Naming.Logging.NullLogger());
|
||||
var namingOptions = ((LibraryManager)libraryManager).GetNamingOptions();
|
||||
|
||||
var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions, new Naming.Logging.NullLogger());
|
||||
var episodeInfo = episodeResolver.Resolve(fullName, FileInfoType.File, false);
|
||||
if (episodeInfo != null && episodeInfo.EpisodeNumber.HasValue)
|
||||
{
|
||||
|
|
|
@ -262,10 +262,13 @@
|
|||
"MessagePleaseEnterNameOrId": "Please enter a name or an external Id.",
|
||||
"MessageValueNotCorrect": "The value entered is not correct. Please try again.",
|
||||
"MessageItemSaved": "Item saved.",
|
||||
"MessagePleaseAcceptTermsOfServiceBeforeContinuing": "Please accept the terms of service before continuing.",
|
||||
"OptionEnded": "Ended",
|
||||
"OptionContinuing": "Continuing",
|
||||
"OptionOff": "Off",
|
||||
"OptionOn": "On",
|
||||
"ButtonSettings": "Settings",
|
||||
"ButtonUninstall": "Uninstall",
|
||||
"HeaderFields": "Fields",
|
||||
"HeaderFieldsHelp": "Slide a field to 'off' to lock it and prevent it's data from being changed.",
|
||||
"HeaderLiveTV": "Live TV",
|
||||
|
@ -674,5 +677,5 @@
|
|||
"SyncJobItemStatusFailed": "Failed",
|
||||
"SyncJobItemStatusRemovedFromDevice": "Removed from device",
|
||||
"SyncJobItemStatusCancelled": "Cancelled",
|
||||
"MessageJobItemHasNoActions": "d"
|
||||
"MessageJobItemHasNoActions": "d"
|
||||
}
|
||||
|
|
|
@ -43,8 +43,13 @@
|
|||
"ButtonOk": "Ok",
|
||||
"ButtonCancel": "Cancel",
|
||||
"ButtonNew": "New",
|
||||
"HeaderTV": "TV",
|
||||
"HeaderAudio": "Audio",
|
||||
"HeaderVideo": "Video",
|
||||
"OptionDetectArchiveFilesAsMedia": "Detect archive files as media",
|
||||
"OptionDetectArchiveFilesAsMediaHelp": "If enabled, files with .rar and .zip extensions will be detected as media files.",
|
||||
"LabelEnterConnectUserName": "User name or email:",
|
||||
"LabelEnterConnectUserNameHelp": "This is your Media Browser online account user name or password.",
|
||||
"LabelEnterConnectUserNameHelp": "This is your Media Browser online account user name or password.",
|
||||
"HeaderSyncJobInfo": "Sync Job",
|
||||
"FolderTypeMixed": "Mixed content",
|
||||
"FolderTypeMovies": "Movies",
|
||||
|
|
Loading…
Reference in New Issue
Block a user