diff options
Diffstat (limited to 'src/content/dependencies')
-rw-r--r-- | src/content/dependencies/generateAlbumSocialEmbed.js | 71 | ||||
-rw-r--r-- | src/content/dependencies/generateAlbumSocialEmbedDescription.js | 48 | ||||
-rw-r--r-- | src/content/dependencies/generateAlbumStylesheet.js | 59 | ||||
-rw-r--r-- | src/content/dependencies/index.js | 108 |
4 files changed, 286 insertions, 0 deletions
diff --git a/src/content/dependencies/generateAlbumSocialEmbed.js b/src/content/dependencies/generateAlbumSocialEmbed.js new file mode 100644 index 00000000..699b3d26 --- /dev/null +++ b/src/content/dependencies/generateAlbumSocialEmbed.js @@ -0,0 +1,71 @@ +export default { + contentDependencies: [ + 'generateSocialEmbedDescription', + ], + + extraDependencies: [ + 'absoluteTo', + 'language', + 'to', + 'urls', + ], + + data(album, { + generateSocialEmbedDescription, + }) { + const data = {}; + + data.descriptionData = generateSocialEmbedDescription.data(album); + + data.hasHeading = !empty(album.groups); + + if (data.hasHeading) { + const firstGroup = album.groups[0]; + data.headingGroupName = firstGroup.directory; + data.headingGroupDirectory = firstGroup.directory; + } + + data.albumName = album.name; + data.albumColor = album.color; + + return data; + }, + + generate(data, { + generateSocialEmbedDescription, + + absoluteTo, + language, + to, + urls, + }) { + const socialEmbed = {}; + + if (data.hasHeading) { + socialEmbed.heading = + language.$('albumPage.socialEmbed.heading', { + group: data.headingGroupName, + }); + + socialEmbed.headingLink = + absoluteTo('localized.album', data.headingGroupDirectory); + } else { + socialEmbed.heading = ''; + socialEmbed.headingLink = null; + } + + socialEmbed.title = + language.$('albumPage.socialEmbed.title', { + album: data.albumName, + }); + + socialEmbed.description = generateSocialEmbedDescription(data.descriptionData); + + socialEmbed.image = + '/' + getAlbumCover(album, {to: urls.from('shared.root').to}); + + socialEmbed.color = data.albumColor; + + return socialEmbed; + }, +}; diff --git a/src/content/dependencies/generateAlbumSocialEmbedDescription.js b/src/content/dependencies/generateAlbumSocialEmbedDescription.js new file mode 100644 index 00000000..2bb62596 --- /dev/null +++ b/src/content/dependencies/generateAlbumSocialEmbedDescription.js @@ -0,0 +1,48 @@ +export default { + extraDependencies: ['language'], + + data(album) { + const data = {}; + + const duration = getTotalDuration(album); + + data.hasDuration = duration > 0; + data.hasTracks = album.tracks.length > 0; + data.hasDate = !!album.date; + data.hasAny = (data.hasDuration || data.hasTracks || data.hasDuration); + + if (!data.hasAny) + return data; + + if (data.hasDuration) + data.duration = duration; + + if (data.hasTracks) + data.tracks = album.tracks.length; + + if (data.hasDate) + data.date = album.date; + + return data; + }, + + generate(data, { + language, + }) { + return language.formatString( + 'albumPage.socialEmbed.body' + [ + data.hasDuration && '.withDuration', + data.hasTracks && '.withTracks', + data.hasDate && '.withReleaseDate', + ].filter(Boolean).join(''), + + Object.fromEntries([ + data.hasDuration && + ['duration', language.formatDuration(data.duration)], + data.hasTracks && + ['tracks', language.countTracks(data.tracks, {unit: true})], + data.hasDate && + ['date', language.formatDate(data.date)], + ].filter(Boolean))); + }, +}; diff --git a/src/content/dependencies/generateAlbumStylesheet.js b/src/content/dependencies/generateAlbumStylesheet.js new file mode 100644 index 00000000..575f7d59 --- /dev/null +++ b/src/content/dependencies/generateAlbumStylesheet.js @@ -0,0 +1,59 @@ +export default { + extraDependencies: [ + 'to', + ], + + data: function(album) { + const data = {}; + + data.hasWallpaper = !empty(album.wallpaperArtistContribs); + data.hasBanner = !empty(album.bannerArtistContribs); + + if (data.hasWallpaper) { + data.hasWallpaperStyle = !!album.wallpaperStyle; + data.wallpaperPath = ['media.albumWallpaper', album.directory, album.wallpaperFileExtension]; + data.wallpaperStyle = album.wallpaperStyle; + } + + if (data.hasBanner) { + data.hasBannerStyle = !!album.bannerStyle; + data.bannerStyle = album.bannerStyle; + } + + return data; + }, + + generate(data, {to}) { + const wallpaperPart = + (data.hasWallpaper + ? [ + `body::before {`, + ` background-image: url("${to(...data.wallpaperPath)}");`, + ...(data.hasWallpaperStyle + ? data.wallpaperStyle + .split('\n') + .map(line => ` ${line}`) + : []), + `}`, + ] + : []); + + const bannerPart = + (data.hasBannerStyle + ? [ + `#banner img {`, + ...data.bannerStyle + .split('\n') + .map(line => ` ${line}`), + `}`, + ] + : []); + + return [ + ...wallpaperPart, + ...bannerPart, + ] + .filter(Boolean) + .join('\n'); + }, +}; diff --git a/src/content/dependencies/index.js b/src/content/dependencies/index.js new file mode 100644 index 00000000..5cd116d4 --- /dev/null +++ b/src/content/dependencies/index.js @@ -0,0 +1,108 @@ +import chokidar from 'chokidar'; +import EventEmitter from 'events'; +import * as path from 'path'; +import {fileURLToPath} from 'url'; + +import contentFunction from '../../content-function.js'; +import {color, logWarn} from '../../util/cli.js'; +import {annotateFunction} from '../../util/sugar.js'; + +export function watchContentDependencies() { + const events = new EventEmitter(); + const contentDependencies = {}; + + Object.assign(events, { + contentDependencies, + }); + + // Watch adjacent files + const metaPath = fileURLToPath(import.meta.url); + const metaDirname = path.dirname(metaPath); + const watcher = chokidar.watch(metaDirname); + + watcher.on('all', (event, filePath) => { + if (!['add', 'change'].includes(event)) return; + if (filePath === metaPath) return; + handlePathUpdated(filePath); + }); + + watcher.on('unlink', (filePath) => { + if (filePath === metaPath) { + console.error(`Yeowzers content dependencies just got nuked.`); + return; + } + handlePathRemoved(filePath); + }) + + return events; + + function getFunctionName(filePath) { + const shortPath = path.basename(filePath); + const functionName = shortPath.slice(0, -path.extname(shortPath).length); + return functionName; + } + + async function handlePathRemoved(filePath) { + const functionName = getFunctionName(filePath); + delete contentDependencies[functionName]; + } + + async function handlePathUpdated(filePath) { + const functionName = getFunctionName(filePath); + let error = null; + + main: { + let spec; + try { + spec = (await import(`${filePath}?${Date.now()}`)).default; + } catch (caughtError) { + error = caughtError; + error.message = `Error importing: ${error.message}`; + break main; + } + + try { + if (typeof spec.data === 'function') { + annotateFunction(spec.data, {name: functionName, description: 'data'}); + } + + if (typeof spec.generate === 'function') { + annotateFunction(spec.generate, {name: functionName}); + } + } catch (caughtError) { + error = caughtError; + error.message = `Error annotating functions: ${error.message}`; + break main; + } + + let fn; + try { + fn = contentFunction(spec); + } catch (caughtError) { + error = caughtError; + error.message = `Error loading spec: ${error.message}`; + break main; + } + + contentDependencies[functionName] = fn; + } + + if (!error) { + return true; + } + + if (contentDependencies[functionName]) { + logWarn`Failed to import ${functionName} - using existing version`; + } else { + logWarn`Failed to import ${functionName} - no prior version loaded`; + } + + if (typeof error === 'string') { + console.error(color.yellow(error)); + } else { + console.error(error); + } + + return false; + } +} |