Merge pull request #1075 from peppy/import-from-stable-part-two

Tidy up osu-stable import process
This commit is contained in:
Dan Balasescu 2017-08-02 17:18:39 +09:30 committed by GitHub
commit d72a479d9d
9 changed files with 104 additions and 49 deletions

@ -1 +1 @@
Subproject commit 2204764944ff693e857649253547054cc91764a0
Subproject commit 96daf2053a8a19fe221fef2557674ca5bee808fb

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game;
using System.Linq;
using System.Windows.Forms;
@ -11,6 +12,7 @@ using System.Reflection;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Win32;
using osu.Framework.Graphics.Containers;
using osu.Game.Screens.Menu;
@ -30,6 +32,58 @@ namespace osu.Desktop
};
}
public override Storage GetStorageForStableInstall()
{
try
{
return new StableStorage();
}
catch
{
return null;
}
}
/// <summary>
/// A method of accessing an osu-stable install in a controlled fashion.
/// </summary>
private class StableStorage : DesktopStorage
{
protected override string LocateBasePath()
{
Func<string, bool> checkExists = p => Directory.Exists(Path.Combine(p, "Songs"));
string stableInstallPath;
try
{
using (RegistryKey key = Registry.ClassesRoot.OpenSubKey("osu"))
stableInstallPath = key?.OpenSubKey(@"shell\open\command")?.GetValue(String.Empty).ToString().Split('"')[1].Replace("osu!.exe", "");
if (checkExists(stableInstallPath))
return stableInstallPath;
}
catch
{
}
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!");
if (checkExists(stableInstallPath))
return stableInstallPath;
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu");
if (checkExists(stableInstallPath))
return stableInstallPath;
return null;
}
public StableStorage()
: base(string.Empty)
{
}
}
protected override void LoadComplete()
{
base.LoadComplete();

View File

@ -47,6 +47,8 @@ namespace osu.Game.Beatmaps
private readonly FileStore files;
private readonly SQLiteConnection connection;
private readonly RulesetStore rulesets;
private readonly BeatmapStore beatmaps;
@ -59,6 +61,11 @@ namespace osu.Game.Beatmaps
/// </summary>
public Action<Notification> PostNotification { private get; set; }
/// <summary>
/// Set a storage with access to an osu-stable install for import purposes.
/// </summary>
public Func<Storage> GetStableStorage { private get; set; }
public BeatmapManager(Storage storage, FileStore files, SQLiteConnection connection, RulesetStore rulesets, IIpcHost importHost = null)
{
beatmaps = new BeatmapStore(connection);
@ -67,6 +74,7 @@ namespace osu.Game.Beatmaps
this.storage = storage;
this.files = files;
this.connection = connection;
this.rulesets = rulesets;
if (importHost != null)
@ -136,13 +144,13 @@ namespace osu.Game.Beatmaps
/// <param name="archiveReader">The beatmap to be imported.</param>
public BeatmapSetInfo Import(ArchiveReader archiveReader)
{
BeatmapSetInfo set = null;
// let's only allow one concurrent import at a time for now.
lock (importLock)
{
BeatmapSetInfo set = importToStorage(archiveReader);
Import(set);
return set;
}
connection.RunInTransaction(() => Import(set = importToStorage(archiveReader)));
return set;
}
/// <summary>
@ -169,7 +177,7 @@ namespace osu.Game.Beatmaps
if (!beatmaps.Delete(beatmapSet)) return;
if (!beatmapSet.Protected)
files.Dereference(beatmapSet.Files.Select(f => f.FileInfo));
files.Dereference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
}
/// <summary>
@ -183,7 +191,7 @@ namespace osu.Game.Beatmaps
if (!beatmaps.Undelete(beatmapSet)) return;
if (!beatmapSet.Protected)
files.Reference(beatmapSet.Files.Select(f => f.FileInfo));
files.Reference(beatmapSet.Files.Select(f => f.FileInfo).ToArray());
}
/// <summary>
@ -451,19 +459,20 @@ namespace osu.Game.Beatmaps
}
}
/// <summary>
/// This is a temporary method and will likely be replaced by a full-fledged (and more correctly placed) migration process in the future.
/// </summary>
public void ImportFromStable()
{
string stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"osu!", "Songs");
if (!Directory.Exists(stableInstallPath))
stableInstallPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".osu", "Songs");
var stable = GetStableStorage?.Invoke();
if (!Directory.Exists(stableInstallPath))
if (stable == null)
{
Logger.Log("Couldn't find an osu!stable installation!", LoggingTarget.Information, LogLevel.Error);
Logger.Log("No osu!stable installation available!", LoggingTarget.Information, LogLevel.Error);
return;
}
Import(Directory.GetDirectories(stableInstallPath));
Import(stable.GetDirectories("Songs"));
}
public void DeleteAll()

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Logging;
using osu.Game.Database;
using SQLite.Net;
using SQLiteNetExtensions.Extensions;
@ -130,23 +129,11 @@ namespace osu.Game.Beatmaps
private void cleanupPendingDeletions()
{
foreach (var b in QueryAndPopulate<BeatmapSetInfo>(b => b.DeletePending && !b.Protected))
Connection.RunInTransaction(() =>
{
try
{
// many-to-many join table entries are not automatically tidied.
Connection.Table<BeatmapSetFileInfo>().Delete(f => f.BeatmapSetInfoID == b.ID);
foreach (var b in QueryAndPopulate<BeatmapSetInfo>(b => b.DeletePending && !b.Protected))
Connection.Delete(b, true);
}
catch (Exception e)
{
Logger.Error(e, $@"Could not delete beatmap {b}");
}
}
//this is required because sqlite migrations don't work, initially inserting nulls into this field.
//see https://github.com/praeclarum/sqlite-net/issues/326
Connection.Query<BeatmapSetInfo>("UPDATE BeatmapSetInfo SET DeletePending = 0 WHERE DeletePending IS NULL");
});
}
}
}

View File

@ -41,7 +41,7 @@ namespace osu.Game.Database
{
var storeName = GetType().Name;
var reportedVersion = Connection.Table<StoreVersion>().FirstOrDefault(s => s.StoreName == storeName) ?? new StoreVersion
var reportedVersion = Connection.Table<StoreVersion>().Where(s => s.StoreName == storeName).FirstOrDefault() ?? new StoreVersion
{
StoreName = storeName,
Version = 0

View File

@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using osu.Framework.Extensions;
@ -83,10 +82,9 @@ namespace osu.Game.IO
{
string hash = data.ComputeSHA2Hash();
var info = new FileInfo { Hash = hash };
var existing = Connection.Table<FileInfo>().FirstOrDefault(f => f.Hash == info.Hash);
var existing = Connection.Table<FileInfo>().Where(f => f.Hash == hash).FirstOrDefault();
var info = existing ?? new FileInfo { Hash = hash };
if (existing != null)
{
info = existing;
@ -106,11 +104,11 @@ namespace osu.Game.IO
Connection.Insert(info);
}
Reference(new[] { info });
Reference(info);
return info;
}
public void Reference(IEnumerable<FileInfo> files)
public void Reference(params FileInfo[] files)
{
Connection.RunInTransaction(() =>
{
@ -125,7 +123,7 @@ namespace osu.Game.IO
});
}
public void Dereference(IEnumerable<FileInfo> files)
public void Dereference(params FileInfo[] files)
{
Connection.RunInTransaction(() =>
{
@ -142,18 +140,21 @@ namespace osu.Game.IO
private void deletePending()
{
foreach (var f in QueryAndPopulate<FileInfo>(f => f.ReferenceCount < 1))
Connection.RunInTransaction(() =>
{
try
foreach (var f in Query<FileInfo>(f => f.ReferenceCount < 1))
{
Connection.Delete(f);
Storage.Delete(Path.Combine(prefix, f.StoragePath));
try
{
Storage.Delete(Path.Combine(prefix, f.StoragePath));
Connection.Delete(f);
}
catch (Exception e)
{
Logger.Error(e, $@"Could not delete beatmap {f}");
}
}
catch (Exception e)
{
Logger.Error(e, $@"Could not delete beatmap {f}");
}
}
});
}
}
}

View File

@ -20,6 +20,7 @@ using osu.Game.Screens.Menu;
using OpenTK;
using System.Linq;
using System.Threading.Tasks;
using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
@ -47,6 +48,8 @@ namespace osu.Game
private UserProfileOverlay userProfile;
public virtual Storage GetStorageForStableInstall() => null;
private Intro intro
{
get
@ -151,6 +154,7 @@ namespace osu.Game
// hook up notifications to components.
BeatmapManager.PostNotification = n => notificationOverlay?.Post(n);
BeatmapManager.GetStableStorage = GetStorageForStableInstall;
AddRange(new Drawable[] {
new VolumeControlReceptor

View File

@ -62,7 +62,7 @@ namespace osu.Game.Rulesets
{
var us = createRulesetInfo(r);
var existing = Query<RulesetInfo>().FirstOrDefault(ri => ri.InstantiationInfo == us.InstantiationInfo);
var existing = Query<RulesetInfo>().Where(ri => ri.InstantiationInfo == us.InstantiationInfo).FirstOrDefault();
if (existing == null)
Connection.Insert(us);

View File

@ -115,7 +115,7 @@
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleCallToAny/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleCallToCount/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleCallToFirst/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleCallToFirstOrDefault/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleCallToFirstOrDefault/@EntryIndexedValue">HINT</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleCallToLast/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleCallToLastOrDefault/@EntryIndexedValue">WARNING</s:String>
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=ReplaceWithSingleCallToSingle/@EntryIndexedValue">WARNING</s:String>