fluncle 0.76.0 → 0.78.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.
- package/bin/fluncle.mjs +140 -27
- package/package.json +1 -1
package/bin/fluncle.mjs
CHANGED
|
@@ -482,6 +482,20 @@ function resolveClipTracks(options) {
|
|
|
482
482
|
startMs: member.startMs
|
|
483
483
|
}));
|
|
484
484
|
}
|
|
485
|
+
function mixcloudSections(members) {
|
|
486
|
+
return members.filter((member) => member.startMs != null).sort((a, b) => a.startMs - b.startMs).map((member) => ({
|
|
487
|
+
artist: member.artists.join(", "),
|
|
488
|
+
song: member.title,
|
|
489
|
+
start_time: Math.floor(member.startMs / 1000)
|
|
490
|
+
}));
|
|
491
|
+
}
|
|
492
|
+
function mixcloudSectionFields(sections) {
|
|
493
|
+
return sections.flatMap((section, index) => [
|
|
494
|
+
[`sections-${index}-artist`, section.artist],
|
|
495
|
+
[`sections-${index}-song`, section.song],
|
|
496
|
+
[`sections-${index}-start_time`, String(section.start_time)]
|
|
497
|
+
]);
|
|
498
|
+
}
|
|
485
499
|
function baseTitleMatches(findingTitle, candidateTitle) {
|
|
486
500
|
const want = new Set(tokenize(stripVersionSuffix(findingTitle)));
|
|
487
501
|
const have = new Set(tokenize(stripVersionSuffix(candidateTitle)));
|
|
@@ -544,7 +558,7 @@ function parseVersion(version) {
|
|
|
544
558
|
var currentVersion;
|
|
545
559
|
var init_version = __esm(() => {
|
|
546
560
|
init_output();
|
|
547
|
-
currentVersion = "0.
|
|
561
|
+
currentVersion = "0.78.0".trim() ? "0.78.0".trim() : "0.1.0";
|
|
548
562
|
});
|
|
549
563
|
|
|
550
564
|
// src/update-notifier.ts
|
|
@@ -1029,6 +1043,7 @@ var init_mixtape_api = __esm(() => {
|
|
|
1029
1043
|
// src/commands/mixtape-youtube.ts
|
|
1030
1044
|
var exports_mixtape_youtube = {};
|
|
1031
1045
|
__export(exports_mixtape_youtube, {
|
|
1046
|
+
resyncYoutube: () => resyncYoutube,
|
|
1032
1047
|
publishYoutubeCommand: () => publishYoutubeCommand,
|
|
1033
1048
|
nextOffset: () => nextOffset,
|
|
1034
1049
|
distributeYoutube: () => distributeYoutube,
|
|
@@ -1084,6 +1099,10 @@ async function publishYoutubeCommand(idOrLogId) {
|
|
|
1084
1099
|
const response = await adminApiPost(`/api/admin/mixtapes/${encodeURIComponent(mixtapeId)}/youtube/publish`);
|
|
1085
1100
|
return { url: response.url };
|
|
1086
1101
|
}
|
|
1102
|
+
async function resyncYoutube(mixtapeId) {
|
|
1103
|
+
const response = await adminApiPost(`/api/admin/mixtapes/${encodeURIComponent(mixtapeId)}/youtube/resync`);
|
|
1104
|
+
return { url: response.url, videoId: response.videoId };
|
|
1105
|
+
}
|
|
1087
1106
|
async function authYoutubeCommand() {
|
|
1088
1107
|
const response = await adminApiGet("/api/admin/youtube/auth/start");
|
|
1089
1108
|
console.log(`Open this YouTube authorization URL:
|
|
@@ -1189,8 +1208,8 @@ var init_mixtape_youtube = __esm(() => {
|
|
|
1189
1208
|
// src/commands/mixtape-mixcloud.ts
|
|
1190
1209
|
var exports_mixtape_mixcloud = {};
|
|
1191
1210
|
__export(exports_mixtape_mixcloud, {
|
|
1211
|
+
resyncMixcloud: () => resyncMixcloud,
|
|
1192
1212
|
mixtapeDescription: () => mixtapeDescription,
|
|
1193
|
-
mixcloudSections: () => mixcloudSections,
|
|
1194
1213
|
distributeMixcloud: () => distributeMixcloud,
|
|
1195
1214
|
authMixcloudCommand: () => authMixcloudCommand
|
|
1196
1215
|
});
|
|
@@ -1218,10 +1237,8 @@ async function distributeMixcloud(mixtapeId, audioPath, onProgress, unlisted = f
|
|
|
1218
1237
|
form.append(`tags-${index}-tag`, tag);
|
|
1219
1238
|
}
|
|
1220
1239
|
const sections = mixcloudSections(mixtape.members);
|
|
1221
|
-
for (const [
|
|
1222
|
-
form.append(
|
|
1223
|
-
form.append(`sections-${index}-song`, section.song);
|
|
1224
|
-
form.append(`sections-${index}-start_time`, String(section.start_time));
|
|
1240
|
+
for (const [name, value] of mixcloudSectionFields(sections)) {
|
|
1241
|
+
form.append(name, value);
|
|
1225
1242
|
}
|
|
1226
1243
|
if (unlisted) {
|
|
1227
1244
|
form.append("unlisted", "1");
|
|
@@ -1253,6 +1270,10 @@ async function distributeMixcloud(mixtapeId, audioPath, onProgress, unlisted = f
|
|
|
1253
1270
|
});
|
|
1254
1271
|
return { url };
|
|
1255
1272
|
}
|
|
1273
|
+
async function resyncMixcloud(mixtapeId) {
|
|
1274
|
+
const response = await adminApiPost(`/api/admin/mixtapes/${encodeURIComponent(mixtapeId)}/mixcloud/resync`);
|
|
1275
|
+
return { url: response.url };
|
|
1276
|
+
}
|
|
1256
1277
|
async function authMixcloudCommand() {
|
|
1257
1278
|
const response = await adminApiGet("/api/admin/mixcloud/auth/start");
|
|
1258
1279
|
console.log(`Open this Mixcloud authorization URL:
|
|
@@ -1276,13 +1297,6 @@ ${breadcrumb}` : breadcrumb;
|
|
|
1276
1297
|
|
|
1277
1298
|
${breadcrumb}` : breadcrumb;
|
|
1278
1299
|
}
|
|
1279
|
-
function mixcloudSections(members) {
|
|
1280
|
-
return members.filter((member) => typeof member.startMs === "number").sort((a, b) => a.startMs - b.startMs).map((member) => ({
|
|
1281
|
-
artist: member.artists.join(", "),
|
|
1282
|
-
song: member.title,
|
|
1283
|
-
start_time: Math.floor(member.startMs / 1000)
|
|
1284
|
-
}));
|
|
1285
|
-
}
|
|
1286
1300
|
function mixtapeTags(_mixtape) {
|
|
1287
1301
|
return ["Drum & Bass", "Fluncle"];
|
|
1288
1302
|
}
|
|
@@ -1327,6 +1341,7 @@ function throwMixcloudError(status, body) {
|
|
|
1327
1341
|
}
|
|
1328
1342
|
var MIXCLOUD_API = "https://api.mixcloud.com", DESCRIPTION_MAX = 1000, PICTURE_MAX_BYTES, COVER_BASE = "https://www.fluncle.com/api/mixtape-cover";
|
|
1329
1343
|
var init_mixtape_mixcloud = __esm(() => {
|
|
1344
|
+
init_util();
|
|
1330
1345
|
init_api();
|
|
1331
1346
|
init_output();
|
|
1332
1347
|
init_mixtape_api();
|
|
@@ -1509,6 +1524,7 @@ var exports_mixtapes = {};
|
|
|
1509
1524
|
__export(exports_mixtapes, {
|
|
1510
1525
|
mixtapesCommand: () => mixtapesCommand,
|
|
1511
1526
|
mixtapeUpdateCommand: () => mixtapeUpdateCommand,
|
|
1527
|
+
mixtapeResyncCommand: () => mixtapeResyncCommand,
|
|
1512
1528
|
mixtapePublishCommand: () => mixtapePublishCommand,
|
|
1513
1529
|
mixtapeMembersCommand: () => mixtapeMembersCommand,
|
|
1514
1530
|
mixtapeListCommand: () => mixtapeListCommand,
|
|
@@ -1615,6 +1631,44 @@ async function mixtapeDistributeCommand(idOrLogId, options, onProgress = () => {
|
|
|
1615
1631
|
}
|
|
1616
1632
|
return { logId, mixtapeId, results };
|
|
1617
1633
|
}
|
|
1634
|
+
async function mixtapeResyncCommand(idOrLogId, options, onProgress = () => {}) {
|
|
1635
|
+
const mixtape = await mixtapeGetCommand(idOrLogId);
|
|
1636
|
+
if (!mixtape.id) {
|
|
1637
|
+
throw new CliError2("mixtape_not_found", `No mixtape with id or log id ${idOrLogId}`);
|
|
1638
|
+
}
|
|
1639
|
+
if (!mixtape.logId) {
|
|
1640
|
+
throw new CliError2("mixtape_no_log_id", "The mixtape isn't published yet — distribute it before re-syncing.");
|
|
1641
|
+
}
|
|
1642
|
+
const mixtapeId = mixtape.id;
|
|
1643
|
+
const explicit = Boolean(options.youtube) || Boolean(options.mixcloud);
|
|
1644
|
+
let doYoutube = Boolean(options.youtube);
|
|
1645
|
+
let doMixcloud = Boolean(options.mixcloud);
|
|
1646
|
+
if (!explicit) {
|
|
1647
|
+
const social = await adminApiGet(`/api/admin/mixtapes/${encodeURIComponent(mixtapeId)}/social`);
|
|
1648
|
+
const platforms = new Set(social.posts.map((post) => post.platform));
|
|
1649
|
+
doYoutube = platforms.has("youtube");
|
|
1650
|
+
doMixcloud = platforms.has("mixcloud");
|
|
1651
|
+
if (!doYoutube && !doMixcloud) {
|
|
1652
|
+
throw new CliError2("mixtape_not_distributed", "The mixtape has no YouTube or Mixcloud link to re-sync.");
|
|
1653
|
+
}
|
|
1654
|
+
}
|
|
1655
|
+
const results = [];
|
|
1656
|
+
if (doYoutube) {
|
|
1657
|
+
onProgress("YouTube: re-syncing description + chapters…");
|
|
1658
|
+
const { resyncYoutube: resyncYoutube2 } = await Promise.resolve().then(() => (init_mixtape_youtube(), exports_mixtape_youtube));
|
|
1659
|
+
const result = await resyncYoutube2(mixtapeId);
|
|
1660
|
+
results.push({ platform: "youtube", url: result.url });
|
|
1661
|
+
onProgress(`YouTube: ${result.url}`);
|
|
1662
|
+
}
|
|
1663
|
+
if (doMixcloud) {
|
|
1664
|
+
onProgress("Mixcloud: re-syncing sections…");
|
|
1665
|
+
const { resyncMixcloud: resyncMixcloud2 } = await Promise.resolve().then(() => (init_mixtape_mixcloud(), exports_mixtape_mixcloud));
|
|
1666
|
+
const result = await resyncMixcloud2(mixtapeId);
|
|
1667
|
+
results.push({ platform: "mixcloud", url: result.url });
|
|
1668
|
+
onProgress(`Mixcloud: ${result.url}`);
|
|
1669
|
+
}
|
|
1670
|
+
return { logId: mixtape.logId, mixtapeId, results };
|
|
1671
|
+
}
|
|
1618
1672
|
async function probeDurationMs(filePath) {
|
|
1619
1673
|
try {
|
|
1620
1674
|
const proc = Bun.spawn(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", filePath], { stderr: "ignore", stdout: "pipe" });
|
|
@@ -2401,7 +2455,7 @@ function parseVersion2(version) {
|
|
|
2401
2455
|
var currentVersion2, latestReleaseUrl = "https://api.github.com/repos/mauricekleine/fluncle/releases/latest";
|
|
2402
2456
|
var init_version2 = __esm(() => {
|
|
2403
2457
|
init_output();
|
|
2404
|
-
currentVersion2 = "0.
|
|
2458
|
+
currentVersion2 = "0.78.0".trim() ? "0.78.0".trim() : "0.1.0";
|
|
2405
2459
|
});
|
|
2406
2460
|
|
|
2407
2461
|
// ../../packages/registry/src/index.ts
|
|
@@ -3626,6 +3680,7 @@ var init_preview_archive = __esm(() => {
|
|
|
3626
3680
|
// src/commands/mixtape-youtube.ts
|
|
3627
3681
|
var exports_mixtape_youtube2 = {};
|
|
3628
3682
|
__export(exports_mixtape_youtube2, {
|
|
3683
|
+
resyncYoutube: () => resyncYoutube2,
|
|
3629
3684
|
publishYoutubeCommand: () => publishYoutubeCommand2,
|
|
3630
3685
|
nextOffset: () => nextOffset2,
|
|
3631
3686
|
distributeYoutube: () => distributeYoutube2,
|
|
@@ -3681,6 +3736,10 @@ async function publishYoutubeCommand2(idOrLogId) {
|
|
|
3681
3736
|
const response = await adminApiPost(`/api/admin/mixtapes/${encodeURIComponent(mixtapeId)}/youtube/publish`);
|
|
3682
3737
|
return { url: response.url };
|
|
3683
3738
|
}
|
|
3739
|
+
async function resyncYoutube2(mixtapeId) {
|
|
3740
|
+
const response = await adminApiPost(`/api/admin/mixtapes/${encodeURIComponent(mixtapeId)}/youtube/resync`);
|
|
3741
|
+
return { url: response.url, videoId: response.videoId };
|
|
3742
|
+
}
|
|
3684
3743
|
async function authYoutubeCommand2() {
|
|
3685
3744
|
const response = await adminApiGet("/api/admin/youtube/auth/start");
|
|
3686
3745
|
console.log(`Open this YouTube authorization URL:
|
|
@@ -3788,6 +3847,7 @@ var exports_mixtapes2 = {};
|
|
|
3788
3847
|
__export(exports_mixtapes2, {
|
|
3789
3848
|
mixtapesCommand: () => mixtapesCommand2,
|
|
3790
3849
|
mixtapeUpdateCommand: () => mixtapeUpdateCommand2,
|
|
3850
|
+
mixtapeResyncCommand: () => mixtapeResyncCommand2,
|
|
3791
3851
|
mixtapePublishCommand: () => mixtapePublishCommand2,
|
|
3792
3852
|
mixtapeMembersCommand: () => mixtapeMembersCommand2,
|
|
3793
3853
|
mixtapeListCommand: () => mixtapeListCommand,
|
|
@@ -3894,6 +3954,44 @@ async function mixtapeDistributeCommand2(idOrLogId, options, onProgress = () =>
|
|
|
3894
3954
|
}
|
|
3895
3955
|
return { logId, mixtapeId, results };
|
|
3896
3956
|
}
|
|
3957
|
+
async function mixtapeResyncCommand2(idOrLogId, options, onProgress = () => {}) {
|
|
3958
|
+
const mixtape = await mixtapeGetCommand(idOrLogId);
|
|
3959
|
+
if (!mixtape.id) {
|
|
3960
|
+
throw new CliError2("mixtape_not_found", `No mixtape with id or log id ${idOrLogId}`);
|
|
3961
|
+
}
|
|
3962
|
+
if (!mixtape.logId) {
|
|
3963
|
+
throw new CliError2("mixtape_no_log_id", "The mixtape isn't published yet — distribute it before re-syncing.");
|
|
3964
|
+
}
|
|
3965
|
+
const mixtapeId = mixtape.id;
|
|
3966
|
+
const explicit = Boolean(options.youtube) || Boolean(options.mixcloud);
|
|
3967
|
+
let doYoutube = Boolean(options.youtube);
|
|
3968
|
+
let doMixcloud = Boolean(options.mixcloud);
|
|
3969
|
+
if (!explicit) {
|
|
3970
|
+
const social = await adminApiGet(`/api/admin/mixtapes/${encodeURIComponent(mixtapeId)}/social`);
|
|
3971
|
+
const platforms = new Set(social.posts.map((post) => post.platform));
|
|
3972
|
+
doYoutube = platforms.has("youtube");
|
|
3973
|
+
doMixcloud = platforms.has("mixcloud");
|
|
3974
|
+
if (!doYoutube && !doMixcloud) {
|
|
3975
|
+
throw new CliError2("mixtape_not_distributed", "The mixtape has no YouTube or Mixcloud link to re-sync.");
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
const results = [];
|
|
3979
|
+
if (doYoutube) {
|
|
3980
|
+
onProgress("YouTube: re-syncing description + chapters…");
|
|
3981
|
+
const { resyncYoutube: resyncYoutube3 } = await Promise.resolve().then(() => (init_mixtape_youtube(), exports_mixtape_youtube));
|
|
3982
|
+
const result = await resyncYoutube3(mixtapeId);
|
|
3983
|
+
results.push({ platform: "youtube", url: result.url });
|
|
3984
|
+
onProgress(`YouTube: ${result.url}`);
|
|
3985
|
+
}
|
|
3986
|
+
if (doMixcloud) {
|
|
3987
|
+
onProgress("Mixcloud: re-syncing sections…");
|
|
3988
|
+
const { resyncMixcloud: resyncMixcloud2 } = await Promise.resolve().then(() => (init_mixtape_mixcloud(), exports_mixtape_mixcloud));
|
|
3989
|
+
const result = await resyncMixcloud2(mixtapeId);
|
|
3990
|
+
results.push({ platform: "mixcloud", url: result.url });
|
|
3991
|
+
onProgress(`Mixcloud: ${result.url}`);
|
|
3992
|
+
}
|
|
3993
|
+
return { logId: mixtape.logId, mixtapeId, results };
|
|
3994
|
+
}
|
|
3897
3995
|
async function probeDurationMs2(filePath) {
|
|
3898
3996
|
try {
|
|
3899
3997
|
const proc = Bun.spawn(["ffprobe", "-v", "error", "-show_entries", "format=duration", "-of", "csv=p=0", filePath], { stderr: "ignore", stdout: "pipe" });
|
|
@@ -4480,8 +4578,8 @@ var init_auth = __esm(() => {
|
|
|
4480
4578
|
// src/commands/mixtape-mixcloud.ts
|
|
4481
4579
|
var exports_mixtape_mixcloud2 = {};
|
|
4482
4580
|
__export(exports_mixtape_mixcloud2, {
|
|
4581
|
+
resyncMixcloud: () => resyncMixcloud2,
|
|
4483
4582
|
mixtapeDescription: () => mixtapeDescription2,
|
|
4484
|
-
mixcloudSections: () => mixcloudSections2,
|
|
4485
4583
|
distributeMixcloud: () => distributeMixcloud2,
|
|
4486
4584
|
authMixcloudCommand: () => authMixcloudCommand2
|
|
4487
4585
|
});
|
|
@@ -4508,11 +4606,9 @@ async function distributeMixcloud2(mixtapeId, audioPath, onProgress, unlisted =
|
|
|
4508
4606
|
for (const [index, tag] of mixtapeTags2(mixtape).entries()) {
|
|
4509
4607
|
form.append(`tags-${index}-tag`, tag);
|
|
4510
4608
|
}
|
|
4511
|
-
const sections =
|
|
4512
|
-
for (const [
|
|
4513
|
-
form.append(
|
|
4514
|
-
form.append(`sections-${index}-song`, section.song);
|
|
4515
|
-
form.append(`sections-${index}-start_time`, String(section.start_time));
|
|
4609
|
+
const sections = mixcloudSections(mixtape.members);
|
|
4610
|
+
for (const [name, value] of mixcloudSectionFields(sections)) {
|
|
4611
|
+
form.append(name, value);
|
|
4516
4612
|
}
|
|
4517
4613
|
if (unlisted) {
|
|
4518
4614
|
form.append("unlisted", "1");
|
|
@@ -4544,6 +4640,10 @@ async function distributeMixcloud2(mixtapeId, audioPath, onProgress, unlisted =
|
|
|
4544
4640
|
});
|
|
4545
4641
|
return { url };
|
|
4546
4642
|
}
|
|
4643
|
+
async function resyncMixcloud2(mixtapeId) {
|
|
4644
|
+
const response = await adminApiPost(`/api/admin/mixtapes/${encodeURIComponent(mixtapeId)}/mixcloud/resync`);
|
|
4645
|
+
return { url: response.url };
|
|
4646
|
+
}
|
|
4547
4647
|
async function authMixcloudCommand2() {
|
|
4548
4648
|
const response = await adminApiGet("/api/admin/mixcloud/auth/start");
|
|
4549
4649
|
console.log(`Open this Mixcloud authorization URL:
|
|
@@ -4567,13 +4667,6 @@ ${breadcrumb}` : breadcrumb;
|
|
|
4567
4667
|
|
|
4568
4668
|
${breadcrumb}` : breadcrumb;
|
|
4569
4669
|
}
|
|
4570
|
-
function mixcloudSections2(members) {
|
|
4571
|
-
return members.filter((member) => typeof member.startMs === "number").sort((a, b) => a.startMs - b.startMs).map((member) => ({
|
|
4572
|
-
artist: member.artists.join(", "),
|
|
4573
|
-
song: member.title,
|
|
4574
|
-
start_time: Math.floor(member.startMs / 1000)
|
|
4575
|
-
}));
|
|
4576
|
-
}
|
|
4577
4670
|
function mixtapeTags2(_mixtape) {
|
|
4578
4671
|
return ["Drum & Bass", "Fluncle"];
|
|
4579
4672
|
}
|
|
@@ -4618,6 +4711,7 @@ function throwMixcloudError2(status, body) {
|
|
|
4618
4711
|
}
|
|
4619
4712
|
var MIXCLOUD_API2 = "https://api.mixcloud.com", DESCRIPTION_MAX2 = 1000, PICTURE_MAX_BYTES2, COVER_BASE2 = "https://www.fluncle.com/api/mixtape-cover";
|
|
4620
4713
|
var init_mixtape_mixcloud2 = __esm(() => {
|
|
4714
|
+
init_util();
|
|
4621
4715
|
init_api();
|
|
4622
4716
|
init_output();
|
|
4623
4717
|
init_mixtape_api();
|
|
@@ -7295,6 +7389,10 @@ function addAdminCommands(program2) {
|
|
|
7295
7389
|
const { publishYoutubeCommand: publishYoutubeCommand3 } = await Promise.resolve().then(() => (init_mixtape_youtube2(), exports_mixtape_youtube2));
|
|
7296
7390
|
await runMixtapePublishYoutube(idOrLogId, options, publishYoutubeCommand3);
|
|
7297
7391
|
});
|
|
7392
|
+
adminMixtapes.command("resync").description("Re-push a published mixtape's YouTube chapters + Mixcloud sections from its current cues (no re-upload)").argument("[idOrLogId]").option("--youtube", "Only re-sync YouTube").option("--mixcloud", "Only re-sync Mixcloud").option("--json", "Print JSON", false).allowExcessArguments().action(async (idOrLogId, options) => {
|
|
7393
|
+
const { mixtapeResyncCommand: mixtapeResyncCommand3 } = await Promise.resolve().then(() => (init_mixtapes(), exports_mixtapes));
|
|
7394
|
+
await runMixtapeResync(idOrLogId, options, mixtapeResyncCommand3);
|
|
7395
|
+
});
|
|
7298
7396
|
const adminClips = configureCommand(admin.command("clips").description("Mixtape clip (Fluncle Studio) commands"));
|
|
7299
7397
|
adminClips.action(() => {
|
|
7300
7398
|
adminClips.outputHelp();
|
|
@@ -7854,6 +7952,21 @@ async function runMixtapePublishYoutube(idOrLogId, options, publishYoutubeComman
|
|
|
7854
7952
|
}
|
|
7855
7953
|
console.log(`YouTube video is now public: ${result.url}`);
|
|
7856
7954
|
}
|
|
7955
|
+
async function runMixtapeResync(idOrLogId, options, mixtapeResyncCommand3) {
|
|
7956
|
+
if (!idOrLogId) {
|
|
7957
|
+
throw new Error("Missing mixtape id. Usage: fluncle admin mixtapes resync <idOrLogId>");
|
|
7958
|
+
}
|
|
7959
|
+
const onProgress = options.json ? () => {} : (message) => console.log(message);
|
|
7960
|
+
const result = await mixtapeResyncCommand3(idOrLogId, options, onProgress);
|
|
7961
|
+
if (options.json) {
|
|
7962
|
+
printJson(result);
|
|
7963
|
+
return;
|
|
7964
|
+
}
|
|
7965
|
+
const links = result.results.map((r) => ` ${r.platform}: ${r.url}`).join(`
|
|
7966
|
+
`);
|
|
7967
|
+
console.log(`Re-synced ${result.logId} (fluncle://${result.logId}).
|
|
7968
|
+
${links}`);
|
|
7969
|
+
}
|
|
7857
7970
|
async function runMixtapeDelete(id, options, mixtapeDeleteCommand3) {
|
|
7858
7971
|
if (!id) {
|
|
7859
7972
|
throw new Error("Missing mixtape id. Usage: fluncle admin mixtapes delete <id> --yes");
|
package/package.json
CHANGED