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.
Files changed (132) hide show
  1. package/.env.local +2 -0
  2. package/.vercel/README.txt +11 -0
  3. package/.vercel/project.json +1 -0
  4. package/LICENSE +22 -0
  5. package/README.md +554 -0
  6. package/dist/cli.d.ts +6 -0
  7. package/dist/cli.d.ts.map +1 -0
  8. package/dist/cli.js +172 -0
  9. package/dist/cli.js.map +1 -0
  10. package/dist/generator/embedGenerator.d.ts +38 -0
  11. package/dist/generator/embedGenerator.d.ts.map +1 -0
  12. package/dist/generator/embedGenerator.js +92 -0
  13. package/dist/generator/embedGenerator.js.map +1 -0
  14. package/dist/generator/feedGenerator.d.ts +50 -0
  15. package/dist/generator/feedGenerator.d.ts.map +1 -0
  16. package/dist/generator/feedGenerator.js +167 -0
  17. package/dist/generator/feedGenerator.js.map +1 -0
  18. package/dist/generator/podcastFeedGenerator.d.ts +54 -0
  19. package/dist/generator/podcastFeedGenerator.d.ts.map +1 -0
  20. package/dist/generator/podcastFeedGenerator.js +173 -0
  21. package/dist/generator/podcastFeedGenerator.js.map +1 -0
  22. package/dist/generator/proceduralCoverGenerator.d.ts +51 -0
  23. package/dist/generator/proceduralCoverGenerator.d.ts.map +1 -0
  24. package/dist/generator/proceduralCoverGenerator.js +228 -0
  25. package/dist/generator/proceduralCoverGenerator.js.map +1 -0
  26. package/dist/generator/siteGenerator.d.ts +55 -0
  27. package/dist/generator/siteGenerator.d.ts.map +1 -0
  28. package/dist/generator/siteGenerator.js +539 -0
  29. package/dist/generator/siteGenerator.js.map +1 -0
  30. package/dist/generator/templateEngine.d.ts +13 -0
  31. package/dist/generator/templateEngine.d.ts.map +1 -0
  32. package/dist/generator/templateEngine.js +146 -0
  33. package/dist/generator/templateEngine.js.map +1 -0
  34. package/dist/index.d.ts +12 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +32 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/parser/catalogParser.d.ts +13 -0
  39. package/dist/parser/catalogParser.d.ts.map +1 -0
  40. package/dist/parser/catalogParser.js +120 -0
  41. package/dist/parser/catalogParser.js.map +1 -0
  42. package/dist/tools/generate-codes.d.ts +14 -0
  43. package/dist/tools/generate-codes.d.ts.map +1 -0
  44. package/dist/tools/generate-codes.js +274 -0
  45. package/dist/tools/generate-codes.js.map +1 -0
  46. package/dist/tools/generate-sea-pair.d.ts +14 -0
  47. package/dist/tools/generate-sea-pair.d.ts.map +1 -0
  48. package/dist/tools/generate-sea-pair.js +111 -0
  49. package/dist/tools/generate-sea-pair.js.map +1 -0
  50. package/dist/types/index.d.ts +117 -0
  51. package/dist/types/index.d.ts.map +1 -0
  52. package/dist/types/index.js +5 -0
  53. package/dist/types/index.js.map +1 -0
  54. package/dist/utils/audioUtils.d.ts +9 -0
  55. package/dist/utils/audioUtils.d.ts.map +1 -0
  56. package/dist/utils/audioUtils.js +67 -0
  57. package/dist/utils/audioUtils.js.map +1 -0
  58. package/dist/utils/configUtils.d.ts +11 -0
  59. package/dist/utils/configUtils.d.ts.map +1 -0
  60. package/dist/utils/configUtils.js +50 -0
  61. package/dist/utils/configUtils.js.map +1 -0
  62. package/dist/utils/fileUtils.d.ts +14 -0
  63. package/dist/utils/fileUtils.d.ts.map +1 -0
  64. package/dist/utils/fileUtils.js +73 -0
  65. package/dist/utils/fileUtils.js.map +1 -0
  66. package/examples/artist-free/README.md +36 -0
  67. package/examples/artist-paycurtain/README.md +49 -0
  68. package/examples/label/README.md +33 -0
  69. package/gundb-keypair.json +8 -0
  70. package/logo.svg +30 -0
  71. package/package-lock.json +1176 -0
  72. package/package.json +42 -0
  73. package/public/assets/community-registry.js +291 -0
  74. package/public/assets/download-stats.js +263 -0
  75. package/public/assets/player.js +219 -0
  76. package/public/assets/style.css +1170 -0
  77. package/public/assets/theme-widget.js +353 -0
  78. package/public/assets/unlock-codes.js +225 -0
  79. package/public/atom.xml +22 -0
  80. package/public/catalog.m3u +3 -0
  81. package/public/feed.xml +22 -0
  82. package/public/image.png +0 -0
  83. package/public/index.html +249 -0
  84. package/public/logo.svg +30 -0
  85. package/public/releases/chirichetto/Homologo - Chirichetto.wav +0 -0
  86. package/public/releases/chirichetto/cover.png +0 -0
  87. package/public/releases/chirichetto/embed-code.txt +16 -0
  88. package/public/releases/chirichetto/embed-compact.txt +8 -0
  89. package/public/releases/chirichetto/embed.html +39 -0
  90. package/public/releases/chirichetto/index.html +389 -0
  91. package/public/releases/chirichetto/playlist.m3u +3 -0
  92. package/templates/dark/assets/community-registry.js +291 -0
  93. package/templates/dark/assets/download-stats.js +263 -0
  94. package/templates/dark/assets/player.js +219 -0
  95. package/templates/dark/assets/style.css +740 -0
  96. package/templates/dark/index.hbs +73 -0
  97. package/templates/dark/layout.hbs +84 -0
  98. package/templates/dark/release.hbs +212 -0
  99. package/templates/default/assets/community-registry.js +291 -0
  100. package/templates/default/assets/download-stats.js +263 -0
  101. package/templates/default/assets/player.js +219 -0
  102. package/templates/default/assets/style.css +1170 -0
  103. package/templates/default/assets/theme-widget.js +353 -0
  104. package/templates/default/assets/unlock-codes.js +225 -0
  105. package/templates/default/index.hbs +188 -0
  106. package/templates/default/layout.hbs +117 -0
  107. package/templates/default/release.hbs +553 -0
  108. package/templates/minimal/assets/community-registry.js +291 -0
  109. package/templates/minimal/assets/download-stats.js +263 -0
  110. package/templates/minimal/assets/player.js +219 -0
  111. package/templates/minimal/assets/style.css +796 -0
  112. package/templates/minimal/index.hbs +73 -0
  113. package/templates/minimal/layout.hbs +84 -0
  114. package/templates/minimal/release.hbs +212 -0
  115. package/templates/retro/assets/community-registry.js +291 -0
  116. package/templates/retro/assets/download-stats.js +263 -0
  117. package/templates/retro/assets/player.js +219 -0
  118. package/templates/retro/assets/style.css +872 -0
  119. package/templates/retro/index.hbs +73 -0
  120. package/templates/retro/layout.hbs +84 -0
  121. package/templates/retro/release.hbs +212 -0
  122. package/templates/translucent/assets/community-registry.js +291 -0
  123. package/templates/translucent/assets/download-stats.js +263 -0
  124. package/templates/translucent/assets/player.js +219 -0
  125. package/templates/translucent/assets/style.css +1352 -0
  126. package/templates/translucent/index.hbs +73 -0
  127. package/templates/translucent/layout.hbs +84 -0
  128. package/templates/translucent/release.hbs +212 -0
  129. package/website/community.html +492 -0
  130. package/website/index.html +195 -0
  131. package/website/styles.css +396 -0
  132. 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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"}
@@ -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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"}