nixamp 0.48.0 → 0.49.0

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 (56) hide show
  1. package/dist/ai-keys.d.ts +1 -1
  2. package/dist/ai-keys.js +2 -0
  3. package/dist/ai-label.d.ts +210 -0
  4. package/dist/ai-label.js +725 -0
  5. package/dist/ai-radio.d.ts +11 -5
  6. package/dist/ai-radio.js +27 -11
  7. package/dist/ai-text.d.ts +2 -0
  8. package/dist/ai-text.js +4 -2
  9. package/dist/label-routes.d.ts +15 -0
  10. package/dist/label-routes.js +255 -0
  11. package/dist/server.d.ts +3 -0
  12. package/dist/server.js +28 -1
  13. package/dist/station-cli.js +109 -0
  14. package/dist/station-guide.d.ts +1 -1
  15. package/dist/station-guide.js +8 -3
  16. package/dist/station-mcp.d.ts +247 -20
  17. package/dist/station-mcp.js +36 -0
  18. package/dist/station-metering.js +2 -2
  19. package/dist/station-usage.d.ts +1 -1
  20. package/dist/station-usage.js +15 -1
  21. package/package.json +1 -1
  22. package/src/ai-keys.ts +2 -0
  23. package/src/ai-label.ts +826 -0
  24. package/src/ai-radio.ts +30 -13
  25. package/src/ai-text.ts +6 -2
  26. package/src/label-routes.ts +268 -0
  27. package/src/server.ts +30 -1
  28. package/src/station-cli.ts +102 -0
  29. package/src/station-guide.ts +8 -3
  30. package/src/station-mcp.ts +34 -0
  31. package/src/station-metering.ts +2 -2
  32. package/src/station-usage.ts +15 -1
  33. package/web/dist/assets/{ar-ropwGSSV.js → ar-BEVjqkqM.js} +1 -1
  34. package/web/dist/assets/{cs-Bi7uRYjd.js → cs-DjazXQ15.js} +1 -1
  35. package/web/dist/assets/{da-B8OELPch.js → da-DnyF6SYK.js} +1 -1
  36. package/web/dist/assets/{de-DJOTeXET.js → de--q1ph9Ez.js} +1 -1
  37. package/web/dist/assets/{es-BIupzuzS.js → es-Do3DkBwD.js} +1 -1
  38. package/web/dist/assets/{fi-A6MoWkzZ.js → fi-B7sJTx1N.js} +1 -1
  39. package/web/dist/assets/{fr-DerIDIwl.js → fr-Dy7f5rGi.js} +1 -1
  40. package/web/dist/assets/{hi-CyUtRBH4.js → hi-C9vSnTQ4.js} +1 -1
  41. package/web/dist/assets/{hu-DD1vrIhY.js → hu-h-eueQE8.js} +1 -1
  42. package/web/dist/assets/{id-C6OAMzii.js → id-D8Ffd-Pc.js} +1 -1
  43. package/web/dist/assets/{it-Xy86vmU7.js → it-BKM_2rj1.js} +1 -1
  44. package/web/dist/assets/main-Dl_uRj22.js +22 -0
  45. package/web/dist/assets/{nl-4Q-K2Jf1.js → nl-Cj3ETrcl.js} +1 -1
  46. package/web/dist/assets/radio-zPgyISLf.js +1 -0
  47. package/web/dist/assets/{ru-CpxwOhC4.js → ru-B5zmGSnG.js} +1 -1
  48. package/web/dist/assets/{sv-aRvu7MWa.js → sv-BMtjA1uI.js} +1 -1
  49. package/web/dist/assets/{uk-BCwwfngg.js → uk-B2vuB6Un.js} +1 -1
  50. package/web/dist/assets/{vi-tvWPiAH_.js → vi-CXam3lDh.js} +1 -1
  51. package/web/dist/assets/{zh-Bp2aLEyX.js → zh-DnYhyzqb.js} +1 -1
  52. package/web/dist/index.html +1 -1
  53. package/web/dist/radio.html +33 -1
  54. package/web/dist/sw.js +20 -20
  55. package/web/dist/assets/main-CuG8-ADK.js +0 -22
  56. package/web/dist/assets/radio-DI0H8flN.js +0 -1
package/dist/ai-keys.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export declare const AI_KEY_NAMES: readonly ["ELEVENLABS_API_KEY", "GEMINI_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "NIXAMP_OPENAI_URL", "NIXAMP_OPENAI_MODEL", "NIXAMP_LYRICS_KEY"];
1
+ export declare const AI_KEY_NAMES: readonly ["ELEVENLABS_API_KEY", "GEMINI_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY", "NIXAMP_OPENAI_URL", "NIXAMP_OPENAI_MODEL", "NIXAMP_MUSIC_MODEL", "NIXAMP_LYRICS_KEY"];
2
2
  export type AiKeyName = (typeof AI_KEY_NAMES)[number];
3
3
  export declare function aiKeysFile(): string;
4
4
  /** The key, from the environment or ai.json; "" when neither has it. */
package/dist/ai-keys.js CHANGED
@@ -13,6 +13,8 @@ export const AI_KEY_NAMES = [
13
13
  "ELEVENLABS_API_KEY", "GEMINI_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY",
14
14
  // Any OpenAI-compatible endpoint in OpenAI's place (Cloudflare Workers AI, Ollama, ...) and its model.
15
15
  "NIXAMP_OPENAI_URL", "NIXAMP_OPENAI_MODEL",
16
+ // Which ElevenLabs music model composes: music_v1, music_v2 or music_v2_5 (the default).
17
+ "NIXAMP_MUSIC_MODEL",
16
18
  // nichedb.dev's key for publishing our own songs' words (lyrics-publish.ts).
17
19
  "NIXAMP_LYRICS_KEY",
18
20
  ];
@@ -0,0 +1,210 @@
1
+ import { type Composer } from "./ai-radio.ts";
2
+ import { askJson } from "./ai-text.ts";
3
+ import type { LibraryIndex, Probed } from "./library-index.ts";
4
+ import type { StationRunner } from "./station-runner.ts";
5
+ import { type StationStore } from "./stations.ts";
6
+ export interface LabelSong {
7
+ /** Its place on the album, from 1. */
8
+ n: number;
9
+ title: string;
10
+ /** The production brief the music model is given beside the artist's sound. */
11
+ style: string;
12
+ /** The words, with [Verse]/[Chorus] section lines; "" until written. */
13
+ lyrics: string;
14
+ seconds: number;
15
+ /** The MP3, "" until composed. */
16
+ file: string;
17
+ lyricsFile: string;
18
+ /** Who wrote the words and who sang them, e.g. "llama-3.3 + elevenlabs/music_v2_5". */
19
+ made: string;
20
+ composedAt: number;
21
+ }
22
+ export interface LabelAlbum {
23
+ key: string;
24
+ title: string;
25
+ year: number;
26
+ concept: string;
27
+ /** The album's folder. */
28
+ folder: string;
29
+ /** cover.jpg in the folder, "" when there is none. */
30
+ cover: string;
31
+ coverTried: boolean;
32
+ songs: LabelSong[];
33
+ /** Which model planned it. */
34
+ writer: string;
35
+ plannedAt: number;
36
+ /** When its last song landed; 0 while it is being made. */
37
+ doneAt: number;
38
+ }
39
+ export interface LabelArtist {
40
+ key: string;
41
+ name: string;
42
+ genre: string;
43
+ /** The music in words: every song's brief starts from it. */
44
+ sound: string;
45
+ /** The lead voice. */
46
+ voice: string;
47
+ bio: string;
48
+ /** What the songs are about. */
49
+ themes: string;
50
+ enabled: boolean;
51
+ albums: LabelAlbum[];
52
+ createdAt: number;
53
+ lastAttemptAt: number;
54
+ lastError: string;
55
+ failures: number;
56
+ }
57
+ export interface LabelSettings {
58
+ /** Who holds the rights: publisher and copyright on every file, and the station's declaration. */
59
+ holder: string;
60
+ enabled: boolean;
61
+ /** Seconds of music a month across the label; 0 for no cap. */
62
+ monthlySeconds: number;
63
+ /** Days between an artist's albums once it has its first ones. */
64
+ everyDays: number;
65
+ /** Albums every artist gets as soon as it can. */
66
+ seedAlbums: number;
67
+ songsPerAlbum: number;
68
+ songSeconds: number;
69
+ /** Public, listed and owned, so the genre stations are on the guide. */
70
+ publish: boolean;
71
+ /** Set by a provider that is out of credit; cleared by `resume`. */
72
+ pausedReason: string;
73
+ }
74
+ export type ArtistPreset = Pick<LabelArtist, "name" | "genre" | "sound" | "voice" | "bio" | "themes">;
75
+ /**
76
+ * The label's first roster: invented names, one genre each, sung in English.
77
+ * `nixamp station label artist add --preset KEY`, or all of them with `--roster`.
78
+ */
79
+ export declare const ROSTER: Record<string, ArtistPreset>;
80
+ export declare const DEFAULTS: Omit<LabelSettings, "pausedReason">;
81
+ /** A name as a folder or file name: what a filesystem or a zip refuses is dropped. */
82
+ export declare function fileName(text: string): string;
83
+ /** A genre's folder and station key: "Hip-Hop" is hip-hop, "R&B" is rnb. */
84
+ export declare function genreSlug(genre: string): string;
85
+ /** "001. Title", the start of a song's file names. */
86
+ export declare function songBase(song: Pick<LabelSong, "n" | "title">): string;
87
+ /** Words as a singer gets them: section lines kept, the model's own notes and fences dropped. */
88
+ export declare function cleanLyrics(text: string): string;
89
+ export declare function planAlbum(artist: Pick<LabelArtist, "name" | "genre" | "sound" | "voice" | "bio" | "themes" | "albums">, songs: number, ask?: typeof askJson): Promise<{
90
+ title: string;
91
+ concept: string;
92
+ songs: Array<{
93
+ title: string;
94
+ style: string;
95
+ }>;
96
+ model: string;
97
+ }>;
98
+ export declare function writeLyrics(artist: Pick<LabelArtist, "name" | "genre" | "voice" | "themes">, album: Pick<LabelAlbum, "title" | "concept">, song: Pick<LabelSong, "title" | "style" | "seconds">, ask?: typeof askJson): Promise<{
99
+ lyrics: string;
100
+ model: string;
101
+ }>;
102
+ /**
103
+ * A cover from Cloudflare Workers AI (flux-1-schnell), when the language model
104
+ * slot is Workers AI (NIXAMP_OPENAI_URL is an account's /ai/v1 and
105
+ * OPENAI_API_KEY its token); null when there is no image model to ask.
106
+ */
107
+ export declare function workersAiCover(prompt: string, env?: Record<string, string | undefined>, fetcher?: typeof fetch): Promise<Buffer | null>;
108
+ export interface AiLabelDeps {
109
+ index: LibraryIndex;
110
+ store: StationStore;
111
+ runner: StationRunner;
112
+ probe: (path: string) => Promise<Probed>;
113
+ ffmpeg: string[];
114
+ /** Where the label's music goes: <root>/<genre>/<artist>/<year>-<album>/. */
115
+ root: string;
116
+ owner: string;
117
+ mayStart: () => boolean;
118
+ composer?: () => Composer | null;
119
+ ask?: typeof askJson;
120
+ canAsk?: () => boolean;
121
+ /** A cover picture for this prompt, or null when nothing can draw one. */
122
+ cover?: (prompt: string) => Promise<Buffer | null>;
123
+ tag?: (from: string, to: string, tags: Record<string, string>, cover: string) => Promise<void>;
124
+ now?: () => number;
125
+ log?: (line: string) => void;
126
+ }
127
+ export declare class AiLabel {
128
+ private readonly file;
129
+ private readonly deps;
130
+ private artists;
131
+ private settings;
132
+ private usage;
133
+ private busy;
134
+ private timer;
135
+ constructor(file: string, deps: AiLabelDeps);
136
+ static fileFor(stateDir: string, port: number): string;
137
+ private now;
138
+ private log;
139
+ private save;
140
+ private composer;
141
+ private canAsk;
142
+ /** The library source the root is in: the one that already holds it, else a managed one of its own. */
143
+ private source;
144
+ get root(): string;
145
+ list(): LabelArtist[];
146
+ get(key: string): LabelArtist | undefined;
147
+ holder(): string;
148
+ private budgetLeft;
149
+ /** When this artist's next album is due: its first ones at once, then every `everyDays`, staggered by artist. */
150
+ dueAt(artist: LabelArtist): number;
151
+ private backingOff;
152
+ status(): Record<string, unknown>;
153
+ configure(input: Record<string, unknown>): LabelSettings;
154
+ /** Sign an artist: a preset from the roster, or a name, genre and sound of the owner's own. */
155
+ sign(input: Record<string, unknown>): LabelArtist;
156
+ update(key: string, input: Record<string, unknown>): LabelArtist;
157
+ /** Let an artist go. Its music stays (and stays on the air) unless `files` is true. */
158
+ drop(key: string, files?: boolean): Promise<void>;
159
+ /** Plan the next album for this artist now, whatever its schedule; it is composed song by song from here. */
160
+ plan(key: string): Promise<LabelAlbum>;
161
+ startSchedule(everyMs?: number): void;
162
+ stopSchedule(): void;
163
+ /** Everything that is due, one song at a time, until nothing is or something stops it. */
164
+ tick(limit?: number): Promise<void>;
165
+ private step;
166
+ private failed;
167
+ private compose;
168
+ private ensureCover;
169
+ /** album.json beside the songs: what the album is, for anybody who copies the folder. */
170
+ private writeAlbumJson;
171
+ private genres;
172
+ private stationFor;
173
+ /** The genre's station gets every song of that genre: made with the first, then kept in step. */
174
+ private place;
175
+ /** The catalogue anybody may see: artists, their released songs, the words, and where each genre plays. */
176
+ publicView(): {
177
+ holder: string;
178
+ genres: Array<{
179
+ genre: string;
180
+ station: string;
181
+ artists: PublicArtist[];
182
+ }>;
183
+ };
184
+ /** A released file anybody may fetch: a song, its words, or its album's cover. */
185
+ publicFile(artistKey: string, albumKey: string, what: string): {
186
+ path: string;
187
+ type: string;
188
+ } | null;
189
+ private require;
190
+ }
191
+ export interface PublicArtist {
192
+ key: string;
193
+ name: string;
194
+ genre: string;
195
+ bio: string;
196
+ albums: Array<{
197
+ key: string;
198
+ title: string;
199
+ year: number;
200
+ concept: string;
201
+ cover: boolean;
202
+ complete: boolean;
203
+ songs: Array<{
204
+ n: number;
205
+ title: string;
206
+ seconds: number;
207
+ lyrics: string;
208
+ }>;
209
+ }>;
210
+ }