nixamp 0.78.5 → 0.80.1
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.
- package/README.md +6 -5
- package/backtoschool/dist/assets/{ar-CYu0byMf.js → ar-BYDVUbTZ.js} +1 -1
- package/backtoschool/dist/assets/{cs-BTTM7DeB.js → cs-DsCx7BAy.js} +1 -1
- package/backtoschool/dist/assets/{da-iixezQtd.js → da-5tLM7Wal.js} +1 -1
- package/backtoschool/dist/assets/{de-Sx3acevR.js → de-2Q8k6IKm.js} +1 -1
- package/backtoschool/dist/assets/{es-ByCnw8ja.js → es-CVwFkQ0E.js} +1 -1
- package/backtoschool/dist/assets/{fi-BCeFoeH5.js → fi-D7KuvUvL.js} +1 -1
- package/backtoschool/dist/assets/{fr-Nz5hMzAE.js → fr-CkWg-nDv.js} +1 -1
- package/backtoschool/dist/assets/{hi-x5-IG1rl.js → hi-DayNvhO4.js} +1 -1
- package/backtoschool/dist/assets/{hls-OR5FY6YP-O3qhTXm4.js → hls-OR5FY6YP-D2hwLr-P.js} +1 -1
- package/backtoschool/dist/assets/{hu-QqIkGcMe.js → hu-BqS46UyM.js} +1 -1
- package/backtoschool/dist/assets/{id-OVwO1Uoi.js → id-DTiKw_BS.js} +1 -1
- package/backtoschool/dist/assets/{index-Dgqs6yM_.js → index-EM-0SwBp.js} +3 -3
- package/backtoschool/dist/assets/{it-CmssY5Ut.js → it-B60apeGa.js} +1 -1
- package/backtoschool/dist/assets/{mpegts-RQlLEU1A.js → mpegts-C4v5WI5W.js} +1 -1
- package/backtoschool/dist/assets/{mpegts-Y2FARLFE-y49QH7a5.js → mpegts-Y2FARLFE-D4OTyGyp.js} +1 -1
- package/backtoschool/dist/assets/{nl-VkQxlPku.js → nl-DctlIuWS.js} +1 -1
- package/backtoschool/dist/assets/{ru-Dk8FAbDx.js → ru-CO8g0gfi.js} +1 -1
- package/backtoschool/dist/assets/{sv-CNs1UySx.js → sv-CoFMz-Qg.js} +1 -1
- package/backtoschool/dist/assets/{uk-BDB1YayY.js → uk-C3_GprSP.js} +1 -1
- package/backtoschool/dist/assets/{vi-DnEIKzy7.js → vi-B0RDG4iN.js} +1 -1
- package/backtoschool/dist/assets/{zh-B_atHA4S.js → zh--yO6izcR.js} +1 -1
- package/backtoschool/dist/index.html +3 -3
- package/dist/ai-keys.d.ts +1 -1
- package/dist/ai-keys.js +1 -1
- package/dist/ai-label.d.ts +38 -0
- package/dist/ai-label.js +237 -5
- package/dist/ai-text.d.ts +42 -1
- package/dist/ai-text.js +69 -7
- package/dist/event-writer.d.ts +10 -1
- package/dist/event-writer.js +27 -8
- package/dist/label-routes.js +9 -0
- package/dist/library-mcp.d.ts +6 -6
- package/dist/music-hub.d.ts +11 -0
- package/dist/music-hub.js +33 -2
- package/dist/nixamp-link-api.d.ts +5 -1
- package/dist/nixamp-link-api.js +12 -2
- package/dist/nixamp-link-types.d.ts +2 -0
- package/dist/nixamp-link.d.ts +22 -9
- package/dist/nixamp-link.js +75 -58
- package/dist/oauth.d.ts +1 -0
- package/dist/oauth.js +8 -1
- package/dist/profiles.d.ts +1 -1
- package/dist/profiles.js +0 -0
- package/dist/server.js +19 -10
- package/dist/station-cli.js +24 -0
- package/dist/station-mcp.d.ts +6 -6
- package/dist/station-usage.d.ts +1 -1
- package/dist/station-usage.js +4 -1
- package/dist/universal-footer.d.ts +2 -0
- package/dist/universal-footer.js +39 -0
- package/package.json +7 -5
- package/src/ai-keys.ts +1 -1
- package/src/ai-label.ts +240 -5
- package/src/ai-text.ts +84 -8
- package/src/event-writer.ts +36 -11
- package/src/label-routes.ts +9 -0
- package/src/music-hub.ts +38 -2
- package/src/nixamp-link-api.ts +15 -3
- package/src/nixamp-link-types.ts +2 -0
- package/src/nixamp-link.ts +77 -61
- package/src/oauth.ts +9 -1
- package/src/profiles.ts +0 -0
- package/src/server.ts +19 -10
- package/src/station-cli.ts +23 -0
- package/src/station-usage.ts +4 -1
- package/src/universal-footer.ts +41 -0
- package/web/dist/assets/{ar-CqVLKLkR.js → ar-C5qVRZh9.js} +1 -1
- package/web/dist/assets/{chunk-55GBNX64-BDcCEJUP.js → chunk-NW3WDGEQ-CnyjTRUh.js} +1 -1
- package/web/dist/assets/{cs-DwgnZ10A.js → cs-fajZsJrs.js} +1 -1
- package/web/dist/assets/{da-C6V-fzX4.js → da-fycV1k1N.js} +1 -1
- package/web/dist/assets/{de-CyZ00iAl.js → de-QUzTQ_9X.js} +1 -1
- package/web/dist/assets/{es-jlhhpSVu.js → es-CQu2leAJ.js} +1 -1
- package/web/dist/assets/{fi-o92ZPZq7.js → fi-AgKEsukC.js} +1 -1
- package/web/dist/assets/{fr-BcCMVWa_.js → fr-CRbHnW4y.js} +1 -1
- package/web/dist/assets/{hi-Dbgp5pgN.js → hi-BuU7xy9L.js} +1 -1
- package/web/dist/assets/{hls-OR5FY6YP-_WOdANsC.js → hls-OR5FY6YP-BxZwqFB9.js} +1 -1
- package/web/dist/assets/{hu-CRa0dBhC.js → hu-C40Z-HcJ.js} +1 -1
- package/web/dist/assets/{id-BF7Q2wE4.js → id-2b7626UL.js} +1 -1
- package/web/dist/assets/{it-z9MVvK3u.js → it-7sr3nJuS.js} +1 -1
- package/web/dist/assets/main-zuI3sOLC.js +33 -0
- package/web/dist/assets/{mpegts-CL9RC5Ls.js → mpegts-DPRtdBXL.js} +1 -1
- package/web/dist/assets/{mpegts-Y2FARLFE-C91SD8Pl.js → mpegts-Y2FARLFE-BY7parIQ.js} +1 -1
- package/web/dist/assets/{nl-C-9KQwX0.js → nl-BdDGgfzj.js} +1 -1
- package/web/dist/assets/{radio-BnQ78a-O.js → radio-CeRhNjpU.js} +1 -1
- package/web/dist/assets/{ru-D835Y1dr.js → ru-BMGI8Oa4.js} +1 -1
- package/web/dist/assets/{station-C_6L4CDU.js → station-D5tW9jq4.js} +1 -1
- package/web/dist/assets/station-D6KfgFXf.css +1 -0
- package/web/dist/assets/{sv-DLSLSdjL.js → sv-BXASdl7c.js} +1 -1
- package/web/dist/assets/{uk-DgCBRDrZ.js → uk-urnhuqtG.js} +1 -1
- package/web/dist/assets/{vi-C23GbtRZ.js → vi-BhLPAKuD.js} +1 -1
- package/web/dist/assets/{vod-CbSC5NFA.js → vod-Dt73bt8P.js} +1 -1
- package/web/dist/assets/{watch-D-ZRxfJ_.js → watch-GxgHBQcI.js} +1 -1
- package/web/dist/assets/{zh-D0dXViuL.js → zh-BaOCIB9u.js} +1 -1
- package/web/dist/index.html +58 -12
- package/web/dist/music.html +1 -1
- package/web/dist/radio.html +5 -5
- package/web/dist/station.html +4 -4
- package/web/dist/sw.js +30 -30
- package/web/dist/vod.html +4 -4
- package/web/dist/watch.html +4 -4
- package/dist/pit-footer.d.ts +0 -3
- package/dist/pit-footer.js +0 -40
- package/src/pit-footer.ts +0 -40
- package/web/dist/assets/main-Q8gHrr9u.js +0 -26
- package/web/dist/assets/station-CxJvFwzV.css +0 -1
- /package/web/dist/assets/{chunk-55GBNX64-DR1XNWCJ.css → chunk-NW3WDGEQ-DR1XNWCJ.css} +0 -0
package/dist/server.js
CHANGED
|
@@ -37,7 +37,7 @@ import { anonymousHandle, Handles } from "./handles.js";
|
|
|
37
37
|
import { OpenDirs } from "./opendirs.js";
|
|
38
38
|
import { Servers } from "./servers.js";
|
|
39
39
|
import { AccountProfiles, hostCard, PHOTO_LIMIT } from "./profiles.js";
|
|
40
|
-
import { createProfileHandlers, saveSignupProfile, validateProfile } from "@profullstack/profile";
|
|
40
|
+
import { createProfileHandlers, saveOpenProfileContact, saveSignupProfile, validateProfile } from "@profullstack/profile";
|
|
41
41
|
import { SIGNUP_PROFILE_FIELDS, signupProfiles } from "./signup-profiles.js";
|
|
42
42
|
import { NixampLinks, nixampExchange } from "./nixamp-link.js";
|
|
43
43
|
import { handleNixampLinkApi, nixampLinkPath } from "./nixamp-link-api.js";
|
|
@@ -67,7 +67,7 @@ import { handleAiRoutes, isPublicSharePath } from "./ai-routes.js";
|
|
|
67
67
|
import { AiLabel, VeoUnavailable } from "./ai-label.js";
|
|
68
68
|
import { buyLink, handleLabelRoutes, isPublicLabelPath } from "./label-routes.js";
|
|
69
69
|
import { GrantVerifier, MusicStore, PRICES } from "./music-store.js";
|
|
70
|
-
import { handleMusicRoutes, isMusicPath, MusicHub, musicPageFor, serverId } from "./music-hub.js";
|
|
70
|
+
import { handleMusicRoutes, isMusicPath, MusicHub, musicPageFor, parseMoved, serverId } from "./music-hub.js";
|
|
71
71
|
import { fileLedger, pgLedger, VeoBudgetError, VeoStudio } from "./veo-studio.js";
|
|
72
72
|
import { KnownServers } from "./known-servers.js";
|
|
73
73
|
import { handleStoreRoutes, isStorePath } from "./store-routes.js";
|
|
@@ -83,7 +83,7 @@ import { aiKey } from "./ai-keys.js";
|
|
|
83
83
|
import { STATION_APP_CSP, STATION_PAGE, STATION_PAGE_CSP, STATION_PAGE_HTML, WATCH_APP_CSP } from "./station-page.js";
|
|
84
84
|
import { handleRoomProxy, handleRooms, isRoomPath, ROOM_PAGE, roomAddress, RoomProbe, roomEntries, roomSlugOf, roomSlugs, RoomStore, watchBase } from "./guide-rooms.js";
|
|
85
85
|
import { handleVods, VodProbe, vodEntries, VodStore, vodView } from "./guide-vods.js";
|
|
86
|
-
import {
|
|
86
|
+
import { withUniversalFooter } from "./universal-footer.js";
|
|
87
87
|
import { handleFollowPage } from "./follow-page.js";
|
|
88
88
|
import { asRadioPage, buildCatalogue, isRadioPath, NAV_STYLE, RADIO_ALBUM, RADIO_ARTIST, RADIO_CSP, RADIO_STATION, RADIO_SUB, RADIO_BUY, radioBuyLink, radioBuyQuery, radioNav, radioPeers, radioRewriter, radioStations, renderAlbum, stationImage, renderArtist, renderDirectory, renderMissing, rewriteResponse, } from "./radio-pages.js";
|
|
89
89
|
import { adCsp, guideAds, guideCsp, withPageAd, guideHosts, guidePeers, peerBase, PeerGuides, renderGuide } from "./station-guide.js";
|
|
@@ -112,6 +112,7 @@ import { Durable } from "./durable.js";
|
|
|
112
112
|
import { notifyAll, resendEmail, webPush } from "./notify.js";
|
|
113
113
|
import { vapidKeysFromEnv, vapidPublicKeyResponse } from "@profullstack/notifications/server";
|
|
114
114
|
import { inviteSubject, inviteText, isEmail, isPhone, watchLink } from "./invite.js";
|
|
115
|
+
import { c0mputeKey } from "./ai-text.js";
|
|
115
116
|
import { EventWriter } from "./event-writer.js";
|
|
116
117
|
import { handleLiveApi } from "./live-api.js";
|
|
117
118
|
import { LiveEvents, eventStructuredData, isTicketed } from "./live-events.js";
|
|
@@ -4594,7 +4595,7 @@ export function createHandler(engine, options) {
|
|
|
4594
4595
|
bytes = await readBytes(request, PHOTO_LIMIT);
|
|
4595
4596
|
}
|
|
4596
4597
|
catch {
|
|
4597
|
-
json(response, 413, { error: "a photo may be up to
|
|
4598
|
+
json(response, 413, { error: "a photo may be up to 10 MB" });
|
|
4598
4599
|
return;
|
|
4599
4600
|
}
|
|
4600
4601
|
const kept = await profiles.setPhoto(who.id, bytes);
|
|
@@ -4847,6 +4848,14 @@ export function createHandler(engine, options) {
|
|
|
4847
4848
|
html(response, 401, signInFailedPage(result.error || "Could not sign in."));
|
|
4848
4849
|
return;
|
|
4849
4850
|
}
|
|
4851
|
+
if (identity.provider === "coinpay" && identity.openprofileUrl && options.signupProfiles) {
|
|
4852
|
+
try {
|
|
4853
|
+
await saveOpenProfileContact(options.signupProfiles, String(result.account.id), identity.openprofileUrl);
|
|
4854
|
+
}
|
|
4855
|
+
catch (e) {
|
|
4856
|
+
console.warn("[coinpay] OpenProfile import skipped:", e instanceof Error ? e.message : e);
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4850
4859
|
if (pending.userCode) {
|
|
4851
4860
|
// This round trip was approving a terminal. The browser is finished;
|
|
4852
4861
|
// the session belongs to whatever is polling.
|
|
@@ -6667,7 +6676,7 @@ export function createHandler(engine, options) {
|
|
|
6667
6676
|
// somewhere usable, but it says 404 so it is never indexed.
|
|
6668
6677
|
const shell = readIfPossible(join(web, "index.html"));
|
|
6669
6678
|
if (shell !== null) {
|
|
6670
|
-
html(response, 404, requestHost === "nixamp.com" ?
|
|
6679
|
+
html(response, 404, requestHost === "nixamp.com" ? withUniversalFooter(shell) : shell);
|
|
6671
6680
|
return;
|
|
6672
6681
|
}
|
|
6673
6682
|
}
|
|
@@ -6707,17 +6716,17 @@ export function createHandler(engine, options) {
|
|
|
6707
6716
|
}
|
|
6708
6717
|
const shell = subject ? readIfPossible(file) : null;
|
|
6709
6718
|
if (subject && shell !== null) {
|
|
6710
|
-
html(response, 200, requestHost === "nixamp.com" ?
|
|
6719
|
+
html(response, 200, requestHost === "nixamp.com" ? withUniversalFooter(joinDocument(shell, subject, eventSite ?? "https://nixamp.com")) : joinDocument(shell, subject, eventSite ?? "https://nixamp.com"));
|
|
6711
6720
|
return;
|
|
6712
6721
|
}
|
|
6713
6722
|
}
|
|
6714
6723
|
// nixamp.com's own pages (the app shell, the radio console) carry the
|
|
6715
|
-
//
|
|
6724
|
+
// shared Profullstack footer, appended on the way out. A local install
|
|
6716
6725
|
// serving the same files on its own host is left alone.
|
|
6717
6726
|
if (requestHost === "nixamp.com" && file.endsWith(".html") && (request.method === "GET" || request.method === "HEAD")) {
|
|
6718
6727
|
const shell = readIfPossible(file);
|
|
6719
6728
|
if (shell !== null) {
|
|
6720
|
-
const page =
|
|
6729
|
+
const page = withUniversalFooter(shell);
|
|
6721
6730
|
response.writeHead(200, {
|
|
6722
6731
|
...CORS,
|
|
6723
6732
|
"content-type": "text/html; charset=utf-8",
|
|
@@ -7748,7 +7757,7 @@ export async function serve(argv, version = "0.1.0") {
|
|
|
7748
7757
|
return [...new Set([...guidePeers(), ...listed, ...(knownServers?.list().map((one) => one.base) ?? [])])];
|
|
7749
7758
|
};
|
|
7750
7759
|
// nixamp.com/music: every server's label in one catalogue (music-hub.ts).
|
|
7751
|
-
const musicHub = options.directory ? new MusicHub({ peers: musicServers }) : undefined;
|
|
7760
|
+
const musicHub = options.directory ? new MusicHub({ peers: musicServers, moved: parseMoved(process.env["NIXAMP_MUSIC_MOVED"]) }) : undefined;
|
|
7752
7761
|
const musicStore = supportPayments && supportPayments.enabled && pool && jwtSecret ? new MusicStore({
|
|
7753
7762
|
db: pool,
|
|
7754
7763
|
support: supportPayments,
|
|
@@ -8440,7 +8449,7 @@ export async function serve(argv, version = "0.1.0") {
|
|
|
8440
8449
|
...(favorites ? { favorites } : {}),
|
|
8441
8450
|
...(settingsSync ? { settingsSync } : {}),
|
|
8442
8451
|
...(events ? { events, eventWriter: new EventWriter({
|
|
8443
|
-
openaiKey: process.env["OPENAI_API_KEY"], anthropicKey: process.env["ANTHROPIC_API_KEY"],
|
|
8452
|
+
c0mputeKey: c0mputeKey(), openaiKey: process.env["OPENAI_API_KEY"], anthropicKey: process.env["ANTHROPIC_API_KEY"],
|
|
8444
8453
|
openaiModel: process.env["NIXAMP_WRITER_OPENAI_MODEL"], claudeModel: process.env["NIXAMP_WRITER_CLAUDE_MODEL"],
|
|
8445
8454
|
}) } : {}),
|
|
8446
8455
|
...(events && follows ? { onEventUpdated: (updated, previous) => {
|
package/dist/station-cli.js
CHANGED
|
@@ -552,6 +552,30 @@ async function labelCommand(argv, call, show) {
|
|
|
552
552
|
return `Label: ${String(settings["holder"])}, ${String(settings["songsPerAlbum"])} songs of ${String(settings["songSeconds"])} s an album, every ${String(settings["everyDays"])} days, at most ${Number(settings["monthlySeconds"]) === 0 ? "unlimited" : `${String(settings["monthlySeconds"])} s`} a month${settings["enabled"] ? "" : " (off)"}.`;
|
|
553
553
|
});
|
|
554
554
|
}
|
|
555
|
+
case "adopt": {
|
|
556
|
+
// Albums already under the label's folder (copied from another server) back on the label.
|
|
557
|
+
// --keys FILE: {"artists": {"<name>": "<old key>"}, "albums": {"<artist key>/<album title>": "<old key>"}}.
|
|
558
|
+
const file = flag(argv, "--keys");
|
|
559
|
+
let keys = {};
|
|
560
|
+
if (file) {
|
|
561
|
+
try {
|
|
562
|
+
keys = JSON.parse((await import("node:fs")).readFileSync(file, "utf8"));
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
console.error(`nixamp station label adopt: could not read ${file}: ${error.message}`);
|
|
566
|
+
return 2;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
const body = { keys: keys["artists"] ?? {}, albums: keys["albums"] ?? {}, ...(has(argv, "--off") ? { enabled: false } : {}) };
|
|
570
|
+
return show(await call("POST", "/api/v1/stations/label/adopt", body), (answer) => {
|
|
571
|
+
const done = answer["adopted"];
|
|
572
|
+
const skipped = done["skipped"] ?? [];
|
|
573
|
+
return [
|
|
574
|
+
`Adopted ${String(done["artists"])} artists, ${String(done["albums"])} albums, ${String(done["songs"])} songs and ${String(done["videos"])} filmed videos; ${String(done["oldKeys"])} artists kept their old keys.`,
|
|
575
|
+
...skipped.map((one) => ` skipped ${one}`),
|
|
576
|
+
].join("\n");
|
|
577
|
+
});
|
|
578
|
+
}
|
|
555
579
|
case "run":
|
|
556
580
|
return show(await call("POST", "/api/v1/stations/label/run", {}), (body) => body["waiting"] ? `Nothing to record now: ${String(body["waiting"])}.` : "Recording whatever is due.");
|
|
557
581
|
case "resume":
|
package/dist/station-mcp.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export declare const STATION_TOOLS: ({
|
|
|
2
2
|
name: string;
|
|
3
3
|
description: string;
|
|
4
4
|
inputSchema: {
|
|
5
|
+
required?: undefined;
|
|
5
6
|
type: string;
|
|
6
7
|
properties: {
|
|
7
8
|
server: {
|
|
@@ -72,7 +73,6 @@ export declare const STATION_TOOLS: ({
|
|
|
72
73
|
id?: undefined;
|
|
73
74
|
private?: undefined;
|
|
74
75
|
};
|
|
75
|
-
required?: undefined;
|
|
76
76
|
};
|
|
77
77
|
} | {
|
|
78
78
|
name: string;
|
|
@@ -531,6 +531,7 @@ export declare const STATION_TOOLS: ({
|
|
|
531
531
|
name: string;
|
|
532
532
|
description: string;
|
|
533
533
|
inputSchema: {
|
|
534
|
+
required?: undefined;
|
|
534
535
|
type: string;
|
|
535
536
|
properties: {
|
|
536
537
|
playlist?: undefined;
|
|
@@ -615,12 +616,12 @@ export declare const STATION_TOOLS: ({
|
|
|
615
616
|
id?: undefined;
|
|
616
617
|
private?: undefined;
|
|
617
618
|
};
|
|
618
|
-
required?: undefined;
|
|
619
619
|
};
|
|
620
620
|
} | {
|
|
621
621
|
name: string;
|
|
622
622
|
description: string;
|
|
623
623
|
inputSchema: {
|
|
624
|
+
required?: undefined;
|
|
624
625
|
type: string;
|
|
625
626
|
properties: {
|
|
626
627
|
playlist?: undefined;
|
|
@@ -718,12 +719,12 @@ export declare const STATION_TOOLS: ({
|
|
|
718
719
|
id?: undefined;
|
|
719
720
|
private?: undefined;
|
|
720
721
|
};
|
|
721
|
-
required?: undefined;
|
|
722
722
|
};
|
|
723
723
|
} | {
|
|
724
724
|
name: string;
|
|
725
725
|
description: string;
|
|
726
726
|
inputSchema: {
|
|
727
|
+
required?: undefined;
|
|
727
728
|
type: string;
|
|
728
729
|
properties: {
|
|
729
730
|
playlist?: undefined;
|
|
@@ -862,12 +863,12 @@ export declare const STATION_TOOLS: ({
|
|
|
862
863
|
id?: undefined;
|
|
863
864
|
private?: undefined;
|
|
864
865
|
};
|
|
865
|
-
required?: undefined;
|
|
866
866
|
};
|
|
867
867
|
} | {
|
|
868
868
|
name: string;
|
|
869
869
|
description: string;
|
|
870
870
|
inputSchema: {
|
|
871
|
+
required?: undefined;
|
|
871
872
|
type: string;
|
|
872
873
|
properties: {
|
|
873
874
|
playlist?: undefined;
|
|
@@ -960,12 +961,12 @@ export declare const STATION_TOOLS: ({
|
|
|
960
961
|
license?: undefined;
|
|
961
962
|
private?: undefined;
|
|
962
963
|
};
|
|
963
|
-
required?: undefined;
|
|
964
964
|
};
|
|
965
965
|
} | {
|
|
966
966
|
name: string;
|
|
967
967
|
description: string;
|
|
968
968
|
inputSchema: {
|
|
969
|
+
required?: undefined;
|
|
969
970
|
type: string;
|
|
970
971
|
properties: {
|
|
971
972
|
playlist?: undefined;
|
|
@@ -1060,7 +1061,6 @@ export declare const STATION_TOOLS: ({
|
|
|
1060
1061
|
};
|
|
1061
1062
|
private?: undefined;
|
|
1062
1063
|
};
|
|
1063
|
-
required?: undefined;
|
|
1064
1064
|
};
|
|
1065
1065
|
} | {
|
|
1066
1066
|
name: string;
|
package/dist/station-usage.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
/** `nixamp station` usage, apart so `nixamp help station` does not load the client. */
|
|
2
|
-
export declare const USAGE = "nixamp station \u2014 make a collection into a station\n\n nixamp station list\n nixamp station create --name NAME (--folder PATH | --playlist ID | --items 0,1,2) [--shuffle] [--no-repeat] [--bitrate 128] [--breaks 600 (the default; 0 for none)]\n nixamp station show ID\n nixamp station status ID\n nixamp station start ID\n nixamp station stop ID\n nixamp station skip ID\n nixamp station breaks ID SECONDS a spot between entries every SECONDS (120..86400; --off for none)\n nixamp station retry ID try every left-out file again\n nixamp station publish ID --basis owned|permission|licensed|public-domain [--note TEXT] [--unlisted] [--no-list]\n nixamp station unpublish ID [--private]\n nixamp station link ID [--rotate]\n nixamp station delete ID\n nixamp station guide-host NAME [--remove]\n NAME's front page is the public station guide (e.g. a Moshpit name)\n nixamp station guide-ads [--site ID] [--slot ID] [--format banner_300x250|text_link] [--off]\n crawlproof analytics (project ID) and an ad unit (slot ID) on the guide\n nixamp station guide-peer URL [--remove]\n the guide also lists the public, listed stations of the server at URL\n nixamp station auto [--no-video] [--min 10] [--films 3] [--tv 2] [--breaks 600] [--no-start]\n a station per genre, kept in step with the library (--off stops it)\n\n AI radio (music composed for a station, a few tracks a day):\n nixamp station ai what is composing, this month's seconds, the stations\n nixamp station ai create (--preset lofi|synthwave|ambient|chiptune|punk|jazz | --name NAME --style TEXT)\n [--vocals] [--seconds 120] [--per-day 2] [--unlisted] public on the guide unless --unlisted\n nixamp station ai generate KEY compose one now\n nixamp station ai set KEY [--style TEXT] [--per-day N] [--seconds N] [--off|--on]\n nixamp station ai remove KEY [--files]\n nixamp station ai budget SECONDS seconds of music a month across AI stations (0: no cap)\n nixamp station ai resume after the provider ran out of credit\n nixamp station ai key NAME ELEVENLABS_API_KEY | GEMINI_API_KEY | OPENAI_API_KEY | ANTHROPIC_API_KEY,\n NIXAMP_OPENAI_URL | NIXAMP_OPENAI_MODEL (an OpenAI-compatible stand-in),\n NIXAMP_MUSIC_MODEL (music_v1|music_v2|music_v2_5), from stdin\n\n AI label (AI artists who release albums on their own; a station per genre; the catalogue at /label):\n nixamp station label artists, albums, this month's seconds, what it is waiting for\n nixamp station label sign --roster the roster: three artists in each of Rock, Hip-Hop, Synthwave, Country,\n Metal and Pop (nixamp station label lists their keys)\n nixamp station label sign (--preset KEY | --name NAME --genre GENRE --sound TEXT [--voice TEXT] [--bio TEXT] [--themes TEXT])\n nixamp station label album KEY plan the artist's next album now and record it\n nixamp station label set KEY [--sound TEXT] [--voice TEXT] [--bio TEXT] [--themes TEXT] [--off|--on]\n nixamp station label drop KEY [--files]\n nixamp station label config [--holder \"Profullstack, Inc.\"] [--budget SECONDS] [--every-days 30] [--seed-albums 1]\n [--songs 8] [--seconds 150] [--on|--off] [--public|--unlisted]\n [--parallel 4] [--genres \"Punk,Blues,Country\"] songs made at once; genres first\n [--per-day 1] [--new-artists 0.25] after the first sweep: albums a day, share by new artists\n [--videos|--no-videos] a music video per song and a TV station of them\n [--reset-day 21] the day the monthly budget renews (the provider's billing day)\n [--veo|--no-veo] [--veo-shots 4] filmed Veo shots of the band in each album's single (nixamp.com caps the month)\n nixamp station label run record whatever is due now\n nixamp station label resume after the provider ran out of credit\n\n Listener shares (anybody shares an MP3 link or file from the guide; AI files and checks it):\n nixamp station shares the setting and what is waiting\n nixamp station shares on [--owner-moderation] [--unlisted] [--max-mb 60] [--per-hour 6]\n nixamp station shares off\n nixamp station shares list [--state held|approved|reported|rejected]\n nixamp station shares approve|reject|delete SHARE\n nixamp station shares trust ADDRESS [--remove] believe this proxy's X-Forwarded-For\n\n Lyrics (shown on the station page from each file's own tag, else nichedb.dev; ours published back):\n nixamp station lyrics the key, what was published, which stations may publish\n nixamp station lyrics publish ID publish a station's words now (public, listed, basis owned only)\n nixamp station lyrics key nichedb's key (NIXAMP_LYRICS_KEY), from stdin (--clear forgets it)\n\n Copyright holders (the now-playing card: who holds a track's copyright, and how to support them):\n nixamp station holder list\n nixamp station holder set NAME [--artist ARTIST]... [--station ID]... [--support https://...] [--label Subscribe] [--license TEXT]\n nixamp station holder remove ID\n\n --json machine-readable output\n --url, --key another server, as with `nixamp admin`\n\nA new station is private and stopped. Starting it does not publish it.";
|
|
2
|
+
export declare const USAGE = "nixamp station \u2014 make a collection into a station\n\n nixamp station list\n nixamp station create --name NAME (--folder PATH | --playlist ID | --items 0,1,2) [--shuffle] [--no-repeat] [--bitrate 128] [--breaks 600 (the default; 0 for none)]\n nixamp station show ID\n nixamp station status ID\n nixamp station start ID\n nixamp station stop ID\n nixamp station skip ID\n nixamp station breaks ID SECONDS a spot between entries every SECONDS (120..86400; --off for none)\n nixamp station retry ID try every left-out file again\n nixamp station publish ID --basis owned|permission|licensed|public-domain [--note TEXT] [--unlisted] [--no-list]\n nixamp station unpublish ID [--private]\n nixamp station link ID [--rotate]\n nixamp station delete ID\n nixamp station guide-host NAME [--remove]\n NAME's front page is the public station guide (e.g. a Moshpit name)\n nixamp station guide-ads [--site ID] [--slot ID] [--format banner_300x250|text_link] [--off]\n crawlproof analytics (project ID) and an ad unit (slot ID) on the guide\n nixamp station guide-peer URL [--remove]\n the guide also lists the public, listed stations of the server at URL\n nixamp station auto [--no-video] [--min 10] [--films 3] [--tv 2] [--breaks 600] [--no-start]\n a station per genre, kept in step with the library (--off stops it)\n\n AI radio (music composed for a station, a few tracks a day):\n nixamp station ai what is composing, this month's seconds, the stations\n nixamp station ai create (--preset lofi|synthwave|ambient|chiptune|punk|jazz | --name NAME --style TEXT)\n [--vocals] [--seconds 120] [--per-day 2] [--unlisted] public on the guide unless --unlisted\n nixamp station ai generate KEY compose one now\n nixamp station ai set KEY [--style TEXT] [--per-day N] [--seconds N] [--off|--on]\n nixamp station ai remove KEY [--files]\n nixamp station ai budget SECONDS seconds of music a month across AI stations (0: no cap)\n nixamp station ai resume after the provider ran out of credit\n nixamp station ai key NAME ELEVENLABS_API_KEY | C0MPUTE_API_KEY | GEMINI_API_KEY | OPENAI_API_KEY | ANTHROPIC_API_KEY,\n NIXAMP_OPENAI_URL | NIXAMP_OPENAI_MODEL (an OpenAI-compatible stand-in),\n NIXAMP_MUSIC_MODEL (music_v1|music_v2|music_v2_5), from stdin\n\n AI label (AI artists who release albums on their own; a station per genre; the catalogue at /label):\n nixamp station label artists, albums, this month's seconds, what it is waiting for\n nixamp station label sign --roster the roster: three artists in each of Rock, Hip-Hop, Synthwave, Country,\n Metal and Pop (nixamp station label lists their keys)\n nixamp station label sign (--preset KEY | --name NAME --genre GENRE --sound TEXT [--voice TEXT] [--bio TEXT] [--themes TEXT])\n nixamp station label album KEY plan the artist's next album now and record it\n nixamp station label set KEY [--sound TEXT] [--voice TEXT] [--bio TEXT] [--themes TEXT] [--off|--on]\n nixamp station label drop KEY [--files]\n nixamp station label config [--holder \"Profullstack, Inc.\"] [--budget SECONDS] [--every-days 30] [--seed-albums 1]\n [--songs 8] [--seconds 150] [--on|--off] [--public|--unlisted]\n [--parallel 4] [--genres \"Punk,Blues,Country\"] songs made at once; genres first\n [--per-day 1] [--new-artists 0.25] after the first sweep: albums a day, share by new artists\n [--videos|--no-videos] a music video per song and a TV station of them\n [--reset-day 21] the day the monthly budget renews (the provider's billing day)\n [--veo|--no-veo] [--veo-shots 4] filmed Veo shots of the band in each album's single (nixamp.com caps the month)\n nixamp station label adopt [--keys FILE] [--off] albums already in the label's folder (copied from another\n server) back on the label; FILE keeps old keys: {\"artists\": {NAME: KEY},\n \"albums\": {\"ARTISTKEY/TITLE\": KEY}}\n nixamp station label run record whatever is due now\n nixamp station label resume after the provider ran out of credit\n\n Listener shares (anybody shares an MP3 link or file from the guide; AI files and checks it):\n nixamp station shares the setting and what is waiting\n nixamp station shares on [--owner-moderation] [--unlisted] [--max-mb 60] [--per-hour 6]\n nixamp station shares off\n nixamp station shares list [--state held|approved|reported|rejected]\n nixamp station shares approve|reject|delete SHARE\n nixamp station shares trust ADDRESS [--remove] believe this proxy's X-Forwarded-For\n\n Lyrics (shown on the station page from each file's own tag, else nichedb.dev; ours published back):\n nixamp station lyrics the key, what was published, which stations may publish\n nixamp station lyrics publish ID publish a station's words now (public, listed, basis owned only)\n nixamp station lyrics key nichedb's key (NIXAMP_LYRICS_KEY), from stdin (--clear forgets it)\n\n Copyright holders (the now-playing card: who holds a track's copyright, and how to support them):\n nixamp station holder list\n nixamp station holder set NAME [--artist ARTIST]... [--station ID]... [--support https://...] [--label Subscribe] [--license TEXT]\n nixamp station holder remove ID\n\n --json machine-readable output\n --url, --key another server, as with `nixamp admin`\n\nA new station is private and stopped. Starting it does not publish it.";
|
package/dist/station-usage.js
CHANGED
|
@@ -32,7 +32,7 @@ export const USAGE = `nixamp station — make a collection into a station
|
|
|
32
32
|
nixamp station ai remove KEY [--files]
|
|
33
33
|
nixamp station ai budget SECONDS seconds of music a month across AI stations (0: no cap)
|
|
34
34
|
nixamp station ai resume after the provider ran out of credit
|
|
35
|
-
nixamp station ai key NAME ELEVENLABS_API_KEY | GEMINI_API_KEY | OPENAI_API_KEY | ANTHROPIC_API_KEY,
|
|
35
|
+
nixamp station ai key NAME ELEVENLABS_API_KEY | C0MPUTE_API_KEY | GEMINI_API_KEY | OPENAI_API_KEY | ANTHROPIC_API_KEY,
|
|
36
36
|
NIXAMP_OPENAI_URL | NIXAMP_OPENAI_MODEL (an OpenAI-compatible stand-in),
|
|
37
37
|
NIXAMP_MUSIC_MODEL (music_v1|music_v2|music_v2_5), from stdin
|
|
38
38
|
|
|
@@ -51,6 +51,9 @@ export const USAGE = `nixamp station — make a collection into a station
|
|
|
51
51
|
[--videos|--no-videos] a music video per song and a TV station of them
|
|
52
52
|
[--reset-day 21] the day the monthly budget renews (the provider's billing day)
|
|
53
53
|
[--veo|--no-veo] [--veo-shots 4] filmed Veo shots of the band in each album's single (nixamp.com caps the month)
|
|
54
|
+
nixamp station label adopt [--keys FILE] [--off] albums already in the label's folder (copied from another
|
|
55
|
+
server) back on the label; FILE keeps old keys: {"artists": {NAME: KEY},
|
|
56
|
+
"albums": {"ARTISTKEY/TITLE": KEY}}
|
|
54
57
|
nixamp station label run record whatever is due now
|
|
55
58
|
nixamp station label resume after the provider ran out of credit
|
|
56
59
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { footerHtml, footerHtmlSync } from "@profullstack/footer";
|
|
2
|
+
const OPTIONS = {
|
|
3
|
+
site: "https://nixamp.com/",
|
|
4
|
+
since: 2026,
|
|
5
|
+
tagline: "MIT License",
|
|
6
|
+
links: [
|
|
7
|
+
{ label: "Code", href: "https://github.com/profullstack/nixamp" },
|
|
8
|
+
{ label: "r/nixamp", href: "https://www.reddit.com/r/nixamp" },
|
|
9
|
+
],
|
|
10
|
+
builtIndieHref: "https://built.indie.pit.world/for?site=https://nixamp.com/",
|
|
11
|
+
pwamartHref: "https://pwamart.com/for?site=https://nixamp.com/",
|
|
12
|
+
};
|
|
13
|
+
const REFRESH_MS = 15 * 60 * 1000;
|
|
14
|
+
let html = footerHtmlSync(OPTIONS);
|
|
15
|
+
let fetchedAt = 0;
|
|
16
|
+
let inflight = null;
|
|
17
|
+
function refresh() {
|
|
18
|
+
inflight ??= footerHtml(OPTIONS)
|
|
19
|
+
.then((value) => {
|
|
20
|
+
html = value;
|
|
21
|
+
fetchedAt = Date.now();
|
|
22
|
+
})
|
|
23
|
+
.catch(() => {
|
|
24
|
+
fetchedAt = Date.now() - REFRESH_MS + 60_000;
|
|
25
|
+
})
|
|
26
|
+
.finally(() => {
|
|
27
|
+
inflight = null;
|
|
28
|
+
});
|
|
29
|
+
return inflight;
|
|
30
|
+
}
|
|
31
|
+
/** Add the shared footer to Nixamp pages while avoiding a second copy in its app shell. */
|
|
32
|
+
export function withUniversalFooter(page) {
|
|
33
|
+
if (Date.now() - fetchedAt > REFRESH_MS)
|
|
34
|
+
void refresh();
|
|
35
|
+
if (page.includes("data-pfs-footer="))
|
|
36
|
+
return page;
|
|
37
|
+
const at = page.lastIndexOf("</body>");
|
|
38
|
+
return at < 0 ? page : page.slice(0, at) + html + page.slice(at);
|
|
39
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nixamp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.80.1",
|
|
4
4
|
"description": "It really whips the terminal's ass. A Winamp-shaped audio player for your terminal.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -45,15 +45,17 @@
|
|
|
45
45
|
"pack:cli": "bun scripts/pack-cli.ts"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
+
"@profullstack/ai-providers": "0.1.2",
|
|
48
49
|
"@profullstack/auth-system": "^0.8.3",
|
|
50
|
+
"@profullstack/footer": "latest",
|
|
49
51
|
"@profullstack/group-call": "^0.1.0",
|
|
50
52
|
"@profullstack/hqtui": "^0.11.1",
|
|
51
53
|
"@profullstack/live-translate": "^0.3.0",
|
|
52
54
|
"@profullstack/notifications": "^0.1.4",
|
|
53
|
-
"@profullstack/profile": "0.1.
|
|
55
|
+
"@profullstack/profile": "0.1.8",
|
|
54
56
|
"@profullstack/synconfig": "^0.1.3",
|
|
55
|
-
"@profullstack/throttle": "^0.4.
|
|
56
|
-
"@profullstack/x402-gateway": "^0.
|
|
57
|
+
"@profullstack/throttle": "^0.4.1",
|
|
58
|
+
"@profullstack/x402-gateway": "^0.16.1",
|
|
57
59
|
"acme-client": "5.4.0",
|
|
58
60
|
"pg": "^8.23.1"
|
|
59
61
|
},
|
|
@@ -86,6 +88,6 @@
|
|
|
86
88
|
"overrides": {
|
|
87
89
|
"https-proxy-agent": "7.0.6",
|
|
88
90
|
"tslib": "2.8.1",
|
|
89
|
-
"@profullstack/x402-gateway": "^0.
|
|
91
|
+
"@profullstack/x402-gateway": "^0.16.1"
|
|
90
92
|
}
|
|
91
93
|
}
|
package/src/ai-keys.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { dirname, join } from "node:path";
|
|
|
11
11
|
import { stateDir } from "./daemon.ts";
|
|
12
12
|
|
|
13
13
|
export const AI_KEY_NAMES = [
|
|
14
|
-
"ELEVENLABS_API_KEY", "GEMINI_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY",
|
|
14
|
+
"ELEVENLABS_API_KEY", "C0MPUTE_API_KEY", "GEMINI_API_KEY", "OPENAI_API_KEY", "ANTHROPIC_API_KEY",
|
|
15
15
|
// Any OpenAI-compatible endpoint in OpenAI's place (Cloudflare Workers AI, Ollama, ...) and its model.
|
|
16
16
|
"NIXAMP_OPENAI_URL", "NIXAMP_OPENAI_MODEL",
|
|
17
17
|
// The label's songwriter on that endpoint (album plans, lyrics, new artists), e.g. @cf/openai/gpt-oss-120b; NIXAMP_OPENAI_MODEL when unset.
|