Skip to content
This repository has been archived by the owner on Jul 16, 2023. It is now read-only.

Commit

Permalink
[CalculatorViewModel] Added currently playing beatmap recognition usi…
Browse files Browse the repository at this point in the history
…ng OsuSearch. Works only on Ranked maps, may be inaccurate if there are multiple ranked maps with same artist, title, difficulty name.
  • Loading branch information
Tyrrrz committed Aug 28, 2016
1 parent 6cd3eed commit ae5cd75
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 5 deletions.
2 changes: 2 additions & 0 deletions OsuHelper/Locator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ static Locator()

SimpleIoc.Default.Register<APIService>();
SimpleIoc.Default.Register<OppaiService>();
SimpleIoc.Default.Register<OsuGameService>();
SimpleIoc.Default.Register<OsuSearchService>();
SimpleIoc.Default.Register<WindowService>();

SimpleIoc.Default.Register<SetupViewModel>();
Expand Down
2 changes: 2 additions & 0 deletions OsuHelper/OsuHelper.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@
<Compile Include="Persistence.cs" />
<Compile Include="Services\APIService.cs" />
<Compile Include="Services\OppaiService.cs" />
<Compile Include="Services\OsuGameService.cs" />
<Compile Include="Services\OsuSearchService.cs" />
<Compile Include="Services\WindowService.cs" />
<Compile Include="Settings.cs" />
<Compile Include="ViewModels\CalculatorViewModel.cs" />
Expand Down
13 changes: 13 additions & 0 deletions OsuHelper/Services/APIService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ private static string GetAPIHome(APIProvider apiProvider)

private readonly WebClient _webClient = new WebClient();

/// <summary>
/// Tests the given api configuration
/// </summary>
/// <returns>True if everything is working, false otherwise</returns>
public async Task<bool> TestConfigurationAsync(APIServiceConfiguration config)
{
// Ripple doesn't have API keys
Expand All @@ -56,6 +60,9 @@ public async Task<bool> TestConfigurationAsync(APIServiceConfiguration config)
return true;
}

/// <summary>
/// Get beatmap by its beatmap ID for the given game mode
/// </summary>
public async Task<Beatmap> GetBeatmapAsync(APIServiceConfiguration config, GameMode mode, string id)
{
string home = GetAPIHome(config.APIProvider);
Expand All @@ -64,6 +71,9 @@ public async Task<Beatmap> GetBeatmapAsync(APIServiceConfiguration config, GameM
return JsonConvert.DeserializeObject<Beatmap[]>(response).FirstOrDefault();
}

/// <summary>
/// Get top plays of a user by their user ID or user name
/// </summary>
public async Task<IEnumerable<Play>> GetUserTopPlaysAsync(APIServiceConfiguration config, GameMode mode, string userID)
{
string home = GetAPIHome(config.APIProvider);
Expand All @@ -72,6 +82,9 @@ public async Task<IEnumerable<Play>> GetUserTopPlaysAsync(APIServiceConfiguratio
return JsonConvert.DeserializeObject<Play[]>(response);
}

/// <summary>
/// Get top plays of a beatmap by its beatmap ID, for the given game mode, using given mods
/// </summary>
public async Task<IEnumerable<Play>> GetBeatmapTopPlaysAsync(APIServiceConfiguration config, GameMode mode,
string mapID, EnabledMods mods = EnabledMods.Any)
{
Expand Down
6 changes: 5 additions & 1 deletion OsuHelper/Services/OppaiService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ private static async Task<string> RunCmdAsync(string target, params string[] arg
return outputBuffer;
}

public async Task<double> CalculatePerformancePointsAsync(string beatmapFilePath, double accuracy, EnabledMods mods)
/// <summary>
/// Calculates performance points for the given beatmap by its .osu file, with given accuracy and mods
/// </summary>
public async Task<double> CalculatePerformancePointsAsync(string beatmapFilePath, double accuracy = 1,
EnabledMods mods = EnabledMods.None)
{
// Compose command line arguments
var args = new List<string>
Expand Down
35 changes: 35 additions & 0 deletions OsuHelper/Services/OsuGameService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// -------------------------------------------------------------------------
// Solution: OsuHelper
// Project: OsuHelper
// File: OsuGameService.cs
//
// Created by: Tyrrrz
// On: 28.08.2016
// -------------------------------------------------------------------------

using System.Diagnostics;
using System.Linq;
using NegativeLayer.Extensions;

namespace OsuHelper.Services
{
public sealed class OsuGameService
{
public string GetNowPlayingTitle()
{
// Find osu process
var process = Process.GetProcessesByName("osu!").FirstOrDefault();
if (process == null) return null;

// Get title
string windowTitle = process.MainWindowTitle;

// Check if anything is playing
if (!windowTitle.StartsWith("osu! - "))
return null;

// Return the title
return windowTitle.SubstringAfter("osu! - ");
}
}
}
73 changes: 73 additions & 0 deletions OsuHelper/Services/OsuSearchService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// -------------------------------------------------------------------------
// Solution: OsuHelper
// Project: OsuHelper
// File: OsuSearchService.cs
//
// Created by: Tyrrrz
// On: 28.08.2016
// -------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using NegativeLayer.Extensions;
using Newtonsoft.Json;
using OsuHelper.Models.API;

namespace OsuHelper.Services
{
public sealed class OsuSearchService : IDisposable
{
private const string Home = "http://osusearch.com/";

private static string URLEncode(string arg)
{
return Uri.EscapeUriString(arg);
}

private readonly WebClient _webClient = new WebClient();

/// <summary>
/// Searches for beatmaps using given properties and then returns their beatmap IDs
/// </summary>
public async Task<IEnumerable<string>> SearchAsync(GameMode mode = GameMode.Standard, string artist = null, string title = null, string diffName = null)
{
string url = Home + "query";
var args = new List<string>();

// Hidden parameters
args.Add("statuses=Ranked");

// Mandatory parameters
if (mode == GameMode.Standard)
args.Add("modes=Standard");
else if (mode == GameMode.CatchTheBeat)
args.Add("modes=CtB");
else if (mode == GameMode.Taiko)
args.Add("modes=Taiko");
else if (mode == GameMode.Mania)
args.Add("modes=Mania");

// Optional parameters
if (artist.IsNotBlank())
args.Add($"artist={URLEncode(artist.Trim())}");
if (title.IsNotBlank())
args.Add($"title={URLEncode(title.Trim())}");
if (diffName.IsNotBlank())
args.Add($"diff_name={URLEncode(diffName.Trim())}");
url += "?" + args.JoinToString("&");
string response = await _webClient.DownloadStringTaskAsync(url);

// High-danger zone (no typechecks)
dynamic result = JsonConvert.DeserializeObject(response);
return ((IEnumerable<dynamic>) result.beatmaps).Select(b => b.beatmap_id.ToString()).Cast<string>();
}

public void Dispose()
{
_webClient.Dispose();
}
}
}
3 changes: 3 additions & 0 deletions OsuHelper/Services/WindowService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public sealed class WindowService
{
private static Dispatcher Dispatcher => Dispatcher.CurrentDispatcher;

/// <summary>
/// Show an error dialog
/// </summary>
public void ShowError(string message)
{
Dispatcher.InvokeSafe(
Expand Down
42 changes: 41 additions & 1 deletion OsuHelper/ViewModels/CalculatorViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
Expand All @@ -23,6 +24,8 @@ public sealed class CalculatorViewModel : ViewModelBase, IDisposable
{
private readonly OppaiService _oppaiService;
private readonly WindowService _windowService;
private readonly OsuGameService _osuGameService;
private readonly OsuSearchService _osuSearchService;
private readonly WebClient _webClient;

private string _beatmapFilePath;
Expand Down Expand Up @@ -156,16 +159,53 @@ public double ExpectedPerformancePoints
}

// Commands
public RelayCommand SetCurrentlyPlayingBeatmapIDCommand { get; }
public RelayCommand AnalyzeCommand { get; }

public CalculatorViewModel(OppaiService oppaiService, WindowService windowService)
public CalculatorViewModel(OppaiService oppaiService, WindowService windowService, OsuGameService osuGameService,
OsuSearchService osuSearchService)
{
_oppaiService = oppaiService;
_windowService = windowService;
_osuGameService = osuGameService;
_osuSearchService = osuSearchService;
_webClient = new WebClient();

// Commands
AnalyzeCommand = new RelayCommand(Analyze, () => CanAnalyze);
SetCurrentlyPlayingBeatmapIDCommand = new RelayCommand(SetCurrentlyPlayingBeatmapID);
}

private async void SetCurrentlyPlayingBeatmapID()
{
string fullTitle = _osuGameService.GetNowPlayingTitle();
if (fullTitle.IsBlank())
{
_windowService.ShowError("Currently not playing any beatmap");
return;
}
var match = Regex.Match(fullTitle, @"(.*?)\s-\s(.*?)\[(.*?)\]");
if (!match.Success)
{
_windowService.ShowError($"Unable to parse beatmap title ({fullTitle})");
return;
}

// Get results
string artist = match.Groups[1].Value;
string title = match.Groups[2].Value;
string difficulty = match.Groups[3].Value;

// Search (only on standard)
var beatmapIDs = (await _osuSearchService.SearchAsync(GameMode.Standard, artist, title, difficulty)).ToArray();
if (!beatmapIDs.Any())
{
_windowService.ShowError($"Could not resolve a ranked beatmap by title ({fullTitle})");
return;
}

// Get first ID and copy it over
BeatmapID = beatmapIDs.First();
}

private async Task DownloadMap()
Expand Down
8 changes: 5 additions & 3 deletions OsuHelper/Views/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -253,9 +253,11 @@
<TextBlock x:Name="CalculatorBeatmapID"
Margin="5"
Text="Beatmap ID or URL:" />
<TextBox Margin="5"
FontWeight="SemiBold"
Text="{Binding BeatmapID}" />
<Grid>
<Grid.ColumnDefinitions><ColumnDefinition Width="*"></ColumnDefinition><ColumnDefinition Width="Auto"></ColumnDefinition></Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Margin="5" FontWeight="SemiBold" Text="{Binding BeatmapID}" />
<Button Grid.Column="1" Margin="5" Content="Get Currently Playing Beatmap" Command="{Binding SetCurrentlyPlayingBeatmapIDCommand}" />
</Grid>
<TextBlock Margin="5" Text="Mods:" />
<WrapPanel>
<CheckBox Margin="5"
Expand Down

0 comments on commit ae5cd75

Please sign in to comment.