Skip to content
This repository has been archived by the owner on Nov 8, 2021. It is now read-only.

Commit

Permalink
Logic reshuffle and URI SCHEMES!
Browse files Browse the repository at this point in the history
  • Loading branch information
nicholastay committed May 27, 2016
1 parent d432259 commit 4dfff7d
Show file tree
Hide file tree
Showing 9 changed files with 184 additions and 37 deletions.
Binary file added Designs/download-with-nexdirect.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions WpfNexDirect/App.xaml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<Application x:Class="NexDirect.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:NexDirect"
StartupUri="MainWindow.xaml">
xmlns:local="clr-namespace:NexDirect">
<Application.Resources>

</Application.Resources>
</Application>

<!-- https://stackoverflow.com/questions/24251244/wpf-and-unity-no-matching-constructor-found-on-type mainwindow removed -->
12 changes: 6 additions & 6 deletions WpfNexDirect/App.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
using Microsoft.Shell;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;

namespace NexDirect
Expand All @@ -18,14 +14,15 @@ public partial class App : Application, ISingleInstanceApp
private const string AppUnique = "NexDirect_SIApp";

[STAThread]
public static void Main()
public static void Main(string[] args)
{
if (SingleInstance<App>.InitializeAsFirstInstance(AppUnique))
{
var app = new App();

app.InitializeComponent();
app.Run();
//app.HandleURIArgs(args.ToList());
app.Run(new MainWindow(args));

SingleInstance<App>.Cleanup();
}
Expand All @@ -43,6 +40,9 @@ public bool SignalExternalCommandLineArgs(IList<string> args)

MainWindow.Activate();

args.RemoveAt(0); // args includes the executing path, lets remove that
MainWindow _MainWindow = (MainWindow)Current.MainWindow; // get the mainwin instance
_MainWindow.handleURIArgs(args);
return true;
}

Expand Down
19 changes: 13 additions & 6 deletions WpfNexDirect/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<DataGridTextColumn Binding="{Binding Mapper}" Width="150" Header="Mapper"/>
</DataGrid.Columns>
</DataGrid>
<TextBox x:Name="searchBox" Height="44" Margin="10,23,377,0" TextWrapping="Wrap" VerticalAlignment="Top" BorderThickness="3" BorderBrush="#FFCD8721" Background="#FFE0DDDD" FontSize="26.667"/>
<TextBox x:Name="searchBox" Height="44" Margin="10,23,377,0" TextWrapping="Wrap" VerticalAlignment="Top" BorderThickness="3" BorderBrush="#FFCD8721" Background="#FFE0DDDD" FontSize="26.667" TextChanged="searchBox_TextChanged"/>
<Button x:Name="searchButton" Content="Search" Margin="0,23,200,0" BorderBrush="#FF5294A2" BorderThickness="3" FontSize="26.667" Click="searchButton_Click" IsDefault="True" Height="44" VerticalAlignment="Top" HorizontalAlignment="Right" Width="172"/>
<DataGrid x:Name="progressGrid" Margin="0,23,10,26" ItemsSource="{Binding}" AutoGenerateColumns="False" IsReadOnly="True" MouseDoubleClick="progressGrid_DoubleClick" HeadersVisibility="Column" HorizontalAlignment="Right" Width="185">
<DataGrid.Columns>
Expand All @@ -36,22 +36,29 @@
<ProgressBar x:Name="searchingLoading" Margin="328,278,530,0" IsIndeterminate="True" ToolTip="Loading..." Height="22" VerticalAlignment="Top" Visibility="Hidden"/>
<Label x:Name="overlayModeNotice" Content="Overlay mode is active. Use CTRL+SHIFT+HOME to toggle the interface." HorizontalAlignment="Left" Margin="10,0,0,0" Width="393" Height="32" VerticalAlignment="Bottom" Visibility="Hidden"/>
<Label x:Name="overlayModeExit" Content="X" Height="23" VerticalAlignment="Top" HorizontalAlignment="Right" Width="19" MouseUp="overlayModeExit_MouseUp" Cursor="Hand" Margin="0,1,0,0" Visibility="Hidden"/>
<ComboBox x:Name="rankedStatusBox" Height="23" Margin="0,73,831,0" VerticalAlignment="Top" SelectedIndex="0" HorizontalAlignment="Right" Width="161">
<ComboBox x:Name="rankedStatusBox" Height="23" Margin="0,73,889,0" VerticalAlignment="Top" SelectedIndex="0" HorizontalAlignment="Right" Width="161">
<ComboBoxItem Content="All"/>
<ComboBoxItem Content="Ranked"/>
<ComboBoxItem Content="Qualified"/>
<ComboBoxItem Content="Unranked"/>
</ComboBox>
<ComboBox x:Name="modeSelectBox" Height="23" Margin="0,73,540,0" VerticalAlignment="Top" SelectedIndex="0" HorizontalAlignment="Right" Width="161">
<ComboBox x:Name="modeSelectBox" Height="23" Margin="0,73,629,0" VerticalAlignment="Top" SelectedIndex="0" HorizontalAlignment="Right" Width="161">
<ComboBoxItem Content="All"/>
<ComboBoxItem Content="osu!"/>
<ComboBoxItem Content="Catch the Beat"/>
<ComboBoxItem Content="Taiko"/>
<ComboBoxItem Content="osu!mania"/>
</ComboBox>
<Label x:Name="label" Content="Show:" Height="23" Margin="0,72,992,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="46"/>
<Label x:Name="label1" Content="Mode:" Height="27" Margin="0,71,701,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="45"/>
<Button x:Name="popularLoadButton" Content="Load Popular Maps" Height="25" Margin="0,72,291,0" VerticalAlignment="Top" Click="popularLoadButton_Click" HorizontalAlignment="Right" Width="159"/>
<Label x:Name="label" Content="Show:" Height="23" Margin="0,73,1050,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="46"/>
<Label x:Name="label1" Content="Mode:" Height="27" Margin="0,71,795,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="45"/>
<Button x:Name="popularLoadButton" Content="Load Popular Maps" Height="25" Margin="0,71,206,0" VerticalAlignment="Top" Click="popularLoadButton_Click" HorizontalAlignment="Right" Width="159"/>
<Label x:Name="searchViaLabel" Content="Search via: " Height="27" Margin="0,71,530,0" VerticalAlignment="Top" HorizontalAlignment="Right" Width="68" Visibility="Hidden"/>
<ComboBox x:Name="searchViaSelectBox" HorizontalAlignment="Left" Height="22" Margin="593,73,0,0" VerticalAlignment="Top" Width="135" SelectedIndex="1" Visibility="Hidden">
<ComboBoxItem Content="Normal (Title/Artist)"/>
<ComboBoxItem Content="Beatmap Set ID"/>
<ComboBoxItem Content="Beatmap ID"/>
<ComboBoxItem Content="Mapper User ID"/>
</ComboBox>
</Grid>


Expand Down
101 changes: 80 additions & 21 deletions WpfNexDirect/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Diagnostics;
using System.Text.RegularExpressions;
using System.Collections.Generic;

// TODO
// Multiple same beatmap d/l -- DONE
Expand Down Expand Up @@ -65,7 +67,7 @@ private static extern bool UnregisterHotKey(
public string uiBackground = Properties.Settings.Default.customBgPath;
public bool launchOsu = Properties.Settings.Default.launchOsu;

public MainWindow()
public MainWindow(string[] startupArgs)
{
InitializeComponent();
dataGrid.ItemsSource = beatmaps;
Expand All @@ -92,6 +94,8 @@ public MainWindow()
//beatmap.RankingStatus = "Ranked";
//beatmap.AlreadyHave = false;
//beatmaps.Add(beatmap);

handleURIArgs(startupArgs);
}

public class BeatmapSet
Expand All @@ -104,6 +108,18 @@ public class BeatmapSet
public bool AlreadyHave { get; set; }
public Uri PreviewImage { get; set; }
public JObject BloodcatData { get; set; }

public BeatmapSet(MainWindow _this, JObject rawData)
{
Id = rawData["id"].ToString();
Artist = rawData["artist"].ToString();
Title = rawData["title"].ToString();
Mapper = rawData["creator"].ToString();
RankingStatus = Tools.resolveRankingStatus(rawData["status"].ToString());
PreviewImage = new Uri(string.Format("http://b.ppy.sh/thumb/{0}l.jpg", Id));
AlreadyHave = _this.alreadyDownloaded.Any(b => b.Contains(Id + " "));
BloodcatData = rawData;
}
}

// i dont even 100% know how this notifypropertychanged works
Expand Down Expand Up @@ -166,7 +182,7 @@ private async void searchButton_Click(object sender, RoutedEventArgs e)
try
{
searchingLoading.Visibility = Visibility.Visible;
var beatmapsData = await getBloodcatSearch<JArray>(searchBox.Text, rankedStatusBox.Text.ToString(), modeSelectBox.Text.ToString());
var beatmapsData = await getBloodcatSearch<JArray>(searchBox.Text, rankedStatusBox.Text.ToString(), modeSelectBox.Text.ToString(), searchViaSelectBox.Text.ToString(), false);
populateBeatmaps(beatmapsData);
}
catch (Exception ex) { MessageBox.Show("There was an error searching for beatmaps...\n\n" + ex.ToString()); }
Expand All @@ -189,22 +205,31 @@ private async void popularLoadButton_Click(object sender, RoutedEventArgs e)
finally { searchingLoading.Visibility = Visibility.Hidden; }
}

private Regex onlyNumbersReg = new Regex(@"^\d+$");
private void searchBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
{
// if only numbers show the search via, just like the bloodcat website
if (onlyNumbersReg.IsMatch(searchBox.Text))
{
if (searchViaLabel.Visibility == Visibility.Hidden)
{
searchViaLabel.Visibility = Visibility.Visible;
searchViaSelectBox.Visibility = Visibility.Visible;
}
}
else if (searchViaLabel.Visibility == Visibility.Visible)
{
searchViaLabel.Visibility = Visibility.Hidden;
searchViaSelectBox.Visibility = Visibility.Hidden;
}
}

private void populateBeatmaps(JArray beatmapsData)
{
beatmaps.Clear();
foreach (JObject beatmapData in beatmapsData)
{
var beatmapSet = new BeatmapSet();
beatmapSet.Id = (string)beatmapData.Property("id").Value;
beatmapSet.Artist = (string)beatmapData.Property("artist").Value;
beatmapSet.Title = (string)beatmapData.Property("title").Value;
beatmapSet.Mapper = (string)beatmapData.Property("creator").Value;
beatmapSet.RankingStatus = Tools.resolveRankingStatus((string)beatmapData.Property("status").Value);
beatmapSet.PreviewImage = new Uri(string.Format("http://b.ppy.sh/thumb/{0}l.jpg", (string)beatmapData.Property("id").Value));
beatmapSet.AlreadyHave = alreadyDownloaded.Any(b => b.Contains(beatmapSet.Id + " ")); // .StartsWith doesnt work as intended for some reason
beatmapSet.BloodcatData = beatmapData;

beatmaps.Add(beatmapSet);
beatmaps.Add(new BeatmapSet(this, beatmapData));
}
}

Expand All @@ -221,13 +246,6 @@ private async void dataGrid_DoubleClick(object sender, MouseButtonEventArgs e)
return;
}

// check for already have
if (beatmap.AlreadyHave)
{
MessageBoxResult prompt = MessageBox.Show(string.Format("You already have this beatmap {0} ({1}). Do you wish to redownload it?", beatmap.Title, beatmap.Mapper), "NexDirect - Cancel Download", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (prompt == MessageBoxResult.No) return;
}

// start dl
await downloadBloodcatSet(beatmap);
}
Expand Down Expand Up @@ -343,14 +361,15 @@ private void loadAlreadyDownloadedMaps()
alreadyDownloaded = Directory.GetDirectories(osuFolder);
}

private async Task<T> getBloodcatSearch<T>(string query, string selectRanked, string selectMode)
private async Task<T> getBloodcatSearch<T>(string query, string selectRanked, string selectMode, string selectSearchVia, bool throughUri)
{
// build query string -- https://stackoverflow.com/questions/17096201/build-query-string-for-system-net-httpclient-get
var qs = HttpUtility.ParseQueryString(string.Empty);
qs["mod"] = "json";
qs["q"] = query;
if (selectRanked != "All") qs["s"] = Tools.resolveRankingComboBox(selectRanked);
if (selectMode != "All") qs["m"] = Tools.resolveModeComboBox(selectMode);
if (throughUri || searchViaLabel.Visibility != Visibility.Hidden) qs["c"] = Tools.resolveSearchViaComboBox(selectSearchVia);

return await getJson<T>("http://bloodcat.com/osu/?" + qs.ToString());
}
Expand All @@ -360,6 +379,29 @@ private async Task<T> getBloodcatPopular<T>()
return await getJson<T>("http://bloodcat.com/osu/popular.php?mod=json");
}

public async void resolveSetAndDownload(string beatmapSetId)
{
try
{
JArray results = await getBloodcatSearch<JArray>(beatmapSetId, "All", "All", "Beatmap Set ID", true);
JObject map = results.Children<JObject>().FirstOrDefault(r => r["id"].ToString() == beatmapSetId);
if (map == null)
{
MessageBox.Show("Could not find the beatmap on Bloodcat. Cannot proceed to download :(");
return;
}

BeatmapSet set = new BeatmapSet(this, map);
MessageBoxResult confirmPrompt = MessageBox.Show(string.Format("Are you sure you wish to download: {0} - {1} (mapped by {2})?", set.Artist, set.Title, set.Mapper), "NexDirect - Confirm Download", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (confirmPrompt == MessageBoxResult.No) return;
await downloadBloodcatSet(set);
}
catch (Exception ex)
{
MessageBox.Show("An error has occurred...\n" + ex.ToString());
}
}

private async Task<T> getJson<T>(string url)
{
using (var client = new HttpClient())
Expand All @@ -373,6 +415,13 @@ private async Task<T> getJson<T>(string url)

private async Task downloadBloodcatSet(BeatmapSet set)
{
// check for already have
if (set.AlreadyHave)
{
MessageBoxResult prompt = MessageBox.Show(string.Format("You already have this beatmap {0} ({1}). Do you wish to redownload it?", set.Title, set.Mapper), "NexDirect - Cancel Download", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (prompt == MessageBoxResult.No) return;
}

Uri downloadUri;
if (string.IsNullOrEmpty(beatmapMirror))
{
Expand Down Expand Up @@ -448,6 +497,16 @@ private void loadDoong()
audioDoong.PlaybackStopped += (o, e) => reader.Position = 0;
}

Regex uriReg = new Regex(@"nexdirect:\/\/(\d+)\/");
public void handleURIArgs(IList<string> args)
{
if (args.Count < 1) return; // no args
string fullArgs = string.Join(" ", args);

Match m = uriReg.Match(fullArgs);
resolveSetAndDownload(m.Groups[1].ToString());
}

private async Task playPreviewAudio(BeatmapSet set)
{
audioWaveOut.Stop(); // if already playing something just stop it
Expand Down
3 changes: 2 additions & 1 deletion WpfNexDirect/Settings.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:NexDirect"
mc:Ignorable="d"
Title="Settings - NexDirect" Height="386.411" Width="513.001" ResizeMode="NoResize" WindowStyle="ToolWindow" Topmost="True">
Title="Settings - NexDirect" Height="424.411" Width="513.001" ResizeMode="NoResize" WindowStyle="ToolWindow" Topmost="True">
<Grid>
<Label x:Name="label" Content="Settings" Margin="152,10,0,0" FontSize="48" Height="76" VerticalAlignment="Top" HorizontalAlignment="Left" Width="182"/>
<Button x:Name="changeFolderButton" Content="Update osu! folder location" HorizontalAlignment="Left" Height="21" Margin="118,113,0,0" VerticalAlignment="Top" Width="243" Click="changeFolderButton_Click"/>
Expand All @@ -20,6 +20,7 @@
<Button x:Name="setCustomBgButton" Content="Set custom UI background" HorizontalAlignment="Left" Height="21" Margin="35,185,0,0" VerticalAlignment="Top" Width="209" Click="setCustomBgButton_Click"/>
<Button x:Name="clearBgButton" Content="Clear UI background" HorizontalAlignment="Left" Height="21" Margin="249,185,0,0" VerticalAlignment="Top" Width="209" Click="clearBgButton_Click"/>
<CheckBox x:Name="launchOsuCheckbox" Content="Launch osu! with params instead of moving to songs folder (Advanced)" HorizontalAlignment="Left" Height="23" Margin="58,325,0,0" VerticalAlignment="Top" Width="400" Checked="launchOsuCheckbox_Toggled" Unchecked="launchOsuCheckbox_Toggled"/>
<Button x:Name="registerUriButton" Content="Register NexDirect URI Scheme for User" HorizontalAlignment="Left" Height="23" Margin="129,353,0,0" VerticalAlignment="Top" Width="244" Click="registerUriButton_Click"/>

</Grid>
</Window>
35 changes: 34 additions & 1 deletion WpfNexDirect/Settings.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.WindowsAPICodePack.Dialogs;
using Microsoft.Win32;
using Microsoft.WindowsAPICodePack.Dialogs;
using System;
using System.Collections.Generic;
using System.Linq;
Expand Down Expand Up @@ -108,5 +109,37 @@ private void launchOsuCheckbox_Toggled(object sender, RoutedEventArgs e)
Properties.Settings.Default.launchOsu = parent.launchOsu;
Properties.Settings.Default.Save();
}

private const string regUriSubKey = @"Software\Classes\nexdirect";
private void registerUriButton_Click(object sender, RoutedEventArgs e)
{
string appLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;

try
{
// https://msdn.microsoft.com/en-AU/library/h5e7chcf.aspx
using (RegistryKey key = Registry.CurrentUser.CreateSubKey(regUriSubKey))
{
key.SetValue("", "NexDirect Handling Protocol"); // "" = (default)
key.SetValue("URL Protocol", "");

using (RegistryKey iconKey = key.CreateSubKey(@"DefaultIcon"))
{
iconKey.SetValue("", string.Format("{0},1", appLocation));
}

using (RegistryKey shellOpenKey = key.CreateSubKey(@"shell\open\command"))
{
shellOpenKey.SetValue("", string.Format("\"{0}\" \"%1\"", appLocation));
}
}

MessageBox.Show("The URI Scheme handler was registered successfully.");
}
catch (Exception ex)
{
MessageBox.Show(string.Format("An error occured whilst registering the handler..."), ex.ToString());
}
}
}
}
17 changes: 17 additions & 0 deletions WpfNexDirect/Tools.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ static public string resolveModeComboBox(string input)
}
}

static public string resolveSearchViaComboBox(string input)
{
switch (input)
{
case "Normal (Title/Artist)":
return "o";
case "Beatmap Set ID":
return "s";
case "Beatmap ID":
return "b";
case "Mapper User ID":
return "u";
default:
return null;
}
}

// https://stackoverflow.com/questions/309485/c-sharp-sanitize-file-name sol #2
static public string sanitizeFilename(string filename)
{
Expand Down
Loading

0 comments on commit 4dfff7d

Please sign in to comment.