Exemple #1
0
 public void DeleteAll()
 {
     Collections.Clear();
     PostNotification?.Invoke(new ProgressCompletionNotification {
         Text = "Deleted all collections!"
     });
 }
Exemple #2
0
        public void DeleteAll()
        {
            var maps = GetAllUsableBeatmapSets();

            if (maps.Count == 0)
            {
                return;
            }

            var notification = new ProgressNotification
            {
                Progress = 0,
                State    = ProgressNotificationState.Active,
            };

            PostNotification?.Invoke(notification);

            int i = 0;

            foreach (var b in maps)
            {
                if (notification.State == ProgressNotificationState.Cancelled)
                {
                    // user requested abort
                    return;
                }

                notification.Text     = $"Deleting ({i} of {maps.Count})";
                notification.Progress = (float)++i / maps.Count;
                Delete(b);
            }

            notification.State = ProgressNotificationState.Completed;
        }
Exemple #3
0
        public void UndeleteAll()
        {
            var deleteMaps = QueryBeatmapSets(bs => bs.DeletePending).ToList();

            if (!deleteMaps.Any())
            {
                return;
            }

            var notification = new ProgressNotification
            {
                CompletionText = "Restored all deleted beatmaps!",
                Progress       = 0,
                State          = ProgressNotificationState.Active,
            };

            PostNotification?.Invoke(notification);

            int i = 0;

            foreach (var bs in deleteMaps)
            {
                if (notification.State == ProgressNotificationState.Cancelled)
                {
                    // user requested abort
                    return;
                }

                notification.Text     = $"Restoring ({i} of {deleteMaps.Count})";
                notification.Progress = (float)++i / deleteMaps.Count;
                Undelete(bs);
            }

            notification.State = ProgressNotificationState.Completed;
        }
        public async Task Import(Stream stream)
        {
            var notification = new ProgressNotification
            {
                State = ProgressNotificationState.Active,
                Text  = "匯入圖譜收藏準備中..."
            };

            PostNotification?.Invoke(notification);

            var  collection      = readCollections(stream, notification);
            bool importCompleted = false;

            Schedule(() =>
            {
                importCollections(collection);
                importCompleted = true;
            });

            while (!IsDisposed && !importCompleted)
            {
                await Task.Delay(10);
            }

            notification.CompletionText = $"已匯入 {collection.Count} 個圖譜收藏";
            notification.State          = ProgressNotificationState.Completed;
        }
Exemple #5
0
 public async Task OnPostActionAsync(PostNotification info)
 {
     _postActionProducer.Produce(TOPIC, new Message <Null, PostNotification>()
     {
         Value = info
     });
 }
Exemple #6
0
        /// <summary>
        /// Import one or more <see cref="BeatmapSetInfo"/> from filesystem <paramref name="paths"/>.
        /// This will post notifications tracking progress.
        /// </summary>
        /// <param name="paths">One or more beatmap locations on disk.</param>
        public List <BeatmapSetInfo> Import(params string[] paths)
        {
            var notification = new ProgressNotification
            {
                Text           = "Beatmap import is initialising...",
                CompletionText = "Import successful!",
                Progress       = 0,
                State          = ProgressNotificationState.Active,
            };

            PostNotification?.Invoke(notification);

            List <BeatmapSetInfo> imported = new List <BeatmapSetInfo>();

            int i = 0;

            foreach (string path in paths)
            {
                if (notification.State == ProgressNotificationState.Cancelled)
                {
                    // user requested abort
                    return(imported);
                }

                try
                {
                    notification.Text = $"Importing ({i} of {paths.Length})\n{Path.GetFileName(path)}";
                    using (ArchiveReader reader = getReaderFrom(path))
                        imported.Add(Import(reader));

                    notification.Progress = (float)++i / paths.Length;

                    // We may or may not want to delete the file depending on where it is stored.
                    //  e.g. reconstructing/repairing database with beatmaps from default storage.
                    // Also, not always a single file, i.e. for LegacyFilesystemReader
                    // TODO: Add a check to prevent files from storage to be deleted.
                    try
                    {
                        if (File.Exists(path))
                        {
                            File.Delete(path);
                        }
                    }
                    catch (Exception e)
                    {
                        Logger.Error(e, $@"Could not delete original file after import ({Path.GetFileName(path)})");
                    }
                }
                catch (Exception e)
                {
                    e = e.InnerException ?? e;
                    Logger.Error(e, $@"Could not import beatmap set ({Path.GetFileName(path)})");
                }
            }

            notification.State = ProgressNotificationState.Completed;
            return(imported);
        }
Exemple #7
0
        public ScoreManager(RulesetStore rulesets, Func <BeatmapManager> beatmaps, Storage storage, RealmAccess realm, Scheduler scheduler,
                            BeatmapDifficultyCache difficultyCache = null, OsuConfigManager configManager = null)
            : base(storage, realm)
        {
            this.scheduler       = scheduler;
            this.difficultyCache = difficultyCache;
            this.configManager   = configManager;

            scoreImporter = new ScoreImporter(rulesets, beatmaps, storage, realm)
            {
                PostNotification = obj => PostNotification?.Invoke(obj)
            };
        }
Exemple #8
0
        public SkinManager(Storage storage, RealmAccess realm, GameHost host, IResourceStore <byte[]> resources, AudioManager audio, Scheduler scheduler)
            : base(storage, realm)
        {
            this.audio     = audio;
            this.scheduler = scheduler;
            this.host      = host;
            this.resources = resources;

            userFiles = new StorageBackedResourceStore(storage.GetStorageForDirectory("files"));

            skinImporter = new SkinImporter(storage, realm, this)
            {
                PostNotification = obj => PostNotification?.Invoke(obj),
            };

            var defaultSkins = new[]
            {
                DefaultLegacySkin = new DefaultLegacySkin(this),
                DefaultSkin       = new DefaultSkin(this),
            };

            // Ensure the default entries are present.
            realm.Write(r =>
            {
                foreach (var skin in defaultSkins)
                {
                    if (r.Find <SkinInfo>(skin.SkinInfo.ID) == null)
                    {
                        r.Add(skin.SkinInfo.Value);
                    }
                }
            });

            CurrentSkinInfo.ValueChanged += skin =>
            {
                CurrentSkin.Value = skin.NewValue.PerformRead(GetSkin);
            };

            CurrentSkin.Value         = DefaultSkin;
            CurrentSkin.ValueChanged += skin =>
            {
                if (!skin.NewValue.SkinInfo.Equals(CurrentSkinInfo.Value))
                {
                    throw new InvalidOperationException($"Setting {nameof(CurrentSkin)}'s value directly is not supported. Use {nameof(CurrentSkinInfo)} instead.");
                }

                SourceChanged?.Invoke();
            };
        }
        public async Task OnPostActionAsync(PostNotification info)
        {
            var currentDayStatistic = await _dbAccess.GetBlogDayStatisticAsync(DateTime.UtcNow.Date);

            var countDelta = info.Action switch
            {
                PostAction.CREATED => 1,
                PostAction.DELETED => - 1,
                _ => throw new NotSupportedException()
            };

            currentDayStatistic.PostsCount += countDelta;

            await _db.SaveChangesAsync();
        }
        public void DeleteVideos(List <BeatmapSetInfo> items, bool silent = false)
        {
            if (items.Count == 0)
            {
                return;
            }

            var notification = new ProgressNotification
            {
                Progress       = 0,
                Text           = $"Preparing to delete all {HumanisedModelName} videos...",
                CompletionText = "No videos found to delete!",
                State          = ProgressNotificationState.Active,
            };

            if (!silent)
            {
                PostNotification?.Invoke(notification);
            }

            int i       = 0;
            int deleted = 0;

            foreach (var b in items)
            {
                if (notification.State == ProgressNotificationState.Cancelled)
                {
                    // user requested abort
                    return;
                }

                var video = b.Files.FirstOrDefault(f => VIDEO_EXTENSIONS.Any(ex => f.Filename.EndsWith(ex, StringComparison.Ordinal)));

                if (video != null)
                {
                    DeleteFile(b, video);
                    deleted++;
                    notification.CompletionText = $"Deleted {deleted} {HumanisedModelName} video(s)!";
                }

                notification.Text = $"Deleting videos from {HumanisedModelName}s ({deleted} deleted)";

                notification.Progress = (float)++i / items.Count;
            }

            notification.State = ProgressNotificationState.Completed;
        }
Exemple #11
0
        public async Task Import(Stream stream)
        {
            var notification = new ProgressNotification
            {
                State = ProgressNotificationState.Active,
                Text  = "Collections import is initialising..."
            };

            PostNotification?.Invoke(notification);

            var collection = readCollections(stream, notification);

            await importCollections(collection);

            notification.CompletionText = $"Imported {collection.Count} collections";
            notification.State          = ProgressNotificationState.Completed;
        }
Exemple #12
0
        async Task dispatcherLoopAsync()
        {
            await ThreadingUtils.ContinueAtDedicatedThread();

            while (true)
            {
                try
                {
                    var message = _consumer.Consume();
                    var scope   = _scopeBuilder
                                  .CreateScope();
                    var controller = scope.ServiceProvider
                                     .GetRequiredService <IStatisticServiceAPI>();
                    var parameter = message.Value;
                    var handler   = getHandler();
                    executeAsync();

                    /////////////////////////////////////////////

                    Func <Task> getHandler()
                    {
                        return(message.Value switch
                        {
                            CommentaryNotification cn => () => controller.OnCommentaryActionAsync(cn),
                            PostNotification pn => () => controller.OnPostActionAsync(pn),
                            SeenNotification sn => () => controller.OnSeenAsync(sn),
                            UserNotification un => () => controller.OnUserActionAsync(un),
                            _ => throw new NotSupportedException()
                        });
                    }

                    async void executeAsync()
                    {
                        using (scope)
                        {
                            await ThreadingUtils.ContinueAtThreadPull();

                            await handler();

                            _logger.LogInformation($"Consumed message '{message.Value}' at: '{message.TopicPartitionOffset}'.");
                        }
                    }
                }
        /// <summary>
        /// Restore multiple items that were previously deleted.
        /// This will post notifications tracking progress.
        /// </summary>
        public void Undelete(List <TModel> items, bool silent = false)
        {
            if (!items.Any())
            {
                return;
            }

            var notification = new ProgressNotification
            {
                CompletionText = "Restored all deleted items!",
                Progress       = 0,
                State          = ProgressNotificationState.Active,
            };

            if (!silent)
            {
                PostNotification?.Invoke(notification);
            }

            int i = 0;

            using (ContextFactory.GetForWrite())
            {
                foreach (var item in items)
                {
                    if (notification.State == ProgressNotificationState.Cancelled)
                    {
                        // user requested abort
                        return;
                    }

                    notification.Text = $"Restoring ({++i} of {items.Count})";

                    Undelete(item);

                    notification.Progress = (float)i / items.Count;
                }
            }

            notification.State = ProgressNotificationState.Completed;
        }
        /// <summary>
        /// Delete multiple items.
        /// This will post notifications tracking progress.
        /// </summary>
        public void Delete(List <TModel> items, bool silent = false)
        {
            if (items.Count == 0)
            {
                return;
            }

            var notification = new ProgressNotification
            {
                Progress       = 0,
                CompletionText = $"Deleted all {typeof(TModel).Name.Replace("Info", "").ToLower()}s!",
                State          = ProgressNotificationState.Active,
            };

            if (!silent)
            {
                PostNotification?.Invoke(notification);
            }

            int i = 0;

            using (ContextFactory.GetForWrite())
            {
                foreach (var b in items)
                {
                    if (notification.State == ProgressNotificationState.Cancelled)
                    {
                        // user requested abort
                        return;
                    }

                    notification.Text = $"Deleting ({++i} of {items.Count})";

                    Delete(b);

                    notification.Progress = (float)i / items.Count;
                }
            }

            notification.State = ProgressNotificationState.Completed;
        }
        public void Delete(List <TModel> items, bool silent = false)
        {
            if (items.Count == 0)
            {
                return;
            }

            var notification = new ProgressNotification
            {
                Progress       = 0,
                Text           = $"Preparing to delete all {HumanisedModelName}s...",
                CompletionText = $"Deleted all {HumanisedModelName}s!",
                State          = ProgressNotificationState.Active,
            };

            if (!silent)
            {
                PostNotification?.Invoke(notification);
            }

            int i = 0;

            foreach (var b in items)
            {
                if (notification.State == ProgressNotificationState.Cancelled)
                {
                    // user requested abort
                    return;
                }

                notification.Text = $"Deleting {HumanisedModelName}s ({++i} of {items.Count})";

                Delete(b);

                notification.Progress = (float)i / items.Count;
            }

            notification.State = ProgressNotificationState.Completed;
        }
Exemple #16
0
        public BeatmapManager(Storage storage, RealmAccess realm, RulesetStore rulesets, IAPIProvider?api, AudioManager audioManager, IResourceStore <byte[]> gameResources, GameHost?host = null,
                              WorkingBeatmap?defaultBeatmap = null, bool performOnlineLookups = false)
            : base(storage, realm)
        {
            if (performOnlineLookups)
            {
                if (api == null)
                {
                    throw new ArgumentNullException(nameof(api), "API must be provided if online lookups are required.");
                }

                onlineBeatmapLookupQueue = new BeatmapOnlineLookupQueue(api, storage);
            }

            var userResources = new RealmFileStore(realm, storage).Store;

            BeatmapTrackStore = audioManager.GetTrackStore(userResources);

            beatmapImporter = CreateBeatmapImporter(storage, realm, rulesets, onlineBeatmapLookupQueue);
            beatmapImporter.PostNotification = obj => PostNotification?.Invoke(obj);

            workingBeatmapCache = CreateWorkingBeatmapCache(audioManager, gameResources, userResources, defaultBeatmap, host);
        }
Exemple #17
0
        /// <summary>
        /// Begin a download for the requested <see cref="TModel"/>.
        /// </summary>
        /// <param name="model">The <see cref="TModel"/> to be downloaded.</param>
        /// <param name="minimiseDownloadSize">Whether this download should be optimised for slow connections. Generally means extras are not included in the download bundle.</param>
        /// <returns>Whether the download was started.</returns>
        public bool Download(TModel model, bool minimiseDownloadSize = false)
        {
            if (!canDownload(model))
            {
                return(false);
            }

            var request = CreateDownloadRequest(model, minimiseDownloadSize);

            DownloadNotification notification = new DownloadNotification
            {
                Text = $"Downloading {request.Model}",
            };

            request.DownloadProgressed += progress =>
            {
                notification.State    = ProgressNotificationState.Active;
                notification.Progress = progress;
            };

            request.Success += filename =>
            {
                Task.Factory.StartNew(async() =>
                {
                    // This gets scheduled back to the update thread, but we want the import to run in the background.
                    await Import(notification, filename);
                    currentDownloads.Remove(request);
                }, TaskCreationOptions.LongRunning);
            };

            request.Failure += error =>
            {
                DownloadFailed?.Invoke(request);

                if (error is OperationCanceledException)
                {
                    return;
                }

                notification.State = ProgressNotificationState.Cancelled;
                Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!");
                currentDownloads.Remove(request);
            };

            notification.CancelRequested += () =>
            {
                request.Cancel();
                currentDownloads.Remove(request);
                notification.State = ProgressNotificationState.Cancelled;
                return(true);
            };

            currentDownloads.Add(request);
            PostNotification?.Invoke(notification);

            Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);

            DownloadBegan?.Invoke(request);

            return(true);
        }
Exemple #18
0
        /// <summary>
        /// Downloads a beatmap.
        /// </summary>
        /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
        /// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
        public void Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
        {
            var existing = GetExistingDownload(beatmapSetInfo);

            if (existing != null || api == null)
            {
                return;
            }

            if (!api.LocalUser.Value.IsSupporter)
            {
                PostNotification?.Invoke(new SimpleNotification
                {
                    Icon = FontAwesome.fa_superpowers,
                    Text = "You gotta be a supporter to download for now 'yo"
                });
                return;
            }

            ProgressNotification downloadNotification = new ProgressNotification
            {
                Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
            };

            var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);

            request.DownloadProgressed += progress =>
            {
                downloadNotification.State    = ProgressNotificationState.Active;
                downloadNotification.Progress = progress;
            };

            request.Success += data =>
            {
                downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}";

                Task.Factory.StartNew(() =>
                {
                    // This gets scheduled back to the update thread, but we want the import to run in the background.
                    using (var stream = new MemoryStream(data))
                        using (var archive = new OszArchiveReader(stream))
                            Import(archive);

                    downloadNotification.State = ProgressNotificationState.Completed;
                }, TaskCreationOptions.LongRunning);

                currentDownloads.Remove(request);
            };

            request.Failure += data =>
            {
                downloadNotification.State = ProgressNotificationState.Completed;
                Logger.Error(data, "Failed to get beatmap download information");
                currentDownloads.Remove(request);
            };

            downloadNotification.CancelRequested += () =>
            {
                request.Cancel();
                currentDownloads.Remove(request);
                downloadNotification.State = ProgressNotificationState.Cancelled;
                return(true);
            };

            currentDownloads.Add(request);
            PostNotification?.Invoke(downloadNotification);

            // don't run in the main api queue as this is a long-running task.
            Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
            BeatmapDownloadBegan?.Invoke(request);
        }
Exemple #19
0
        /// <summary>
        /// Downloads a beatmap.
        /// </summary>
        /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
        /// <returns>A new <see cref="DownloadBeatmapSetRequest"/>, or an existing one if a download is already in progress.</returns>
        public DownloadBeatmapSetRequest Download(BeatmapSetInfo beatmapSetInfo)
        {
            var existing = GetExistingDownload(beatmapSetInfo);

            if (existing != null)
            {
                return(existing);
            }

            if (api == null)
            {
                return(null);
            }

            ProgressNotification downloadNotification = new ProgressNotification
            {
                Text = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
            };

            var request = new DownloadBeatmapSetRequest(beatmapSetInfo);

            request.DownloadProgressed += progress =>
            {
                downloadNotification.State    = ProgressNotificationState.Active;
                downloadNotification.Progress = progress;
            };

            request.Success += data =>
            {
                downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}";

                Task.Factory.StartNew(() =>
                {
                    // This gets scheduled back to the update thread, but we want the import to run in the background.
                    using (var stream = new MemoryStream(data))
                        using (var archive = new OszArchiveReader(stream))
                            Import(archive);

                    downloadNotification.State = ProgressNotificationState.Completed;
                }, TaskCreationOptions.LongRunning);

                currentDownloads.Remove(request);
            };

            request.Failure += data =>
            {
                downloadNotification.State = ProgressNotificationState.Completed;
                Logger.Error(data, "Failed to get beatmap download information");
                currentDownloads.Remove(request);
            };

            downloadNotification.CancelRequested += () =>
            {
                request.Cancel();
                currentDownloads.Remove(request);
                downloadNotification.State = ProgressNotificationState.Cancelled;
                return(true);
            };

            currentDownloads.Add(request);
            PostNotification?.Invoke(downloadNotification);

            // don't run in the main api queue as this is a long-running task.
            Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);

            return(request);
        }
Exemple #20
0
        /// <summary>
        /// Downloads a beatmap.
        /// This will post notifications tracking progress.
        /// </summary>
        /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
        /// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
        /// <returns>Downloading can happen</returns>
        public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
        {
            var existing = GetExistingDownload(beatmapSetInfo);

            if (existing != null || api == null)
            {
                return(false);
            }

            var downloadNotification = new DownloadNotification
            {
                CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!",
                Text           = $"Downloading {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}",
            };

            var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);

            request.DownloadProgressed += progress =>
            {
                downloadNotification.State    = ProgressNotificationState.Active;
                downloadNotification.Progress = progress;
            };

            request.Success += filename =>
            {
                downloadNotification.Text = $"Importing {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}";

                Task.Factory.StartNew(() =>
                {
                    // This gets scheduled back to the update thread, but we want the import to run in the background.
                    var importedBeatmap = Import(filename);

                    downloadNotification.CompletionClickAction = () =>
                    {
                        PresentCompletedImport(importedBeatmap.Yield());
                        return(true);
                    };
                    downloadNotification.State = ProgressNotificationState.Completed;

                    currentDownloads.Remove(request);
                }, TaskCreationOptions.LongRunning);
            };

            request.Failure += error =>
            {
                BeatmapDownloadFailed?.Invoke(request);

                if (error is OperationCanceledException)
                {
                    return;
                }

                downloadNotification.State = ProgressNotificationState.Cancelled;
                Logger.Error(error, "Beatmap download failed!");
                currentDownloads.Remove(request);
            };

            downloadNotification.CancelRequested += () =>
            {
                request.Cancel();
                currentDownloads.Remove(request);
                downloadNotification.State = ProgressNotificationState.Cancelled;
                return(true);
            };

            currentDownloads.Add(request);
            PostNotification?.Invoke(downloadNotification);

            // don't run in the main api queue as this is a long-running task.
            Task.Factory.StartNew(() => request.Perform(api), TaskCreationOptions.LongRunning);
            BeatmapDownloadBegan?.Invoke(request);
            return(true);
        }
        /// <summary>
        /// Begin a download for the requested <typeparamref name="TModel"/>.
        /// </summary>
        /// <param name="model">The <typeparamref name="TModel"/> to be downloaded.</param>
        /// <param name="minimiseDownloadSize">Whether this download should be optimised for slow connections. Generally means extras are not included in the download bundle.</param>
        /// <returns>Whether the download was started.</returns>
        public bool Download(TModel model, bool minimiseDownloadSize = false)
        {
            if (!canDownload(model))
            {
                return(false);
            }

            var request = CreateDownloadRequest(model, minimiseDownloadSize);

            DownloadNotification notification = new DownloadNotification
            {
                Text = $"Downloading {request.Model}",
            };

            request.DownloadProgressed += progress =>
            {
                notification.State    = ProgressNotificationState.Active;
                notification.Progress = progress;
            };

            request.Success += filename =>
            {
                Task.Factory.StartNew(async() =>
                {
                    // This gets scheduled back to the update thread, but we want the import to run in the background.
                    var imported = await Import(notification, filename);

                    // for now a failed import will be marked as a failed download for simplicity.
                    if (!imported.Any())
                    {
                        downloadFailed.Value = new WeakReference <ArchiveDownloadRequest <TModel> >(request);
                    }

                    currentDownloads.Remove(request);
                }, TaskCreationOptions.LongRunning);
            };

            request.Failure += triggerFailure;

            notification.CancelRequested += () =>
            {
                request.Cancel();
                return(true);
            };

            currentDownloads.Add(request);
            PostNotification?.Invoke(notification);

            api.PerformAsync(request);

            downloadBegan.Value = new WeakReference <ArchiveDownloadRequest <TModel> >(request);
            return(true);

            void triggerFailure(Exception error)
            {
                currentDownloads.Remove(request);

                downloadFailed.Value = new WeakReference <ArchiveDownloadRequest <TModel> >(request);

                notification.State = ProgressNotificationState.Cancelled;

                if (!(error is OperationCanceledException))
                {
                    Logger.Error(error, $"{HumanisedModelName.Titleize()} download failed!");
                }
            }
        }
Exemple #22
0
        /// <summary>
        /// Downloads a beatmap.
        /// This will post notifications tracking progress.
        /// </summary>
        /// <param name="beatmapSetInfo">The <see cref="BeatmapSetInfo"/> to be downloaded.</param>
        /// <param name="noVideo">Whether the beatmap should be downloaded without video. Defaults to false.</param>
        /// <returns>Downloading can happen</returns>
        public bool Download(BeatmapSetInfo beatmapSetInfo, bool noVideo = false)
        {
            var existing = GetExistingDownload(beatmapSetInfo);

            if (existing != null || api == null)
            {
                return(false);
            }

            var downloadNotification = new DownloadNotification
            {
                Text = $"Downloading {beatmapSetInfo}",
            };

            var request = new DownloadBeatmapSetRequest(beatmapSetInfo, noVideo);

            request.DownloadProgressed += progress =>
            {
                downloadNotification.State    = ProgressNotificationState.Active;
                downloadNotification.Progress = progress;
            };

            request.Success += filename =>
            {
                Task.Factory.StartNew(() =>
                {
                    // This gets scheduled back to the update thread, but we want the import to run in the background.
                    Import(downloadNotification, filename);
                    currentDownloads.Remove(request);
                }, TaskCreationOptions.LongRunning);
            };

            request.Failure += error =>
            {
                BeatmapDownloadFailed?.Invoke(request);

                if (error is OperationCanceledException)
                {
                    return;
                }

                downloadNotification.State = ProgressNotificationState.Cancelled;
                Logger.Error(error, "Beatmap download failed!");
                currentDownloads.Remove(request);
            };

            downloadNotification.CancelRequested += () =>
            {
                request.Cancel();
                currentDownloads.Remove(request);
                downloadNotification.State = ProgressNotificationState.Cancelled;
                return(true);
            };

            currentDownloads.Add(request);
            PostNotification?.Invoke(downloadNotification);

            // don't run in the main api queue as this is a long-running task.
            Task.Factory.StartNew(() =>
            {
                try
                {
                    request.Perform(api);
                }
                catch
                {
                    // no need to handle here as exceptions will filter down to request.Failure above.
                }
            }, TaskCreationOptions.LongRunning);
            BeatmapDownloadBegan?.Invoke(request);
            return(true);
        }