From e0d28564d0d69b4132f6dea94f1e2a162d18c2e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 14 Feb 2018 20:26:49 +0900 Subject: [PATCH] Move import logic to shared implementation --- osu.Desktop/OsuGameDesktop.cs | 25 ++- osu.Desktop/Program.cs | 2 +- .../Beatmaps/IO/ImportBeatmapTest.cs | 8 +- .../Beatmaps/ArchiveModelImportManager.cs | 181 ++++++++++++++++ osu.Game/Beatmaps/BeatmapManager.cs | 194 ++++-------------- osu.Game/Beatmaps/BeatmapSetFileInfo.cs | 3 +- osu.Game/Beatmaps/BeatmapSetInfo.cs | 3 +- osu.Game/Beatmaps/BeatmapStore.cs | 3 +- osu.Game/Beatmaps/ICanImportArchives.cs | 9 + osu.Game/Database/INamedFileInfo.cs | 13 ++ osu.Game/IO/IAddableStore.cs | 14 ++ osu.Game/IO/IHasFiles.cs | 9 + ...CChannel.cs => ArchiveImportIPCChannel.cs} | 21 +- osu.Game/Online/API/APIDownloadRequest.cs | 30 +++ osu.Game/Online/API/APIRequest.cs | 26 --- osu.Game/osu.Game.csproj | 8 +- 16 files changed, 340 insertions(+), 209 deletions(-) create mode 100644 osu.Game/Beatmaps/ArchiveModelImportManager.cs create mode 100644 osu.Game/Beatmaps/ICanImportArchives.cs create mode 100644 osu.Game/Database/INamedFileInfo.cs create mode 100644 osu.Game/IO/IAddableStore.cs create mode 100644 osu.Game/IO/IHasFiles.cs rename osu.Game/IPC/{BeatmapIPCChannel.cs => ArchiveImportIPCChannel.cs} (57%) create mode 100644 osu.Game/Online/API/APIDownloadRequest.cs diff --git a/osu.Desktop/OsuGameDesktop.cs b/osu.Desktop/OsuGameDesktop.cs index f37282366a..c563201f0a 100644 --- a/osu.Desktop/OsuGameDesktop.cs +++ b/osu.Desktop/OsuGameDesktop.cs @@ -111,14 +111,23 @@ namespace osu.Desktop { var filePaths = new [] { e.FileName }; - if (filePaths.All(f => Path.GetExtension(f) == @".osz")) - Task.Factory.StartNew(() => BeatmapManager.Import(filePaths), TaskCreationOptions.LongRunning); - else if (filePaths.All(f => Path.GetExtension(f) == @".osr")) - Task.Run(() => - { - var score = ScoreStore.ReadReplayFile(filePaths.First()); - Schedule(() => LoadScore(score)); - }); + var firstExtension = Path.GetExtension(filePaths.First()); + + if (filePaths.Any(f => Path.GetExtension(f) != firstExtension)) return; + + switch (firstExtension) + { + case ".osz": + Task.Factory.StartNew(() => BeatmapManager.Import(filePaths), TaskCreationOptions.LongRunning); + return; + case ".osr": + Task.Run(() => + { + var score = ScoreStore.ReadReplayFile(filePaths.First()); + Schedule(() => LoadScore(score)); + }); + return; + } } private static readonly string[] allowed_extensions = { @".osz", @".osr" }; diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 9760538197..048fe93c11 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -22,7 +22,7 @@ namespace osu.Desktop { if (!host.IsPrimaryInstance) { - var importer = new BeatmapIPCChannel(host); + var importer = new ArchiveImportIPCChannel(host); // Restore the cwd so relative paths given at the command line work correctly Directory.SetCurrentDirectory(cwd); foreach (var file in args) diff --git a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs index cade50a9f3..6428881b54 100644 --- a/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs +++ b/osu.Game.Tests/Beatmaps/IO/ImportBeatmapTest.cs @@ -165,7 +165,7 @@ namespace osu.Game.Tests.Beatmaps.IO var temp = prepareTempCopy(osz_path); Assert.IsTrue(File.Exists(temp)); - var importer = new BeatmapIPCChannel(client); + var importer = new ArchiveImportIPCChannel(client); if (!importer.ImportAsync(temp).Wait(10000)) Assert.Fail(@"IPC took too long to send"); @@ -209,7 +209,11 @@ namespace osu.Game.Tests.Beatmaps.IO Assert.IsTrue(File.Exists(temp)); - var imported = osu.Dependencies.Get().Import(temp); + var manager = osu.Dependencies.Get(); + + manager.Import(temp); + + var imported = manager.GetAllUsableBeatmapSets(); ensureLoaded(osu); diff --git a/osu.Game/Beatmaps/ArchiveModelImportManager.cs b/osu.Game/Beatmaps/ArchiveModelImportManager.cs new file mode 100644 index 0000000000..af0cdad0a3 --- /dev/null +++ b/osu.Game/Beatmaps/ArchiveModelImportManager.cs @@ -0,0 +1,181 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Ionic.Zip; +using osu.Framework.Logging; +using osu.Framework.Platform; +using osu.Game.Beatmaps.IO; +using osu.Game.Database; +using osu.Game.IO; +using osu.Game.IPC; +using osu.Game.Overlays.Notifications; +using FileInfo = osu.Game.IO.FileInfo; + +namespace osu.Game.Beatmaps +{ + public abstract class ArchiveModelImportManager : ICanImportArchives + where TModel : class, IHasFiles + where TFileModel : INamedFileInfo, new() + { + /// + /// Set an endpoint for notifications to be posted to. + /// + public Action PostNotification { protected get; set; } + + public virtual string[] HandledExtensions => new[] { ".zip" }; + + protected readonly FileStore Files; + + protected readonly IDatabaseContextFactory ContextFactory; + + protected readonly IAddableStore ModelStore; + + // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) + private ArchiveImportIPCChannel ipc; + + protected ArchiveModelImportManager(Storage storage, IDatabaseContextFactory contextFactory, IAddableStore modelStore, IIpcHost importHost = null) + { + ContextFactory = contextFactory; + ModelStore = modelStore; + Files = new FileStore(contextFactory, storage); + + if (importHost != null) + ipc = new ArchiveImportIPCChannel(importHost, this); + } + + /// + /// Import one or more from filesystem . + /// This will post notifications tracking progress. + /// + /// One or more beatmap locations on disk. + public void Import(params string[] paths) + { + var notification = new ProgressNotification + { + Text = "Import is initialising...", + CompletionText = "Import successful!", + Progress = 0, + State = ProgressNotificationState.Active, + }; + + PostNotification?.Invoke(notification); + + List imported = new List(); + + int i = 0; + foreach (string path in paths) + { + if (notification.State == ProgressNotificationState.Cancelled) + // user requested abort + return; + + 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; + } + + /// + /// Import a model from an . + /// + /// The beatmap to be imported. + public TModel Import(ArchiveReader archive) + { + using (ContextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. + { + // create a new set info (don't yet add to database) + var model = CreateModel(archive); + + var existing = CheckForExisting(model); + + if (existing != null) return existing; + + model.Files = createFileInfos(archive, Files); + + Populate(model, archive); + + // import to store + ModelStore.Add(model); + + return model; + } + } + + /// + /// Create all required s for the provided archive, adding them to the global file store. + /// + private List createFileInfos(ArchiveReader reader, FileStore files) + { + var fileInfos = new List(); + + // import files to manager + foreach (string file in reader.Filenames) + using (Stream s = reader.GetStream(file)) + fileInfos.Add(new TFileModel + { + Filename = file, + FileInfo = files.Add(s) + }); + + return fileInfos; + } + + /// + /// Create a barebones model from the provided archive. + /// Actual expensive population should be done in ; this should just prepare for duplicate checking. + /// + /// + /// + protected abstract TModel CreateModel(ArchiveReader archive); + + /// + /// Populate the provided model completely from the given archive. + /// After this method, the model should be in a state ready to commit to a store. + /// + /// The model to populate. + /// The archive to use as a reference for population. + protected virtual void Populate(TModel model, ArchiveReader archive) + { + } + + protected virtual TModel CheckForExisting(TModel beatmapSet) => null; + + /// + /// Creates an from a valid storage path. + /// + /// A file or folder path resolving the beatmap content. + /// A reader giving access to the beatmap's content. + private ArchiveReader getReaderFrom(string path) + { + if (ZipFile.IsZipFile(path)) + return new OszArchiveReader(Files.Storage.GetStream(path)); + return new LegacyFilesystemReader(path); + } + } +} diff --git a/osu.Game/Beatmaps/BeatmapManager.cs b/osu.Game/Beatmaps/BeatmapManager.cs index 47773528a6..0a7bf255c5 100644 --- a/osu.Game/Beatmaps/BeatmapManager.cs +++ b/osu.Game/Beatmaps/BeatmapManager.cs @@ -7,7 +7,6 @@ using System.IO; using System.Linq; using System.Linq.Expressions; using System.Threading.Tasks; -using Ionic.Zip; using Microsoft.EntityFrameworkCore; using osu.Framework.Extensions; using osu.Framework.Logging; @@ -16,8 +15,6 @@ using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.IO; using osu.Game.Database; using osu.Game.Graphics; -using osu.Game.IO; -using osu.Game.IPC; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Overlays.Notifications; @@ -28,7 +25,7 @@ namespace osu.Game.Beatmaps /// /// Handles the storage and retrieval of Beatmaps/WorkingBeatmaps. /// - public partial class BeatmapManager + public partial class BeatmapManager : ArchiveModelImportManager { /// /// Fired when a new becomes available in the database. @@ -60,9 +57,7 @@ namespace osu.Game.Beatmaps /// public WorkingBeatmap DefaultBeatmap { private get; set; } - private readonly IDatabaseContextFactory contextFactory; - - private readonly FileStore files; + public override string[] HandledExtensions => new[] { ".osz" }; private readonly RulesetStore rulesets; @@ -72,142 +67,58 @@ namespace osu.Game.Beatmaps private readonly List currentDownloads = new List(); - // ReSharper disable once NotAccessedField.Local (we should keep a reference to this so it is not finalised) - private BeatmapIPCChannel ipc; - - /// - /// Set an endpoint for notifications to be posted to. - /// - public Action PostNotification { private get; set; } - /// /// Set a storage with access to an osu-stable install for import purposes. /// public Func GetStableStorage { private get; set; } public BeatmapManager(Storage storage, IDatabaseContextFactory contextFactory, RulesetStore rulesets, APIAccess api, IIpcHost importHost = null) + : base(storage, contextFactory, new BeatmapStore(contextFactory), importHost) { - this.contextFactory = contextFactory; - - beatmaps = new BeatmapStore(contextFactory); - + beatmaps = (BeatmapStore)ModelStore; beatmaps.BeatmapSetAdded += s => BeatmapSetAdded?.Invoke(s); beatmaps.BeatmapSetRemoved += s => BeatmapSetRemoved?.Invoke(s); beatmaps.BeatmapHidden += b => BeatmapHidden?.Invoke(b); beatmaps.BeatmapRestored += b => BeatmapRestored?.Invoke(b); - files = new FileStore(contextFactory, storage); - this.rulesets = rulesets; this.api = api; - if (importHost != null) - ipc = new BeatmapIPCChannel(importHost, this); - beatmaps.Cleanup(); } - /// - /// Import one or more from filesystem . - /// This will post notifications tracking progress. - /// - /// One or more beatmap locations on disk. - public List Import(params string[] paths) + protected override void Populate(BeatmapSetInfo model, ArchiveReader archive) { - var notification = new ProgressNotification - { - Text = "Beatmap import is initialising...", - CompletionText = "Import successful!", - Progress = 0, - State = ProgressNotificationState.Active, - }; + model.Beatmaps = createBeatmapDifficulties(archive); - PostNotification?.Invoke(notification); - - List imported = new List(); - - 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; + // remove metadata from difficulties where it matches the set + foreach (BeatmapInfo b in model.Beatmaps) + if (model.Metadata.Equals(b.Metadata)) + b.Metadata = null; } - /// - /// Import a beatmap from an . - /// - /// The beatmap to be imported. - public BeatmapSetInfo Import(ArchiveReader archive) + protected override BeatmapSetInfo CheckForExisting(BeatmapSetInfo beatmapSet) { - using (contextFactory.GetForWrite()) // used to share a context for full import. keep in mind this will block all writes. + // check if this beatmap has already been imported and exit early if so + var existingHashMatch = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == beatmapSet.Hash); + if (existingHashMatch != null) { - // create a new set info (don't yet add to database) - var beatmapSet = createBeatmapSetInfo(archive); - - // check if this beatmap has already been imported and exit early if so - var existingHashMatch = beatmaps.BeatmapSets.FirstOrDefault(b => b.Hash == beatmapSet.Hash); - if (existingHashMatch != null) - { - Undelete(existingHashMatch); - return existingHashMatch; - } - - // check if a set already exists with the same online id - if (beatmapSet.OnlineBeatmapSetID != null) - { - var existingOnlineId = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID); - if (existingOnlineId != null) - { - Delete(existingOnlineId); - beatmaps.Cleanup(s => s.ID == existingOnlineId.ID); - } - } - - beatmapSet.Files = createFileInfos(archive, files); - beatmapSet.Beatmaps = createBeatmapDifficulties(archive); - - // remove metadata from difficulties where it matches the set - foreach (BeatmapInfo b in beatmapSet.Beatmaps) - if (beatmapSet.Metadata.Equals(b.Metadata)) - b.Metadata = null; - - // import to beatmap store - Import(beatmapSet); - return beatmapSet; + Undelete(existingHashMatch); + return existingHashMatch; } + + // check if a set already exists with the same online id + if (beatmapSet.OnlineBeatmapSetID != null) + { + var existingOnlineId = beatmaps.BeatmapSets.FirstOrDefault(b => b.OnlineBeatmapSetID == beatmapSet.OnlineBeatmapSetID); + if (existingOnlineId != null) + { + Delete(existingOnlineId); + beatmaps.Cleanup(s => s.ID == existingOnlineId.ID); + } + } + + return null; } /// @@ -313,7 +224,7 @@ namespace osu.Game.Beatmaps /// The beatmap set to delete. public void Delete(BeatmapSetInfo beatmapSet) { - using (var usage = contextFactory.GetForWrite()) + using (var usage = ContextFactory.GetForWrite()) { var context = usage.Context; @@ -325,7 +236,7 @@ namespace osu.Game.Beatmaps if (beatmaps.Delete(beatmapSet)) { if (!beatmapSet.Protected) - files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); + Files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); } context.ChangeTracker.AutoDetectChangesEnabled = true; @@ -376,14 +287,14 @@ namespace osu.Game.Beatmaps if (beatmapSet.Protected) return; - using (var usage = contextFactory.GetForWrite()) + using (var usage = ContextFactory.GetForWrite()) { usage.Context.ChangeTracker.AutoDetectChangesEnabled = false; if (!beatmaps.Undelete(beatmapSet)) return; if (!beatmapSet.Protected) - files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); + Files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray()); usage.Context.ChangeTracker.AutoDetectChangesEnabled = true; } @@ -415,7 +326,7 @@ namespace osu.Game.Beatmaps if (beatmapInfo.Metadata == null) beatmapInfo.Metadata = beatmapInfo.BeatmapSet.Metadata; - WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(files.Store, beatmapInfo); + WorkingBeatmap working = new BeatmapManagerWorkingBeatmap(Files.Store, beatmapInfo); previous?.TransferTo(working); @@ -519,19 +430,6 @@ namespace osu.Game.Beatmaps notification.State = ProgressNotificationState.Completed; } - /// - /// Creates an from a valid storage path. - /// - /// A file or folder path resolving the beatmap content. - /// A reader giving access to the beatmap's content. - private ArchiveReader getReaderFrom(string path) - { - if (ZipFile.IsZipFile(path)) - // ReSharper disable once InconsistentlySynchronizedField - return new OszArchiveReader(files.Storage.GetStream(path)); - return new LegacyFilesystemReader(path); - } - /// /// Create a SHA-2 hash from the provided archive based on contained beatmap (.osu) file content. /// @@ -546,10 +444,7 @@ namespace osu.Game.Beatmaps return hashable.ComputeSHA2Hash(); } - /// - /// Create a from a provided archive. - /// - private BeatmapSetInfo createBeatmapSetInfo(ArchiveReader reader) + protected override BeatmapSetInfo CreateModel(ArchiveReader reader) { // let's make sure there are actually .osu files to import. string mapName = reader.Filenames.FirstOrDefault(f => f.EndsWith(".osu")); @@ -568,25 +463,6 @@ namespace osu.Game.Beatmaps }; } - /// - /// Create all required s for the provided archive, adding them to the global file store. - /// - private List createFileInfos(ArchiveReader reader, FileStore files) - { - List fileInfos = new List(); - - // import files to manager - foreach (string file in reader.Filenames) - using (Stream s = reader.GetStream(file)) - fileInfos.Add(new BeatmapSetFileInfo - { - Filename = file, - FileInfo = files.Add(s) - }); - - return fileInfos; - } - /// /// Create all required s for the provided archive. /// diff --git a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs index ae4a6772a2..e88af6ed30 100644 --- a/osu.Game/Beatmaps/BeatmapSetFileInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetFileInfo.cs @@ -3,11 +3,12 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using osu.Game.Database; using osu.Game.IO; namespace osu.Game.Beatmaps { - public class BeatmapSetFileInfo + public class BeatmapSetFileInfo : INamedFileInfo { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapSetInfo.cs b/osu.Game/Beatmaps/BeatmapSetInfo.cs index 982e41c92c..0566807179 100644 --- a/osu.Game/Beatmaps/BeatmapSetInfo.cs +++ b/osu.Game/Beatmaps/BeatmapSetInfo.cs @@ -5,10 +5,11 @@ using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using osu.Game.Database; +using osu.Game.IO; namespace osu.Game.Beatmaps { - public class BeatmapSetInfo : IHasPrimaryKey + public class BeatmapSetInfo : IHasPrimaryKey, IHasFiles { [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int ID { get; set; } diff --git a/osu.Game/Beatmaps/BeatmapStore.cs b/osu.Game/Beatmaps/BeatmapStore.cs index 29373c0715..8bc2dd8b13 100644 --- a/osu.Game/Beatmaps/BeatmapStore.cs +++ b/osu.Game/Beatmaps/BeatmapStore.cs @@ -6,13 +6,14 @@ using System.Linq; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore; using osu.Game.Database; +using osu.Game.IO; namespace osu.Game.Beatmaps { /// /// Handles the storage and retrieval of Beatmaps/BeatmapSets to the database backing /// - public class BeatmapStore : DatabaseBackedStore + public class BeatmapStore : DatabaseBackedStore, IAddableStore { public event Action BeatmapSetAdded; public event Action BeatmapSetRemoved; diff --git a/osu.Game/Beatmaps/ICanImportArchives.cs b/osu.Game/Beatmaps/ICanImportArchives.cs new file mode 100644 index 0000000000..246c5d04b2 --- /dev/null +++ b/osu.Game/Beatmaps/ICanImportArchives.cs @@ -0,0 +1,9 @@ +namespace osu.Game.Beatmaps +{ + public interface ICanImportArchives + { + void Import(params string[] paths); + + string[] HandledExtensions { get; } + } +} diff --git a/osu.Game/Database/INamedFileInfo.cs b/osu.Game/Database/INamedFileInfo.cs new file mode 100644 index 0000000000..7922c72974 --- /dev/null +++ b/osu.Game/Database/INamedFileInfo.cs @@ -0,0 +1,13 @@ +using osu.Game.IO; + +namespace osu.Game.Database +{ + /// + /// Represent a join model which gives a filename and scope to a . + /// + public interface INamedFileInfo + { + FileInfo FileInfo { get; set; } + string Filename { get; set; } + } +} diff --git a/osu.Game/IO/IAddableStore.cs b/osu.Game/IO/IAddableStore.cs new file mode 100644 index 0000000000..2452dda3b4 --- /dev/null +++ b/osu.Game/IO/IAddableStore.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2007-2018 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +namespace osu.Game.IO +{ + public interface IAddableStore + { + /// + /// Add an object to the store. + /// + /// The object to add. + void Add(T item); + } +} diff --git a/osu.Game/IO/IHasFiles.cs b/osu.Game/IO/IHasFiles.cs new file mode 100644 index 0000000000..df313b4eae --- /dev/null +++ b/osu.Game/IO/IHasFiles.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; + +namespace osu.Game.IO +{ + public interface IHasFiles + { + List Files { get; set; } + } +} diff --git a/osu.Game/IPC/BeatmapIPCChannel.cs b/osu.Game/IPC/ArchiveImportIPCChannel.cs similarity index 57% rename from osu.Game/IPC/BeatmapIPCChannel.cs rename to osu.Game/IPC/ArchiveImportIPCChannel.cs index 64e5d526e6..a5859e56a4 100644 --- a/osu.Game/IPC/BeatmapIPCChannel.cs +++ b/osu.Game/IPC/ArchiveImportIPCChannel.cs @@ -2,23 +2,25 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System.Diagnostics; +using System.IO; +using System.Linq; using System.Threading.Tasks; using osu.Framework.Platform; using osu.Game.Beatmaps; namespace osu.Game.IPC { - public class BeatmapIPCChannel : IpcChannel + public class ArchiveImportIPCChannel : IpcChannel { - private readonly BeatmapManager beatmaps; + private readonly ICanImportArchives importer; - public BeatmapIPCChannel(IIpcHost host, BeatmapManager beatmaps = null) + public ArchiveImportIPCChannel(IIpcHost host, ICanImportArchives importer = null) : base(host) { - this.beatmaps = beatmaps; + this.importer = importer; MessageReceived += msg => { - Debug.Assert(beatmaps != null); + Debug.Assert(importer != null); ImportAsync(msg.Path).ContinueWith(t => { if (t.Exception != null) throw t.Exception; @@ -28,18 +30,19 @@ namespace osu.Game.IPC public async Task ImportAsync(string path) { - if (beatmaps == null) + if (importer == null) { //we want to contact a remote osu! to handle the import. - await SendMessageAsync(new BeatmapImportMessage { Path = path }); + await SendMessageAsync(new ArchiveImportMessage { Path = path }); return; } - beatmaps.Import(path); + if (importer.HandledExtensions.Contains(Path.GetExtension(path))) + importer.Import(path); } } - public class BeatmapImportMessage + public class ArchiveImportMessage { public string Path; } diff --git a/osu.Game/Online/API/APIDownloadRequest.cs b/osu.Game/Online/API/APIDownloadRequest.cs new file mode 100644 index 0000000000..f1cbd1eb0b --- /dev/null +++ b/osu.Game/Online/API/APIDownloadRequest.cs @@ -0,0 +1,30 @@ +using osu.Framework.IO.Network; + +namespace osu.Game.Online.API +{ + public abstract class APIDownloadRequest : APIRequest + { + protected override WebRequest CreateWebRequest() + { + var request = new WebRequest(Uri); + request.DownloadProgress += request_Progress; + return request; + } + + private void request_Progress(long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); }); + + protected APIDownloadRequest() + { + base.Success += onSuccess; + } + + private void onSuccess() + { + Success?.Invoke(WebRequest.ResponseData); + } + + public event APIProgressHandler Progress; + + public new event APISuccessHandler Success; + } +} \ No newline at end of file diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index ce6f3c7c7d..35af8eefd7 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -27,32 +27,6 @@ namespace osu.Game.Online.API public new event APISuccessHandler Success; } - public abstract class APIDownloadRequest : APIRequest - { - protected override WebRequest CreateWebRequest() - { - var request = new WebRequest(Uri); - request.DownloadProgress += request_Progress; - return request; - } - - private void request_Progress(long current, long total) => API.Scheduler.Add(delegate { Progress?.Invoke(current, total); }); - - protected APIDownloadRequest() - { - base.Success += onSuccess; - } - - private void onSuccess() - { - Success?.Invoke(WebRequest.ResponseData); - } - - public event APIProgressHandler Progress; - - public new event APISuccessHandler Success; - } - /// /// AN API request with no specified response type. /// diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 02801eb81f..189886f5d1 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -243,6 +243,7 @@ + @@ -270,6 +271,7 @@ + @@ -278,9 +280,13 @@ + + + + @@ -470,7 +476,7 @@ - +