@freqhole/api-client 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/auth-test.html +651 -0
  2. package/package.json +42 -0
  3. package/src/AdminClient.ts +154 -0
  4. package/src/CharnelLocalTransport.ts +598 -0
  5. package/src/CharnelTransport.ts +616 -0
  6. package/src/FreqholeClient.ts +261 -0
  7. package/src/WasmTransport.ts +1077 -0
  8. package/src/automerge/IrohNetworkAdapter.ts +727 -0
  9. package/src/automerge/index.ts +14 -0
  10. package/src/codegen/admin_commands.ts +61 -0
  11. package/src/codegen/routes.ts +213 -0
  12. package/src/codegen/schema.ts +6721 -0
  13. package/src/domains/admin.ts +97 -0
  14. package/src/domains/app.ts +98 -0
  15. package/src/domains/auth.ts +191 -0
  16. package/src/domains/favorites.types.ts +52 -0
  17. package/src/domains/jobs.ts +48 -0
  18. package/src/domains/music.ts +1935 -0
  19. package/src/domains/types.ts +19 -0
  20. package/src/domains/upload.ts +214 -0
  21. package/src/index.ts +48 -0
  22. package/src/permissions.ts +468 -0
  23. package/src/playlistz/convert.ts +267 -0
  24. package/src/playlistz/index.ts +6 -0
  25. package/src/playlistz/mutations.ts +181 -0
  26. package/src/playlistz/protocol.ts +222 -0
  27. package/src/playlistz/schema.ts +138 -0
  28. package/src/playlistz/shareLink.ts +76 -0
  29. package/src/storage/blobs.ts +272 -0
  30. package/src/storage/identity.ts +190 -0
  31. package/src/storage/index.ts +5 -0
  32. package/src/storage/webLocks.ts +92 -0
  33. package/src/test/automerge.ts +796 -0
  34. package/src/test/coverage.ts +105 -0
  35. package/src/test/fixtures.ts +460 -0
  36. package/src/test/integration.ts +320 -0
  37. package/src/test/playlistz.ts +631 -0
  38. package/src/test/stateful.ts +561 -0
  39. package/src/test/storage.ts +443 -0
  40. package/src/test.ts +75 -0
  41. package/src/transport.ts +391 -0
  42. package/src/utils.ts +79 -0
  43. package/src/webauthn.ts +228 -0
  44. package/tsconfig.json +22 -0
  45. package/vite.config.js +9 -0
@@ -0,0 +1,14 @@
1
+ // automerge transport layer - iroh QUIC network adapter for automerge-repo.
2
+ //
3
+ // re-exports everything needed by consuming packages.
4
+ export {
5
+ IrohNetworkAdapter,
6
+ SYNC_ALPN,
7
+ } from "./IrohNetworkAdapter.js";
8
+
9
+ export type {
10
+ BiStreamLike,
11
+ MiddenStreamNode,
12
+ ConnectionSummary,
13
+ IrohNetworkAdapterOptions,
14
+ } from "./IrohNetworkAdapter.js";
@@ -0,0 +1,61 @@
1
+ // generated freqhole-admin/1 ALPN command map
2
+ //
3
+ // do not edit by hand: regenerate with `cd client-codegen && make all`.
4
+ import * as s from './schema';
5
+ import { z } from 'zod';
6
+
7
+ export type AdminAuthType = 'admin';
8
+
9
+ export type AdminAuth = { type: AdminAuthType };
10
+
11
+ export const adminCommands = {
12
+ invites_generate: { req: s.AdminInvitesGenerateRequestSchema, resp: s.AdminInvitesGenerateResponseSchema, auth: { type: 'admin' } as const },
13
+ invites_list: { req: s.AdminInvitesListRequestSchema, resp: s.AdminInviteInfoSchema.array(), auth: { type: 'admin' } as const },
14
+ invites_revoke: { req: s.AdminInvitesRevokeRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
15
+ invites_revoke_all: { req: z.void().optional(), resp: s.AdminInvitesRevokeAllResponseSchema, auth: { type: 'admin' } as const },
16
+ invites_update_role: { req: s.AdminInvitesUpdateRoleRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
17
+ knocks_accept: { req: s.KnocksAcceptRequestSchema, resp: s.KnockRequestSchema, auth: { type: 'admin' } as const },
18
+ knocks_delete: { req: s.KnocksDeleteRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
19
+ knocks_list: { req: z.void().optional(), resp: s.KnockRequestSchema.array(), auth: { type: 'admin' } as const },
20
+ knocks_list_all: { req: z.void().optional(), resp: s.KnockRequestSchema.array(), auth: { type: 'admin' } as const },
21
+ knocks_reject: { req: s.KnocksRejectRequestSchema, resp: s.KnockRequestSchema, auth: { type: 'admin' } as const },
22
+ knocks_reject_all: { req: z.void().optional(), resp: s.KnocksRejectAllResponseSchema, auth: { type: 'admin' } as const },
23
+ peers_allow: { req: s.AdminPeersAllowRequestSchema, resp: s.AdminPeersAllowResponseSchema, auth: { type: 'admin' } as const },
24
+ peers_hard_delete: { req: s.AdminPeersHardDeleteRequestSchema, resp: s.AdminPeersHardDeleteResponseSchema, auth: { type: 'admin' } as const },
25
+ peers_list_all: { req: s.AdminPeersListAllRequestSchema, resp: s.AdminPeerSummarySchema.array(), auth: { type: 'admin' } as const },
26
+ peers_list_for_user: { req: s.AdminPeersListForUserRequestSchema, resp: s.AdminPeerNodeSummarySchema.array(), auth: { type: 'admin' } as const },
27
+ peers_reassign_user: { req: s.AdminPeersReassignUserRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
28
+ peers_remove: { req: s.AdminPeersRemoveRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
29
+ peers_restore: { req: s.AdminPeersRestoreRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
30
+ radio_bumpers_add: { req: s.RadioBumpersAddRequestSchema, resp: s.RadioBumperSchema, auth: { type: 'admin' } as const },
31
+ radio_bumpers_list: { req: s.RadioBumpersListRequestSchema, resp: s.RadioBumperSchema.array(), auth: { type: 'admin' } as const },
32
+ radio_bumpers_remove: { req: s.RadioBumpersRemoveRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
33
+ radio_bumpers_set_frequency: { req: s.RadioBumpersSetFrequencyRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
34
+ radio_config_get: { req: z.void().optional(), resp: s.RadioConfigPayloadSchema, auth: { type: 'admin' } as const },
35
+ radio_config_set: { req: s.RadioConfigPayloadSchema, resp: s.RadioConfigPayloadSchema, auth: { type: 'admin' } as const },
36
+ radio_filters_add: { req: s.RadioFiltersAddRequestSchema, resp: s.StationFilterSchema, auth: { type: 'admin' } as const },
37
+ radio_filters_list: { req: s.RadioStationByStationIdRequestSchema, resp: s.StationFilterSchema.array(), auth: { type: 'admin' } as const },
38
+ radio_filters_remove: { req: s.RadioFiltersRemoveRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
39
+ radio_seed_suggest: { req: s.RadioSeedSuggestRequestSchema, resp: s.RadioSeedSuggestionSchema.array(), auth: { type: 'admin' } as const },
40
+ radio_stations_create: { req: s.CreateStationRequestSchema, resp: s.RadioStationSchema, auth: { type: 'admin' } as const },
41
+ radio_stations_delete: { req: s.RadioStationsByIdRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
42
+ radio_stations_get: { req: s.RadioStationsByIdRequestSchema, resp: s.RadioStationSchema, auth: { type: 'admin' } as const },
43
+ radio_stations_list: { req: z.void().optional(), resp: s.RadioStationSchema.array(), auth: { type: 'admin' } as const },
44
+ radio_stations_update: { req: s.UpdateStationRequestSchema, resp: s.RadioStationSchema, auth: { type: 'admin' } as const },
45
+ radio_supervisor_restart: { req: s.RadioSupervisorStationRequestSchema, resp: s.RadioSupervisorStatusResponseSchema, auth: { type: 'admin' } as const },
46
+ radio_supervisor_skip_track: { req: s.RadioSupervisorStationRequestSchema, resp: s.RadioSupervisorStatusResponseSchema, auth: { type: 'admin' } as const },
47
+ radio_supervisor_start: { req: s.RadioSupervisorStationRequestSchema, resp: s.RadioSupervisorStatusResponseSchema, auth: { type: 'admin' } as const },
48
+ radio_supervisor_status: { req: z.void().optional(), resp: s.RadioSupervisorStatusResponseSchema, auth: { type: 'admin' } as const },
49
+ radio_supervisor_stop: { req: s.RadioSupervisorStationRequestSchema, resp: s.RadioSupervisorStatusResponseSchema, auth: { type: 'admin' } as const },
50
+ users_add_peer_node: { req: s.AdminUsersAddPeerNodeRequestSchema, resp: s.AdminPeerNodeSummarySchema, auth: { type: 'admin' } as const },
51
+ users_delete: { req: s.AdminUsersDeleteRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
52
+ users_generate_account_link: { req: s.AdminUsersGenerateAccountLinkRequestSchema, resp: s.AdminAccountLinkResponseSchema, auth: { type: 'admin' } as const },
53
+ users_get: { req: s.AdminUsersGetRequestSchema, resp: s.AdminUserSummarySchema, auth: { type: 'admin' } as const },
54
+ users_hard_delete: { req: s.AdminUsersHardDeleteRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
55
+ users_list: { req: s.AdminUsersListRequestSchema, resp: s.AdminUserSummarySchema.array(), auth: { type: 'admin' } as const },
56
+ users_remove_peer_node: { req: s.AdminUsersRemovePeerNodeRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
57
+ users_restore: { req: s.AdminUsersRestoreRequestSchema, resp: s.AdminUserSummarySchema, auth: { type: 'admin' } as const },
58
+ users_update_role: { req: s.AdminUsersUpdateRoleRequestSchema, resp: z.unknown().optional(), auth: { type: 'admin' } as const },
59
+ } as const;
60
+
61
+ export type AdminCommandName = keyof typeof adminCommands;
@@ -0,0 +1,213 @@
1
+ // generated route config
2
+ import * as s from './schema';
3
+ import { z } from 'zod';
4
+
5
+ // role hierarchy - lower number = higher privilege
6
+ export const roleHierarchy = {
7
+ root: 0,
8
+ admin: 10,
9
+ member: 20,
10
+ viewer: 30,
11
+ } as const;
12
+
13
+ export type UserRoleName = keyof typeof roleHierarchy;
14
+ export type RouteAuthType = 'public' | 'authenticated' | 'role' | 'owner' | 'owner_or';
15
+ export type RouteAuth =
16
+ | { type: 'public' }
17
+ | { type: 'authenticated' }
18
+ | { type: 'role'; role: UserRoleName }
19
+ | { type: 'owner' }
20
+ | { type: 'owner_or'; role: UserRoleName };
21
+
22
+ export const routes = {
23
+ admin: {
24
+ accept_knock: { method: 'POST', path: '/api/admin/knocks/accept', req: s.AcceptKnockRequestSchema, resp: s.KnockRequestSchema, auth: { type: 'role', role: 'admin' } as const },
25
+ create_knock_public: { method: 'POST', path: '/api/knock', req: s.CreateKnockRequestSchema, resp: s.KnockRequestSchema, auth: { type: 'public' } as const },
26
+ delete_knock: { method: 'POST', path: '/api/admin/knocks/delete', req: s.DeleteKnockRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
27
+ device_linked_callback: { method: 'POST', path: '/api/internal/device-linked', req: s.DeviceLinkedCallbackRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'public' } as const },
28
+ get_knock: { method: 'POST', path: '/api/admin/knocks/get', req: s.GetKnockRequestSchema, resp: s.KnockRequestSchema, auth: { type: 'role', role: 'admin' } as const },
29
+ get_knock_status_public: { method: 'GET', path: '/api/knock/status', req: null, resp: s.KnockStatusResponseSchema, auth: { type: 'public' } as const },
30
+ knock_accepted_callback: { method: 'POST', path: '/api/internal/knock-accepted', req: s.KnockAcceptedCallbackRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'public' } as const },
31
+ list_all_knocks: { method: 'GET', path: '/api/admin/knocks/all', req: null, resp: s.KnockRequestSchema.array(), auth: { type: 'role', role: 'admin' } as const },
32
+ list_knocks: { method: 'GET', path: '/api/admin/knocks', req: null, resp: s.KnockRequestSchema.array(), auth: { type: 'role', role: 'admin' } as const },
33
+ reject_knock: { method: 'POST', path: '/api/admin/knocks/reject', req: s.RejectKnockRequestSchema, resp: s.KnockRequestSchema, auth: { type: 'role', role: 'admin' } as const },
34
+ },
35
+ app: {
36
+ health_check: { method: 'GET', path: '/health', req: null, resp: s.HealthResponseSchema, auth: { type: 'public' } as const },
37
+ radio_info: { method: 'GET', path: '/api/radio/info', req: null, resp: s.RadioInfoResponseSchema, auth: { type: 'public' } as const },
38
+ radio_public_blob: { method: 'GET', path: '/api/radio/stations/{station_id}/blobs/{blob_id}', req: null, resp: null, auth: { type: 'public' } as const },
39
+ radio_public_blob_data: { method: 'GET', path: '/api/radio/stations/{station_id}/blobs/{blob_id}/data', req: null, resp: null, auth: { type: 'public' } as const },
40
+ radio_public_blob_thumbnail: { method: 'GET', path: '/api/radio/stations/{station_id}/blobs/{blob_id}/thumb/{size}', req: null, resp: null, auth: { type: 'public' } as const },
41
+ radio_public_timeline: { method: 'GET', path: '/api/radio/stations/{station_id}/timeline', req: null, resp: s.PublicTimelineManifestSchema, auth: { type: 'public' } as const },
42
+ radio_stations: { method: 'GET', path: '/api/radio/stations', req: null, resp: s.RadioStationsResponseSchema, auth: { type: 'public' } as const },
43
+ server_info: { method: 'GET', path: '/api/hello', req: null, resp: s.ServerInfoResponseSchema, auth: { type: 'public' } as const },
44
+ },
45
+ auth: {
46
+ api_key_status: { method: 'GET', path: '/api/auth/api-key/status', req: null, resp: s.ApiKeyStatusResponseSchema, auth: { type: 'authenticated' } as const },
47
+ delete_passkey: { method: 'POST', path: '/api/auth/webauthn/passkeys/delete', req: s.DeletePasskeyRequestSchema, resp: z.any(), auth: { type: 'owner' } as const },
48
+ generate_self_account_link: { method: 'POST', path: '/api/auth/profile/generate-invite', req: null, resp: s.SelfAccountLinkResponseSchema, auth: { type: 'authenticated' } as const },
49
+ link_node: { method: 'POST', path: '/api/auth/webauthn/link-node', req: s.LinkNodeRequestSchema, resp: z.any(), auth: { type: 'owner' } as const },
50
+ list_own_invites: { method: 'GET', path: '/api/auth/profile/invites', req: null, resp: s.SelfAccountLinkResponseSchema.array(), auth: { type: 'authenticated' } as const },
51
+ list_passkeys: { method: 'GET', path: '/api/auth/webauthn/passkeys', req: null, resp: s.PasskeyCredentialSummarySchema.array(), auth: { type: 'owner' } as const },
52
+ login_finish: { method: 'POST', path: '/api/auth/webauthn/login/finish', req: z.any(), resp: z.any(), auth: { type: 'public' } as const },
53
+ login_start: { method: 'POST', path: '/api/auth/webauthn/login/start', req: s.StartLoginRequestSchema, resp: z.any(), auth: { type: 'public' } as const },
54
+ logout: { method: 'POST', path: '/api/auth/logout', req: null, resp: z.any(), auth: { type: 'authenticated' } as const },
55
+ redeem_invite: { method: 'POST', path: '/api/auth/invite', req: s.RedeemInviteRequestSchema, resp: z.any(), auth: { type: 'public' } as const },
56
+ regenerate_api_key: { method: 'POST', path: '/api/auth/api-key/regenerate', req: null, resp: s.ApiKeyRegenerateResponseSchema, auth: { type: 'authenticated' } as const },
57
+ register_finish: { method: 'POST', path: '/api/auth/webauthn/register/finish', req: z.any(), resp: z.any(), auth: { type: 'public' } as const },
58
+ register_start: { method: 'POST', path: '/api/auth/webauthn/register/start', req: s.RegisterStartRequestSchema, resp: z.any(), auth: { type: 'public' } as const },
59
+ revoke_own_invite: { method: 'POST', path: '/api/auth/profile/invites/revoke', req: s.RevokeOwnInviteRequestSchema, resp: z.any(), auth: { type: 'authenticated' } as const },
60
+ update_passkey_name: { method: 'POST', path: '/api/auth/profile/passkey-name', req: s.UpdatePasskeyNameRequestSchema, resp: z.any(), auth: { type: 'authenticated' } as const },
61
+ update_username: { method: 'POST', path: '/api/auth/profile/update-username', req: s.UpdateUsernameRequestSchema, resp: s.WhoAmIResponseSchema, auth: { type: 'authenticated' } as const },
62
+ whoami: { method: 'GET', path: '/api/auth/whoami', req: null, resp: s.WhoAmIResponseSchema, auth: { type: 'authenticated' } as const },
63
+ },
64
+ music: {
65
+ activity_feed: { method: 'POST', path: '/api/analytics/feed', req: s.FeedRequestSchema, resp: s.FeedResponseSchema, auth: { type: 'authenticated' } as const },
66
+ add_album_taxon: { method: 'POST', path: '/api/taxonomy/album-links/add', req: s.AddAlbumTaxonRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
67
+ add_albums_tags: { method: 'POST', path: '/api/tags/albums/add', req: s.AddAlbumsTagsRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
68
+ add_songs_to_playlist: { method: 'POST', path: '/api/playlists/add-songs', req: s.AddSongsToPlaylistRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'owner_or', role: 'admin' } as const },
69
+ add_taxon_parent: { method: 'POST', path: '/api/taxonomy/parents/add', req: s.AddTaxonParentRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
70
+ album_pending: { method: 'POST', path: '/api/music/import/album-pending', req: s.AlbumPendingRequestSchema, resp: s.AlbumPendingResponseSchema, auth: { type: 'authenticated' } as const },
71
+ albums_by_value: { method: 'POST', path: '/api/music/relations/albums-by-value', req: s.AlbumsByValueRequestSchema, resp: s.AlbumsByValueResponseSchema, auth: { type: 'authenticated' } as const },
72
+ apply_artist_bio: { method: 'POST', path: '/api/artists/apply-bio', req: s.ApplyArtistBioRequestSchema, resp: s.ApplyArtistBioResultSchema, auth: { type: 'role', role: 'admin' } as const },
73
+ apply_external_urls: { method: 'POST', path: '/api/albums/apply-external-urls', req: s.ApplyExternalUrlsRequestSchema, resp: s.ApplyExternalUrlsResultSchema, auth: { type: 'role', role: 'admin' } as const },
74
+ apply_related_artists: { method: 'POST', path: '/api/artists/apply-related', req: s.ApplyRelatedArtistsRequestSchema, resp: s.ApplyRelatedArtistsResultSchema, auth: { type: 'role', role: 'admin' } as const },
75
+ apply_taxon_proposals: { method: 'POST', path: '/api/albums/apply-taxon-proposals', req: s.ApplyTaxonProposalsRequestSchema, resp: s.ApplyTaxonProposalsResultSchema, auth: { type: 'role', role: 'admin' } as const },
76
+ auto_confirm_mb_matches: { method: 'POST', path: '/api/albums/mb-auto-confirm', req: s.AutoConfirmMbMatchesRequestSchema, resp: s.AutoConfirmMbMatchesResultSchema, auth: { type: 'role', role: 'admin' } as const },
77
+ blob_metadata: { method: 'POST', path: '/api/blob_metadata', req: s.GetBlobMetadataRequestSchema, resp: s.BlobMetadataResponseSchema, auth: { type: 'authenticated' } as const },
78
+ blob_metadata_by_blake3: { method: 'POST', path: '/api/blob_metadata_by_blake3', req: s.GetBlobMetadataByBlake3RequestSchema, resp: s.BlobMetadataResponseSchema, auth: { type: 'authenticated' } as const },
79
+ build_atlas: { method: 'POST', path: '/api/blobs/atlas', req: s.BuildAtlasRequestSchema, resp: s.AtlasManifestSchema, auth: { type: 'authenticated' } as const },
80
+ bulk_clear_song_artwork: { method: 'POST', path: '/api/songs/bulk-clear-artwork', req: s.BulkClearSongArtworkRequestSchema, resp: s.BulkClearSongArtworkResponseSchema, auth: { type: 'role', role: 'admin' } as const },
81
+ bulk_delete_songs: { method: 'POST', path: '/api/songs/bulk-delete', req: s.BulkDeleteSongsRequestSchema, resp: s.BulkDeleteSongsResponseSchema, auth: { type: 'role', role: 'admin' } as const },
82
+ cancel_bulk_enrichment: { method: 'POST', path: '/api/music/albums/enrichment/cancel', req: s.CancelBulkEnrichmentRequestSchema, resp: s.CancelBulkEnrichmentResponseSchema, auth: { type: 'role', role: 'admin' } as const },
83
+ cancel_job: { method: 'POST', path: '/api/jobs/cancel', req: s.CancelJobRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'authenticated' } as const },
84
+ confirm_mb_match: { method: 'POST', path: '/api/albums/mb-confirm', req: s.ConfirmMbMatchRequestSchema, resp: s.MbMatchActionResponseSchema, auth: { type: 'role', role: 'admin' } as const },
85
+ create_artist: { method: 'POST', path: '/api/music/artists', req: s.CreateArtistRequestSchema, resp: s.ArtistSchema, auth: { type: 'role', role: 'admin' } as const },
86
+ create_fetch_job: { method: 'POST', path: '/api/music/fetch', req: s.FetchMediaParamsSchema, resp: s.JobResponseSchema, auth: { type: 'role', role: 'member' } as const },
87
+ create_listen_session: { method: 'POST', path: '/api/analytics/sessions', req: s.CreateListenSessionRequestSchema, resp: s.ListenSessionSchema, auth: { type: 'role', role: 'member' } as const },
88
+ create_playlist: { method: 'POST', path: '/api/music/playlists', req: s.CreatePlaylistRequestSchema, resp: s.PlaylistSchema, auth: { type: 'role', role: 'member' } as const },
89
+ create_precheck_fetch_job: { method: 'POST', path: '/api/music/fetch/precheck', req: s.PreCheckFetchParamsSchema, resp: s.JobResponseSchema, auth: { type: 'role', role: 'member' } as const },
90
+ create_taxon: { method: 'POST', path: '/api/taxonomy/taxons/create', req: s.CreateTaxonRequestSchema, resp: s.TaxonSchema, auth: { type: 'role', role: 'admin' } as const },
91
+ create_taxon_kind: { method: 'POST', path: '/api/taxonomy/kinds/create', req: s.CreateTaxonKindRequestSchema, resp: s.TaxonKindSchema, auth: { type: 'role', role: 'admin' } as const },
92
+ delete_album: { method: 'POST', path: '/api/albums/delete', req: s.DeleteAlbumRequestSchema, resp: s.DeleteAlbumResponseSchema, auth: { type: 'role', role: 'admin' } as const },
93
+ delete_artist: { method: 'POST', path: '/api/artists/delete', req: s.DeleteArtistRequestSchema, resp: s.DeleteArtistResponseSchema, auth: { type: 'role', role: 'admin' } as const },
94
+ delete_feed_event: { method: 'POST', path: '/api/analytics/feed/delete', req: s.DeleteFeedEventRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'authenticated' } as const },
95
+ delete_image: { method: 'POST', path: '/api/music/images/delete', req: s.DeleteImageRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
96
+ delete_listen_session: { method: 'POST', path: '/api/analytics/sessions/delete', req: s.DeleteListenSessionRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'owner_or', role: 'admin' } as const },
97
+ delete_playlist: { method: 'POST', path: '/api/playlists/delete', req: s.DeletePlaylistRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'owner_or', role: 'admin' } as const },
98
+ delete_song: { method: 'POST', path: '/api/songs/delete', req: s.DeleteSongRequestSchema, resp: s.DeleteSongResponseSchema, auth: { type: 'role', role: 'admin' } as const },
99
+ delete_tag: { method: 'POST', path: '/api/tags/delete', req: s.DeleteTagRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
100
+ delete_taxon: { method: 'POST', path: '/api/taxonomy/taxons/delete', req: s.DeleteTaxonRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
101
+ enqueue_audiodb_album_detail: { method: 'POST', path: '/api/music/albums/audiodb/enqueue', req: s.EnqueueAudioDbAlbumDetailRequestSchema, resp: s.EnqueueAudioDbAlbumDetailResponseSchema, auth: { type: 'role', role: 'admin' } as const },
102
+ enqueue_bulk_enrichment: { method: 'POST', path: '/api/music/albums/enrichment/bulk', req: s.BulkEnrichmentRequestSchema, resp: s.BulkEnrichmentResponseSchema, auth: { type: 'role', role: 'admin' } as const },
103
+ enqueue_lastfm_album_detail: { method: 'POST', path: '/api/music/albums/lastfm/enqueue', req: s.EnqueueLastFmAlbumDetailRequestSchema, resp: s.EnqueueLastFmAlbumDetailResponseSchema, auth: { type: 'role', role: 'admin' } as const },
104
+ enqueue_mb_album_search: { method: 'POST', path: '/api/music/albums/mb-search/enqueue', req: s.EnqueueMbAlbumSearchRequestSchema, resp: s.EnqueueMbAlbumSearchResponseSchema, auth: { type: 'role', role: 'admin' } as const },
105
+ entity_taxons_batch: { method: 'POST', path: '/api/music/entities/taxons', req: s.EntityTaxonsBatchRequestSchema, resp: s.EntityTaxonsBatchResponseSchema, auth: { type: 'authenticated' } as const },
106
+ era_albums: { method: 'POST', path: '/api/music/relations/era-albums', req: s.EraAlbumsRequestSchema, resp: s.EraAlbumsResponseSchema, auth: { type: 'authenticated' } as const },
107
+ era_bins: { method: 'POST', path: '/api/music/relations/era-bins', req: s.EraBinsRequestSchema, resp: s.EraBinsResponseSchema, auth: { type: 'authenticated' } as const },
108
+ events_snapshot: { method: 'POST', path: '/api/jobs/events/snapshot', req: s.EventFilterSchema, resp: s.JobStateSnapshotSchema.array(), auth: { type: 'authenticated' } as const },
109
+ events_subscribe: { method: 'POST', path: '/api/jobs/events/subscribe', req: s.EventFilterSchema, resp: s.JobEventSchema, auth: { type: 'authenticated' } as const },
110
+ find_by_merged_key: { method: 'POST', path: '/api/music/relations/by-merged-key', req: s.FindByMergedKeyRequestSchema, resp: s.FindByMergedKeyResponseSchema, auth: { type: 'authenticated' } as const },
111
+ get_album: { method: 'POST', path: '/api/albums/get', req: s.GetAlbumRequestSchema, resp: s.AlbumSchema, auth: { type: 'authenticated' } as const },
112
+ get_album_images: { method: 'POST', path: '/api/albums/images', req: s.GetAlbumRequestSchema, resp: z.string().array(), auth: { type: 'authenticated' } as const },
113
+ get_album_taxon_links: { method: 'POST', path: '/api/taxonomy/album-links/get', req: s.GetAlbumTaxonLinksRequestSchema, resp: s.AlbumTaxonLinkSchema.array(), auth: { type: 'authenticated' } as const },
114
+ get_albums_tags: { method: 'POST', path: '/api/tags/albums/get', req: s.GetAlbumsTagsRequestSchema, resp: s.TagSchema.array(), auth: { type: 'authenticated' } as const },
115
+ get_artist: { method: 'POST', path: '/api/artists/get', req: s.GetArtistRequestSchema, resp: s.ArtistSchema, auth: { type: 'authenticated' } as const },
116
+ get_artist_images: { method: 'POST', path: '/api/artists/images', req: s.GetArtistRequestSchema, resp: z.string().array(), auth: { type: 'authenticated' } as const },
117
+ get_blob_thumbnail: { method: 'GET', path: '/api/blobs/{id}/thumb/{size}', req: null, resp: null, auth: { type: 'authenticated' } as const },
118
+ get_enrichment_progress: { method: 'POST', path: '/api/music/albums/enrichment/progress', req: s.GetEnrichmentProgressRequestSchema, resp: s.GetEnrichmentProgressResponseSchema, auth: { type: 'role', role: 'admin' } as const },
119
+ get_fetch_job: { method: 'POST', path: '/api/music/fetch/status', req: s.GetJobRequestSchema, resp: s.JobResponseSchema, auth: { type: 'authenticated' } as const },
120
+ get_job_status: { method: 'POST', path: '/api/jobs/status', req: s.GetJobsStatusRequestSchema, resp: s.GetJobsStatusResponseSchema, auth: { type: 'authenticated' } as const },
121
+ get_listen_session: { method: 'POST', path: '/api/analytics/sessions/get', req: s.GetListenSessionRequestSchema, resp: s.ListenSessionSchema, auth: { type: 'authenticated' } as const },
122
+ get_musicbrainz_release: { method: 'POST', path: '/api/musicbrainz/release', req: s.GetReleaseRequestSchema, resp: s.MbReleaseDetailSchema, auth: { type: 'role', role: 'admin' } as const },
123
+ get_playlist_by_id: { method: 'POST', path: '/api/music/playlists/get', req: s.GetPlaylistRequestSchema, resp: s.PlaylistSchema, auth: { type: 'authenticated' } as const },
124
+ get_playlist_etag: { method: 'POST', path: '/api/music/playlists/etag', req: s.GetPlaylistRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'authenticated' } as const },
125
+ get_playlist_images: { method: 'POST', path: '/api/playlists/images', req: s.GetPlaylistRequestSchema, resp: z.string().array(), auth: { type: 'authenticated' } as const },
126
+ get_rating_stats: { method: 'POST', path: '/api/ratings/stats', req: s.GetRatingStatsRequestSchema, resp: s.RatingStatsSchema, auth: { type: 'authenticated' } as const },
127
+ get_tag: { method: 'POST', path: '/api/tags/get', req: s.GetTagRequestSchema, resp: s.TagSchema, auth: { type: 'authenticated' } as const },
128
+ get_taxon: { method: 'POST', path: '/api/taxonomy/taxons/get', req: s.GetTaxonRequestSchema, resp: s.TaxonSchema, auth: { type: 'authenticated' } as const },
129
+ get_taxon_ancestors: { method: 'POST', path: '/api/taxonomy/taxons/ancestors', req: s.GetTaxonRequestSchema, resp: s.TaxonRefSchema.array(), auth: { type: 'authenticated' } as const },
130
+ get_taxon_descendants: { method: 'POST', path: '/api/taxonomy/taxons/descendants', req: s.GetTaxonRequestSchema, resp: s.TaxonRefSchema.array(), auth: { type: 'authenticated' } as const },
131
+ has_blobs: { method: 'POST', path: '/api/blobz/has', req: s.HasBlobsRequestSchema, resp: s.HasBlobsResponseSchema, auth: { type: 'authenticated' } as const },
132
+ image_candidates_for_album: { method: 'POST', path: '/api/music/albums/image-candidates', req: s.AlbumImageCandidatesRequestSchema, resp: s.AlbumImageCandidatesResponseSchema, auth: { type: 'role', role: 'admin' } as const },
133
+ image_candidates_for_artist: { method: 'POST', path: '/api/artists/image-candidates', req: s.ArtistImageCandidatesRequestSchema, resp: s.ArtistImageCandidatesResponseSchema, auth: { type: 'role', role: 'admin' } as const },
134
+ ingest_remote_image: { method: 'POST', path: '/api/music/images/ingest', req: s.IngestRemoteImageRequestSchema, resp: s.IngestRemoteImageResponseSchema, auth: { type: 'role', role: 'admin' } as const },
135
+ list_beloved: { method: 'POST', path: '/api/favorites/beloved', req: s.ListBelovedRequestSchema, resp: s.ListBelovedResponseSchema, auth: { type: 'authenticated' } as const },
136
+ list_favorites: { method: 'POST', path: '/api/favorites/list', req: s.ListFavoritesRequestSchema, resp: s.ListFavoritesResponseSchema, auth: { type: 'role', role: 'member' } as const },
137
+ list_jobs: { method: 'POST', path: '/api/jobs/list', req: s.ListJobsRequestSchema, resp: s.JobResponseSchema.array(), auth: { type: 'authenticated' } as const },
138
+ list_listen_sessions: { method: 'POST', path: '/api/analytics/sessions/list', req: s.ListListenSessionsRequestSchema, resp: s.ListListenSessionsResponseSchema, auth: { type: 'authenticated' } as const },
139
+ list_pending_import_review: { method: 'POST', path: '/api/music/import/pending', req: s.ListPendingReviewRequestSchema, resp: s.PendingReviewSessionSchema.array(), auth: { type: 'authenticated' } as const },
140
+ list_playlists: { method: 'POST', path: '/api/music/playlists/list', req: s.QueryParamsSchema, resp: s.PlaylistsQueryResultSchema, auth: { type: 'authenticated' } as const },
141
+ list_related_artists: { method: 'POST', path: '/api/related-artists/list', req: s.ListRelatedArtistsRequestSchema, resp: s.ListRelatedArtistsResponseSchema, auth: { type: 'authenticated' } as const },
142
+ list_related_artists_batch: { method: 'POST', path: '/api/related-artists/list-batch', req: s.ListRelatedArtistsBatchRequestSchema, resp: s.ListRelatedArtistsBatchResponseSchema, auth: { type: 'authenticated' } as const },
143
+ list_tags: { method: 'GET', path: '/api/tags/list', req: null, resp: s.TagSchema.array(), auth: { type: 'authenticated' } as const },
144
+ list_taxon_kinds: { method: 'POST', path: '/api/taxonomy/kinds/list', req: null, resp: s.TaxonKindSchema.array(), auth: { type: 'authenticated' } as const },
145
+ list_taxon_parents_for_kind: { method: 'POST', path: '/api/taxonomy/parents/list-by-kind', req: s.ListTaxonParentsForKindRequestSchema, resp: s.TaxonParentEdgeSchema.array(), auth: { type: 'authenticated' } as const },
146
+ list_taxons_by_kind: { method: 'POST', path: '/api/taxonomy/taxons/list-by-kind', req: s.ListTaxonsByKindRequestSchema, resp: s.TaxonSchema.array(), auth: { type: 'authenticated' } as const },
147
+ listening_history: { method: 'POST', path: '/api/analytics/listening-history', req: s.ListeningHistoryRequestSchema, resp: s.ListeningHistoryResponseSchema, auth: { type: 'authenticated' } as const },
148
+ mark_album_reviewed: { method: 'POST', path: '/api/music/import/mark-reviewed', req: s.MarkAlbumReviewedRequestSchema, resp: s.ImportReviewOkSchema, auth: { type: 'authenticated' } as const },
149
+ merge_albums_review: { method: 'POST', path: '/api/music/import/merge-albums', req: s.MergeAlbumsReviewRequestSchema, resp: s.ImportReviewOkSchema, auth: { type: 'authenticated' } as const },
150
+ move_song_review: { method: 'POST', path: '/api/music/import/move-song', req: s.MoveSongReviewRequestSchema, resp: s.ImportReviewOkSchema, auth: { type: 'authenticated' } as const },
151
+ patch_album_review: { method: 'POST', path: '/api/music/import/patch-album', req: s.PatchAlbumReviewRequestSchema, resp: s.ImportReviewOkSchema, auth: { type: 'authenticated' } as const },
152
+ propose_artist_bios: { method: 'POST', path: '/api/artists/propose-bios', req: s.ProposeArtistBiosRequestSchema, resp: s.ProposeArtistBiosResponseSchema, auth: { type: 'role', role: 'admin' } as const },
153
+ propose_external_urls: { method: 'POST', path: '/api/albums/propose-external-urls', req: s.ProposeExternalUrlsRequestSchema, resp: s.ProposeExternalUrlsResponseSchema, auth: { type: 'role', role: 'admin' } as const },
154
+ propose_related_artists: { method: 'POST', path: '/api/artists/propose-related', req: s.ProposeRelatedArtistsRequestSchema, resp: s.ProposeRelatedArtistsResponseSchema, auth: { type: 'role', role: 'admin' } as const },
155
+ propose_taxons: { method: 'POST', path: '/api/albums/propose-taxons', req: s.ProposeTaxonsRequestSchema, resp: s.TaxonProposalSchema.array(), auth: { type: 'role', role: 'admin' } as const },
156
+ query_album_status_counts: { method: 'POST', path: '/api/albums/status-counts', req: s.QueryParamsSchema, resp: s.AlbumStatusCountsSchema, auth: { type: 'authenticated' } as const },
157
+ query_albums: { method: 'POST', path: '/api/albums/query', req: s.QueryParamsSchema, resp: s.AlbumsQueryResultSchema, auth: { type: 'authenticated' } as const },
158
+ query_albums_by_scalar_range: { method: 'POST', path: '/api/taxonomy/scalars/query-range', req: s.QueryScalarRangeRequestSchema, resp: z.string().array(), auth: { type: 'authenticated' } as const },
159
+ query_artists: { method: 'POST', path: '/api/artists/query', req: s.QueryParamsSchema, resp: s.ArtistsQueryResultSchema, auth: { type: 'authenticated' } as const },
160
+ query_playlist_songs: { method: 'POST', path: '/api/playlists/songs', req: s.QueryPlaylistSongsRequestSchema, resp: s.PlaylistSongsQueryResultSchema, auth: { type: 'authenticated' } as const },
161
+ query_songs: { method: 'POST', path: '/api/songs/query', req: s.QueryParamsSchema, resp: s.SongsQueryResultSchema, auth: { type: 'authenticated' } as const },
162
+ query_tags: { method: 'POST', path: '/api/tags/query', req: s.QueryTagsRequestSchema, resp: s.TagSchema.array(), auth: { type: 'authenticated' } as const },
163
+ query_taxons: { method: 'POST', path: '/api/taxonomy/taxons/query', req: s.QueryTaxonsRequestSchema, resp: s.TaxonsQueryResultSchema, auth: { type: 'authenticated' } as const },
164
+ recent_songs: { method: 'POST', path: '/api/songs/recent', req: s.RecentSongsRequestSchema, resp: s.SongSchema.array(), auth: { type: 'authenticated' } as const },
165
+ recently_added_albums: { method: 'POST', path: '/api/music/relations/recently-added-albums', req: s.RecentlyAddedAlbumsRequestSchema, resp: s.RecentlyAddedAlbumsResponseSchema, auth: { type: 'authenticated' } as const },
166
+ record_play: { method: 'POST', path: '/api/analytics/play', req: s.RecordPlayRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'member' } as const },
167
+ record_playlist_play: { method: 'POST', path: '/api/playlists/record-play', req: s.GetPlaylistRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'authenticated' } as const },
168
+ reject_mb_match: { method: 'POST', path: '/api/albums/mb-reject', req: s.RejectMbMatchRequestSchema, resp: s.MbMatchActionResponseSchema, auth: { type: 'role', role: 'admin' } as const },
169
+ remove_album_taxon: { method: 'POST', path: '/api/taxonomy/album-links/remove', req: s.RemoveAlbumTaxonRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
170
+ remove_albums_tags: { method: 'POST', path: '/api/tags/albums/remove', req: s.RemoveAlbumsTagsRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
171
+ remove_rating: { method: 'POST', path: '/api/ratings/remove', req: s.RemoveRatingRequestSchema, resp: s.RemoveRatingResponseSchema, auth: { type: 'role', role: 'member' } as const },
172
+ remove_songs_from_playlist: { method: 'POST', path: '/api/playlists/remove-songs', req: s.RemoveSongsFromPlaylistRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'owner_or', role: 'admin' } as const },
173
+ remove_taxon_parent: { method: 'POST', path: '/api/taxonomy/parents/remove', req: s.RemoveTaxonParentRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
174
+ reorder_playlist_songs: { method: 'POST', path: '/api/playlists/reorder', req: s.ReorderPlaylistSongsRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'owner_or', role: 'admin' } as const },
175
+ replace_albums_tags: { method: 'POST', path: '/api/tags/albums/replace', req: s.ReplaceAlbumsTagsRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
176
+ requery_enrichment: { method: 'POST', path: '/api/music/albums/enrichment/requery', req: s.RequeryEnrichmentRequestSchema, resp: s.RequeryEnrichmentResponseSchema, auth: { type: 'role', role: 'admin' } as const },
177
+ search: { method: 'POST', path: '/api/music/search', req: s.SearchRequestSchema, resp: s.SearchResponseSchema, auth: { type: 'authenticated' } as const },
178
+ search_musicbrainz_releases: { method: 'POST', path: '/api/musicbrainz/search/releases', req: s.SearchReleasesRequestSchema, resp: s.MbSearchReleasesResponseSchema, auth: { type: 'role', role: 'admin' } as const },
179
+ set_album_taxons: { method: 'POST', path: '/api/taxonomy/album-links/set', req: s.SetAlbumTaxonsRequestSchema, resp: s.AlbumTaxonLinkSchema.array(), auth: { type: 'role', role: 'admin' } as const },
180
+ set_favorite: { method: 'POST', path: '/api/favorites/set', req: s.SetFavoriteRequestSchema, resp: s.SetFavoriteResponseSchema, auth: { type: 'role', role: 'member' } as const },
181
+ set_mb_lookup_status: { method: 'POST', path: '/api/albums/set-mb-lookup-status', req: s.SetMbLookupStatusRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
182
+ set_primary_image: { method: 'POST', path: '/api/music/images/set-primary', req: s.SetPrimaryImageRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'role', role: 'admin' } as const },
183
+ set_rating: { method: 'POST', path: '/api/ratings/set', req: s.SetRatingRequestSchema, resp: s.SetRatingResponseSchema, auth: { type: 'role', role: 'member' } as const },
184
+ set_related_artist_bandcamp: { method: 'POST', path: '/api/related-artists/set-bandcamp', req: s.SetRelatedArtistBandcampRequestSchema, resp: s.RelatedArtistApiSchema, auth: { type: 'role', role: 'admin' } as const },
185
+ set_scalar_attribute: { method: 'POST', path: '/api/taxonomy/scalars/set', req: s.SetScalarAttributeRequestSchema, resp: s.ScalarAttributeSchema, auth: { type: 'role', role: 'admin' } as const },
186
+ set_taxon_color: { method: 'POST', path: '/api/taxonomy/taxons/set-color', req: s.SetTaxonColorRequestSchema, resp: s.TaxonSchema, auth: { type: 'role', role: 'admin' } as const },
187
+ set_taxon_kind_color: { method: 'POST', path: '/api/taxonomy/kinds/set-color', req: s.SetTaxonKindColorRequestSchema, resp: s.TaxonKindSchema, auth: { type: 'role', role: 'admin' } as const },
188
+ set_taxon_kind_label: { method: 'POST', path: '/api/taxonomy/kinds/set-label', req: s.SetTaxonKindLabelRequestSchema, resp: s.TaxonKindSchema, auth: { type: 'role', role: 'admin' } as const },
189
+ set_taxon_label: { method: 'POST', path: '/api/taxonomy/taxons/set-label', req: s.SetTaxonLabelRequestSchema, resp: s.TaxonSchema, auth: { type: 'role', role: 'admin' } as const },
190
+ song_analytics: { method: 'POST', path: '/api/analytics/song-stats', req: s.SongAnalyticsRequestSchema, resp: s.PlayAnalyticsSchema, auth: { type: 'authenticated' } as const },
191
+ stream_blob: { method: 'GET', path: '/api/blobs/{id}', req: null, resp: null, auth: { type: 'authenticated' } as const },
192
+ suggestions: { method: 'POST', path: '/api/music/suggestions', req: s.SuggestionsRequestSchema, resp: s.SuggestionsResponseSchema, auth: { type: 'authenticated' } as const },
193
+ sync_album: { method: 'POST', path: '/api/sync/album', req: s.SyncAlbumRequestSchema, resp: s.SyncAlbumResponseSchema, auth: { type: 'role', role: 'member' } as const },
194
+ sync_playlist: { method: 'POST', path: '/api/sync/playlist', req: s.SyncPlaylistRequestSchema, resp: s.SyncPlaylistResponseSchema, auth: { type: 'role', role: 'member' } as const },
195
+ sync_song_by_blake3: { method: 'POST', path: '/api/sync/song-by-blake3', req: s.SyncSongByBlake3RequestSchema, resp: s.SyncSongByBlake3ResponseSchema, auth: { type: 'role', role: 'member' } as const },
196
+ synced_sha256s: { method: 'GET', path: '/api/sync/sha256s', req: null, resp: z.string().array(), auth: { type: 'role', role: 'member' } as const },
197
+ top_albums: { method: 'POST', path: '/api/analytics/top-albums', req: s.TopAlbumsRequestSchema, resp: s.TopAlbumSchema.array(), auth: { type: 'authenticated' } as const },
198
+ top_artists: { method: 'POST', path: '/api/analytics/top-artists', req: s.TopArtistsRequestSchema, resp: s.TopArtistSchema.array(), auth: { type: 'authenticated' } as const },
199
+ top_songs: { method: 'POST', path: '/api/analytics/top-songs', req: s.TopSongsRequestSchema, resp: s.TopSongSchema.array(), auth: { type: 'authenticated' } as const },
200
+ unassigned_albums: { method: 'POST', path: '/api/music/relations/unassigned-albums', req: s.UnassignedAlbumsRequestSchema, resp: s.UnassignedAlbumsResponseSchema, auth: { type: 'authenticated' } as const },
201
+ update_album: { method: 'POST', path: '/api/albums/update', req: s.UpdateAlbumRequestSchema, resp: s.AlbumSchema, auth: { type: 'role', role: 'admin' } as const },
202
+ update_artist: { method: 'POST', path: '/api/artists/update', req: s.UpdateArtistRequestSchema, resp: s.ArtistSchema, auth: { type: 'role', role: 'admin' } as const },
203
+ update_artist_metadata: { method: 'POST', path: '/api/artists/update-metadata', req: s.UpdateArtistMetadataRequestSchema, resp: s.UpdateArtistMetadataResponseSchema, auth: { type: 'role', role: 'admin' } as const },
204
+ update_listen_session_progress: { method: 'POST', path: '/api/analytics/sessions/progress', req: s.UpdateListenSessionProgressRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'owner' } as const },
205
+ update_listen_session_songs: { method: 'POST', path: '/api/analytics/sessions/songs', req: s.UpdateListenSessionSongsRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'owner' } as const },
206
+ update_listen_session_status: { method: 'POST', path: '/api/analytics/sessions/status', req: s.UpdateListenSessionStatusRequestSchema, resp: s.EmptyResponseSchema, auth: { type: 'owner' } as const },
207
+ update_playlist: { method: 'POST', path: '/api/playlists/update', req: s.UpdatePlaylistRequestSchema, resp: s.PlaylistSchema, auth: { type: 'owner_or', role: 'admin' } as const },
208
+ update_songs: { method: 'POST', path: '/api/songs/update', req: s.UpdateSongsRequestSchema, resp: s.UpdateSongsResultSchema, auth: { type: 'role', role: 'admin' } as const },
209
+ upload_image: { method: 'POST', path: '/api/upload/image', req: null, resp: s.ImageUploadResponseSchema, auth: { type: 'role', role: 'member' } as const },
210
+ upload_music: { method: 'POST', path: '/api/upload/music', req: null, resp: s.MusicUploadResponseSchema, auth: { type: 'role', role: 'member' } as const },
211
+ upload_music_by_blake3: { method: 'POST', path: '/api/upload/music-by-blake3', req: s.UploadMusicByBlake3RequestSchema, resp: s.MusicUploadResponseSchema, auth: { type: 'role', role: 'member' } as const },
212
+ },
213
+ } as const;