♻️ Replace the self-impl spotify api to use lib

This commit is contained in:
2025-11-02 16:13:36 +08:00
parent 74f8221be4
commit bfa9bedeea
2 changed files with 92 additions and 79 deletions

View File

@@ -1,7 +1,7 @@
using System.Text.Json;
using DysonNetwork.Shared.Models; using DysonNetwork.Shared.Models;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using NodaTime; using NodaTime;
using SpotifyAPI.Web;
namespace DysonNetwork.Pass.Account.Presences; namespace DysonNetwork.Pass.Account.Presences;
@@ -45,19 +45,28 @@ public class SpotifyPresenceService(
try try
{ {
var currentlyPlayingJson = await spotifyService.GetCurrentlyPlayingAsync( // Ensure we have a valid access token
connection.RefreshToken, var validToken = await spotifyService.GetValidAccessTokenAsync(connection.RefreshToken, connection.AccessToken);
connection.AccessToken if (string.IsNullOrEmpty(validToken))
);
if (string.IsNullOrEmpty(currentlyPlayingJson) || currentlyPlayingJson == "{}")
{ {
// Nothing playing, remove the presence // Couldn't get a valid token, remove presence
await RemoveSpotifyPresenceAsync(account.Id); await RemoveSpotifyPresenceAsync(account.Id);
return; return;
} }
var presenceActivity = await ParseAndCreatePresenceActivityAsync(account.Id, currentlyPlayingJson); // Create Spotify client with the valid token
var spotify = new SpotifyClient(validToken);
// Get currently playing track
var currentlyPlaying = await spotify.Player.GetCurrentlyPlaying(new PlayerCurrentlyPlayingRequest());
if (currentlyPlaying?.Item == null || currentlyPlaying.IsPlaying == false)
{
// Nothing playing or paused, remove the presence
await RemoveSpotifyPresenceAsync(account.Id);
return;
}
var presenceActivity = ParseCurrentlyPlayingToPresenceActivity(account.Id, currentlyPlaying);
// Update or create the presence activity // Update or create the presence activity
await accountEventService.UpdateActivityByManualId( await accountEventService.UpdateActivityByManualId(
@@ -104,81 +113,98 @@ public class SpotifyPresenceService(
); );
} }
private static Task<SnPresenceActivity> ParseAndCreatePresenceActivityAsync(Guid accountId, string currentlyPlayingJson) private static SnPresenceActivity ParseCurrentlyPlayingToPresenceActivity(Guid accountId, CurrentlyPlaying currentlyPlaying)
{ {
var document = JsonDocument.Parse(currentlyPlayingJson); // Cast the item to FullTrack (it should be a track for music presence)
var root = document.RootElement; if (currentlyPlaying.Item is not FullTrack track)
// Extract track information
var item = root.GetProperty("item");
var trackName = item.GetProperty("name").GetString() ?? "";
var isPlaying = root.GetProperty("is_playing").GetBoolean();
// Only create presence if actually playing
if (!isPlaying)
{ {
throw new InvalidOperationException("Track is not currently playing"); throw new InvalidOperationException("Currently playing item is not a track");
}
// Get track name
var trackName = track.Name ?? "";
if (string.IsNullOrEmpty(trackName))
{
throw new InvalidOperationException("Track name not available");
} }
// Get artists // Get artists
var artists = item.GetProperty("artists"); var artists = track.Artists ?? new List<SimpleArtist>();
var artistNames = artists.EnumerateArray() var artistNames = artists.Select(a => a.Name).Where(name => !string.IsNullOrEmpty(name)).ToList();
.Select(a => a.GetProperty("name").GetString() ?? "")
.Where(name => !string.IsNullOrEmpty(name))
.ToArray();
var artistsString = string.Join(", ", artistNames); var artistsString = string.Join(", ", artistNames);
// Get album // Get artist URLs
var album = item.GetProperty("album"); var artistsUrls = artists
var albumName = album.GetProperty("name").GetString() ?? ""; .Where(a => a.ExternalUrls?.ContainsKey("spotify") == true)
.Select(a => a.ExternalUrls!["spotify"])
.ToList();
// Get album images (artwork) // Get album info
var album = track.Album;
var albumName = album?.Name ?? "";
string? albumImageUrl = null; string? albumImageUrl = null;
if (album.TryGetProperty("images", out var images) && images.ValueKind == JsonValueKind.Array)
// Get largest album image
if (album?.Images != null && album.Images.Count > 0)
{ {
var albumImages = images.EnumerateArray().ToList(); albumImageUrl = album.Images
// Take the largest image (usually last in the array) .OrderByDescending(img => img.Width)
if (albumImages.Count > 0) .FirstOrDefault()?.Url;
{
albumImageUrl = albumImages[0].GetProperty("url").GetString();
}
} }
// Get external URLs // Get track URL
var externalUrls = item.GetProperty("external_urls"); string? trackUrl = null;
var trackUrl = externalUrls.GetProperty("spotify").GetString(); if (track.ExternalUrls?.ContainsKey("spotify") == true)
var artistsUrls = new List<string>();
foreach (var artist in artists.EnumerateArray())
{ {
if (artist.TryGetProperty("external_urls", out var artistUrls)) trackUrl = track.ExternalUrls["spotify"];
{
var spotifyUrl = artistUrls.GetProperty("spotify").GetString();
if (!string.IsNullOrEmpty(spotifyUrl))
{
artistsUrls.Add(spotifyUrl);
}
}
} }
// Get progress and duration for metadata // Get progress and duration
var progressMs = root.GetProperty("progress_ms").GetInt32(); var progressMs = currentlyPlaying.ProgressMs ?? 0;
var durationMs = item.GetProperty("duration_ms").GetInt32(); var durationMs = track.DurationMs;
// Calculate progress percentage
var progressPercent = durationMs > 0 ? (double)progressMs / durationMs * 100 : 0; var progressPercent = durationMs > 0 ? (double)progressMs / durationMs * 100 : 0;
// Get context info (playlist, album, etc.) // Get context info
string? contextType = null; var contextType = currentlyPlaying.Context?.Type;
string? contextUrl = null; string? contextUrl = null;
if (root.TryGetProperty("context", out var context)) if (currentlyPlaying.Context?.ExternalUrls?.ContainsKey("spotify") == true)
{ {
contextType = context.GetProperty("type").GetString(); contextUrl = currentlyPlaying.Context.ExternalUrls["spotify"];
var contextExternalUrls = context.GetProperty("external_urls");
contextUrl = contextExternalUrls.GetProperty("spotify").GetString();
} }
return Task.FromResult(new SnPresenceActivity // Build metadata
var meta = new Dictionary<string, object>
{
["track_duration_ms"] = durationMs,
["progress_ms"] = progressMs,
["progress_percent"] = progressPercent,
["spotify_track_url"] = trackUrl,
["updated_at"] = SystemClock.Instance.GetCurrentInstant()
};
// Add track ID
if (!string.IsNullOrEmpty(track.Id))
meta["track_id"] = track.Id;
// Add album ID
if (!string.IsNullOrEmpty(album?.Id))
meta["album_id"] = album.Id;
// Add artist IDs
var artistIds = artists.Select(a => a.Id).Where(id => !string.IsNullOrEmpty(id)).ToArray();
meta["artist_ids"] = artistIds;
// Add context info
if (!string.IsNullOrEmpty(contextType))
meta["context_type"] = contextType;
if (!string.IsNullOrEmpty(contextUrl))
meta["context_url"] = contextUrl;
// Add track properties
meta["is_explicit"] = track.Explicit;
meta["popularity"] = track.Popularity;
return new SnPresenceActivity
{ {
AccountId = accountId, AccountId = accountId,
Type = PresenceType.Music, Type = PresenceType.Music,
@@ -189,21 +215,7 @@ public class SpotifyPresenceService(
LargeImage = albumImageUrl, LargeImage = albumImageUrl,
TitleUrl = trackUrl, TitleUrl = trackUrl,
SubtitleUrl = artistsUrls.FirstOrDefault(), SubtitleUrl = artistsUrls.FirstOrDefault(),
Meta = new Dictionary<string, object> Meta = meta
{ };
["track_duration_ms"] = durationMs,
["progress_ms"] = progressMs,
["progress_percent"] = progressPercent,
["track_id"] = item.GetProperty("id").GetString() ?? "",
["album_id"] = album.GetProperty("id").GetString() ?? "",
["artist_ids"] = artists.EnumerateArray().Select(a => a.GetProperty("id").GetString() ?? "").ToArray(),
["context_type"] = contextType,
["context_url"] = contextUrl,
["is_explicit"] = item.GetProperty("explicit").GetBoolean(),
["popularity"] = item.GetProperty("popularity").GetInt32(),
["spotify_track_url"] = trackUrl,
["updated_at"] = SystemClock.Instance.GetCurrentInstant()
}
});
} }
} }

View File

@@ -39,6 +39,7 @@
<PackageReference Include="EFCore.BulkExtensions" Version="9.0.2" /> <PackageReference Include="EFCore.BulkExtensions" Version="9.0.2" />
<PackageReference Include="EFCore.BulkExtensions.PostgreSql" Version="9.0.2" /> <PackageReference Include="EFCore.BulkExtensions.PostgreSql" Version="9.0.2" />
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" /> <PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
<PackageReference Include="SpotifyAPI.Web" Version="7.2.1" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="9.0.6" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.6" /> <PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="9.0.6" />
</ItemGroup> </ItemGroup>