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,188 @@
1
+ <div class="container">
2
+ {{#if artist}}
3
+ <section class="artist-section">
4
+ <div class="artist-header">
5
+ {{#if artist.photo}}
6
+ <img src="{{path artist.photo}}" alt="{{artist.name}}" class="artist-photo">
7
+ {{/if}}
8
+ <div class="artist-info">
9
+ <h2>{{artist.name}}</h2>
10
+ {{#if artist.bio}}
11
+ <p class="artist-bio">{{artist.bio}}</p>
12
+ {{/if}}
13
+ </div>
14
+ </div>
15
+ </section>
16
+ {{/if}}
17
+
18
+ <section class="releases-section">
19
+ <h2>Releases</h2>
20
+
21
+ <!-- Search and Filter Controls -->
22
+ <div class="releases-controls">
23
+ <div class="search-box">
24
+ <i class="fas fa-search"></i>
25
+ <input type="text" id="searchInput" placeholder="Search releases..." autocomplete="off">
26
+ </div>
27
+
28
+ <div class="filter-controls">
29
+ <select id="genreFilter" class="filter-select">
30
+ <option value="">All Genres</option>
31
+ </select>
32
+
33
+ <select id="sortBy" class="filter-select">
34
+ <option value="date-desc">Newest First</option>
35
+ <option value="date-asc">Oldest First</option>
36
+ <option value="title-asc">Title A-Z</option>
37
+ <option value="title-desc">Title Z-A</option>
38
+ </select>
39
+ </div>
40
+ </div>
41
+
42
+ {{#if releases}}
43
+ <div class="releases-grid" id="releasesGrid">
44
+ {{#each releases}}
45
+ <article class="release-card"
46
+ data-title="{{config.title}}"
47
+ data-date="{{config.date}}"
48
+ data-genres="{{#if config.genres}}{{join config.genres ","}}{{/if}}"
49
+ data-description="{{config.description}}">
50
+ <a href="{{path url}}" class="release-link">
51
+ {{#if coverUrl}}
52
+ <div class="release-cover">
53
+ <img src="{{path coverUrl}}" alt="{{config.title}}" loading="lazy">
54
+ </div>
55
+ {{else}}
56
+ <div class="release-cover release-cover-placeholder">
57
+ <i class="fas fa-music"></i>
58
+ </div>
59
+ {{/if}}
60
+
61
+ <div class="release-info">
62
+ <h3 class="release-title">{{config.title}}</h3>
63
+ <p class="release-date">{{formatDate config.date}}</p>
64
+
65
+ {{#if config.genres}}
66
+ <div class="release-genres">
67
+ {{#each config.genres}}
68
+ <span class="genre-tag">{{this}}</span>
69
+ {{/each}}
70
+ </div>
71
+ {{/if}}
72
+
73
+ <p class="release-tracks-count">
74
+ {{tracks.length}} track{{#unless (eq tracks.length 1)}}s{{/unless}}
75
+ </p>
76
+
77
+ {{#if config.download}}
78
+ <div class="release-download-badge">
79
+ {{#if (eq config.download "free")}}
80
+ <i class="fas fa-download"></i> Free Download
81
+ {{else if (eq config.download "paycurtain")}}
82
+ <i class="fas fa-dollar-sign"></i> Pay What You Want
83
+ {{else if (eq config.download "codes")}}
84
+ <i class="fas fa-key"></i> Download Codes
85
+ {{/if}}
86
+ </div>
87
+ {{/if}}
88
+ </div>
89
+ </a>
90
+ </article>
91
+ {{/each}}
92
+ </div>
93
+
94
+ <p class="no-results" id="noResults" style="display: none;">No releases match your search.</p>
95
+ {{else}}
96
+ <p class="no-releases">No releases yet. Check back soon!</p>
97
+ {{/if}}
98
+ </section>
99
+ </div>
100
+
101
+ <script>
102
+ document.addEventListener('DOMContentLoaded', function() {
103
+ const searchInput = document.getElementById('searchInput');
104
+ const genreFilter = document.getElementById('genreFilter');
105
+ const sortBy = document.getElementById('sortBy');
106
+ const releasesGrid = document.getElementById('releasesGrid');
107
+ const noResults = document.getElementById('noResults');
108
+
109
+ if (!releasesGrid) return;
110
+
111
+ const releaseCards = Array.from(releasesGrid.querySelectorAll('.release-card'));
112
+
113
+ // Populate genre filter
114
+ const allGenres = new Set();
115
+ releaseCards.forEach(card => {
116
+ const genres = card.dataset.genres;
117
+ if (genres) {
118
+ genres.split(',').forEach(g => allGenres.add(g.trim()));
119
+ }
120
+ });
121
+
122
+ Array.from(allGenres).sort().forEach(genre => {
123
+ const option = document.createElement('option');
124
+ option.value = genre;
125
+ option.textContent = genre;
126
+ genreFilter.appendChild(option);
127
+ });
128
+
129
+ function filterAndSort() {
130
+ const searchTerm = searchInput.value.toLowerCase();
131
+ const selectedGenre = genreFilter.value;
132
+ const sortValue = sortBy.value;
133
+
134
+ // Filter
135
+ let visibleCards = releaseCards.filter(card => {
136
+ const title = (card.dataset.title || '').toLowerCase();
137
+ const description = (card.dataset.description || '').toLowerCase();
138
+ const genres = card.dataset.genres || '';
139
+
140
+ const matchesSearch = !searchTerm ||
141
+ title.includes(searchTerm) ||
142
+ description.includes(searchTerm);
143
+
144
+ const matchesGenre = !selectedGenre ||
145
+ genres.split(',').map(g => g.trim()).includes(selectedGenre);
146
+
147
+ return matchesSearch && matchesGenre;
148
+ });
149
+
150
+ // Sort
151
+ visibleCards.sort((a, b) => {
152
+ switch (sortValue) {
153
+ case 'date-desc':
154
+ return new Date(b.dataset.date) - new Date(a.dataset.date);
155
+ case 'date-asc':
156
+ return new Date(a.dataset.date) - new Date(b.dataset.date);
157
+ case 'title-asc':
158
+ return (a.dataset.title || '').localeCompare(b.dataset.title || '');
159
+ case 'title-desc':
160
+ return (b.dataset.title || '').localeCompare(a.dataset.title || '');
161
+ default:
162
+ return 0;
163
+ }
164
+ });
165
+
166
+ // Apply visibility and order
167
+ releaseCards.forEach(card => {
168
+ card.style.display = 'none';
169
+ card.style.order = '';
170
+ });
171
+
172
+ visibleCards.forEach((card, index) => {
173
+ card.style.display = '';
174
+ card.style.order = index;
175
+ });
176
+
177
+ // Show/hide no results message
178
+ if (noResults) {
179
+ noResults.style.display = visibleCards.length === 0 ? 'block' : 'none';
180
+ }
181
+ }
182
+
183
+ searchInput.addEventListener('input', filterAndSort);
184
+ genreFilter.addEventListener('change', filterAndSort);
185
+ sortBy.addEventListener('change', filterAndSort);
186
+ });
187
+ </script>
188
+
@@ -0,0 +1,117 @@
1
+ <!DOCTYPE html>
2
+ <html lang="{{catalog.language}}" data-theme="dark">
3
+
4
+ <head>
5
+ <meta charset="UTF-8">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>{{#if pageTitle}}{{pageTitle}} - {{/if}}{{catalog.title}}</title>
8
+ {{#if catalog.description}}
9
+ <meta name="description" content="{{catalog.description}}">
10
+ {{/if}}
11
+ <!-- Tunecamp Metadata (for community registry) -->
12
+ <meta name="generator" content="Tunecamp 0.1.0">
13
+ <meta name="tunecamp-title" content="{{catalog.title}}">
14
+ {{#if artist}}
15
+ <meta name="tunecamp-artist" content="{{artist.name}}">
16
+ {{/if}}
17
+ <!-- RSS/Atom Feeds -->
18
+ <link rel="alternate" type="application/rss+xml" title="{{catalog.title}} RSS Feed" href="{{assetPath 'feed.xml'}}">
19
+ <link rel="alternate" type="application/atom+xml" title="{{catalog.title}} Atom Feed" href="{{assetPath 'atom.xml'}}">
20
+ {{#if catalog.customFontUrl}}
21
+ <link rel="stylesheet" href="{{catalog.customFontUrl}}">
22
+ {{/if}}
23
+ <link rel="stylesheet" href="{{assetPath "assets/style.css"}}">
24
+ {{#if catalog.customFontFamily}}
25
+ <style>
26
+ :root {
27
+ --custom-font-family: '{{catalog.customFontFamily}}', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif;
28
+ }
29
+ body {
30
+ font-family: var(--custom-font-family);
31
+ }
32
+ </style>
33
+ {{/if}}
34
+ {{#if catalog.customCSSUrl}}
35
+ {{#if (or (startsWith catalog.customCSSUrl "http://") (startsWith catalog.customCSSUrl "https://"))}}
36
+ <link rel="stylesheet" href="{{catalog.customCSSUrl}}">
37
+ {{else}}
38
+ <link rel="stylesheet" href="{{path catalog.customCSSUrl}}">
39
+ {{/if}}
40
+ {{/if}}
41
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
42
+ <script>
43
+ // Initialize theme from localStorage or system preference
44
+ (function () {
45
+ const saved = localStorage.getItem('theme');
46
+ if (saved) {
47
+ document.documentElement.setAttribute('data-theme', saved);
48
+ } else if (window.matchMedia('(prefers-color-scheme: light)').matches) {
49
+ document.documentElement.setAttribute('data-theme', 'light');
50
+ }
51
+ })();
52
+ </script>
53
+ </head>
54
+
55
+ <body>
56
+ <header class="site-header">
57
+ <div class="container">
58
+ {{#if catalog.headerImageUrl}}
59
+ <div class="header-image-wrapper">
60
+ <div class="site-header-image">
61
+ <a href="{{#if backUrl}}{{path backUrl}}{{else}}{{path "index.html"}}{{/if}}">
62
+ <img src="{{path catalog.headerImageUrl}}" alt="{{catalog.title}}" class="header-image">
63
+ </a>
64
+ </div>
65
+ </div>
66
+ {{else}}
67
+ <div class="header-row">
68
+ <h1 class="site-title">
69
+ <a href="{{#if backUrl}}{{path backUrl}}{{else}}{{path "index.html"}}{{/if}}">{{catalog.title}}</a>
70
+ </h1>
71
+ </div>
72
+ {{/if}}
73
+ {{#if catalog.description}}
74
+ {{#unless catalog.headerImageUrl}}
75
+ <p class="site-description">{{catalog.description}}</p>
76
+ {{/unless}}
77
+ {{/if}}
78
+ </div>
79
+ </header>
80
+
81
+ <main class="site-main">
82
+ {{{content}}}
83
+ </main>
84
+
85
+ <footer class="site-footer">
86
+ <div class="container">
87
+ <p>
88
+ {{#if artist}}
89
+ &copy; {{artist.name}} -
90
+ {{/if}}
91
+ Powered by <a href="https://github.com/scobru/tunecamp" target="_blank">Tunecamp</a>
92
+ </p>
93
+ {{#if artist.links}}
94
+ <div class="social-links">
95
+ {{#each artist.links}}
96
+ {{#each this}}
97
+ <a href="{{this}}" target="_blank" rel="noopener" class="social-link">
98
+ <i class="fab fa-{{@key}}"></i>
99
+ </a>
100
+ {{/each}}
101
+ {{/each}}
102
+ </div>
103
+ {{/if}}
104
+ </div>
105
+ </footer>
106
+
107
+ <script src="{{assetPath "assets/player.js"}}"></script>
108
+
109
+ <!-- Interactive Theme Widget -->
110
+ <script src="{{assetPath "assets/theme-widget.js"}}"></script>
111
+
112
+ <!-- Tunecamp Community Registry (auto-registers site) -->
113
+ <script src="https://cdn.jsdelivr.net/npm/gun/gun.js"></script>
114
+ <script src="{{assetPath "assets/community-registry.js"}}"></script>
115
+ </body>
116
+
117
+ </html>