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,14 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Tunecamp SEA Pair Generator
|
|
4
|
+
* Generates a GunDB SEA (Secure Encryption Algorithm) key pair for authentication
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx ts-node src/tools/generate-sea-pair.ts [options]
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* npx ts-node src/tools/generate-sea-pair.ts
|
|
11
|
+
* npx ts-node src/tools/generate-sea-pair.ts --output ./gundb-keypair.json
|
|
12
|
+
*/
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=generate-sea-pair.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-sea-pair.d.ts","sourceRoot":"","sources":["../../src/tools/generate-sea-pair.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Tunecamp SEA Pair Generator
|
|
4
|
+
* Generates a GunDB SEA (Secure Encryption Algorithm) key pair for authentication
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npx ts-node src/tools/generate-sea-pair.ts [options]
|
|
8
|
+
*
|
|
9
|
+
* Examples:
|
|
10
|
+
* npx ts-node src/tools/generate-sea-pair.ts
|
|
11
|
+
* npx ts-node src/tools/generate-sea-pair.ts --output ./gundb-keypair.json
|
|
12
|
+
*/
|
|
13
|
+
import Gun from 'gun';
|
|
14
|
+
import SEA from 'gun/sea.js';
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
const DEFAULT_OUTPUT_FILE = './gundb-keypair.json';
|
|
18
|
+
/**
|
|
19
|
+
* Generate SEA key pair
|
|
20
|
+
*/
|
|
21
|
+
async function generateSEAPair() {
|
|
22
|
+
// Initialize Gun to get SEA
|
|
23
|
+
const gun = Gun();
|
|
24
|
+
// Wait for SEA to be ready
|
|
25
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
26
|
+
// Generate pair
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
SEA.pair((data) => {
|
|
29
|
+
if (!data || !data.pub || !data.priv || !data.epub || !data.epriv) {
|
|
30
|
+
reject(new Error('Failed to generate SEA pair'));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
resolve(data);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Main CLI
|
|
39
|
+
*/
|
|
40
|
+
async function main() {
|
|
41
|
+
const args = process.argv.slice(2);
|
|
42
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
43
|
+
console.log(`
|
|
44
|
+
Tunecamp SEA Pair Generator
|
|
45
|
+
|
|
46
|
+
Generates a GunDB SEA (Secure Encryption Algorithm) key pair for secure authentication.
|
|
47
|
+
This pair allows you to write to your private GunDB space instead of public space.
|
|
48
|
+
|
|
49
|
+
Usage:
|
|
50
|
+
npx ts-node src/tools/generate-sea-pair.ts [options]
|
|
51
|
+
|
|
52
|
+
Options:
|
|
53
|
+
--output <file> Output file path (default: ./gundb-keypair.json)
|
|
54
|
+
--help, -h Show this help message
|
|
55
|
+
|
|
56
|
+
Examples:
|
|
57
|
+
npx ts-node src/tools/generate-sea-pair.ts
|
|
58
|
+
npx ts-node src/tools/generate-sea-pair.ts --output ./my-keypair.json
|
|
59
|
+
|
|
60
|
+
Security Notes:
|
|
61
|
+
- Keep this file secure and never commit it to version control
|
|
62
|
+
- Add gundb-keypair.json to your .gitignore
|
|
63
|
+
- This pair gives full access to your private GunDB space
|
|
64
|
+
- If compromised, generate a new pair and update your configuration
|
|
65
|
+
`);
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
const outputIndex = args.indexOf('--output');
|
|
69
|
+
const outputFile = outputIndex >= 0 && args[outputIndex + 1]
|
|
70
|
+
? args[outputIndex + 1]
|
|
71
|
+
: DEFAULT_OUTPUT_FILE;
|
|
72
|
+
try {
|
|
73
|
+
console.log('\n🔑 Tunecamp SEA Pair Generator');
|
|
74
|
+
console.log('================================\n');
|
|
75
|
+
console.log('Generating SEA key pair...');
|
|
76
|
+
const pair = await generateSEAPair();
|
|
77
|
+
// Create output object with metadata
|
|
78
|
+
const output = {
|
|
79
|
+
generated: new Date().toISOString(),
|
|
80
|
+
pub: pair.pub,
|
|
81
|
+
priv: pair.priv,
|
|
82
|
+
epub: pair.epub,
|
|
83
|
+
epriv: pair.epriv,
|
|
84
|
+
note: 'Keep this file secure! Never commit it to version control.',
|
|
85
|
+
};
|
|
86
|
+
// Ensure directory exists
|
|
87
|
+
const outputDir = path.dirname(outputFile);
|
|
88
|
+
if (outputDir !== '.' && !fs.existsSync(outputDir)) {
|
|
89
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
90
|
+
}
|
|
91
|
+
// Write to file
|
|
92
|
+
fs.writeFileSync(outputFile, JSON.stringify(output, null, 2));
|
|
93
|
+
console.log(`\n✅ SEA pair generated successfully!`);
|
|
94
|
+
console.log(`\n📁 Saved to: ${path.resolve(outputFile)}`);
|
|
95
|
+
console.log(`\n📋 Public Key (pub): ${pair.pub.substring(0, 20)}...`);
|
|
96
|
+
console.log(`\n⚠️ Security Reminders:`);
|
|
97
|
+
console.log(` - Add ${path.basename(outputFile)} to your .gitignore`);
|
|
98
|
+
console.log(` - Keep this file secure and private`);
|
|
99
|
+
console.log(` - Use this pair with generate-codes.ts for private code storage`);
|
|
100
|
+
console.log(`\n💡 Next steps:`);
|
|
101
|
+
console.log(` Use this pair with generate-codes.ts:`);
|
|
102
|
+
console.log(` npx ts-node src/tools/generate-codes.ts <release-slug> --keypair ${outputFile}`);
|
|
103
|
+
process.exit(0);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error('\n❌ Error generating SEA pair:', error.message);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
main().catch(console.error);
|
|
111
|
+
//# sourceMappingURL=generate-sea-pair.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generate-sea-pair.js","sourceRoot":"","sources":["../../src/tools/generate-sea-pair.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type definitions for Tunecamp
|
|
3
|
+
*/
|
|
4
|
+
export interface CatalogConfig {
|
|
5
|
+
title: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
url?: string;
|
|
8
|
+
basePath?: string;
|
|
9
|
+
theme?: string;
|
|
10
|
+
language?: string;
|
|
11
|
+
headerImage?: string;
|
|
12
|
+
customFont?: string;
|
|
13
|
+
customCSS?: string;
|
|
14
|
+
labelMode?: boolean;
|
|
15
|
+
podcast?: PodcastConfig;
|
|
16
|
+
metadata?: Record<string, any>;
|
|
17
|
+
}
|
|
18
|
+
export interface PodcastConfig {
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
title?: string;
|
|
21
|
+
description?: string;
|
|
22
|
+
author?: string;
|
|
23
|
+
email?: string;
|
|
24
|
+
category?: string;
|
|
25
|
+
image?: string;
|
|
26
|
+
explicit?: boolean;
|
|
27
|
+
}
|
|
28
|
+
export interface ArtistConfig {
|
|
29
|
+
name: string;
|
|
30
|
+
bio?: string;
|
|
31
|
+
photo?: string;
|
|
32
|
+
links?: ArtistLink[];
|
|
33
|
+
donationLinks?: DonationLink[];
|
|
34
|
+
slug?: string;
|
|
35
|
+
metadata?: Record<string, any>;
|
|
36
|
+
}
|
|
37
|
+
export interface DonationLink {
|
|
38
|
+
platform: string;
|
|
39
|
+
url: string;
|
|
40
|
+
description?: string;
|
|
41
|
+
}
|
|
42
|
+
export interface ArtistLink {
|
|
43
|
+
[platform: string]: string;
|
|
44
|
+
}
|
|
45
|
+
export type DownloadMode = 'free' | 'paycurtain' | 'codes' | 'none';
|
|
46
|
+
export type LicenseType = 'copyright' | 'cc-by' | 'cc-by-sa' | 'cc-by-nc' | 'cc-by-nc-sa' | 'cc-by-nc-nd' | 'cc-by-nd' | 'public-domain';
|
|
47
|
+
export interface UnlockCodesConfig {
|
|
48
|
+
enabled: boolean;
|
|
49
|
+
namespace?: string;
|
|
50
|
+
peers?: string[];
|
|
51
|
+
}
|
|
52
|
+
export interface ReleaseConfig {
|
|
53
|
+
title: string;
|
|
54
|
+
date: string;
|
|
55
|
+
description?: string;
|
|
56
|
+
cover?: string;
|
|
57
|
+
download?: DownloadMode;
|
|
58
|
+
unlockCodes?: UnlockCodesConfig;
|
|
59
|
+
price?: number;
|
|
60
|
+
paypalLink?: string;
|
|
61
|
+
stripeLink?: string;
|
|
62
|
+
license?: LicenseType;
|
|
63
|
+
genres?: string[];
|
|
64
|
+
credits?: Credit[];
|
|
65
|
+
unlisted?: boolean;
|
|
66
|
+
artistSlug?: string;
|
|
67
|
+
metadata?: Record<string, any>;
|
|
68
|
+
}
|
|
69
|
+
export interface Credit {
|
|
70
|
+
role: string;
|
|
71
|
+
name: string;
|
|
72
|
+
}
|
|
73
|
+
export interface TrackConfig {
|
|
74
|
+
file: string;
|
|
75
|
+
title?: string;
|
|
76
|
+
description?: string;
|
|
77
|
+
metadata?: Record<string, any>;
|
|
78
|
+
}
|
|
79
|
+
export interface TrackMetadata {
|
|
80
|
+
file: string;
|
|
81
|
+
filename: string;
|
|
82
|
+
title: string;
|
|
83
|
+
artist?: string;
|
|
84
|
+
album?: string;
|
|
85
|
+
year?: number;
|
|
86
|
+
track?: number;
|
|
87
|
+
duration?: number;
|
|
88
|
+
format?: string;
|
|
89
|
+
bitrate?: number;
|
|
90
|
+
sampleRate?: number;
|
|
91
|
+
description?: string;
|
|
92
|
+
genre?: string[];
|
|
93
|
+
}
|
|
94
|
+
export interface Release {
|
|
95
|
+
config: ReleaseConfig;
|
|
96
|
+
tracks: TrackMetadata[];
|
|
97
|
+
coverPath?: string;
|
|
98
|
+
path: string;
|
|
99
|
+
slug: string;
|
|
100
|
+
}
|
|
101
|
+
export interface Catalog {
|
|
102
|
+
config: CatalogConfig;
|
|
103
|
+
artist?: ArtistConfig;
|
|
104
|
+
artists?: ArtistConfig[];
|
|
105
|
+
releases: Release[];
|
|
106
|
+
}
|
|
107
|
+
export interface BuildOptions {
|
|
108
|
+
inputDir: string;
|
|
109
|
+
outputDir: string;
|
|
110
|
+
theme?: string;
|
|
111
|
+
basePath?: string;
|
|
112
|
+
verbose?: boolean;
|
|
113
|
+
}
|
|
114
|
+
export interface GeneratorOptions extends BuildOptions {
|
|
115
|
+
watch?: boolean;
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TrackMetadata } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Audio file utilities
|
|
4
|
+
*/
|
|
5
|
+
export declare function readAudioMetadata(filePath: string): Promise<TrackMetadata>;
|
|
6
|
+
export declare function formatDuration(seconds?: number): string;
|
|
7
|
+
export declare function formatFileSize(bytes?: number): string;
|
|
8
|
+
export declare function getAudioFormat(filename: string): string;
|
|
9
|
+
//# sourceMappingURL=audioUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audioUtils.d.ts","sourceRoot":"","sources":["../../src/utils/audioUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD;;GAEG;AAEH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CA4BhF;AAED,wBAAgB,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAMvD;AAED,wBAAgB,cAAc,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAarD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAavD"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { parseFile } from 'music-metadata';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
/**
|
|
4
|
+
* Audio file utilities
|
|
5
|
+
*/
|
|
6
|
+
export async function readAudioMetadata(filePath) {
|
|
7
|
+
try {
|
|
8
|
+
const metadata = await parseFile(filePath);
|
|
9
|
+
const filename = path.basename(filePath);
|
|
10
|
+
return {
|
|
11
|
+
file: filePath,
|
|
12
|
+
filename,
|
|
13
|
+
title: metadata.common.title || filename.replace(/\.[^.]+$/, ''),
|
|
14
|
+
artist: metadata.common.artist,
|
|
15
|
+
album: metadata.common.album,
|
|
16
|
+
year: metadata.common.year,
|
|
17
|
+
track: metadata.common.track.no ?? undefined,
|
|
18
|
+
duration: metadata.format.duration,
|
|
19
|
+
format: metadata.format.container,
|
|
20
|
+
bitrate: metadata.format.bitrate,
|
|
21
|
+
sampleRate: metadata.format.sampleRate,
|
|
22
|
+
genre: metadata.common.genre,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
// Fallback if metadata reading fails
|
|
27
|
+
const filename = path.basename(filePath);
|
|
28
|
+
return {
|
|
29
|
+
file: filePath,
|
|
30
|
+
filename,
|
|
31
|
+
title: filename.replace(/\.[^.]+$/, ''),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
export function formatDuration(seconds) {
|
|
36
|
+
if (!seconds)
|
|
37
|
+
return '0:00';
|
|
38
|
+
const mins = Math.floor(seconds / 60);
|
|
39
|
+
const secs = Math.floor(seconds % 60);
|
|
40
|
+
return `${mins}:${secs.toString().padStart(2, '0')}`;
|
|
41
|
+
}
|
|
42
|
+
export function formatFileSize(bytes) {
|
|
43
|
+
if (!bytes)
|
|
44
|
+
return '0 B';
|
|
45
|
+
const units = ['B', 'KB', 'MB', 'GB'];
|
|
46
|
+
let size = bytes;
|
|
47
|
+
let unitIndex = 0;
|
|
48
|
+
while (size >= 1024 && unitIndex < units.length - 1) {
|
|
49
|
+
size /= 1024;
|
|
50
|
+
unitIndex++;
|
|
51
|
+
}
|
|
52
|
+
return `${size.toFixed(1)} ${units[unitIndex]}`;
|
|
53
|
+
}
|
|
54
|
+
export function getAudioFormat(filename) {
|
|
55
|
+
const ext = path.extname(filename).toLowerCase().slice(1);
|
|
56
|
+
const formats = {
|
|
57
|
+
'mp3': 'MP3',
|
|
58
|
+
'flac': 'FLAC',
|
|
59
|
+
'ogg': 'OGG Vorbis',
|
|
60
|
+
'wav': 'WAV',
|
|
61
|
+
'm4a': 'M4A/AAC',
|
|
62
|
+
'aac': 'AAC',
|
|
63
|
+
'opus': 'OPUS',
|
|
64
|
+
};
|
|
65
|
+
return formats[ext] || ext.toUpperCase();
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=audioUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audioUtils.js","sourceRoot":"","sources":["../../src/utils/audioUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { CatalogConfig, ArtistConfig, ReleaseConfig } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration file utilities
|
|
4
|
+
*/
|
|
5
|
+
export declare function readYamlFile<T>(filePath: string): Promise<T | null>;
|
|
6
|
+
export declare function readCatalogConfig(directory: string): Promise<CatalogConfig>;
|
|
7
|
+
export declare function readArtistConfig(directory: string): Promise<ArtistConfig | null>;
|
|
8
|
+
export declare function readReleaseConfig(directory: string): Promise<ReleaseConfig | null>;
|
|
9
|
+
export declare function validateCatalogConfig(config: CatalogConfig): void;
|
|
10
|
+
export declare function validateReleaseConfig(config: ReleaseConfig): void;
|
|
11
|
+
//# sourceMappingURL=configUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configUtils.d.ts","sourceRoot":"","sources":["../../src/utils/configUtils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAG/E;;GAEG;AAEH,wBAAsB,YAAY,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAOzE;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CASjF;AAED,wBAAsB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAGtF;AAED,wBAAsB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CASxF;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAIjE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,aAAa,GAAG,IAAI,CAYjE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { parse as parseYaml } from 'yaml';
|
|
2
|
+
import { readFile, fileExists } from './fileUtils.js';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Configuration file utilities
|
|
6
|
+
*/
|
|
7
|
+
export async function readYamlFile(filePath) {
|
|
8
|
+
if (!(await fileExists(filePath))) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
const content = await readFile(filePath);
|
|
12
|
+
return parseYaml(content);
|
|
13
|
+
}
|
|
14
|
+
export async function readCatalogConfig(directory) {
|
|
15
|
+
const configPath = path.join(directory, 'catalog.yaml');
|
|
16
|
+
const config = await readYamlFile(configPath);
|
|
17
|
+
if (!config || !config.title) {
|
|
18
|
+
throw new Error(`Invalid or missing catalog.yaml in ${directory}`);
|
|
19
|
+
}
|
|
20
|
+
return config;
|
|
21
|
+
}
|
|
22
|
+
export async function readArtistConfig(directory) {
|
|
23
|
+
const configPath = path.join(directory, 'artist.yaml');
|
|
24
|
+
return await readYamlFile(configPath);
|
|
25
|
+
}
|
|
26
|
+
export async function readReleaseConfig(directory) {
|
|
27
|
+
const configPath = path.join(directory, 'release.yaml');
|
|
28
|
+
const config = await readYamlFile(configPath);
|
|
29
|
+
if (config && !config.title) {
|
|
30
|
+
throw new Error(`Release config missing title in ${directory}`);
|
|
31
|
+
}
|
|
32
|
+
return config;
|
|
33
|
+
}
|
|
34
|
+
export function validateCatalogConfig(config) {
|
|
35
|
+
if (!config.title) {
|
|
36
|
+
throw new Error('Catalog config must have a title');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function validateReleaseConfig(config) {
|
|
40
|
+
if (!config.title) {
|
|
41
|
+
throw new Error('Release config must have a title');
|
|
42
|
+
}
|
|
43
|
+
if (!config.date) {
|
|
44
|
+
throw new Error('Release config must have a date');
|
|
45
|
+
}
|
|
46
|
+
if (config.download === 'paycurtain' && !config.price) {
|
|
47
|
+
throw new Error('Release with paycurtain download mode must have a price');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=configUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configUtils.js","sourceRoot":"","sources":["../../src/utils/configUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;GAEG;AAEH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAI,QAAgB;IACpD,IAAI,CAAC,CAAC,MAAM,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACzC,OAAO,SAAS,CAAC,OAAO,CAAM,CAAC;AACjC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAiB;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAgB,UAAU,CAAC,CAAC;IAE7D,IAAI,CAAC,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,sCAAsC,SAAS,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,SAAiB;IACtD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;IACvD,OAAO,MAAM,YAAY,CAAe,UAAU,CAAC,CAAC;AACtD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,SAAiB;IACvD,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC;IACxD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAgB,UAAU,CAAC,CAAC;IAE7D,IAAI,MAAM,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,mCAAmC,SAAS,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAqB;IACzD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,MAAqB;IACzD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,kCAAkC,CAAC,CAAC;IACtD,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;IACrD,CAAC;IAED,IAAI,MAAM,CAAC,QAAQ,KAAK,YAAY,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File utility functions
|
|
3
|
+
*/
|
|
4
|
+
export declare function findAudioFiles(directory: string): Promise<string[]>;
|
|
5
|
+
export declare function findImageFiles(directory: string, name?: string): Promise<string[]>;
|
|
6
|
+
export declare function findCover(directory: string): Promise<string | undefined>;
|
|
7
|
+
export declare function ensureDir(dir: string): Promise<void>;
|
|
8
|
+
export declare function copyFile(src: string, dest: string): Promise<void>;
|
|
9
|
+
export declare function readFile(filePath: string): Promise<string>;
|
|
10
|
+
export declare function writeFile(filePath: string, content: string): Promise<void>;
|
|
11
|
+
export declare function fileExists(filePath: string): Promise<boolean>;
|
|
12
|
+
export declare function createSlug(text: string): string;
|
|
13
|
+
export declare function getRelativePath(from: string, to: string): string;
|
|
14
|
+
//# sourceMappingURL=fileUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileUtils.d.ts","sourceRoot":"","sources":["../../src/utils/fileUtils.ts"],"names":[],"mappings":"AAIA;;GAEG;AAEH,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAWzE;AAED,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAaxF;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAa9E;AAED,wBAAsB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1D;AAED,wBAAsB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGvE;AAED,wBAAsB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAEhE;AAED,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGhF;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAOnE;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAK/C;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEhE"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { glob } from 'glob';
|
|
4
|
+
/**
|
|
5
|
+
* File utility functions
|
|
6
|
+
*/
|
|
7
|
+
export async function findAudioFiles(directory) {
|
|
8
|
+
const audioExtensions = ['mp3', 'flac', 'ogg', 'wav', 'm4a', 'aac', 'opus'];
|
|
9
|
+
const pattern = `**/*.{${audioExtensions.join(',')}}`;
|
|
10
|
+
const files = await glob(pattern, {
|
|
11
|
+
cwd: directory,
|
|
12
|
+
absolute: false,
|
|
13
|
+
nodir: true,
|
|
14
|
+
});
|
|
15
|
+
return files.sort();
|
|
16
|
+
}
|
|
17
|
+
export async function findImageFiles(directory, name) {
|
|
18
|
+
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
|
19
|
+
const pattern = name
|
|
20
|
+
? `**/${name}.{${imageExtensions.join(',')}}`
|
|
21
|
+
: `**/*.{${imageExtensions.join(',')}}`;
|
|
22
|
+
const files = await glob(pattern, {
|
|
23
|
+
cwd: directory,
|
|
24
|
+
absolute: false,
|
|
25
|
+
nodir: true,
|
|
26
|
+
});
|
|
27
|
+
return files;
|
|
28
|
+
}
|
|
29
|
+
export async function findCover(directory) {
|
|
30
|
+
const coverNames = ['cover', 'artwork', 'folder', 'album'];
|
|
31
|
+
for (const name of coverNames) {
|
|
32
|
+
const covers = await findImageFiles(directory, name);
|
|
33
|
+
if (covers.length > 0) {
|
|
34
|
+
return covers[0];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Fallback to any image in the directory
|
|
38
|
+
const images = await findImageFiles(directory);
|
|
39
|
+
return images[0];
|
|
40
|
+
}
|
|
41
|
+
export async function ensureDir(dir) {
|
|
42
|
+
await fs.ensureDir(dir);
|
|
43
|
+
}
|
|
44
|
+
export async function copyFile(src, dest) {
|
|
45
|
+
await fs.ensureDir(path.dirname(dest));
|
|
46
|
+
await fs.copy(src, dest);
|
|
47
|
+
}
|
|
48
|
+
export async function readFile(filePath) {
|
|
49
|
+
return await fs.readFile(filePath, 'utf-8');
|
|
50
|
+
}
|
|
51
|
+
export async function writeFile(filePath, content) {
|
|
52
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
53
|
+
await fs.writeFile(filePath, content, 'utf-8');
|
|
54
|
+
}
|
|
55
|
+
export async function fileExists(filePath) {
|
|
56
|
+
try {
|
|
57
|
+
await fs.access(filePath);
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function createSlug(text) {
|
|
65
|
+
return text
|
|
66
|
+
.toLowerCase()
|
|
67
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
68
|
+
.replace(/^-|-$/g, '');
|
|
69
|
+
}
|
|
70
|
+
export function getRelativePath(from, to) {
|
|
71
|
+
return path.relative(from, to).replace(/\\/g, '/');
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=fileUtils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fileUtils.js","sourceRoot":"","sources":["../../src/utils/fileUtils.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Example: Artist with Free Downloads
|
|
2
|
+
|
|
3
|
+
This example shows a simple artist catalog with free downloads.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
artist-free/
|
|
9
|
+
├── catalog.yaml # Main catalog config
|
|
10
|
+
├── artist.yaml # Artist information
|
|
11
|
+
└── releases/
|
|
12
|
+
└── debut-album/
|
|
13
|
+
├── release.yaml # Release config
|
|
14
|
+
├── cover.jpg # Album artwork (add your own)
|
|
15
|
+
└── tracks/
|
|
16
|
+
├── 01-track-one.mp3
|
|
17
|
+
├── 02-track-two.mp3
|
|
18
|
+
└── 03-track-three.mp3
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Features
|
|
22
|
+
|
|
23
|
+
- Free downloads for all tracks
|
|
24
|
+
- Artist bio and social links
|
|
25
|
+
- Album metadata and credits
|
|
26
|
+
- Genre tags
|
|
27
|
+
|
|
28
|
+
## Building
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
tunecamp build ./examples/artist-free -o ./output
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Notes
|
|
35
|
+
|
|
36
|
+
This example doesn't include actual audio files. Add your own MP3/FLAC files to the `tracks/` directory and cover art as `cover.jpg` and header image.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Artist Paycurtain Example
|
|
2
|
+
|
|
3
|
+
This example demonstrates how to use TuneCamp with the honor system payment model.
|
|
4
|
+
|
|
5
|
+
## Features Demonstrated
|
|
6
|
+
|
|
7
|
+
- **Paycurtain Mode**: Honor system where users can download for free but are encouraged to support the artist
|
|
8
|
+
- **Payment Links**: PayPal and Stripe integration
|
|
9
|
+
- **License System**: Creative Commons licensing
|
|
10
|
+
- **Donation Links**: Multiple donation platforms for artist support
|
|
11
|
+
|
|
12
|
+
## Configuration
|
|
13
|
+
|
|
14
|
+
### Catalog Configuration
|
|
15
|
+
- Basic catalog setup with theme selection
|
|
16
|
+
|
|
17
|
+
### Artist Configuration
|
|
18
|
+
- Artist bio and social links
|
|
19
|
+
- Multiple donation platforms (PayPal, Ko-fi, Patreon)
|
|
20
|
+
|
|
21
|
+
### Release Configuration
|
|
22
|
+
- Paycurtain mode with suggested price
|
|
23
|
+
- Payment links for PayPal and Stripe
|
|
24
|
+
- Creative Commons license (CC BY-NC)
|
|
25
|
+
- Genre and credit information
|
|
26
|
+
|
|
27
|
+
## How It Works
|
|
28
|
+
|
|
29
|
+
1. **Honor System**: Users can download tracks for free, but the interface encourages support
|
|
30
|
+
2. **Payment Links**: Direct links to PayPal and Stripe for easy payment
|
|
31
|
+
3. **License Display**: Clear licensing information for each release
|
|
32
|
+
4. **Donation Section**: Additional ways to support the artist
|
|
33
|
+
|
|
34
|
+
## Building the Example
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Build the example
|
|
38
|
+
tunecamp build ./examples/artist-paycurtain --output ./output
|
|
39
|
+
|
|
40
|
+
# Serve locally
|
|
41
|
+
tunecamp serve ./output --port 3000
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Key Points
|
|
45
|
+
|
|
46
|
+
- All files are technically downloadable (static site limitation)
|
|
47
|
+
- The paycurtain is more about encouraging support than restricting access
|
|
48
|
+
- Multiple payment options give users flexibility
|
|
49
|
+
- Clear licensing helps with legal compliance
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Example: Music Label
|
|
2
|
+
|
|
3
|
+
This example shows a label catalog with multiple artists.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
label/
|
|
9
|
+
├── catalog.yaml # Label info
|
|
10
|
+
└── releases/
|
|
11
|
+
├── artist-one-album/
|
|
12
|
+
│ └── release.yaml
|
|
13
|
+
└── artist-two-ep/
|
|
14
|
+
└── release.yaml
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
- Label-level configuration
|
|
20
|
+
- Multiple artists
|
|
21
|
+
- Mixed download modes
|
|
22
|
+
- Various genres
|
|
23
|
+
|
|
24
|
+
## Building
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
tunecamp build ./examples/label -o ./output
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Notes
|
|
31
|
+
|
|
32
|
+
For a label catalog, you don't need an `artist.yaml` at the root. Instead, each release can specify its own artist in the track metadata or release config.
|
|
33
|
+
|