mirror of
https://github.com/knah/VRCMelonAssistant.git
synced 2024-05-29 08:41:21 +12:00
Refactored OneClick Install code
This commit is contained in:
parent
aeceb27226
commit
00561c98b7
171
ModAssistant/Classes/External Interfaces/BeatSaver.cs
Normal file
171
ModAssistant/Classes/External Interfaces/BeatSaver.cs
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using static ModAssistant.Http;
|
||||||
|
|
||||||
|
namespace ModAssistant.API
|
||||||
|
{
|
||||||
|
public class BeatSaver
|
||||||
|
{
|
||||||
|
private const string BeatSaverURLPrefix = "https://beatsaver.com";
|
||||||
|
private static readonly string CustomSongsFolder = Path.Combine("Beat Saber_Data", "CustomLevels");
|
||||||
|
private const bool BypassDownloadCounter = false;
|
||||||
|
|
||||||
|
public static async Task GetFromKey(string Key)
|
||||||
|
{
|
||||||
|
BeatSaverApiResponse Map = await GetResponse(BeatSaverURLPrefix + "/api/maps/detail/" + Key);
|
||||||
|
await InstallMap(Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task GetFromHash(string Hash)
|
||||||
|
{
|
||||||
|
BeatSaverApiResponse Map = await GetResponse(BeatSaverURLPrefix + "/api/maps/by-hash/" + Hash);
|
||||||
|
await InstallMap(Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<BeatSaverApiResponse> GetResponse(string url)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var resp = await HttpClient.GetAsync(url);
|
||||||
|
var body = await resp.Content.ReadAsStringAsync();
|
||||||
|
|
||||||
|
return JsonSerializer.Deserialize<BeatSaverApiResponse>(body);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
MessageBox.Show($"{Application.Current.FindResource("OneClick:MapDownloadFailed")}\n\n" + e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static async Task InstallMap(BeatSaverApiResponse Map)
|
||||||
|
{
|
||||||
|
string zip = Path.Combine(Utils.BeatSaberPath, CustomSongsFolder, Map.hash) + ".zip";
|
||||||
|
string mapName = string.Concat(($"{Map.key} ({Map.metadata.songName} - {Map.metadata.levelAuthorName})")
|
||||||
|
.Split(ModAssistant.Utils.Constants.IllegalCharacters));
|
||||||
|
string directory = Path.Combine(Utils.BeatSaberPath, CustomSongsFolder, mapName);
|
||||||
|
|
||||||
|
if (BypassDownloadCounter)
|
||||||
|
{
|
||||||
|
await Utils.DownloadAsset(BeatSaverURLPrefix + Map.directDownload, CustomSongsFolder, Map.hash + ".zip", mapName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
await Utils.DownloadAsset(BeatSaverURLPrefix + Map.downloadURL, CustomSongsFolder, Map.hash + ".zip", mapName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (File.Exists(zip))
|
||||||
|
{
|
||||||
|
using (FileStream stream = new FileStream(zip, FileMode.Open))
|
||||||
|
using (ZipArchive archive = new ZipArchive(stream))
|
||||||
|
{
|
||||||
|
foreach (ZipArchiveEntry file in archive.Entries)
|
||||||
|
{
|
||||||
|
string fileDirectory = Path.GetDirectoryName(Path.Combine(directory, file.FullName));
|
||||||
|
if (!Directory.Exists(fileDirectory))
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(fileDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(file.Name))
|
||||||
|
{
|
||||||
|
file.ExtractToFile(Path.Combine(directory, file.FullName), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
File.Delete(zip);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
string line1 = (string)Application.Current.FindResource("OneClick:SongDownload:Failed");
|
||||||
|
string line2 = (string)Application.Current.FindResource("OneClick:SongDownload:NetworkIssues");
|
||||||
|
string title = (string)Application.Current.FindResource("OneClick:SongDownload:FailedTitle");
|
||||||
|
MessageBox.Show($"{line1}\n{line2}", title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BeatSaverApiResponse
|
||||||
|
{
|
||||||
|
public Metadata metadata { get; set; }
|
||||||
|
public Stats stats { get; set; }
|
||||||
|
public string description { get; set; }
|
||||||
|
public DateTime? deletedAt { get; set; }
|
||||||
|
public string _id { get; set; }
|
||||||
|
public string key { get; set; }
|
||||||
|
public string name { get; set; }
|
||||||
|
public Uploader uploader { get; set; }
|
||||||
|
public DateTime uploaded { get; set; }
|
||||||
|
public string hash { get; set; }
|
||||||
|
public string directDownload { get; set; }
|
||||||
|
public string downloadURL { get; set; }
|
||||||
|
public string coverURL { get; set; }
|
||||||
|
|
||||||
|
public class Difficulties
|
||||||
|
{
|
||||||
|
public bool easy { get; set; }
|
||||||
|
public bool normal { get; set; }
|
||||||
|
public bool hard { get; set; }
|
||||||
|
public bool expert { get; set; }
|
||||||
|
public bool expertPlus { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Metadata
|
||||||
|
{
|
||||||
|
public Difficulties difficulties { get; set; }
|
||||||
|
public Characteristic[] characteristics { get; set; }
|
||||||
|
public double duration { get; set; }
|
||||||
|
public string songName { get; set; }
|
||||||
|
public string songSubName { get; set; }
|
||||||
|
public string songAuthorName { get; set; }
|
||||||
|
public string levelAuthorName { get; set; }
|
||||||
|
public double bpm { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Characteristic
|
||||||
|
{
|
||||||
|
public string name { get; set; }
|
||||||
|
public CharacteristicDifficulties difficulties { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CharacteristicDifficulties
|
||||||
|
{
|
||||||
|
public Difficulty easy { get; set; }
|
||||||
|
public Difficulty normal { get; set; }
|
||||||
|
public Difficulty hard { get; set; }
|
||||||
|
public Difficulty expert { get; set; }
|
||||||
|
public Difficulty expertPlus { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Difficulty
|
||||||
|
{
|
||||||
|
public double? duration { get; set; }
|
||||||
|
public double? length { get; set; }
|
||||||
|
public double bombs { get; set; }
|
||||||
|
public double notes { get; set; }
|
||||||
|
public double obstacles { get; set; }
|
||||||
|
public double njs { get; set; }
|
||||||
|
public double njsOffset { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Stats
|
||||||
|
{
|
||||||
|
public int downloads { get; set; }
|
||||||
|
public int plays { get; set; }
|
||||||
|
public int downVotes { get; set; }
|
||||||
|
public int upVotes { get; set; }
|
||||||
|
public double heat { get; set; }
|
||||||
|
public double rating { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Uploader
|
||||||
|
{
|
||||||
|
public string _id { get; set; }
|
||||||
|
public string username { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
ModAssistant/Classes/External Interfaces/ModelSaber.cs
Normal file
34
ModAssistant/Classes/External Interfaces/ModelSaber.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace ModAssistant.API
|
||||||
|
{
|
||||||
|
class ModelSaber
|
||||||
|
{
|
||||||
|
private const string ModelSaberURLPrefix = "https://modelsaber.com/files/";
|
||||||
|
private const string CustomAvatarsFolder = "CustomAvatars";
|
||||||
|
private const string CustomSabersFolder = "CustomSabers";
|
||||||
|
private const string CustomPlatformsFolder = "CustomPlatforms";
|
||||||
|
private const string CustomBloqsFolder = "CustomNotes";
|
||||||
|
|
||||||
|
public static async Task GetModel(Uri uri)
|
||||||
|
{
|
||||||
|
switch (uri.Host)
|
||||||
|
{
|
||||||
|
case "avatar":
|
||||||
|
await Utils.DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomAvatarsFolder);
|
||||||
|
break;
|
||||||
|
case "saber":
|
||||||
|
await Utils.DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomSabersFolder);
|
||||||
|
break;
|
||||||
|
case "platform":
|
||||||
|
await Utils.DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomPlatformsFolder);
|
||||||
|
break;
|
||||||
|
case "bloq":
|
||||||
|
await Utils.DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomBloqsFolder);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
46
ModAssistant/Classes/External Interfaces/Utils.cs
Normal file
46
ModAssistant/Classes/External Interfaces/Utils.cs
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace ModAssistant.API
|
||||||
|
{
|
||||||
|
public class Utils
|
||||||
|
{
|
||||||
|
public static readonly string BeatSaberPath = App.BeatSaberInstallDirectory;
|
||||||
|
|
||||||
|
public static async Task DownloadAsset(string link, string folder, string fileName = null, string displayName = null)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(displayName))
|
||||||
|
{
|
||||||
|
displayName = Path.GetFileNameWithoutExtension(fileName);
|
||||||
|
}
|
||||||
|
if (string.IsNullOrEmpty(BeatSaberPath))
|
||||||
|
{
|
||||||
|
ModAssistant.Utils.SendNotify((string)Application.Current.FindResource("OneClick:InstallDirNotFound"));
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Directory.CreateDirectory(Path.Combine(BeatSaberPath, folder));
|
||||||
|
if (string.IsNullOrEmpty(fileName))
|
||||||
|
{
|
||||||
|
fileName = WebUtility.UrlDecode(Path.Combine(BeatSaberPath, folder, new Uri(link).Segments.Last()));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fileName = WebUtility.UrlDecode(Path.Combine(BeatSaberPath, folder, fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
await ModAssistant.Utils.Download(link, fileName);
|
||||||
|
ModAssistant.Utils.SendNotify(string.Format((string)Application.Current.FindResource("OneClick:InstalledAsset"), displayName));
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
ModAssistant.Utils.SendNotify((string)Application.Current.FindResource("OneClick:AssetInstallFailed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +1,15 @@
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
|
||||||
using System.IO.Compression;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using static ModAssistant.Http;
|
|
||||||
|
|
||||||
namespace ModAssistant
|
namespace ModAssistant
|
||||||
{
|
{
|
||||||
class OneClickInstaller
|
class OneClickInstaller
|
||||||
{
|
{
|
||||||
private const string ModelSaberURLPrefix = "https://modelsaber.com/files/";
|
|
||||||
private const string BeatSaverURLPrefix = "https://beatsaver.com";
|
|
||||||
|
|
||||||
private static readonly string BeatSaberPath = App.BeatSaberInstallDirectory;
|
|
||||||
|
|
||||||
private const string CustomAvatarsFolder = "CustomAvatars";
|
|
||||||
private const string CustomSabersFolder = "CustomSabers";
|
|
||||||
private const string CustomPlatformsFolder = "CustomPlatforms";
|
|
||||||
private const string CustomBloqsFolder = "CustomNotes";
|
|
||||||
private static readonly string CustomSongsFolder = Path.Combine("Beat Saber_Data", "CustomLevels");
|
|
||||||
|
|
||||||
private static readonly string[] Protocols = new[] { "modelsaber", "beatsaver" };
|
private static readonly string[] Protocols = new[] { "modelsaber", "beatsaver" };
|
||||||
|
|
||||||
private const bool BypassDownloadCounter = false;
|
|
||||||
public static async Task InstallAsset(string link)
|
public static async Task InstallAsset(string link)
|
||||||
{
|
{
|
||||||
Uri uri = new Uri(link);
|
Uri uri = new Uri(link);
|
||||||
|
@ -45,115 +29,12 @@ namespace ModAssistant
|
||||||
private static async Task BeatSaver(Uri uri)
|
private static async Task BeatSaver(Uri uri)
|
||||||
{
|
{
|
||||||
string Key = uri.Host;
|
string Key = uri.Host;
|
||||||
BeatSaverApiResponse Response;
|
await API.BeatSaver.GetFromKey(Key);
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var resp = await HttpClient.GetAsync(BeatSaverURLPrefix + "/api/maps/detail/" + Key);
|
|
||||||
var body = await resp.Content.ReadAsStringAsync();
|
|
||||||
|
|
||||||
Response = JsonSerializer.Deserialize<BeatSaverApiResponse>(body);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
MessageBox.Show($"{Application.Current.FindResource("OneClick:MapDownloadFailed")}\n\n" + e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
string zip = Path.Combine(BeatSaberPath, CustomSongsFolder, Response.hash) + ".zip";
|
|
||||||
string directory = Path.Combine(
|
|
||||||
BeatSaberPath,
|
|
||||||
CustomSongsFolder,
|
|
||||||
string.Concat(
|
|
||||||
(Response.key + " (" + Response.metadata.songName + " - " + Response.metadata.levelAuthorName + ")")
|
|
||||||
.Split(Utils.Constants.IllegalCharacters)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (BypassDownloadCounter)
|
|
||||||
{
|
|
||||||
await DownloadAsset(BeatSaverURLPrefix + Response.directDownload, CustomSongsFolder, Response.hash + ".zip");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
await DownloadAsset(BeatSaverURLPrefix + Response.downloadURL, CustomSongsFolder, Response.hash + ".zip");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (File.Exists(zip))
|
|
||||||
{
|
|
||||||
using (FileStream stream = new FileStream(zip, FileMode.Open))
|
|
||||||
using (ZipArchive archive = new ZipArchive(stream))
|
|
||||||
{
|
|
||||||
foreach (ZipArchiveEntry file in archive.Entries)
|
|
||||||
{
|
|
||||||
string fileDirectory = Path.GetDirectoryName(Path.Combine(directory, file.FullName));
|
|
||||||
if (!Directory.Exists(fileDirectory))
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(fileDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(file.Name))
|
|
||||||
{
|
|
||||||
file.ExtractToFile(Path.Combine(directory, file.FullName), true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
File.Delete(zip);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string line1 = (string)Application.Current.FindResource("OneClick:SongDownload:Failed");
|
|
||||||
string line2 = (string)Application.Current.FindResource("OneClick:SongDownload:NetworkIssues");
|
|
||||||
string title = (string)Application.Current.FindResource("OneClick:SongDownload:FailedTitle");
|
|
||||||
MessageBox.Show($"{line1}\n{line2}", title);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task ModelSaber(Uri uri)
|
private static async Task ModelSaber(Uri uri)
|
||||||
{
|
{
|
||||||
switch (uri.Host)
|
await API.ModelSaber.GetModel(uri);
|
||||||
{
|
|
||||||
case "avatar":
|
|
||||||
await DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomAvatarsFolder);
|
|
||||||
break;
|
|
||||||
case "saber":
|
|
||||||
await DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomSabersFolder);
|
|
||||||
break;
|
|
||||||
case "platform":
|
|
||||||
await DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomPlatformsFolder);
|
|
||||||
break;
|
|
||||||
case "bloq":
|
|
||||||
await DownloadAsset(ModelSaberURLPrefix + uri.Host + uri.AbsolutePath, CustomBloqsFolder);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task DownloadAsset(string link, string folder, string fileName = null)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(BeatSaberPath))
|
|
||||||
{
|
|
||||||
Utils.SendNotify((string)Application.Current.FindResource("OneClick:InstallDirNotFound"));
|
|
||||||
}
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Directory.CreateDirectory(Path.Combine(BeatSaberPath, folder));
|
|
||||||
if (string.IsNullOrEmpty(fileName))
|
|
||||||
{
|
|
||||||
fileName = WebUtility.UrlDecode(Path.Combine(BeatSaberPath, folder, new Uri(link).Segments.Last()));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fileName = WebUtility.UrlDecode(Path.Combine(BeatSaberPath, folder, fileName));
|
|
||||||
}
|
|
||||||
|
|
||||||
await Utils.Download(link, fileName);
|
|
||||||
Utils.SendNotify(string.Format((string)Application.Current.FindResource("OneClick:InstalledAsset"), Path.GetFileNameWithoutExtension(fileName)));
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
Utils.SendNotify((string)Application.Current.FindResource("OneClick:AssetInstallFailed"));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Register(string Protocol, bool Background = false)
|
public static void Register(string Protocol, bool Background = false)
|
||||||
|
@ -242,84 +123,4 @@ namespace ModAssistant
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma warning disable IDE1006 // Naming Styles
|
|
||||||
class BeatSaverApiResponse
|
|
||||||
{
|
|
||||||
public Metadata metadata { get; set; }
|
|
||||||
public Stats stats { get; set; }
|
|
||||||
public string description { get; set; }
|
|
||||||
public DateTime? deletedAt { get; set; }
|
|
||||||
public string _id { get; set; }
|
|
||||||
public string key { get; set; }
|
|
||||||
public string name { get; set; }
|
|
||||||
public Uploader uploader { get; set; }
|
|
||||||
public DateTime uploaded { get; set; }
|
|
||||||
public string hash { get; set; }
|
|
||||||
public string directDownload { get; set; }
|
|
||||||
public string downloadURL { get; set; }
|
|
||||||
public string coverURL { get; set; }
|
|
||||||
|
|
||||||
public class Difficulties
|
|
||||||
{
|
|
||||||
public bool easy { get; set; }
|
|
||||||
public bool normal { get; set; }
|
|
||||||
public bool hard { get; set; }
|
|
||||||
public bool expert { get; set; }
|
|
||||||
public bool expertPlus { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Metadata
|
|
||||||
{
|
|
||||||
public Difficulties difficulties { get; set; }
|
|
||||||
public Characteristic[] characteristics { get; set; }
|
|
||||||
public string songName { get; set; }
|
|
||||||
public string songSubName { get; set; }
|
|
||||||
public string songAuthorName { get; set; }
|
|
||||||
public string levelAuthorName { get; set; }
|
|
||||||
public double bpm { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Characteristic
|
|
||||||
{
|
|
||||||
public string name { get; set; }
|
|
||||||
public CharacteristicDifficulties difficulties { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CharacteristicDifficulties
|
|
||||||
{
|
|
||||||
public Difficulty easy { get; set; }
|
|
||||||
public Difficulty normal { get; set; }
|
|
||||||
public Difficulty hard { get; set; }
|
|
||||||
public Difficulty expert { get; set; }
|
|
||||||
public Difficulty expertPlus { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Difficulty
|
|
||||||
{
|
|
||||||
public double? duration { get; set; }
|
|
||||||
public double? length { get; set; }
|
|
||||||
public double bombs { get; set; }
|
|
||||||
public double notes { get; set; }
|
|
||||||
public double obstacles { get; set; }
|
|
||||||
public double njs { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Stats
|
|
||||||
{
|
|
||||||
public int downloads { get; set; }
|
|
||||||
public int plays { get; set; }
|
|
||||||
public int downVotes { get; set; }
|
|
||||||
public int upVotes { get; set; }
|
|
||||||
public double heat { get; set; }
|
|
||||||
public double rating { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Uploader
|
|
||||||
{
|
|
||||||
public string _id { get; set; }
|
|
||||||
public string username { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#pragma warning restore IDE1006 // Naming Styles
|
|
||||||
|
|
|
@ -65,6 +65,9 @@
|
||||||
<Generator>MSBuild:Compile</Generator>
|
<Generator>MSBuild:Compile</Generator>
|
||||||
<SubType>Designer</SubType>
|
<SubType>Designer</SubType>
|
||||||
</ApplicationDefinition>
|
</ApplicationDefinition>
|
||||||
|
<Compile Include="Classes\External Interfaces\BeatSaver.cs" />
|
||||||
|
<Compile Include="Classes\External Interfaces\ModelSaber.cs" />
|
||||||
|
<Compile Include="Classes\External Interfaces\Utils.cs" />
|
||||||
<Compile Include="Classes\Http.cs" />
|
<Compile Include="Classes\Http.cs" />
|
||||||
<Compile Include="Classes\HyperlinkExtensions.cs" />
|
<Compile Include="Classes\HyperlinkExtensions.cs" />
|
||||||
<Compile Include="Classes\Promotions.cs" />
|
<Compile Include="Classes\Promotions.cs" />
|
||||||
|
|
Loading…
Reference in a new issue