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:
parent
6be173855a
commit
f688cd63a9
|
@ -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">
|
||||
|
@ -45,20 +45,27 @@
|
|||
<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="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 HorizontalAlignment="Right">
|
||||
<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="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"/>
|
||||
<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>
|
||||
</StatusBar>
|
||||
|
|
|
@ -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.
|
||||
// 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;
|
||||
}
|
||||
|
|
|
@ -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 & 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>
|
||||
|
|
|
@ -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
|
||||
|
|
160
libplaza/Main.cs
160
libplaza/Main.cs
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue