1.8: Make Nightwave for Workgroups/Nightwave.NET work again

Nightwave Plaza had a couple of API changes! i have now made it work.
This commit is contained in:
Maff 2023-05-18 20:05:30 +00:00
parent 6be173855a
commit f688cd63a9
7 changed files with 253 additions and 109 deletions

View File

@ -33,9 +33,9 @@
<Label x:Name="lbElapsed" Content="00:00" HorizontalAlignment="Left" Margin="0,99,0,0" VerticalAlignment="Top" FontStyle="Italic" FontSize="9" Grid.Column="1"/>
<Label x:Name="lbTime" Content="00:00" Margin="0,99,0,0" VerticalAlignment="Top" HorizontalAlignment="Right" FontStyle="Italic" FontSize="9" Grid.Column="2"/>
<Button x:Name="btLike" Content="❤" ToolTip="Like this track" IsEnabled="False" HorizontalAlignment="Right" Margin="0,40,2,0" Width="22" Height="20" VerticalAlignment="Top" FontSize="10" Grid.Column="2" Click="BtLike_Click"/>
<Button x:Name="btDislike" Content="💔" ToolTip="Dislike this track" IsEnabled="False" HorizontalAlignment="Right" Margin="0,64,2,0" Width="22" Height="20" VerticalAlignment="Top" FontSize="10" Grid.Column="2" Click="BtDislike_Click"/>
<!--<Button x:Name="btDislike" Content="💔" ToolTip="Dislike this track" IsEnabled="False" HorizontalAlignment="Right" Margin="0,64,2,0" Width="22" Height="20" VerticalAlignment="Top" FontSize="10" Grid.Column="2" Click="BtDislike_Click"/>-->
<Label x:Name="lbLikeCt" Content="0" HorizontalAlignment="Right" Margin="0,36.5,22,0" VerticalAlignment="Top" Grid.Column="2" Grid.ColumnSpan="2"/>
<Label x:Name="lbDislikeCt" Content="0" HorizontalAlignment="Right" Margin="0,60.5,22,0" VerticalAlignment="Top" Grid.Column="2" Grid.ColumnSpan="2"/>
<!--<Label x:Name="lbDislikeCt" Content="0" HorizontalAlignment="Right" Margin="0,60.5,22,0" VerticalAlignment="Top" Grid.Column="2" Grid.ColumnSpan="2"/>-->
<Image x:Name="imLogin" IsHitTestVisible="False" Panel.ZIndex="1" Width="18" Height="18" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,17,3,0" Grid.Column="2" Source="Resources/usr.png" Stretch="UniformToFill"/>
<Button x:Name="btLogin" Content="" ToolTip="Log into Nightwave Plaza" HorizontalAlignment="Right" Margin="0,16,2,0" Width="22" Height="20" VerticalAlignment="Top" FontSize="9" Grid.Column="2" Click="BtLogin_Click"/>
<TextBlock ToolTip="Visit Nightwave Plaza! If you don't have an account, you can make one there!" FontSize="10" FontStyle="Italic" Grid.Column="2" Grid.Row="0" Margin="0,0,2,13" HorizontalAlignment="Right" VerticalAlignment="Bottom" Foreground="#FF5A5A5A">
@ -43,24 +43,31 @@
</TextBlock>
<DockPanel Grid.ColumnSpan="5" Margin="0,0,0,0" Grid.Row="1">
<StatusBar x:Name="statusBar" Margin="0,0,0,-2" DockPanel.Dock="Bottom" VerticalAlignment="Bottom" BorderBrush="#FFDFDFDF" BorderThickness="1" Height="28" VerticalContentAlignment="Bottom">
<StatusBarItem HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="sbListeners" Text="Listener count" Margin="4,0,4,0"/>
<ToggleButton x:Name="btPlayPause" Content="⏯" ToolTip="Start/Stop playback" VerticalContentAlignment="Center" Checked="BtPlayPause_Click" Unchecked="BtPlayPause_Click" Margin="0,-2,4,-2"/>
<TextBlock x:Name="sbStatus" Text="Status"/>
</StackPanel>
</StatusBarItem>
<StatusBarItem HorizontalAlignment="Right">
<StackPanel Orientation="Horizontal">
<!--TODO implement tracking of time spent listening (this session and all-time)
<StatusBarItem HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal">
<ToggleButton x:Name="btPlayPause" Content="⏯" ToolTip="Start/Stop playback" VerticalContentAlignment="Center" Checked="BtPlayPause_Click" Unchecked="BtPlayPause_Click" Margin="0,-2,4,-2"/>
<TextBlock x:Name="sbListeners" Text="Listener count" Margin="2,0,0,0"/>
</StackPanel>
</StatusBarItem>
<Separator/>
<StatusBarItem HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal">
<TextBlock x:Name="sbStatus" Text="Status"/>
</StackPanel>
</StatusBarItem>
<StatusBarItem DockPanel.Dock="Right" HorizontalAlignment="Right">
<Slider x:Name="sbVol" Maximum="100" Minimum="0" ToolTip="Volume level" Value="100" SmallChange="1" LargeChange="5" Width="80" ValueChanged="SbVol_ValueChanged" MouseWheel="SbVol_MouseWheel" Margin="0,0,4,0"/>
</StatusBarItem>
<Separator DockPanel.Dock="Right" HorizontalAlignment="Right" />
<StatusBarItem DockPanel.Dock="Right" HorizontalAlignment="Right" >
<StackPanel Orientation="Horizontal">
<!--TODO implement tracking of time spent listening (this session and all-time)
<TextBlock x:Name="sbListeningTimers" Text="00:00 (00:00)"/>-->
<TextBlock x:Name="sbVote" Text="Vote" Margin="0,0,4,0"/>
<ToggleButton x:Name="sbOnTop" Content="📌" ToolTip="Toggle always-on-top" Checked="SbOnTop_Click" Margin="0,-2,4,-2"/>
<ToggleButton x:Name="sbMute" Content="🔊" ToolTip="Toggle mute" Checked="SbMute_Click" Unchecked="SbMute_Click" Margin="0,-2,0,-2"/>
<Separator Width="6" Background="{x:Null}" Foreground="{x:Null}"/>
<Slider x:Name="sbVol" Maximum="100" Minimum="0" ToolTip="Volume level" Value="100" SmallChange="1" LargeChange="5" Width="80" ValueChanged="SbVol_ValueChanged" MouseWheel="SbVol_MouseWheel" Margin="0,0,4,0"/>
</StackPanel>
</StatusBarItem>
<TextBlock x:Name="sbVote" Text="Vote" Margin="0,0,4,0"/>
<ToggleButton x:Name="sbOnTop" Content="📌" ToolTip="Toggle always-on-top" Checked="SbOnTop_Click" Unchecked="SbOnTop_Click" Margin="0,-2,4,-2"/>
<ToggleButton x:Name="sbMute" Content="🔊" ToolTip="Toggle mute" Checked="SbMute_Click" Unchecked="SbMute_Click" Margin="0,-2,0,-2"/>
</StackPanel>
</StatusBarItem>
</StatusBar>
</DockPanel>
<Rectangle Height="1" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="0" Margin="0,0,0,10" Fill="#FF003C49" VerticalAlignment="Bottom"/>

View File

@ -65,7 +65,7 @@ namespace NightwaveForWorkgroups {
btLogin.Visibility = Visibility.Hidden;
imLogin.IsEnabled = false;
imLogin.Visibility = Visibility.Hidden;
btDislike.IsEnabled = true;
//btDislike.IsEnabled = true;
btLike.IsEnabled = true;
SetStatus($"Logged in as {await NwP.GetUser()}");
}
@ -88,43 +88,43 @@ namespace NightwaveForWorkgroups {
slDuration.Value = s.CalculatedElapsed;
sbListeners.Text = $"{s.Listeners} listeners";
lbLikeCt.Content = s.Likes;
lbDislikeCt.Content = s.Dislikes;
//lbDislikeCt.Content = s.Dislikes;
lbElapsed.Content = $"{(s.CalculatedElapsed / 60).ToString("D")}:{(s.CalculatedElapsed % 60).ToString("D2")}";
if(LastArtwork != s.ArtworkUri || Force) {
//TODO should probably break this part out into a separate function cause this feels real messy
Vote v = await NwP.Vote() ?? Vote.Neutral;
switch(v) {
case Vote.Dislike:
/*case Vote.Dislike:
sbVote.Text = "👎";
sbVote.ToolTip = "You dislike this.";
btLike.Content = "👍";
btLike.ToolTip = "Like this track";
btDislike.Content = "🤷‍♀️";
btDislike.ToolTip = "Remove your vote for this track";
break;
break;*/
case Vote.Like:
sbVote.Text = "👍";
sbVote.ToolTip = "You like this.";
btLike.Content = "💖";
btLike.ToolTip = "Favourite this track";
btDislike.Content = "👎";
btDislike.ToolTip = "Dislike this track";
//btDislike.Content = "👎";
//btDislike.ToolTip = "Dislike this track";
break;
case Vote.Favourite:
sbVote.Text = "💖";
sbVote.ToolTip = "You have favourited this.";
btLike.Content = "🤷‍♀️";
btLike.ToolTip = "Remove your vote for this track";
btDislike.Content = "👎";
btDislike.ToolTip = "Dislike this track";
//btDislike.Content = "👎";
//btDislike.ToolTip = "Dislike this track";
break;
default:
sbVote.Text = "🤷‍♀️";
sbVote.ToolTip = "You have no feelings towards this, or you have not expressed your feelings for this.";
btLike.Content = "👍";
btLike.ToolTip = "Like this track";
btDislike.Content = "👎";
btDislike.ToolTip = "Dislike this track";
//btDislike.Content = "👎";
//btDislike.ToolTip = "Dislike this track";
break;
}
//TODO visual effect on the like/dislike buttons to indicate user's vote
@ -259,7 +259,10 @@ namespace NightwaveForWorkgroups {
return;
}
//user may have stored credentials and not want to keep them any more, delete with reckless abandon.
CredentialManager.DeleteCredential("Nightwave.NET");
// on certain versions of windows, the lack of existing creds here will cause an exception.
try {
CredentialManager.DeleteCredential("Nightwave.NET");
} catch(Exception) { }
if(_c.CredentialSaved == CredentialSaveOption.Selected) {
CredentialManager.WriteCredential("Nightwave.NET", _c.UserName, _c.Password, CredentialPersistence.Enterprise);
Credential c=CredentialManager.ReadCredential("Nightwave.NET");
@ -278,13 +281,14 @@ namespace NightwaveForWorkgroups {
btLogin.Visibility = Visibility.Hidden;
imLogin.IsEnabled = false;
imLogin.Visibility = Visibility.Hidden;
btDislike.IsEnabled = true;
//btDislike.IsEnabled = true;
btLike.IsEnabled = true;
SetStatus($"Logged in as {await NwP.GetUser()}");
}
private async void BtLike_Click(object sender, RoutedEventArgs e) {
_ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btLike);
Vote v = await NwP.Vote() ?? Vote.Neutral;
// Kinda like a garbage state machine
v = v switch {
Vote.Like => Vote.Favourite,
Vote.Favourite => Vote.Neutral,
@ -297,7 +301,7 @@ namespace NightwaveForWorkgroups {
_ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btLike);
_ = Dispatcher.BeginInvoke(new dgtBool(DgtRefresh), true);
}
private async void BtDislike_Click(object sender, RoutedEventArgs e) {
/*private async void BtDislike_Click(object sender, RoutedEventArgs e) {
_ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btDislike);
Vote v = Vote.Dislike;
if(await NwP.Vote() == Vote.Dislike)
@ -308,9 +312,13 @@ namespace NightwaveForWorkgroups {
SetStatus($"Something went weird while casting {v}");
_ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btDislike);
_ = Dispatcher.BeginInvoke(new dgtBool(DgtRefresh), true);
}
}*/
private void SbOnTop_Click(object sender, RoutedEventArgs e) {
Topmost = sbOnTop.IsChecked == true;
Topmost = sbOnTop.IsChecked.GetValueOrDefault(false);
WindowStyle = Topmost ? WindowStyle.None : WindowStyle.SingleBorderWindow;
ResizeMode = Topmost ? ResizeMode.NoResize : ResizeMode.CanResize;
//I don't remember what this is and I don't remember why it's being set
// only when always-on-top is enabled and then never again.
if(tGfx.Enabled && Topmost)
tGfx.Interval = 1000.0 / 60.0;
}

View File

@ -1,20 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
<Product>Nightwave.NET for Workgroups</Product>
<Company>Softpup Aesthetics &amp; Petting, p.r.p.l.</Company>
<Authors>Maff</Authors>
<AssemblyVersion>1.7.0.1</AssemblyVersion>
<FileVersion>1.7.0.1</FileVersion>
<AssemblyVersion>1.8.0.0</AssemblyVersion>
<FileVersion>1.8.0.0</FileVersion>
<NeutralLanguage>en-GB</NeutralLanguage>
<PackageProjectUrl>https://soft.pup.cloud/Nightwave.NET/</PackageProjectUrl>
<RepositoryUrl>https://git.maff.scot/maff/PlazaSharp/src/branch/master/NightwaveForWorkgroups</RepositoryUrl>
<RepositoryUrl>https://commit.pup.cloud/maff/PlazaSharp/src/branch/master/NightwaveForWorkgroups</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageLicenseExpression>BSD-3-CLAUSE</PackageLicenseExpression>
<Version>1.7-0r1</Version>
<Version>1.8-0r0</Version>
<ApplicationManifest>app.manifest</ApplicationManifest>
<ApplicationIcon>Resources\app.ico</ApplicationIcon>
<StartupObject>NightwaveForWorkgroups.App</StartupObject>

View File

@ -7,10 +7,12 @@ while maintaining aesthetics.
You can [get it here][6].
Intended future improvements include:
* Optional session storage between sessions to avoid logging in again
* Multiple visualiser types
* Better use of additional application real-estate when resized
* Integration with the Windows native media APIs (so keyboard play/pause buttons work)
* Storage for window height/width, always-on-top and volume state
* Log-out button lmfao
* Miniplayer!
## First-Party
@ -25,6 +27,20 @@ Nightwave.NET uses the following third-party libraries and resources:
* [JSON.Net][4] by Newtonsoft for deserialisation of JSON strings.
* [CredentialManager][5] by Meziantou for access to the Windows Credential Manager APIs
## Privacy, data collection & usage
Nightwave.NET collects the following information and stores it locally in your Windows install's Enterprise Credentials Store:
* Username for Nightwave Plaza
* Password for Nightwave Plaza (encrypted)
Nightwave.NET does not transmit any data over the internet itself, however the Nightwave Plaza library interface, **libplaza**, uploads the following information to remote servers:
* Username and Password for Nightwave Plaza, uploaded by means of the Nightwave Plaza standard login mechanism, to **api.plaza.one/user/auth**, the standard login endpoint for Nightwave Plaza
* Your vote for the current track, whenever you cast it, uploaded by means of the Nightwave Plaza reactions mechanism, to **api.plaza.one/reactions**, the standard voting endpoint for Nightwave Plaza
Nightwave.NET and libplaza are both developed solely by me, and do not collect any information not mentioned here. To the best of my knowledge and personal verification,
no third-party library in use by either Nightwave.NET or libplaza collects any data or makes any network connections, with the exception of **BASS**, which
makes one network connection: to stream the Nightwave Radio audio stream to your computer.
[1]: https://plaza.one
[2]: https://www.un4seen.com/bass.html
[3]: https://github.com/ManagedBass/ManagedBass

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
@ -38,22 +39,22 @@ namespace libplaza {
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public string artwork;
public string artwork_src;
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public string artwork_s;
public string artwork_sm_src;
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public int likes;
public int reactions;
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public int hates;
//public int hates;
}
/// <summary>
@ -79,7 +80,7 @@ namespace libplaza {
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
[JsonProperty]
public PlazaPlayback playback;
public PlazaPlayback song;
}
/// <summary>
@ -110,6 +111,11 @@ namespace libplaza {
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public string created_at;
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public PlazaCurrentVote current_like;
}
/// <summary>
@ -119,12 +125,12 @@ namespace libplaza {
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public string status;
public string result;
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public int[] ratings;
public int reactions;
}
/// <summary>
@ -134,7 +140,42 @@ namespace libplaza {
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public int rate;
public int reaction;
}
/// <summary>
/// A transitional class used internally by <see cref="JsonConvert.DeserializeObject{T}(string)"/>
/// </summary>
public class PlazaCurrentVote {
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public bool like_id;
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public PlazaLikedSong song;
}
/// <summary>
/// A transitional class used internally by <see cref="JsonConvert.DeserializeObject{T}(string)"/>
/// </summary>
public class PlazaLikedSong {
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public string artist;
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public string artwork_src;
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public string title;
}
/// <summary>
@ -154,7 +195,7 @@ namespace libplaza {
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
/// </summary>
public string status;
public string result;
/// <summary>
/// A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
@ -182,6 +223,11 @@ namespace libplaza {
/// An <see cref="Exception"/> object indicating that an error or exception occurred when communicating with the <see cref="Nightwave"/> API.
/// </summary>
internal class PlazaUnavailableException : Exception { }
/// <summary>
/// A <see cref="PlazaUnavailableException"/> object indicating that, while an error occurred, it's one that we can silently retry.
/// </summary>
internal class PlazaQuietlyUnavailableException : PlazaUnavailableException { }
#endregion
#region Type Definitions
/// <summary>
@ -252,7 +298,7 @@ namespace libplaza {
/// <summary>
/// Contains an integer value of the number of dislikes the currently-playing track has received
/// </summary>
public int Dislikes;
//public int Dislikes;
/// <summary>
/// Contains an integer value of the number of people currently listening to the broadcast
@ -266,8 +312,9 @@ namespace libplaza {
/// <summary>
/// To <c>Dislike</c> is to indicate the user's disposition towards this track is one of displeasure;
/// that the user does not appreciate hearing this track, perhaps even that the user wishes not to hear this track.
/// This field was removed at some point, because it "no longer makes any sense" - I disagree.
/// </summary>
Dislike = -1,
//Dislike = -1,
/// <summary>
/// To be of <c>Neutral</c> disposition is to indicate the user has no feelings towards this track,
@ -288,6 +335,7 @@ namespace libplaza {
Favourite
}
#endregion
#region Nightwave
/// <summary>
/// <see cref="Nightwave"/> is the primary class by which the Nightwave Plaza API can be used.
/// </summary>
@ -313,7 +361,7 @@ namespace libplaza {
/// <code><see cref="Nightwave"/> nwd = new <see cref="Nightwave"/>("http://localhost:8000");</code>
/// </example>
public Nightwave(string baseuri = "https://api.plaza.one/") {
MinRefreshTime = 10;
MinRefreshTime = 15;
_hcl.BaseAddress = new Uri(baseuri);
_hcl.Timeout = TimeSpan.FromSeconds(2);
_hcl.DefaultRequestHeaders.Accept.Clear();
@ -373,7 +421,7 @@ namespace libplaza {
// or if the currently-playing track's elapsed time is more than the track duration.
if(Force
|| (tsLastRefresh + MinRefreshTime < DateTimeOffset.Now.ToUnixTimeSeconds())
|| _bc.CalculatedElapsed < _bc.Duration) {
|| _bc.CalculatedElapsed > _bc.Duration) {
await Update();
tsLastRefresh = DateTimeOffset.Now.ToUnixTimeSeconds();
}
@ -393,6 +441,10 @@ namespace libplaza {
} catch(PlazaInMaintenanceException) {
_bc.InMaintenance = true;
_bc.FaultOccurred = false;
} catch(PlazaQuietlyUnavailableException) {
_bc.FaultOccurred = false;
} catch(PlazaUnavailableException) {
_bc.FaultOccurred = true;
} catch(Exception) { //TODO make this more reliable?
_bc.FaultOccurred = true;
}
@ -407,23 +459,25 @@ namespace libplaza {
public async Task<Status> GetNightwaveAsync() {
Status status;
HttpResponseMessage _r=await _hcl.GetAsync("/status");
if(_r.StatusCode == HttpStatusCode.TooManyRequests)
throw new PlazaQuietlyUnavailableException();
if(!_r.IsSuccessStatusCode)
throw new PlazaUnavailableException();
PlazaJson j=JsonConvert.DeserializeObject<PlazaJson>(await _r.Content.ReadAsStringAsync());
if(j.maintenance)
throw new PlazaInMaintenanceException();
// Previously this calculated ServerTimeOffset as Now - Headers.Date.Value.ToUnixTimeSeconds
status = new Status {
Title = j.playback.title,
Album = j.playback.album,
Artist = j.playback.artist,
Elapsed = j.playback.position,
Duration = j.playback.length,
Title = j.song.title,
Album = j.song.album,
Artist = j.song.artist,
Elapsed = j.song.position,
Duration = j.song.length,
Since = j.updated_at,
Likes = j.playback.likes,
Dislikes = j.playback.hates,
Likes = j.song.reactions,
Listeners = j.listeners,
ServerTimeOffset = (int)(DateTimeOffset.Now.ToUnixTimeSeconds() - _r.Headers.Date.Value.ToUnixTimeSeconds()),
ArtworkUri = $"https://plaza.one/{j.playback.artwork}"
ServerTimeOffset = (int)(DateTimeOffset.Now.ToUnixTimeSeconds() - j.updated_at),
ArtworkUri = j.song.artwork_src
};
return status;
}
@ -435,11 +489,12 @@ namespace libplaza {
public async Task<Vote?> Vote() {
if(!await CheckSession())
return null;
HttpResponseMessage _r=await _hcl.GetAsync("/vote");
if(!_r.IsSuccessStatusCode)
return null;
PlazaGetVote r=JsonConvert.DeserializeObject<PlazaGetVote>(await _r.Content.ReadAsStringAsync());
return (Vote)r.rate;
PlazaUser r=await GetUser();
if(r.current_like == null)
return libplaza.Vote.Neutral;
if(r.current_like.like_id)
return libplaza.Vote.Favourite;
return libplaza.Vote.Like;
}
/// <summary>
@ -450,12 +505,12 @@ namespace libplaza {
public async Task<bool> Vote(Vote v) {
if(!await CheckSession())
return false;
HttpResponseMessage _r=await _hcl.PostAsync("/vote", new FormUrlEncodedContent(new[] {
new KeyValuePair<string, string>("rate", ((int)v).ToString())}));
HttpResponseMessage _r=await _hcl.PostAsync("/reactions", new StringContent(
$"{{\"reaction\":{(int)v}}}", Encoding.UTF8, "application/json"));
if(!_r.IsSuccessStatusCode)
return false;
PlazaVote r=JsonConvert.DeserializeObject<PlazaVote>(await _r.Content.ReadAsStringAsync());
return r.status == "success";
return r.result == "success";
}
/// <summary>
@ -474,18 +529,27 @@ namespace libplaza {
/// </summary>
/// <returns>A <c>bool</c>ean value indicating whether the request succeeded</returns>
public async Task<bool> CheckSession() =>
await GetUser() != null;
await GetUsername() != null;
/// <summary>
/// Performs a basic read-only API request to fetch the profile information for the current user
/// </summary>
/// <returns>If successful, returns a <see cref="PlazaUser"/> containing the user's profile information. If unsuccessful, returns null.</returns>
public async Task<PlazaUser> GetUser() {
HttpResponseMessage _r=await _hcl.GetAsync("/user");
if(!_r.IsSuccessStatusCode)
return null;
PlazaUser r=JsonConvert.DeserializeObject<PlazaUser>(await _r.Content.ReadAsStringAsync());
return r;
}
/// <summary>
/// Performs a basic read-only API request to fetch the currently-authenticated user's profile details.
/// </summary>
/// <returns>If successful, returns a <c>string</c> containing the authenticated user's username. If unsuccessful, will return null.</returns>
public async Task<string> GetUser() {
HttpResponseMessage _r=await _hcl.GetAsync("/user");
if(!_r.IsSuccessStatusCode)
return null;
PlazaUser r=JsonConvert.DeserializeObject<PlazaUser>(await _r.Content.ReadAsStringAsync());
return r.id <= 0 ? null : r.username;
public async Task<string> GetUsername() {
PlazaUser r=await GetUser();
return (r==null || r.username==null || r.username?.Length <= 0) ? null : r.username;
}
/// <summary>
@ -497,8 +561,21 @@ namespace libplaza {
/// <remarks>
/// Functionally, this method is a wrapper for <see cref="Login(string)"/>, performing the function of generating a Basic authentication string
/// </remarks>
public async Task<bool> Login(string u, string p) =>
await Login(Convert.ToBase64String(System.Text.Encoding.ASCII.GetBytes($"{u}:{p}")));
public async Task<bool> Login(string u, string p) {
_hcl.DefaultRequestHeaders.Add("Authorization", "Bearer 1");
HttpResponseMessage _r=await _hcl.PostAsync("/user/auth", new StringContent(
$"{{\"username\":\"{u}\",\"password\":\"{p}\"}}", Encoding.UTF8, "application/json"));
_ = _hcl.DefaultRequestHeaders.Remove("Authorization");
if(_r.StatusCode == HttpStatusCode.TooManyRequests)
return false;
if(!_r.IsSuccessStatusCode)
return false;
PlazaLogonResult r=JsonConvert.DeserializeObject<PlazaLogonResult>(await _r.Content.ReadAsStringAsync());
if(r.result != "ok")
return false;
_hcl.DefaultRequestHeaders.Add("Authorization", $"Bearer {r.token}");
return true;
}
/// <summary>
/// Performs a login request to the <see cref="Nightwave"/> API with the given HTTP Basic authentication <paramref name="str">string</paramref>.
@ -511,7 +588,7 @@ namespace libplaza {
if(!_r.IsSuccessStatusCode)
return false;
PlazaLogonResult r=JsonConvert.DeserializeObject<PlazaLogonResult>(await _r.Content.ReadAsStringAsync());
if(r.status != "success")
if(r.result != "success")
return false;
_hcl.DefaultRequestHeaders.Add("X-Access-Token", r.token);
_ = _hcl.DefaultRequestHeaders.Remove("Authorization");
@ -531,4 +608,5 @@ namespace libplaza {
_ = _hcl.DefaultRequestHeaders.Remove("X-Access-Token");
}
}
#endregion
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework>
<TargetFramework>net6.0-windows</TargetFramework>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<NeutralLanguage>en-GB</NeutralLanguage>
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
@ -11,12 +11,12 @@
<Description>A moderately generic .Net interface to the Nightwave Plaza API</Description>
<Copyright>Nightwave Plaza is wholly copyright and owned by its creator, I just wrote this unofficial library that talks to it.</Copyright>
<PackageProjectUrl>https://soft.pup.cloud/Nightwave.NET/</PackageProjectUrl>
<RepositoryUrl>https://git.maff.scot/maff/PlazaSharp/src/branch/master/libplaza</RepositoryUrl>
<RepositoryUrl>https://commit.pup.cloud/maff/PlazaSharp/src/branch/master/libplaza</RepositoryUrl>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants />
<DocumentationFile>C:\Users\MC185353\source\repos\PlazaSharp\libplaza\libplaza.xml</DocumentationFile>
<DocumentationFile>C:\Users\maff\source\repos\PlazaSharp\libplaza\libplaza.xml</DocumentationFile>
</PropertyGroup>
<ItemGroup>

View File

@ -34,22 +34,17 @@
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaPlayback.artwork">
<member name="F:libplaza.PlazaPlayback.artwork_src">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaPlayback.artwork_s">
<member name="F:libplaza.PlazaPlayback.artwork_sm_src">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaPlayback.likes">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaPlayback.hates">
<member name="F:libplaza.PlazaPlayback.reactions">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
@ -74,7 +69,7 @@
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaJson.playback">
<member name="F:libplaza.PlazaJson.song">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
@ -109,17 +104,22 @@
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaUser.current_like">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="T:libplaza.PlazaVote">
<summary>
A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/>
</summary>
</member>
<member name="F:libplaza.PlazaVote.status">
<member name="F:libplaza.PlazaVote.result">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaVote.ratings">
<member name="F:libplaza.PlazaVote.reactions">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
@ -129,7 +129,42 @@
A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/>
</summary>
</member>
<member name="F:libplaza.PlazaGetVote.rate">
<member name="F:libplaza.PlazaGetVote.reaction">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="T:libplaza.PlazaCurrentVote">
<summary>
A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/>
</summary>
</member>
<member name="F:libplaza.PlazaCurrentVote.like_id">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaCurrentVote.song">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="T:libplaza.PlazaLikedSong">
<summary>
A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/>
</summary>
</member>
<member name="F:libplaza.PlazaLikedSong.artist">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaLikedSong.artwork_src">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaLikedSong.title">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
@ -149,7 +184,7 @@
A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/>
</summary>
</member>
<member name="F:libplaza.PlazaLogonResult.status">
<member name="F:libplaza.PlazaLogonResult.result">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
@ -179,6 +214,11 @@
An <see cref="T:System.Exception"/> object indicating that an error or exception occurred when communicating with the <see cref="T:libplaza.Nightwave"/> API.
</summary>
</member>
<member name="T:libplaza.PlazaQuietlyUnavailableException">
<summary>
A <see cref="T:libplaza.PlazaUnavailableException"/> object indicating that, while an error occurred, it's one that we can silently retry.
</summary>
</member>
<member name="T:libplaza.Status">
<summary>
Represents the current broadcast information returned by a <see cref="T:libplaza.Nightwave"/> API object.
@ -244,11 +284,6 @@
Contains an integer value of the number of likes the currently-playing track has received
</summary>
</member>
<member name="F:libplaza.Status.Dislikes">
<summary>
Contains an integer value of the number of dislikes the currently-playing track has received
</summary>
</member>
<member name="F:libplaza.Status.Listeners">
<summary>
Contains an integer value of the number of people currently listening to the broadcast
@ -259,12 +294,6 @@
Represents the user's disposition towards the currently-playing track.
</summary>
</member>
<member name="F:libplaza.Vote.Dislike">
<summary>
To <c>Dislike</c> is to indicate the user's disposition towards this track is one of displeasure;
that the user does not appreciate hearing this track, perhaps even that the user wishes not to hear this track.
</summary>
</member>
<member name="F:libplaza.Vote.Neutral">
<summary>
To be of <c>Neutral</c> disposition is to indicate the user has no feelings towards this track,
@ -395,6 +424,12 @@
<returns>A <c>bool</c>ean value indicating whether the request succeeded</returns>
</member>
<member name="M:libplaza.Nightwave.GetUser">
<summary>
Performs a basic read-only API request to fetch the profile information for the current user
</summary>
<returns>If successful, returns a <see cref="T:libplaza.PlazaUser"/> containing the user's profile information. If unsuccessful, returns null.</returns>
</member>
<member name="M:libplaza.Nightwave.GetUsername">
<summary>
Performs a basic read-only API request to fetch the currently-authenticated user's profile details.
</summary>