update task buttons

pull/702/head
Luke Pulverenti 9 years ago
parent 7bc370bdc7
commit b7e5e21c97

@ -4,6 +4,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Net;
using MediaBrowser.Controller.Session;
using MediaBrowser.Model.Logging;
using ServiceStack.Text.Controller;
using ServiceStack.Web;
using System;
using System.Collections.Generic;
@ -21,7 +22,7 @@ namespace MediaBrowser.Api
/// </summary>
/// <value>The logger.</value>
public ILogger Logger { get; set; }
/// <summary>
/// Gets or sets the HTTP result factory.
/// </summary>
@ -143,7 +144,7 @@ namespace MediaBrowser.Api
{
if (!string.IsNullOrEmpty(parentId))
{
var folder = (Folder) libraryManager.GetItemById(new Guid(parentId));
var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
if (userId.HasValue)
{
@ -287,6 +288,20 @@ namespace MediaBrowser.Api
}) ?? name;
}
protected string GetPathValue(int index)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var first = pathInfo.GetArgumentValue<string>(0);
// backwards compatibility
if (string.Equals(first, "mediabrowser", StringComparison.OrdinalIgnoreCase))
{
index++;
}
return pathInfo.GetArgumentValue<string>(index);
}
/// <summary>
/// Gets the name of the item by.
/// </summary>
@ -294,7 +309,6 @@ namespace MediaBrowser.Api
/// <param name="type">The type.</param>
/// <param name="libraryManager">The library manager.</param>
/// <returns>Task{BaseItem}.</returns>
/// <exception cref="System.ArgumentException"></exception>
protected BaseItem GetItemByName(string name, string type, ILibraryManager libraryManager)
{
BaseItem item;

@ -143,8 +143,7 @@ namespace MediaBrowser.Api
public void Post(UpdateNamedConfiguration request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var key = pathInfo.GetArgumentValue<string>(2);
var key = GetPathValue(2);
var configurationType = _configurationManager.GetConfigurationType(key);
var configuration = _jsonSerializer.DeserializeFromStream(request.RequestStream, configurationType);

@ -120,8 +120,7 @@ namespace MediaBrowser.Api.Dlna
private async Task<ControlResponse> PostAsync(Stream requestStream, IUpnpService service)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = pathInfo.GetArgumentValue<string>(2);
var id = GetPathValue(2);
using (var reader = new StreamReader(requestStream))
{

@ -396,8 +396,7 @@ namespace MediaBrowser.Api.Images
public object Get(GetItemByNameImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var type = pathInfo.GetArgumentValue<string>(0);
var type = GetPathValue(0);
var item = GetItemByName(request.Name, type, _libraryManager);
@ -406,8 +405,7 @@ namespace MediaBrowser.Api.Images
public object Head(GetItemByNameImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var type = pathInfo.GetArgumentValue<string>(0);
var type = GetPathValue(0);
var item = GetItemByName(request.Name, type, _libraryManager);
@ -420,10 +418,9 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param>
public void Post(PostUserImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
var id = new Guid(GetPathValue(1));
request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(3), true);
request.Type = (ImageType)Enum.Parse(typeof(ImageType), GetPathValue(3), true);
var item = _userManager.GetUserById(id);
@ -438,10 +435,9 @@ namespace MediaBrowser.Api.Images
/// <param name="request">The request.</param>
public void Post(PostItemImage request)
{
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
var id = new Guid(GetPathValue(1));
request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(3), true);
request.Type = (ImageType)Enum.Parse(typeof(ImageType), GetPathValue(3), true);
var item = _libraryManager.GetItemById(id);

@ -824,7 +824,7 @@ namespace MediaBrowser.Api.Playback
{
get
{
return false;
return true;
}
}

@ -236,8 +236,7 @@ namespace MediaBrowser.Api
{
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
var id = new Guid(GetPathValue(1));
var plugin = _appHost.Plugins.First(p => p.Id == id);

@ -224,8 +224,7 @@ namespace MediaBrowser.Api.ScheduledTasks
{
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = pathInfo.GetArgumentValue<string>(1);
var id = GetPathValue(1);
var task = TaskManager.ScheduledTasks.FirstOrDefault(i => string.Equals(i.Id, id));

@ -449,8 +449,7 @@ namespace MediaBrowser.Api
{
// We need to parse this manually because we told service stack not to with IRequiresRequestStream
// https://code.google.com/p/servicestack/source/browse/trunk/Common/ServiceStack.Text/ServiceStack.Text/Controller/PathInfo.cs
var pathInfo = PathInfo.Parse(Request.PathInfo);
var id = new Guid(pathInfo.GetArgumentValue<string>(1));
var id = new Guid(GetPathValue(1));
var dtoUser = request;

@ -301,7 +301,7 @@ namespace MediaBrowser.Controller.Entities
public override bool IsVisible(User user)
{
if (this is ICollectionFolder)
if (this is ICollectionFolder && !(this is BasePluginFolder))
{
if (user.Policy.BlockedMediaFolders != null)
{

@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;

namespace MediaBrowser.Controller.Entities
{
/// <summary>
@ -10,10 +8,5 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public interface ISupportsBoxSetGrouping
{
/// <summary>
/// Gets or sets the box set identifier list.
/// </summary>
/// <value>The box set identifier list.</value>
List<Guid> BoxSetIdList { get; set; }
}
}

@ -4,13 +4,13 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Querying;
using MediaBrowser.Model.Users;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Model.Users;
namespace MediaBrowser.Controller.Entities.Movies
{

@ -25,12 +25,6 @@ namespace MediaBrowser.Controller.Entities.Movies
public List<Guid> ThemeVideoIds { get; set; }
public List<string> ProductionLocations { get; set; }
/// <summary>
/// This is just a cache to enable quick access by Id
/// </summary>
[IgnoreDataMember]
public List<Guid> BoxSetIdList { get; set; }
public Movie()
{
SpecialFeatureIds = new List<Guid>();
@ -40,7 +34,6 @@ namespace MediaBrowser.Controller.Entities.Movies
RemoteTrailerIds = new List<Guid>();
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
BoxSetIdList = new List<Guid>();
Taglines = new List<string>();
Keywords = new List<string>();
ProductionLocations = new List<string>();

@ -132,61 +132,74 @@ namespace MediaBrowser.Dlna
{
if (!string.IsNullOrWhiteSpace(profileInfo.DeviceDescription))
{
if (deviceInfo.DeviceDescription == null || !Regex.IsMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
if (deviceInfo.DeviceDescription == null || !IsRegexMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.FriendlyName))
{
if (deviceInfo.FriendlyName == null || !Regex.IsMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
if (deviceInfo.FriendlyName == null || !IsRegexMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.Manufacturer))
{
if (deviceInfo.Manufacturer == null || !Regex.IsMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
if (deviceInfo.Manufacturer == null || !IsRegexMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ManufacturerUrl))
{
if (deviceInfo.ManufacturerUrl == null || !Regex.IsMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
if (deviceInfo.ManufacturerUrl == null || !IsRegexMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelDescription))
{
if (deviceInfo.ModelDescription == null || !Regex.IsMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
if (deviceInfo.ModelDescription == null || !IsRegexMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelName))
{
if (deviceInfo.ModelName == null || !Regex.IsMatch(deviceInfo.ModelName, profileInfo.ModelName))
if (deviceInfo.ModelName == null || !IsRegexMatch(deviceInfo.ModelName, profileInfo.ModelName))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelNumber))
{
if (deviceInfo.ModelNumber == null || !Regex.IsMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
if (deviceInfo.ModelNumber == null || !IsRegexMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.ModelUrl))
{
if (deviceInfo.ModelUrl == null || !Regex.IsMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
if (deviceInfo.ModelUrl == null || !IsRegexMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
return false;
}
if (!string.IsNullOrWhiteSpace(profileInfo.SerialNumber))
{
if (deviceInfo.SerialNumber == null || !Regex.IsMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
if (deviceInfo.SerialNumber == null || !IsRegexMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
return false;
}
return true;
}
private bool IsRegexMatch(string input, string pattern)
{
try
{
return Regex.IsMatch(input, pattern);
}
catch (ArgumentException ex)
{
_logger.ErrorException("Error evaluating regex pattern {0}", ex, pattern);
return false;
}
}
public DeviceProfile GetProfile(IDictionary<string, string> headers)
{
if (headers == null)

@ -497,9 +497,6 @@
<Compile Include="..\MediaBrowser.Model\Dto\UserItemDataDto.cs">
<Link>Dto\UserItemDataDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\VideoStreamOptions.cs">
<Link>Dto\VideoStreamOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Entities\BaseItemInfo.cs">
<Link>Entities\BaseItemInfo.cs</Link>
</Compile>

@ -462,9 +462,6 @@
<Compile Include="..\MediaBrowser.Model\Dto\UserItemDataDto.cs">
<Link>Dto\UserItemDataDto.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Dto\VideoStreamOptions.cs">
<Link>Dto\VideoStreamOptions.cs</Link>
</Compile>
<Compile Include="..\MediaBrowser.Model\Entities\BaseItemInfo.cs">
<Link>Entities\BaseItemInfo.cs</Link>
</Compile>

@ -1316,24 +1316,6 @@ namespace MediaBrowser.Model.ApiClient
/// <returns>Task&lt;QueryResult&lt;BaseItemDto&gt;&gt;.</returns>
Task<QueryResult<BaseItemDto>> GetPlaylistItems(PlaylistItemQuery query);
/// <summary>
/// Gets the url needed to stream a video file
/// </summary>
/// <param name="options">The options.</param>
/// <returns>System.String.</returns>
/// <exception cref="ArgumentNullException">options</exception>
[Obsolete]
string GetVideoStreamUrl(VideoStreamOptions options);
/// <summary>
/// Formulates a url for streaming video using the HLS protocol
/// </summary>
/// <param name="options">The options.</param>
/// <returns>System.String.</returns>
/// <exception cref="ArgumentNullException">options</exception>
[Obsolete]
string GetHlsVideoStreamUrl(VideoStreamOptions options);
/// <summary>
/// Sends the context message asynchronous.
/// </summary>

@ -1,158 +0,0 @@
using System;
namespace MediaBrowser.Model.Dto
{
/// <summary>
/// Class VideoStreamOptions
/// </summary>
[Obsolete]
public class VideoStreamOptions
{
/// <summary>
/// Gets or sets the audio bit rate.
/// </summary>
/// <value>The audio bit rate.</value>
public int? AudioBitRate { get; set; }
/// <summary>
/// Gets or sets the audio codec.
/// Omit to copy the original stream
/// </summary>
/// <value>The audio encoding format.</value>
public string AudioCodec { get; set; }
/// <summary>
/// Gets or sets the item id.
/// </summary>
/// <value>The item id.</value>
public string ItemId { get; set; }
/// <summary>
/// Gets or sets the max audio channels.
/// </summary>
/// <value>The max audio channels.</value>
public int? MaxAudioChannels { get; set; }
/// <summary>
/// Gets or sets the max audio sample rate.
/// </summary>
/// <value>The max audio sample rate.</value>
public int? MaxAudioSampleRate { get; set; }
/// <summary>
/// Gets or sets the start time ticks.
/// </summary>
/// <value>The start time ticks.</value>
public long? StartTimeTicks { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the original media should be served statically
/// Only used with progressive streaming
/// </summary>
/// <value><c>true</c> if static; otherwise, <c>false</c>.</value>
public bool? Static { get; set; }
/// <summary>
/// Gets or sets the output file extension.
/// </summary>
/// <value>The output file extension.</value>
public string OutputFileExtension { get; set; }
/// <summary>
/// Gets or sets the device id.
/// </summary>
/// <value>The device id.</value>
public string DeviceId { get; set; }
/// <summary>
/// Gets or sets the video codec.
/// Omit to copy
/// </summary>
/// <value>The video codec.</value>
public string VideoCodec { get; set; }
/// <summary>
/// Gets or sets the video bit rate.
/// </summary>
/// <value>The video bit rate.</value>
public int? VideoBitRate { get; set; }
/// <summary>
/// Gets or sets the width.
/// </summary>
/// <value>The width.</value>
public int? Width { get; set; }
/// <summary>
/// Gets or sets the height.
/// </summary>
/// <value>The height.</value>
public int? Height { get; set; }
/// <summary>
/// Gets or sets the width of the max.
/// </summary>
/// <value>The width of the max.</value>
public int? MaxWidth { get; set; }
/// <summary>
/// Gets or sets the height of the max.
/// </summary>
/// <value>The height of the max.</value>
public int? MaxHeight { get; set; }
/// <summary>
/// Gets or sets the frame rate.
/// </summary>
/// <value>The frame rate.</value>
public double? FrameRate { get; set; }
/// <summary>
/// Gets or sets the index of the audio stream.
/// </summary>
/// <value>The index of the audio stream.</value>
public int? AudioStreamIndex { get; set; }
/// <summary>
/// Gets or sets the index of the video stream.
/// </summary>
/// <value>The index of the video stream.</value>
public int? VideoStreamIndex { get; set; }
/// <summary>
/// Gets or sets the index of the subtitle stream.
/// </summary>
/// <value>The index of the subtitle stream.</value>
public int? SubtitleStreamIndex { get; set; }
/// <summary>
/// Gets or sets the profile.
/// </summary>
/// <value>The profile.</value>
public string Profile { get; set; }
/// <summary>
/// Gets or sets the level.
/// </summary>
/// <value>The level.</value>
public string Level { get; set; }
/// <summary>
/// Gets or sets the baseline stream audio bit rate.
/// </summary>
/// <value>The baseline stream audio bit rate.</value>
public int? BaselineStreamAudioBitRate { get; set; }
/// <summary>
/// Gets or sets a value indicating whether [append baseline stream].
/// </summary>
/// <value><c>true</c> if [append baseline stream]; otherwise, <c>false</c>.</value>
public bool AppendBaselineStream { get; set; }
/// <summary>
/// Gets or sets the time stamp offset ms. Only used with HLS.
/// </summary>
/// <value>The time stamp offset ms.</value>
public int? TimeStampOffsetMs { get; set; }
}
}

@ -135,7 +135,6 @@
<Compile Include="Dto\NameValuePair.cs" />
<Compile Include="MediaInfo\LiveMediaInfoResult.cs" />
<Compile Include="Dto\MediaSourceType.cs" />
<Compile Include="Dto\VideoStreamOptions.cs" />
<Compile Include="Configuration\DynamicDayOfWeek.cs" />
<Compile Include="Entities\ExtraType.cs" />
<Compile Include="Entities\SupporterInfo.cs" />

@ -167,18 +167,6 @@ namespace MediaBrowser.Server.Implementations.Collections
}
list.Add(LinkedChild.Create(item));
var supportsGrouping = item as ISupportsBoxSetGrouping;
if (supportsGrouping != null)
{
var boxsetIdList = supportsGrouping.BoxSetIdList.ToList();
if (!boxsetIdList.Contains(collectionId))
{
boxsetIdList.Add(collectionId);
}
supportsGrouping.BoxSetIdList = boxsetIdList;
}
}
collection.LinkedChildren.AddRange(list);
@ -228,15 +216,6 @@ namespace MediaBrowser.Server.Implementations.Collections
{
itemList.Add(childItem);
}
var supportsGrouping = childItem as ISupportsBoxSetGrouping;
if (supportsGrouping != null)
{
var boxsetIdList = supportsGrouping.BoxSetIdList.ToList();
boxsetIdList.Remove(collectionId);
supportsGrouping.BoxSetIdList = boxsetIdList;
}
}
var shortcutFiles = Directory
@ -289,29 +268,40 @@ namespace MediaBrowser.Server.Implementations.Collections
public IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user)
{
var itemsToCollapse = new List<ISupportsBoxSetGrouping>();
var boxsets = new List<BaseItem>();
var results = new Dictionary<Guid, BaseItem>();
var allBoxsets = new List<BoxSet>();
var list = items.ToList();
foreach (var item in list.OfType<ISupportsBoxSetGrouping>())
foreach (var item in items)
{
var currentBoxSets = item.BoxSetIdList
.Select(i => _libraryManager.GetItemById(i))
.Where(i => i != null && i.IsVisible(user))
.ToList();
var grouping = item as ISupportsBoxSetGrouping;
if (currentBoxSets.Count > 0)
if (grouping == null)
{
results[item.Id] = item;
}
else
{
itemsToCollapse.Add(item);
boxsets.AddRange(currentBoxSets);
var itemId = item.Id;
var currentBoxSets = allBoxsets
.Where(i => i.GetLinkedChildren().Any(j => j.Id == itemId))
.ToList();
if (currentBoxSets.Count > 0)
{
foreach (var boxset in currentBoxSets)
{
results[boxset.Id] = boxset;
}
}
else
{
results[item.Id] = item;
}
}
}
return list
.Except(itemsToCollapse.Cast<BaseItem>())
.Concat(boxsets)
.DistinctBy(i => i.Id);
return results.Values;
}
}
}

@ -1,50 +0,0 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
public class BoxSetPostScanTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
public BoxSetPostScanTask(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var items = _libraryManager.RootFolder.RecursiveChildren.ToList();
var boxsets = items.OfType<BoxSet>().ToList();
var numComplete = 0;
foreach (var boxset in boxsets)
{
foreach (var child in boxset.Children.Concat(boxset.GetLinkedChildren()).OfType<ISupportsBoxSetGrouping>())
{
var boxsetIdList = child.BoxSetIdList.ToList();
if (!boxsetIdList.Contains(boxset.Id))
{
boxsetIdList.Add(boxset.Id);
}
child.BoxSetIdList = boxsetIdList;
}
numComplete++;
double percent = numComplete;
percent /= boxsets.Count;
progress.Report(percent * 100);
}
progress.Report(100);
return Task.FromResult(true);
}
}
}

@ -45,6 +45,8 @@
"ButtonHelp": "Help",
"ButtonSave": "Save",
"HeaderDevices": "Devices",
"ButtonScheduledTasks": "Scheduled tasks",
"ConfirmMessageScheduledTaskButton": "This operation normally runs automatically as a scheduled task. It can also be run manually here. To configure the scheduled task, see:",
"HeaderSupporterBenefit": "A supporter membership provides additional benefits such as access to premium plugins, internet channel content, and more. {0}Learn more{1}.",
"HeaderWelcomeToMediaBrowserServerDashboard": "Welcome to the Media Browser Dashboard",
"HeaderWelcomeToMediaBrowserWebClient": "Welcome to the Media Browser Web Client",
@ -59,7 +61,7 @@
"ButtonCancelItem": "Cancel item",
"ButtonQueueForRetry": "Queue for retry",
"ButtonReenable": "Re-enable",
"SyncJobItemStatusSyncedMarkForRemoval": "Marked for removal",
"SyncJobItemStatusSyncedMarkForRemoval": "Marked for removal",
"LabelAbortedByServerShutdown": "(Aborted by server shutdown)",
"LabelScheduledTaskLastRan": "Last ran {0}, taking {1}.",
"HeaderDeleteTaskTrigger": "Delete Task Trigger",
@ -71,8 +73,8 @@
"LabelFree": "Free",
"HeaderSelectAudio": "Select Audio",
"HeaderSelectSubtitles": "Select Subtitles",
"ButtonMarkForRemoval": "Mark for removal from device",
"ButtonUnmarkForRemoval": "Unmark for removal from device",
"ButtonMarkForRemoval": "Mark for removal from device",
"ButtonUnmarkForRemoval": "Unmark for removal from device",
"LabelDefaultStream": "(Default)",
"LabelForcedStream": "(Forced)",
"LabelDefaultForcedStream": "(Default/Forced)",

@ -209,7 +209,6 @@
<Compile Include="Library\UserViewManager.cs" />
<Compile Include="Library\Validators\ArtistsPostScanTask.cs" />
<Compile Include="Library\Validators\ArtistsValidator.cs" />
<Compile Include="Library\Validators\BoxSetPostScanTask.cs" />
<Compile Include="Library\Validators\GameGenresPostScanTask.cs" />
<Compile Include="Library\Validators\GameGenresValidator.cs" />
<Compile Include="Library\Validators\GenresPostScanTask.cs" />

Loading…
Cancel
Save