tunecamp 1.0.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/.env.local +2 -0
- package/.vercel/README.txt +11 -0
- package/.vercel/project.json +1 -0
- package/LICENSE +22 -0
- package/README.md +554 -0
- package/dist/cli.d.ts +6 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +172 -0
- package/dist/cli.js.map +1 -0
- package/dist/generator/embedGenerator.d.ts +38 -0
- package/dist/generator/embedGenerator.d.ts.map +1 -0
- package/dist/generator/embedGenerator.js +92 -0
- package/dist/generator/embedGenerator.js.map +1 -0
- package/dist/generator/feedGenerator.d.ts +50 -0
- package/dist/generator/feedGenerator.d.ts.map +1 -0
- package/dist/generator/feedGenerator.js +167 -0
- package/dist/generator/feedGenerator.js.map +1 -0
- package/dist/generator/podcastFeedGenerator.d.ts +54 -0
- package/dist/generator/podcastFeedGenerator.d.ts.map +1 -0
- package/dist/generator/podcastFeedGenerator.js +173 -0
- package/dist/generator/podcastFeedGenerator.js.map +1 -0
- package/dist/generator/proceduralCoverGenerator.d.ts +51 -0
- package/dist/generator/proceduralCoverGenerator.d.ts.map +1 -0
- package/dist/generator/proceduralCoverGenerator.js +228 -0
- package/dist/generator/proceduralCoverGenerator.js.map +1 -0
- package/dist/generator/siteGenerator.d.ts +55 -0
- package/dist/generator/siteGenerator.d.ts.map +1 -0
- package/dist/generator/siteGenerator.js +539 -0
- package/dist/generator/siteGenerator.js.map +1 -0
- package/dist/generator/templateEngine.d.ts +13 -0
- package/dist/generator/templateEngine.d.ts.map +1 -0
- package/dist/generator/templateEngine.js +146 -0
- package/dist/generator/templateEngine.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/index.js.map +1 -0
- package/dist/parser/catalogParser.d.ts +13 -0
- package/dist/parser/catalogParser.d.ts.map +1 -0
- package/dist/parser/catalogParser.js +120 -0
- package/dist/parser/catalogParser.js.map +1 -0
- package/dist/tools/generate-codes.d.ts +14 -0
- package/dist/tools/generate-codes.d.ts.map +1 -0
- package/dist/tools/generate-codes.js +274 -0
- package/dist/tools/generate-codes.js.map +1 -0
- package/dist/tools/generate-sea-pair.d.ts +14 -0
- package/dist/tools/generate-sea-pair.d.ts.map +1 -0
- package/dist/tools/generate-sea-pair.js +111 -0
- package/dist/tools/generate-sea-pair.js.map +1 -0
- package/dist/types/index.d.ts +117 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/audioUtils.d.ts +9 -0
- package/dist/utils/audioUtils.d.ts.map +1 -0
- package/dist/utils/audioUtils.js +67 -0
- package/dist/utils/audioUtils.js.map +1 -0
- package/dist/utils/configUtils.d.ts +11 -0
- package/dist/utils/configUtils.d.ts.map +1 -0
- package/dist/utils/configUtils.js +50 -0
- package/dist/utils/configUtils.js.map +1 -0
- package/dist/utils/fileUtils.d.ts +14 -0
- package/dist/utils/fileUtils.d.ts.map +1 -0
- package/dist/utils/fileUtils.js +73 -0
- package/dist/utils/fileUtils.js.map +1 -0
- package/examples/artist-free/README.md +36 -0
- package/examples/artist-paycurtain/README.md +49 -0
- package/examples/label/README.md +33 -0
- package/gundb-keypair.json +8 -0
- package/logo.svg +30 -0
- package/package-lock.json +1176 -0
- package/package.json +42 -0
- package/public/assets/community-registry.js +291 -0
- package/public/assets/download-stats.js +263 -0
- package/public/assets/player.js +219 -0
- package/public/assets/style.css +1170 -0
- package/public/assets/theme-widget.js +353 -0
- package/public/assets/unlock-codes.js +225 -0
- package/public/atom.xml +22 -0
- package/public/catalog.m3u +3 -0
- package/public/feed.xml +22 -0
- package/public/image.png +0 -0
- package/public/index.html +249 -0
- package/public/logo.svg +30 -0
- package/public/releases/chirichetto/Homologo - Chirichetto.wav +0 -0
- package/public/releases/chirichetto/cover.png +0 -0
- package/public/releases/chirichetto/embed-code.txt +16 -0
- package/public/releases/chirichetto/embed-compact.txt +8 -0
- package/public/releases/chirichetto/embed.html +39 -0
- package/public/releases/chirichetto/index.html +389 -0
- package/public/releases/chirichetto/playlist.m3u +3 -0
- package/templates/dark/assets/community-registry.js +291 -0
- package/templates/dark/assets/download-stats.js +263 -0
- package/templates/dark/assets/player.js +219 -0
- package/templates/dark/assets/style.css +740 -0
- package/templates/dark/index.hbs +73 -0
- package/templates/dark/layout.hbs +84 -0
- package/templates/dark/release.hbs +212 -0
- package/templates/default/assets/community-registry.js +291 -0
- package/templates/default/assets/download-stats.js +263 -0
- package/templates/default/assets/player.js +219 -0
- package/templates/default/assets/style.css +1170 -0
- package/templates/default/assets/theme-widget.js +353 -0
- package/templates/default/assets/unlock-codes.js +225 -0
- package/templates/default/index.hbs +188 -0
- package/templates/default/layout.hbs +117 -0
- package/templates/default/release.hbs +553 -0
- package/templates/minimal/assets/community-registry.js +291 -0
- package/templates/minimal/assets/download-stats.js +263 -0
- package/templates/minimal/assets/player.js +219 -0
- package/templates/minimal/assets/style.css +796 -0
- package/templates/minimal/index.hbs +73 -0
- package/templates/minimal/layout.hbs +84 -0
- package/templates/minimal/release.hbs +212 -0
- package/templates/retro/assets/community-registry.js +291 -0
- package/templates/retro/assets/download-stats.js +263 -0
- package/templates/retro/assets/player.js +219 -0
- package/templates/retro/assets/style.css +872 -0
- package/templates/retro/index.hbs +73 -0
- package/templates/retro/layout.hbs +84 -0
- package/templates/retro/release.hbs +212 -0
- package/templates/translucent/assets/community-registry.js +291 -0
- package/templates/translucent/assets/download-stats.js +263 -0
- package/templates/translucent/assets/player.js +219 -0
- package/templates/translucent/assets/style.css +1352 -0
- package/templates/translucent/index.hbs +73 -0
- package/templates/translucent/layout.hbs +84 -0
- package/templates/translucent/release.hbs +212 -0
- package/website/community.html +492 -0
- package/website/index.html +195 -0
- package/website/styles.css +396 -0
- package/website/tunecamp.svg +30 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import Handlebars from "handlebars";
|
|
2
|
+
import { readFile } from "../utils/fileUtils.js";
|
|
3
|
+
import { formatDuration, getAudioFormat } from "../utils/audioUtils.js";
|
|
4
|
+
/**
|
|
5
|
+
* Handlebars template engine with helpers
|
|
6
|
+
*/
|
|
7
|
+
export class TemplateEngine {
|
|
8
|
+
templates = new Map();
|
|
9
|
+
constructor() {
|
|
10
|
+
this.registerHelpers();
|
|
11
|
+
}
|
|
12
|
+
registerHelpers() {
|
|
13
|
+
// Format duration helper
|
|
14
|
+
Handlebars.registerHelper("formatDuration", (seconds) => {
|
|
15
|
+
return formatDuration(seconds);
|
|
16
|
+
});
|
|
17
|
+
// Format audio format helper
|
|
18
|
+
Handlebars.registerHelper("formatAudioFormat", (filename) => {
|
|
19
|
+
return getAudioFormat(filename);
|
|
20
|
+
});
|
|
21
|
+
// Format date helper
|
|
22
|
+
Handlebars.registerHelper("formatDate", (dateString) => {
|
|
23
|
+
const date = new Date(dateString);
|
|
24
|
+
return date.toLocaleDateString("en-US", {
|
|
25
|
+
year: "numeric",
|
|
26
|
+
month: "long",
|
|
27
|
+
day: "numeric",
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
// Conditional helper
|
|
31
|
+
Handlebars.registerHelper("eq", (a, b) => {
|
|
32
|
+
return a === b;
|
|
33
|
+
});
|
|
34
|
+
// Array join helper
|
|
35
|
+
Handlebars.registerHelper("join", (array, separator) => {
|
|
36
|
+
return array ? array.join(separator) : "";
|
|
37
|
+
});
|
|
38
|
+
// String startsWith helper
|
|
39
|
+
Handlebars.registerHelper("startsWith", (str, prefix) => {
|
|
40
|
+
return str && typeof str === 'string' && str.startsWith(prefix);
|
|
41
|
+
});
|
|
42
|
+
// Logical OR helper
|
|
43
|
+
Handlebars.registerHelper("or", (a, b) => {
|
|
44
|
+
return a || b;
|
|
45
|
+
});
|
|
46
|
+
// Path helper - prepends basePath to URLs
|
|
47
|
+
Handlebars.registerHelper("path", function (url) {
|
|
48
|
+
const basePath = this.basePath || "";
|
|
49
|
+
// If url is already relative (starts with ./ or ../), return as-is
|
|
50
|
+
if (url.startsWith("./") || url.startsWith("../")) {
|
|
51
|
+
return url;
|
|
52
|
+
}
|
|
53
|
+
// If url is already absolute (starts with /), apply basePath
|
|
54
|
+
if (url.startsWith("/")) {
|
|
55
|
+
if (!basePath || basePath === "/") {
|
|
56
|
+
return url;
|
|
57
|
+
}
|
|
58
|
+
const cleanBasePath = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
59
|
+
return cleanBasePath + url;
|
|
60
|
+
}
|
|
61
|
+
// For relative URLs without ./ prefix, make them relative to current directory
|
|
62
|
+
if (!url.startsWith("/")) {
|
|
63
|
+
return "./" + url;
|
|
64
|
+
}
|
|
65
|
+
return url;
|
|
66
|
+
});
|
|
67
|
+
// Release path helper - for release pages, handles relative paths correctly
|
|
68
|
+
Handlebars.registerHelper("releasePath", function (url) {
|
|
69
|
+
// For release pages, always use relative paths
|
|
70
|
+
if (url.startsWith("/")) {
|
|
71
|
+
// Convert absolute path to relative
|
|
72
|
+
return "." + url;
|
|
73
|
+
}
|
|
74
|
+
return url;
|
|
75
|
+
});
|
|
76
|
+
// Asset path helper - for CSS, JS, and other assets
|
|
77
|
+
Handlebars.registerHelper("assetPath", function (url) {
|
|
78
|
+
const basePath = this.basePath || "";
|
|
79
|
+
// If url is already relative (starts with ./ or ../), return as-is
|
|
80
|
+
if (url.startsWith("./") || url.startsWith("../")) {
|
|
81
|
+
return url;
|
|
82
|
+
}
|
|
83
|
+
// Check if we're in a release page (has backUrl context)
|
|
84
|
+
const isReleasePage = this.backUrl !== undefined;
|
|
85
|
+
if (isReleasePage) {
|
|
86
|
+
// For release pages, we need to go up to the root to access assets
|
|
87
|
+
if (url.startsWith("/")) {
|
|
88
|
+
if (!basePath || basePath === "/") {
|
|
89
|
+
return "../../" + url.substring(1);
|
|
90
|
+
}
|
|
91
|
+
const cleanBasePath = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
92
|
+
return "../../" + cleanBasePath + url;
|
|
93
|
+
}
|
|
94
|
+
// For relative URLs without ./ prefix in release pages
|
|
95
|
+
if (!url.startsWith("/")) {
|
|
96
|
+
return "../../" + url;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
// For main pages, use normal path helper logic
|
|
101
|
+
if (url.startsWith("/")) {
|
|
102
|
+
if (!basePath || basePath === "/") {
|
|
103
|
+
return url;
|
|
104
|
+
}
|
|
105
|
+
const cleanBasePath = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
|
|
106
|
+
return cleanBasePath + url;
|
|
107
|
+
}
|
|
108
|
+
// For relative URLs without ./ prefix in main pages
|
|
109
|
+
if (!url.startsWith("/")) {
|
|
110
|
+
return "./" + url;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return url;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
async loadTemplate(templatePath, name) {
|
|
117
|
+
const content = await readFile(templatePath);
|
|
118
|
+
const template = Handlebars.compile(content);
|
|
119
|
+
this.templates.set(name, template);
|
|
120
|
+
}
|
|
121
|
+
render(templateName, data) {
|
|
122
|
+
const template = this.templates.get(templateName);
|
|
123
|
+
if (!template) {
|
|
124
|
+
throw new Error(`Template ${templateName} not found`);
|
|
125
|
+
}
|
|
126
|
+
return template(data);
|
|
127
|
+
}
|
|
128
|
+
renderWithLayout(templateName, data, pageTitle) {
|
|
129
|
+
// First render the content template
|
|
130
|
+
const content = this.render(templateName, data);
|
|
131
|
+
// Then wrap it in the layout
|
|
132
|
+
if (this.hasTemplate("layout")) {
|
|
133
|
+
return this.render("layout", {
|
|
134
|
+
...data,
|
|
135
|
+
content,
|
|
136
|
+
pageTitle,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// If no layout, return content as-is
|
|
140
|
+
return content;
|
|
141
|
+
}
|
|
142
|
+
hasTemplate(templateName) {
|
|
143
|
+
return this.templates.has(templateName);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=templateEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templateEngine.js","sourceRoot":"","sources":["../../src/generator/templateEngine.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tunecamp - Static Site Generator for Musicians
|
|
3
|
+
* Main entry point for programmatic usage
|
|
4
|
+
*/
|
|
5
|
+
import { BuildOptions } from './types/index.js';
|
|
6
|
+
export declare class Tunecamp {
|
|
7
|
+
private options;
|
|
8
|
+
constructor(options: BuildOptions);
|
|
9
|
+
build(): Promise<void>;
|
|
10
|
+
}
|
|
11
|
+
export * from './types/index.js';
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAEhD,qBAAa,QAAQ;IACnB,OAAO,CAAC,OAAO,CAAe;gBAElB,OAAO,EAAE,YAAY;IAI3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAmB7B;AAGD,cAAc,kBAAkB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tunecamp - Static Site Generator for Musicians
|
|
3
|
+
* Main entry point for programmatic usage
|
|
4
|
+
*/
|
|
5
|
+
import { CatalogParser } from './parser/catalogParser.js';
|
|
6
|
+
import { SiteGenerator } from './generator/siteGenerator.js';
|
|
7
|
+
export class Tunecamp {
|
|
8
|
+
options;
|
|
9
|
+
constructor(options) {
|
|
10
|
+
this.options = options;
|
|
11
|
+
}
|
|
12
|
+
async build() {
|
|
13
|
+
console.log('šµ Tunecamp - Static Site Generator');
|
|
14
|
+
console.log('====================================\n');
|
|
15
|
+
try {
|
|
16
|
+
// Parse catalog
|
|
17
|
+
const parser = new CatalogParser(this.options.inputDir);
|
|
18
|
+
const catalog = await parser.parse();
|
|
19
|
+
// Generate site
|
|
20
|
+
const generator = new SiteGenerator(catalog, this.options);
|
|
21
|
+
await generator.generate();
|
|
22
|
+
console.log('\nš Build complete!');
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.error('\nā Build failed:', error);
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Export types
|
|
31
|
+
export * from './types/index.js';
|
|
32
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC1D,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAG7D,MAAM,OAAO,QAAQ;IACX,OAAO,CAAe;IAE9B,YAAY,OAAqB;QAC/B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;IACzB,CAAC;IAED,KAAK,CAAC,KAAK;QACT,OAAO,CAAC,GAAG,CAAC,qCAAqC,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;QAEtD,IAAI,CAAC;YACH,gBAAgB;YAChB,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YACxD,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YAErC,gBAAgB;YAChB,MAAM,SAAS,GAAG,IAAI,aAAa,CAAC,OAAO,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;YAC3D,MAAM,SAAS,CAAC,QAAQ,EAAE,CAAC;YAE3B,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;YAC1C,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAED,eAAe;AACf,cAAc,kBAAkB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Catalog } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Parses catalog directory and extracts all metadata
|
|
4
|
+
*/
|
|
5
|
+
export declare class CatalogParser {
|
|
6
|
+
private inputDir;
|
|
7
|
+
constructor(inputDir: string);
|
|
8
|
+
parse(): Promise<Catalog>;
|
|
9
|
+
private findReleases;
|
|
10
|
+
private parseRelease;
|
|
11
|
+
private findTracks;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=catalogParser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalogParser.d.ts","sourceRoot":"","sources":["../../src/parser/catalogParser.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAyC,MAAM,mBAAmB,CAAC;AASnF;;GAEG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,EAAE,MAAM;IAItB,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC;YAmBjB,YAAY;YAoCZ,YAAY;YA8CZ,UAAU;CA+BzB"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import { readCatalogConfig, readArtistConfig, readReleaseConfig, } from '../utils/configUtils.js';
|
|
4
|
+
import { findAudioFiles, findCover, createSlug } from '../utils/fileUtils.js';
|
|
5
|
+
import { readAudioMetadata } from '../utils/audioUtils.js';
|
|
6
|
+
/**
|
|
7
|
+
* Parses catalog directory and extracts all metadata
|
|
8
|
+
*/
|
|
9
|
+
export class CatalogParser {
|
|
10
|
+
inputDir;
|
|
11
|
+
constructor(inputDir) {
|
|
12
|
+
this.inputDir = inputDir;
|
|
13
|
+
}
|
|
14
|
+
async parse() {
|
|
15
|
+
console.log('š Parsing catalog...');
|
|
16
|
+
// Read main configs
|
|
17
|
+
const catalogConfig = await readCatalogConfig(this.inputDir);
|
|
18
|
+
const artistConfig = await readArtistConfig(this.inputDir);
|
|
19
|
+
// Find all releases
|
|
20
|
+
const releases = await this.findReleases();
|
|
21
|
+
console.log(`ā
Found ${releases.length} release(s)`);
|
|
22
|
+
return {
|
|
23
|
+
config: catalogConfig,
|
|
24
|
+
artist: artistConfig || undefined,
|
|
25
|
+
releases,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
async findReleases() {
|
|
29
|
+
const releases = [];
|
|
30
|
+
const releasesDir = path.join(this.inputDir, 'releases');
|
|
31
|
+
if (!(await fs.pathExists(releasesDir))) {
|
|
32
|
+
console.warn('ā ļø No releases directory found');
|
|
33
|
+
return releases;
|
|
34
|
+
}
|
|
35
|
+
const entries = await fs.readdir(releasesDir, { withFileTypes: true });
|
|
36
|
+
for (const entry of entries) {
|
|
37
|
+
if (entry.isDirectory()) {
|
|
38
|
+
const releaseDir = path.join(releasesDir, entry.name);
|
|
39
|
+
try {
|
|
40
|
+
const release = await this.parseRelease(releaseDir, entry.name);
|
|
41
|
+
if (release) {
|
|
42
|
+
releases.push(release);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.error(`ā Error parsing release ${entry.name}:`, error);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Sort by date (newest first)
|
|
51
|
+
releases.sort((a, b) => {
|
|
52
|
+
const dateA = new Date(a.config.date).getTime();
|
|
53
|
+
const dateB = new Date(b.config.date).getTime();
|
|
54
|
+
return dateB - dateA;
|
|
55
|
+
});
|
|
56
|
+
return releases;
|
|
57
|
+
}
|
|
58
|
+
async parseRelease(releaseDir, dirName) {
|
|
59
|
+
console.log(` š Parsing release: ${dirName}`);
|
|
60
|
+
// Read release config
|
|
61
|
+
let releaseConfig = await readReleaseConfig(releaseDir);
|
|
62
|
+
// If no config, try to use directory name as title
|
|
63
|
+
if (!releaseConfig) {
|
|
64
|
+
const tracksDir = path.join(releaseDir, 'tracks');
|
|
65
|
+
const hasAudio = (await findAudioFiles(releaseDir)).length > 0 ||
|
|
66
|
+
(await fs.pathExists(tracksDir) && (await findAudioFiles(tracksDir)).length > 0);
|
|
67
|
+
if (!hasAudio) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
releaseConfig = {
|
|
71
|
+
title: dirName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
|
|
72
|
+
date: new Date().toISOString().split('T')[0],
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Find tracks
|
|
76
|
+
const tracks = await this.findTracks(releaseDir);
|
|
77
|
+
if (tracks.length === 0) {
|
|
78
|
+
console.warn(` ā ļø No tracks found in ${dirName}`);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
// Find cover
|
|
82
|
+
const coverPath = await findCover(releaseDir);
|
|
83
|
+
const release = {
|
|
84
|
+
config: releaseConfig,
|
|
85
|
+
tracks,
|
|
86
|
+
coverPath,
|
|
87
|
+
path: releaseDir,
|
|
88
|
+
slug: createSlug(releaseConfig.title),
|
|
89
|
+
};
|
|
90
|
+
console.log(` ā
${tracks.length} track(s) found`);
|
|
91
|
+
return release;
|
|
92
|
+
}
|
|
93
|
+
async findTracks(releaseDir) {
|
|
94
|
+
const tracks = [];
|
|
95
|
+
// Check both root and tracks/ subdirectory
|
|
96
|
+
const searchDirs = [
|
|
97
|
+
releaseDir,
|
|
98
|
+
path.join(releaseDir, 'tracks'),
|
|
99
|
+
];
|
|
100
|
+
for (const dir of searchDirs) {
|
|
101
|
+
if (await fs.pathExists(dir)) {
|
|
102
|
+
const audioFiles = await findAudioFiles(dir);
|
|
103
|
+
for (const audioFile of audioFiles) {
|
|
104
|
+
const fullPath = path.join(dir, audioFile);
|
|
105
|
+
const metadata = await readAudioMetadata(fullPath);
|
|
106
|
+
tracks.push(metadata);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Sort by track number or filename
|
|
111
|
+
tracks.sort((a, b) => {
|
|
112
|
+
if (a.track && b.track) {
|
|
113
|
+
return a.track - b.track;
|
|
114
|
+
}
|
|
115
|
+
return a.filename.localeCompare(b.filename);
|
|
116
|
+
});
|
|
117
|
+
return tracks;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=catalogParser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalogParser.js","sourceRoot":"","sources":["../../src/parser/catalogParser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Tunecamp Unlock Codes Generator
|
|
4
|
+
* CLI tool to generate and manage unlock codes for releases
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx ts-node src/tools/generate-codes.ts <release-slug> [options]
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* npx ts-node src/tools/generate-codes.ts my-album --count 10
|
|
11
|
+
* npx ts-node src/tools/generate-codes.ts my-album --count 50 --downloads 3
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=generate-codes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-codes.d.ts","sourceRoot":"","sources":["../../src/tools/generate-codes.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Tunecamp Unlock Codes Generator
|
|
4
|
+
* CLI tool to generate and manage unlock codes for releases
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx ts-node src/tools/generate-codes.ts <release-slug> [options]
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* npx ts-node src/tools/generate-codes.ts my-album --count 10
|
|
11
|
+
* npx ts-node src/tools/generate-codes.ts my-album --count 50 --downloads 3
|
|
12
|
+
*/
|
|
13
|
+
import Gun from 'gun';
|
|
14
|
+
import crypto from 'crypto';
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
// Default public GunDB peers
|
|
17
|
+
const DEFAULT_PEERS = [
|
|
18
|
+
'https://gun.defucc.me/gun',
|
|
19
|
+
'https://a.talkflow.team/gun',
|
|
20
|
+
'https://peer.wallie.io/gun',
|
|
21
|
+
'https://shogun-relay.scobrudot.dev/gun',
|
|
22
|
+
];
|
|
23
|
+
/**
|
|
24
|
+
* Generate a random unlock code
|
|
25
|
+
*/
|
|
26
|
+
function generateCode() {
|
|
27
|
+
// Format: XXXX-XXXX-XXXX (alphanumeric, no ambiguous chars)
|
|
28
|
+
const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // No 0, O, 1, I
|
|
29
|
+
const segments = 3;
|
|
30
|
+
const segmentLength = 4;
|
|
31
|
+
const code = [];
|
|
32
|
+
for (let s = 0; s < segments; s++) {
|
|
33
|
+
let segment = '';
|
|
34
|
+
for (let i = 0; i < segmentLength; i++) {
|
|
35
|
+
segment += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
36
|
+
}
|
|
37
|
+
code.push(segment);
|
|
38
|
+
}
|
|
39
|
+
return code.join('-');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Hash a code for storage
|
|
43
|
+
*/
|
|
44
|
+
function hashCode(code) {
|
|
45
|
+
return crypto
|
|
46
|
+
.createHash('sha256')
|
|
47
|
+
.update(code.toLowerCase().trim())
|
|
48
|
+
.digest('hex');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Load SEA key pair from file
|
|
52
|
+
*/
|
|
53
|
+
function loadKeyPair(keypairPath) {
|
|
54
|
+
try {
|
|
55
|
+
const fileContent = fs.readFileSync(keypairPath, 'utf-8');
|
|
56
|
+
const parsed = JSON.parse(fileContent);
|
|
57
|
+
if (!parsed.pub || !parsed.priv || !parsed.epub || !parsed.epriv) {
|
|
58
|
+
throw new Error('Invalid keypair file: missing required keys');
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
pub: parsed.pub,
|
|
62
|
+
priv: parsed.priv,
|
|
63
|
+
epub: parsed.epub,
|
|
64
|
+
epriv: parsed.epriv,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
throw new Error(`Failed to load keypair from ${keypairPath}: ${error.message}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Authenticate with GunDB using SEA pair
|
|
73
|
+
*/
|
|
74
|
+
async function authenticateGunDB(gun, pair) {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
const user = gun.user();
|
|
77
|
+
// Try to authenticate
|
|
78
|
+
user.auth(pair, (ack) => {
|
|
79
|
+
if (ack.err) {
|
|
80
|
+
// If authentication fails, try to create user first
|
|
81
|
+
user.create(pair, (createAck) => {
|
|
82
|
+
if (createAck.err && createAck.err !== 'User already created!') {
|
|
83
|
+
reject(new Error(`Failed to create/authenticate user: ${createAck.err}`));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
// Now try to authenticate again
|
|
87
|
+
user.auth(pair, (authAck) => {
|
|
88
|
+
if (authAck.err) {
|
|
89
|
+
reject(new Error(`Failed to authenticate: ${authAck.err}`));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
resolve();
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
resolve();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Generate codes and store in GunDB
|
|
104
|
+
*/
|
|
105
|
+
async function generateCodes(options) {
|
|
106
|
+
const { releaseSlug, count, maxDownloads, expiresInDays, namespace, peers, keypair } = options;
|
|
107
|
+
console.log(`\nš Tunecamp Unlock Codes Generator`);
|
|
108
|
+
console.log(`================================`);
|
|
109
|
+
console.log(`Release: ${releaseSlug}`);
|
|
110
|
+
console.log(`Count: ${count}`);
|
|
111
|
+
console.log(`Max downloads per code: ${maxDownloads}`);
|
|
112
|
+
if (expiresInDays)
|
|
113
|
+
console.log(`Expires in: ${expiresInDays} days`);
|
|
114
|
+
if (keypair) {
|
|
115
|
+
console.log(`š Using authenticated private space`);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log(`ā ļø Using public space (consider using --keypair for private storage)`);
|
|
119
|
+
}
|
|
120
|
+
console.log(`\nConnecting to GunDB peers...`);
|
|
121
|
+
// Initialize Gun
|
|
122
|
+
const gun = Gun({ peers });
|
|
123
|
+
// Wait for connection
|
|
124
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
125
|
+
// Authenticate if keypair is provided
|
|
126
|
+
if (keypair) {
|
|
127
|
+
console.log(`\nAuthenticating with SEA pair...`);
|
|
128
|
+
try {
|
|
129
|
+
await authenticateGunDB(gun, keypair);
|
|
130
|
+
console.log(`ā
Authenticated successfully`);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
console.error(`ā Authentication failed: ${error.message}`);
|
|
134
|
+
throw error;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
const codes = [];
|
|
138
|
+
const expiresAt = expiresInDays
|
|
139
|
+
? Date.now() + (expiresInDays * 24 * 60 * 60 * 1000)
|
|
140
|
+
: null;
|
|
141
|
+
console.log(`\nGenerating ${count} codes...`);
|
|
142
|
+
// Use user().get() for private space, or get() for public space
|
|
143
|
+
const dbRoot = keypair ? gun.user() : gun;
|
|
144
|
+
for (let i = 0; i < count; i++) {
|
|
145
|
+
const code = generateCode();
|
|
146
|
+
const codeHash = hashCode(code);
|
|
147
|
+
// Store in GunDB (private space if authenticated, public otherwise)
|
|
148
|
+
dbRoot
|
|
149
|
+
.get(namespace)
|
|
150
|
+
.get('releases')
|
|
151
|
+
.get(releaseSlug)
|
|
152
|
+
.get('codes')
|
|
153
|
+
.get(codeHash)
|
|
154
|
+
.put({
|
|
155
|
+
createdAt: Date.now(),
|
|
156
|
+
used: false,
|
|
157
|
+
downloads: 0,
|
|
158
|
+
maxDownloads,
|
|
159
|
+
expiresAt,
|
|
160
|
+
});
|
|
161
|
+
codes.push(code);
|
|
162
|
+
process.stdout.write(`\r Progress: ${i + 1}/${count}`);
|
|
163
|
+
}
|
|
164
|
+
// Wait for sync
|
|
165
|
+
console.log(`\n\nSyncing to peers...`);
|
|
166
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
167
|
+
return codes;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Main CLI
|
|
171
|
+
*/
|
|
172
|
+
async function main() {
|
|
173
|
+
const args = process.argv.slice(2);
|
|
174
|
+
if (args.length === 0 || args.includes('--help')) {
|
|
175
|
+
console.log(`
|
|
176
|
+
Tunecamp Unlock Codes Generator
|
|
177
|
+
|
|
178
|
+
Usage:
|
|
179
|
+
npx ts-node src/tools/generate-codes.ts <release-slug> [options]
|
|
180
|
+
|
|
181
|
+
Options:
|
|
182
|
+
--count <n> Number of codes to generate (default: 10)
|
|
183
|
+
--downloads <n> Max downloads per code (default: 1)
|
|
184
|
+
--expires <days> Days until codes expire (optional)
|
|
185
|
+
--namespace <ns> GunDB namespace (default: tunecamp)
|
|
186
|
+
--keypair <file> Path to SEA keypair JSON file (for private storage)
|
|
187
|
+
--output <file> Save codes to file (optional)
|
|
188
|
+
--help Show this help
|
|
189
|
+
|
|
190
|
+
Examples:
|
|
191
|
+
npx ts-node src/tools/generate-codes.ts my-album --count 10
|
|
192
|
+
npx ts-node src/tools/generate-codes.ts my-album --count 50 --downloads 3 --output codes.txt
|
|
193
|
+
npx ts-node src/tools/generate-codes.ts my-album --count 20 --keypair ./gundb-keypair.json
|
|
194
|
+
`);
|
|
195
|
+
process.exit(0);
|
|
196
|
+
}
|
|
197
|
+
const releaseSlug = args[0];
|
|
198
|
+
const count = parseInt(args[args.indexOf('--count') + 1] || '10');
|
|
199
|
+
const maxDownloads = parseInt(args[args.indexOf('--downloads') + 1] || '1');
|
|
200
|
+
const expiresInDays = args.includes('--expires')
|
|
201
|
+
? parseInt(args[args.indexOf('--expires') + 1])
|
|
202
|
+
: undefined;
|
|
203
|
+
const namespace = args[args.indexOf('--namespace') + 1] || 'tunecamp';
|
|
204
|
+
const keypairPath = args.includes('--keypair')
|
|
205
|
+
? args[args.indexOf('--keypair') + 1]
|
|
206
|
+
: null;
|
|
207
|
+
const outputFile = args.includes('--output')
|
|
208
|
+
? args[args.indexOf('--output') + 1]
|
|
209
|
+
: null;
|
|
210
|
+
// Load keypair if provided
|
|
211
|
+
let keypair;
|
|
212
|
+
if (keypairPath) {
|
|
213
|
+
try {
|
|
214
|
+
keypair = loadKeyPair(keypairPath);
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
console.error(`\nā Error loading keypair: ${error.message}`);
|
|
218
|
+
console.error(`\nš” Generate a new keypair with:`);
|
|
219
|
+
console.error(` npx ts-node src/tools/generate-sea-pair.ts`);
|
|
220
|
+
process.exit(1);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
try {
|
|
224
|
+
const codes = await generateCodes({
|
|
225
|
+
releaseSlug,
|
|
226
|
+
count,
|
|
227
|
+
maxDownloads,
|
|
228
|
+
expiresInDays,
|
|
229
|
+
namespace,
|
|
230
|
+
peers: DEFAULT_PEERS,
|
|
231
|
+
keypair,
|
|
232
|
+
});
|
|
233
|
+
console.log(`\nā
Generated ${codes.length} codes:\n`);
|
|
234
|
+
console.log(`${'ā'.repeat(50)}`);
|
|
235
|
+
codes.forEach((code, i) => {
|
|
236
|
+
console.log(` ${(i + 1).toString().padStart(3)}. ${code}`);
|
|
237
|
+
});
|
|
238
|
+
console.log(`${'ā'.repeat(50)}`);
|
|
239
|
+
// Save to file if requested
|
|
240
|
+
if (outputFile) {
|
|
241
|
+
const fs = await import('fs');
|
|
242
|
+
const content = [
|
|
243
|
+
`# Unlock Codes for: ${releaseSlug}`,
|
|
244
|
+
`# Generated: ${new Date().toISOString()}`,
|
|
245
|
+
`# Max downloads per code: ${maxDownloads}`,
|
|
246
|
+
expiresInDays ? `# Expires in: ${expiresInDays} days` : '',
|
|
247
|
+
'',
|
|
248
|
+
...codes,
|
|
249
|
+
].filter(Boolean).join('\n');
|
|
250
|
+
fs.writeFileSync(outputFile, content);
|
|
251
|
+
console.log(`\nš Codes saved to: ${outputFile}`);
|
|
252
|
+
}
|
|
253
|
+
console.log(`\nš Instructions for your release.yaml:`);
|
|
254
|
+
console.log(`\n download: codes`);
|
|
255
|
+
console.log(` unlockCodes:`);
|
|
256
|
+
console.log(` enabled: true`);
|
|
257
|
+
console.log(` namespace: ${namespace}`);
|
|
258
|
+
if (keypair) {
|
|
259
|
+
console.log(`\nš Codes stored in your private GunDB space`);
|
|
260
|
+
console.log(` Only you can access and manage these codes`);
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
console.log(`\nā ļø Codes stored in public space`);
|
|
264
|
+
console.log(` Consider using --keypair for private storage`);
|
|
265
|
+
}
|
|
266
|
+
process.exit(0);
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
console.error('\nā Error:', error);
|
|
270
|
+
process.exit(1);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
main().catch(console.error);
|
|
274
|
+
//# sourceMappingURL=generate-codes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-codes.js","sourceRoot":"","sources":["../../src/tools/generate-codes.ts"],"names":[],"mappings":"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"}
|