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="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"/> <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="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="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"/> <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"/> <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"> <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> </TextBlock>
<DockPanel Grid.ColumnSpan="5" Margin="0,0,0,0" Grid.Row="1"> <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"> <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"> <StatusBarItem HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal"> <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"/>
<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"/>
<TextBlock x:Name="sbStatus" Text="Status"/> </StackPanel>
</StackPanel> </StatusBarItem>
</StatusBarItem> <Separator/>
<StatusBarItem HorizontalAlignment="Right"> <StatusBarItem HorizontalAlignment="Left">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<!--TODO implement tracking of time spent listening (this session and all-time) <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="sbListeningTimers" Text="00:00 (00:00)"/>-->
<TextBlock x:Name="sbVote" Text="Vote" Margin="0,0,4,0"/> <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="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"/> <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}"/> </StackPanel>
<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>
</StackPanel>
</StatusBarItem>
</StatusBar> </StatusBar>
</DockPanel> </DockPanel>
<Rectangle Height="1" Grid.Column="1" Grid.ColumnSpan="2" Grid.Row="0" Margin="0,0,0,10" Fill="#FF003C49" VerticalAlignment="Bottom"/> <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; btLogin.Visibility = Visibility.Hidden;
imLogin.IsEnabled = false; imLogin.IsEnabled = false;
imLogin.Visibility = Visibility.Hidden; imLogin.Visibility = Visibility.Hidden;
btDislike.IsEnabled = true; //btDislike.IsEnabled = true;
btLike.IsEnabled = true; btLike.IsEnabled = true;
SetStatus($"Logged in as {await NwP.GetUser()}"); SetStatus($"Logged in as {await NwP.GetUser()}");
} }
@ -88,43 +88,43 @@ namespace NightwaveForWorkgroups {
slDuration.Value = s.CalculatedElapsed; slDuration.Value = s.CalculatedElapsed;
sbListeners.Text = $"{s.Listeners} listeners"; sbListeners.Text = $"{s.Listeners} listeners";
lbLikeCt.Content = s.Likes; lbLikeCt.Content = s.Likes;
lbDislikeCt.Content = s.Dislikes; //lbDislikeCt.Content = s.Dislikes;
lbElapsed.Content = $"{(s.CalculatedElapsed / 60).ToString("D")}:{(s.CalculatedElapsed % 60).ToString("D2")}"; lbElapsed.Content = $"{(s.CalculatedElapsed / 60).ToString("D")}:{(s.CalculatedElapsed % 60).ToString("D2")}";
if(LastArtwork != s.ArtworkUri || Force) { if(LastArtwork != s.ArtworkUri || Force) {
//TODO should probably break this part out into a separate function cause this feels real messy //TODO should probably break this part out into a separate function cause this feels real messy
Vote v = await NwP.Vote() ?? Vote.Neutral; Vote v = await NwP.Vote() ?? Vote.Neutral;
switch(v) { switch(v) {
case Vote.Dislike: /*case Vote.Dislike:
sbVote.Text = "👎"; sbVote.Text = "👎";
sbVote.ToolTip = "You dislike this."; sbVote.ToolTip = "You dislike this.";
btLike.Content = "👍"; btLike.Content = "👍";
btLike.ToolTip = "Like this track"; btLike.ToolTip = "Like this track";
btDislike.Content = "🤷‍♀️"; btDislike.Content = "🤷‍♀️";
btDislike.ToolTip = "Remove your vote for this track"; btDislike.ToolTip = "Remove your vote for this track";
break; break;*/
case Vote.Like: case Vote.Like:
sbVote.Text = "👍"; sbVote.Text = "👍";
sbVote.ToolTip = "You like this."; sbVote.ToolTip = "You like this.";
btLike.Content = "💖"; btLike.Content = "💖";
btLike.ToolTip = "Favourite this track"; btLike.ToolTip = "Favourite this track";
btDislike.Content = "👎"; //btDislike.Content = "👎";
btDislike.ToolTip = "Dislike this track"; //btDislike.ToolTip = "Dislike this track";
break; break;
case Vote.Favourite: case Vote.Favourite:
sbVote.Text = "💖"; sbVote.Text = "💖";
sbVote.ToolTip = "You have favourited this."; sbVote.ToolTip = "You have favourited this.";
btLike.Content = "🤷‍♀️"; btLike.Content = "🤷‍♀️";
btLike.ToolTip = "Remove your vote for this track"; btLike.ToolTip = "Remove your vote for this track";
btDislike.Content = "👎"; //btDislike.Content = "👎";
btDislike.ToolTip = "Dislike this track"; //btDislike.ToolTip = "Dislike this track";
break; break;
default: default:
sbVote.Text = "🤷‍♀️"; sbVote.Text = "🤷‍♀️";
sbVote.ToolTip = "You have no feelings towards this, or you have not expressed your feelings for this."; sbVote.ToolTip = "You have no feelings towards this, or you have not expressed your feelings for this.";
btLike.Content = "👍"; btLike.Content = "👍";
btLike.ToolTip = "Like this track"; btLike.ToolTip = "Like this track";
btDislike.Content = "👎"; //btDislike.Content = "👎";
btDislike.ToolTip = "Dislike this track"; //btDislike.ToolTip = "Dislike this track";
break; break;
} }
//TODO visual effect on the like/dislike buttons to indicate user's vote //TODO visual effect on the like/dislike buttons to indicate user's vote
@ -259,7 +259,10 @@ namespace NightwaveForWorkgroups {
return; return;
} }
//user may have stored credentials and not want to keep them any more, delete with reckless abandon. //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) { if(_c.CredentialSaved == CredentialSaveOption.Selected) {
CredentialManager.WriteCredential("Nightwave.NET", _c.UserName, _c.Password, CredentialPersistence.Enterprise); CredentialManager.WriteCredential("Nightwave.NET", _c.UserName, _c.Password, CredentialPersistence.Enterprise);
Credential c=CredentialManager.ReadCredential("Nightwave.NET"); Credential c=CredentialManager.ReadCredential("Nightwave.NET");
@ -278,13 +281,14 @@ namespace NightwaveForWorkgroups {
btLogin.Visibility = Visibility.Hidden; btLogin.Visibility = Visibility.Hidden;
imLogin.IsEnabled = false; imLogin.IsEnabled = false;
imLogin.Visibility = Visibility.Hidden; imLogin.Visibility = Visibility.Hidden;
btDislike.IsEnabled = true; //btDislike.IsEnabled = true;
btLike.IsEnabled = true; btLike.IsEnabled = true;
SetStatus($"Logged in as {await NwP.GetUser()}"); SetStatus($"Logged in as {await NwP.GetUser()}");
} }
private async void BtLike_Click(object sender, RoutedEventArgs e) { private async void BtLike_Click(object sender, RoutedEventArgs e) {
_ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btLike); _ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btLike);
Vote v = await NwP.Vote() ?? Vote.Neutral; Vote v = await NwP.Vote() ?? Vote.Neutral;
// Kinda like a garbage state machine
v = v switch { v = v switch {
Vote.Like => Vote.Favourite, Vote.Like => Vote.Favourite,
Vote.Favourite => Vote.Neutral, Vote.Favourite => Vote.Neutral,
@ -297,7 +301,7 @@ namespace NightwaveForWorkgroups {
_ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btLike); _ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btLike);
_ = Dispatcher.BeginInvoke(new dgtBool(DgtRefresh), true); _ = 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); _ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btDislike);
Vote v = Vote.Dislike; Vote v = Vote.Dislike;
if(await NwP.Vote() == Vote.Dislike) if(await NwP.Vote() == Vote.Dislike)
@ -308,9 +312,13 @@ namespace NightwaveForWorkgroups {
SetStatus($"Something went weird while casting {v}"); SetStatus($"Something went weird while casting {v}");
_ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btDislike); _ = Dispatcher.BeginInvoke(new dgtCtrl(DgtToggleEnabled), btDislike);
_ = Dispatcher.BeginInvoke(new dgtBool(DgtRefresh), true); _ = Dispatcher.BeginInvoke(new dgtBool(DgtRefresh), true);
} }*/
private void SbOnTop_Click(object sender, RoutedEventArgs e) { 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) if(tGfx.Enabled && Topmost)
tGfx.Interval = 1000.0 / 60.0; tGfx.Interval = 1000.0 / 60.0;
} }

View File

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

View File

@ -7,10 +7,12 @@ while maintaining aesthetics.
You can [get it here][6]. You can [get it here][6].
Intended future improvements include: Intended future improvements include:
* Optional session storage between sessions to avoid logging in again
* Multiple visualiser types * Multiple visualiser types
* Better use of additional application real-estate when resized * Better use of additional application real-estate when resized
* Integration with the Windows native media APIs (so keyboard play/pause buttons work) * 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 ## 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. * [JSON.Net][4] by Newtonsoft for deserialisation of JSON strings.
* [CredentialManager][5] by Meziantou for access to the Windows Credential Manager APIs * [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 [1]: https://plaza.one
[2]: https://www.un4seen.com/bass.html [2]: https://www.un4seen.com/bass.html
[3]: https://github.com/ManagedBass/ManagedBass [3]: https://github.com/ManagedBass/ManagedBass

View File

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

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>netstandard1.4</TargetFramework> <TargetFramework>net6.0-windows</TargetFramework>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild> <GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<NeutralLanguage>en-GB</NeutralLanguage> <NeutralLanguage>en-GB</NeutralLanguage>
<RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild> <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
@ -11,12 +11,12 @@
<Description>A moderately generic .Net interface to the Nightwave Plaza API</Description> <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> <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> <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>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<DefineConstants /> <DefineConstants />
<DocumentationFile>C:\Users\MC185353\source\repos\PlazaSharp\libplaza\libplaza.xml</DocumentationFile> <DocumentationFile>C:\Users\maff\source\repos\PlazaSharp\libplaza\libplaza.xml</DocumentationFile>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -34,22 +34,17 @@
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
</member> </member>
<member name="F:libplaza.PlazaPlayback.artwork"> <member name="F:libplaza.PlazaPlayback.artwork_src">
<summary> <summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
</member> </member>
<member name="F:libplaza.PlazaPlayback.artwork_s"> <member name="F:libplaza.PlazaPlayback.artwork_sm_src">
<summary> <summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
</member> </member>
<member name="F:libplaza.PlazaPlayback.likes"> <member name="F:libplaza.PlazaPlayback.reactions">
<summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary>
</member>
<member name="F:libplaza.PlazaPlayback.hates">
<summary> <summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
@ -74,7 +69,7 @@
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
</member> </member>
<member name="F:libplaza.PlazaJson.playback"> <member name="F:libplaza.PlazaJson.song">
<summary> <summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
@ -109,17 +104,22 @@
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
</member> </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"> <member name="T:libplaza.PlazaVote">
<summary> <summary>
A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/> A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/>
</summary> </summary>
</member> </member>
<member name="F:libplaza.PlazaVote.status"> <member name="F:libplaza.PlazaVote.result">
<summary> <summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
</member> </member>
<member name="F:libplaza.PlazaVote.ratings"> <member name="F:libplaza.PlazaVote.reactions">
<summary> <summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
@ -129,7 +129,42 @@
A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/> A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/>
</summary> </summary>
</member> </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> <summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </summary>
@ -149,7 +184,7 @@
A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/> A transitional class used internally by <see cref="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)"/>
</summary> </summary>
</member> </member>
<member name="F:libplaza.PlazaLogonResult.status"> <member name="F:libplaza.PlazaLogonResult.result">
<summary> <summary>
A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload A representation of a JSON object attribute, used transitionally while processing a deserialized JSON payload
</summary> </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. 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> </summary>
</member> </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"> <member name="T:libplaza.Status">
<summary> <summary>
Represents the current broadcast information returned by a <see cref="T:libplaza.Nightwave"/> API object. 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 Contains an integer value of the number of likes the currently-playing track has received
</summary> </summary>
</member> </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"> <member name="F:libplaza.Status.Listeners">
<summary> <summary>
Contains an integer value of the number of people currently listening to the broadcast 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. Represents the user's disposition towards the currently-playing track.
</summary> </summary>
</member> </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"> <member name="F:libplaza.Vote.Neutral">
<summary> <summary>
To be of <c>Neutral</c> disposition is to indicate the user has no feelings towards this track, 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> <returns>A <c>bool</c>ean value indicating whether the request succeeded</returns>
</member> </member>
<member name="M:libplaza.Nightwave.GetUser"> <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> <summary>
Performs a basic read-only API request to fetch the currently-authenticated user's profile details. Performs a basic read-only API request to fetch the currently-authenticated user's profile details.
</summary> </summary>