nixamp 0.51.0 → 0.52.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 (46) hide show
  1. package/dist/ai-label.d.ts +70 -3
  2. package/dist/ai-label.js +375 -29
  3. package/dist/label-routes.js +2 -1
  4. package/dist/main.js +9 -0
  5. package/dist/mcp.js +42 -0
  6. package/dist/music-cli.d.ts +2 -0
  7. package/dist/music-cli.js +109 -0
  8. package/dist/music-client.d.ts +62 -0
  9. package/dist/music-client.js +73 -0
  10. package/dist/station-cli.js +8 -1
  11. package/dist/station-mcp.d.ts +62 -0
  12. package/dist/station-mcp.js +5 -2
  13. package/dist/station-usage.d.ts +1 -1
  14. package/dist/station-usage.js +3 -0
  15. package/package.json +1 -1
  16. package/src/ai-label.ts +380 -29
  17. package/src/label-routes.ts +2 -1
  18. package/src/main.ts +8 -0
  19. package/src/mcp.ts +38 -0
  20. package/src/music-cli.ts +86 -0
  21. package/src/music-client.ts +86 -0
  22. package/src/station-cli.ts +8 -1
  23. package/src/station-mcp.ts +5 -2
  24. package/src/station-usage.ts +3 -0
  25. package/web/dist/assets/{ar--GRGOL7F.js → ar-DMx2aeAO.js} +1 -1
  26. package/web/dist/assets/{cs-B2ab-8qO.js → cs-Ddu0q3kG.js} +1 -1
  27. package/web/dist/assets/{da-Ek2g1uTg.js → da-CZ9gg_pb.js} +1 -1
  28. package/web/dist/assets/{de-CM7-9Fbk.js → de-CgJX4xg2.js} +1 -1
  29. package/web/dist/assets/{es-BFSOrr3I.js → es-Cp5ryDrJ.js} +1 -1
  30. package/web/dist/assets/{fi-D9Tf69sv.js → fi-D912EKux.js} +1 -1
  31. package/web/dist/assets/{fr-Gh9yYJGo.js → fr-FE_K0MPb.js} +1 -1
  32. package/web/dist/assets/{hi-B3ow3HJC.js → hi-BdYtQs1S.js} +1 -1
  33. package/web/dist/assets/{hu-paju-6Kr.js → hu-oh5tEg0p.js} +1 -1
  34. package/web/dist/assets/{id-mubOQFX0.js → id-NbCUWeB1.js} +1 -1
  35. package/web/dist/assets/{it-0EM-qnDT.js → it-CiHbKZ6Y.js} +1 -1
  36. package/web/dist/assets/{main-CECXge6G.js → main-8wV87D-g.js} +2 -2
  37. package/web/dist/assets/{nl-c6HalphH.js → nl-nLHK_AQ-.js} +1 -1
  38. package/web/dist/assets/{radio-CDUEoM0y.js → radio-BmHvBkVy.js} +1 -1
  39. package/web/dist/assets/{ru-CUKYVlgf.js → ru-BDdAS0Vv.js} +1 -1
  40. package/web/dist/assets/{sv-BUxBsZR5.js → sv-COweXM56.js} +1 -1
  41. package/web/dist/assets/{uk-708lBssa.js → uk-BJFzR1LS.js} +1 -1
  42. package/web/dist/assets/{vi-B1ropUvk.js → vi-qg2DJ6cR.js} +1 -1
  43. package/web/dist/assets/{zh-C_ndTsNh.js → zh-DUWOKDkb.js} +1 -1
  44. package/web/dist/index.html +2 -1
  45. package/web/dist/radio.html +1 -1
  46. package/web/dist/sw.js +20 -20
@@ -15,6 +15,8 @@ export interface LabelSong {
15
15
  /** The MP3, "" until composed. */
16
16
  file: string;
17
17
  lyricsFile: string;
18
+ /** Its music video (001. <title>.mp4), "" until made; absent on songs from before videos. */
19
+ video?: string;
18
20
  /** Who wrote the words and who sang them, e.g. "llama-3.3 + elevenlabs/music_v2_5". */
19
21
  made: string;
20
22
  composedAt: number;
@@ -60,14 +62,28 @@ export interface LabelSettings {
60
62
  enabled: boolean;
61
63
  /** Seconds of music a month across the label; 0 for no cap. */
62
64
  monthlySeconds: number;
63
- /** Days between an artist's albums once it has its first ones. */
65
+ /** Days between an artist's albums once it has its first ones (only when albumsPerDay is 0). */
64
66
  everyDays: number;
67
+ /**
68
+ * Once every artist has its first albums: new albums a day across the label,
69
+ * each by an artist picked at random or, now and then, a new one invented
70
+ * in one of the owner's genres. 0 goes back to one per artist every everyDays.
71
+ */
72
+ albumsPerDay: number;
73
+ /** How often (0-1) the day's album is by a newly invented artist. */
74
+ newArtistChance: number;
65
75
  /** Albums every artist gets as soon as it can. */
66
76
  seedAlbums: number;
67
77
  songsPerAlbum: number;
68
78
  songSeconds: number;
79
+ /** A music video for every song (the cover over itself, blurred, and the song's waveform), and a TV station of them. */
80
+ videos: boolean;
69
81
  /** Public, listed and owned, so the genre stations are on the guide. */
70
82
  publish: boolean;
83
+ /** Songs made at the same time (ElevenLabs takes several requests at once on a paid plan). */
84
+ parallel: number;
85
+ /** Genres whose artists get their albums first, in this order. */
86
+ genreOrder: string[];
71
87
  /** Set by a provider that is out of credit; cleared by `resume`. */
72
88
  pausedReason: string;
73
89
  }
@@ -95,6 +111,8 @@ export declare function planAlbum(artist: Pick<LabelArtist, "name" | "genre" | "
95
111
  }>;
96
112
  model: string;
97
113
  }>;
114
+ /** A new artist for this genre, unlike the ones already signed. */
115
+ export declare function inventArtist(genre: string, taken: string[], ask?: typeof askJson): Promise<ArtistPreset>;
98
116
  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
117
  lyrics: string;
100
118
  model: string;
@@ -105,6 +123,13 @@ export declare function writeLyrics(artist: Pick<LabelArtist, "name" | "genre" |
105
123
  * OPENAI_API_KEY its token); null when there is no image model to ask.
106
124
  */
107
125
  export declare function workersAiCover(prompt: string, env?: Record<string, string | undefined>, fetcher?: typeof fetch): Promise<Buffer | null>;
126
+ /** Writes the tags (and the cover, as the attached picture) into a copy of the file. */
127
+ /**
128
+ * A song's music video: its album cover over a blurred copy of itself, the
129
+ * song's waveform along the bottom, 720p H.264 with the MP3's audio copied in.
130
+ * Without a cover, a dark plate.
131
+ */
132
+ export declare function renderVideo(ffmpeg: string[], audio: string, cover: string, out: string): Promise<void>;
108
133
  export interface AiLabelDeps {
109
134
  index: LibraryIndex;
110
135
  store: StationStore;
@@ -121,6 +146,10 @@ export interface AiLabelDeps {
121
146
  /** A cover picture for this prompt, or null when nothing can draw one. */
122
147
  cover?: (prompt: string) => Promise<Buffer | null>;
123
148
  tag?: (from: string, to: string, tags: Record<string, string>, cover: string) => Promise<void>;
149
+ /** Renders a song's music video to `out`; ffmpeg by default (renderVideo). */
150
+ video?: (audio: string, cover: string, out: string) => Promise<void>;
151
+ /** The day's pick (artist, genre, new or not); Math.random by default. */
152
+ random?: () => number;
124
153
  now?: () => number;
125
154
  log?: (line: string) => void;
126
155
  }
@@ -130,7 +159,19 @@ export declare class AiLabel {
130
159
  private artists;
131
160
  private settings;
132
161
  private usage;
133
- private busy;
162
+ private running;
163
+ /** Songs being made now, by `<album>:<n>`. */
164
+ private claims;
165
+ /** Artists whose next album is being planned now. */
166
+ private planning;
167
+ private covering;
168
+ private filming;
169
+ /** Songs whose video failed since this server started. */
170
+ private videoFailed;
171
+ /** The day's album is tried at most every 30 minutes, so a failing model is not hammered. */
172
+ private dailyTriedAt;
173
+ /** Genre stations are kept in step one change at a time. */
174
+ private placing;
134
175
  private timer;
135
176
  constructor(file: string, deps: AiLabelDeps);
136
177
  static fileFor(stateDir: string, port: number): string;
@@ -159,19 +200,45 @@ export declare class AiLabel {
159
200
  /** Plan the next album for this artist now, whatever its schedule; it is composed song by song from here. */
160
201
  plan(key: string): Promise<LabelAlbum>;
161
202
  startSchedule(everyMs?: number): void;
203
+ /**
204
+ * Music videos for every released song without one, one at a time (a
205
+ * video is CPU, not credit), and the TV station of them kept in step. A
206
+ * video that fails is tried again after the next restart, not in a loop.
207
+ */
208
+ videos(): Promise<number>;
209
+ private film;
210
+ private filmPass;
211
+ /** The label's music videos as one TV station: free to watch, with its breaks. */
212
+ private placeVideos;
162
213
  stopSchedule(): void;
163
- /** Everything that is due, one song at a time, until nothing is or something stops it. */
214
+ /**
215
+ * Everything that is due, `parallel` songs at a time, until nothing is or
216
+ * something stops it. Each worker claims a song (or an artist to plan for),
217
+ * so two never make the same one.
218
+ */
164
219
  tick(limit?: number): Promise<void>;
220
+ /** Where the artist's genre comes in the owner's order (genreOrder); unlisted genres after. */
221
+ private rank;
165
222
  private step;
223
+ /**
224
+ * After the first sweep: the day's album. When the last one was planned a
225
+ * day ago (or 1/albumsPerDay of one), a random artist gets it, or now and
226
+ * then an artist invented for one of the owner's genres. Nothing until every
227
+ * enabled artist has its first albums.
228
+ */
229
+ private daily;
166
230
  private failed;
167
231
  private compose;
232
+ /** The album's cover, drawn once; songs made at the same time wait for the same drawing. */
168
233
  private ensureCover;
234
+ private drawCover;
169
235
  /** album.json beside the songs: what the album is, for anybody who copies the folder. */
170
236
  private writeAlbumJson;
171
237
  private genres;
172
238
  private stationFor;
173
239
  /** The genre's station gets every song of that genre: made with the first, then kept in step. */
174
240
  private place;
241
+ private placeNow;
175
242
  /** The catalogue anybody may see: artists, their released songs, the words, and where each genre plays. */
176
243
  publicView(): {
177
244
  holder: string;