setlist/docs/beatleader-playlist-api.md

90 lines
4.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# BeatLeader Playlist API
Here is the practical picture for a **second plugin** in the same Beat Saber process.
## What “authorization state” actually is
For `/user/playlist*`, the server cares about the **ASP.NET cookie auth session**: the browser-like **cookies** your client got after ticket sign-in, which build a principal with **`NameIdentifier` + `Issued`**. There is **no separate API key or Bearer token** your plugin can copy out of BeatLeader and paste on its own requests unless the server also accepts that (for these routes, you have been told it does not).
## Reference BeatLeader and use its `WebRequestFactory` (real sharing)
In beatleader-mod, session cookies live on a **single static** `HttpClient` whose handler uses **`WebRequestFactory`s `CookieContainer`**. Login is **`Authentication.Login()`** (internal), which fills that container; **`WebRequestFactory.Send` / `Send<T>`** are **public** and, with default **`waitForLogin: true`**, call **`Authentication.WaitLogin()`** before sending.
So **another assembly** can:
- Add a **project reference** (or reference the shipped **BeatLeader.dll** in `Plugins`).
- Build `HttpRequestMessage` for `GET https://api.beatleader.com/user/playlists`
- Call **`WebRequestFactory.Send(...)`** or **`WebRequestFactory.Send<T>(..., parser, waitForLogin: true)`**.
That runs on the **same `HttpClient` and `CookieContainer`** as BeatLeader, and waits on the **same** login gate, so you inherit the session **without** BeatLeader exposing a special “auth API.”
**Requirements / caveats**
- **BeatLeader must be installed and actually complete login** (its menu init calls `Authentication.Login()`). If login never succeeds, `WaitLogin()` never completes happily and you get no cookies.
- **Hard dependency**: no BeatLeader assembly → no compile; disabled/removed mod → your auth path breaks unless you add a fallback.
- **API stability**: you depend on BeatLeaders public surface (`WebRequestFactory`, parsers, etc.) staying compatible across versions.
You do **not** need to “obtain” cookies as strings; you only need to **route requests through that factory** (or duplicate sign-in below).
## Playlists API
Heres a concrete shape that matches how the beatleader mod already does authenticated GETs (`UserRequest`, `ContextsRequest`, `PlatformEventsRequest`): same `HttpClient` + `CookieContainer`, default **`waitForLogin: true`**, so **`GET /user/playlists`** runs after sign-in and sends session cookies automatically.
### 1. Response model
Align property names with your JSON (camelCase matches typical ASP.NET JSON defaults):
```csharp
// e.g. Source/2_Core/Models/API/BeatLeader/UserPlaylistSummary.cs
namespace BeatLeader.Models {
public sealed class UserPlaylistSummary {
public int id { get; set; }
public bool isShared { get; set; }
public string? link { get; set; }
public string? ownerId { get; set; }
public string? hash { get; set; }
public string? guid { get; set; }
public bool deleted { get; set; }
}
}
```
### 2. Request type (mirror `PlatformEventsRequest` / `PlaylistRequest`)
```csharp
using System.Collections.Generic;
using System.Net.Http;
using BeatLeader.Models;
using BeatLeader.Utils;
using BeatLeader.WebRequests;
namespace BeatLeader.API {
internal sealed class UserPlaylistsRequest
: PersistentWebRequestBase<List<UserPlaylistSummary>, JsonResponseParser<List<UserPlaylistSummary>>> {
private static string Endpoint => BLConstants.BEATLEADER_API_URL + "/user/playlists";
public static IWebRequest<List<UserPlaylistSummary>> Send() {
return SendRet(Endpoint, HttpMethod.Get);
}
}
}
```
### 3. Call site (same as `PlaylistRequest` / `PlatformEventsRequest`)
```csharp
var result = await UserPlaylistsRequest.Send().Join();
if (result.RequestState == WebRequests.RequestState.Finished && result.Result != null) {
foreach (var p in result.Result) {
// use p.id, p.guid, p.deleted, etc.
}
} else {
Plugin.Log.Debug($"User playlists failed: {result.FailReason}");
}
```
Thats all thats required on the **client** side for “fetch my playlists”: **GET + existing cookie session**. No extra headers beyond what `WebRequestFactory` already applies (`User-Agent` + cookies).
**Troubleshooting:** right after implementing, compare behavior to `UserRequest` (`GET /user/modinterface`): if one returns `Finished` with data and the other gets `401`, the issue is session/host/HTTPS, not playlist-specific logic.