From c80bdf0b5ba39d3cac6f8635c3df749752b38760 Mon Sep 17 00:00:00 2001 From: "(quasar) nebula" Date: Sun, 24 Mar 2024 22:06:29 -0300 Subject: upd8, thumbs: new --cache-path general cache --- src/gen-thumbs.js | 139 +++++++++++++++++++++++++++++++++++++++++++++++------- src/upd8.js | 115 +++++++++++++++++++++++++++++++++++++++++--- src/web-routes.js | 1 + 3 files changed, 232 insertions(+), 23 deletions(-) diff --git a/src/gen-thumbs.js b/src/gen-thumbs.js index 8a582693..c5c5ee4f 100644 --- a/src/gen-thumbs.js +++ b/src/gen-thumbs.js @@ -610,8 +610,11 @@ async function generateImageThumbnail(imagePath, thumbtack, { export async function determineMediaCachePath({ mediaPath, + wikiCachePath, providedMediaCachePath, + disallowDoubling = false, + regenerateMissingThumbnailCache = false, }) { if (!mediaPath) { return { @@ -643,45 +646,147 @@ export async function determineMediaCachePath({ }; } - const inferredPath = + // Two inferred paths are possible - "adjacent" and "contained". + // "Contained" is the preferred format and we'll create it if + // wikiCachePath is provided, but if it *isn't* we won't know + // where to create it. Since "adjacent" isn't preferred we don't + // ever generate it, and we'd prefer not to *newly* generate + // thumbs in-place with mediaPath, so give up - we've already + // determined mediaPath doesn't include in-place thumbs. + + const adjacentInferredPath = path.join( path.dirname(mediaPath), path.basename(mediaPath) + '-cache'); - let inferredIncludesThumbnailCache; + const containedInferredPath = + (wikiCachePath + ? path.join(wikiCachePath, 'media-cache') + : null); + + let adjacentIncludesThumbnailCache; + let containedIncludesThumbnailCache; try { - const files = await readdir(inferredPath); - inferredIncludesThumbnailCache = files.includes(CACHE_FILE); + const files = await readdir(adjacentInferredPath); + adjacentIncludesThumbnailCache = files.includes(CACHE_FILE); } catch (error) { if (error.code === 'ENOENT') { - inferredIncludesThumbnailCache = null; + adjacentIncludesThumbnailCache = null; } else { - inferredIncludesThumbnailCache = undefined; + adjacentIncludesThumbnailCache = undefined; + } + } + + if (wikiCachePath) { + try { + const files = await readdir(containedInferredPath); + containedIncludesThumbnailCache = files.includes(CACHE_FILE); + } catch (error) { + if (error.code === 'ENOENT') { + containedIncludesThumbnailCache = null; + } else { + containedIncludesThumbnailCache = undefined; + } } } - if (inferredIncludesThumbnailCache === true) { + // Go ahead with the contained path if it exists and contains a cache - + // no other conditions matter. + if (containedIncludesThumbnailCache === true) { return { - annotation: 'inferred path has cache', - mediaCachePath: inferredPath, + annotation: `contained path has cache`, + mediaCachePath: containedInferredPath, }; - } else if (inferredIncludesThumbnailCache === false) { + } + + // Reuse an existing adjacent cache before figuring out what to do + // if there's no extant cache at all. + if (adjacentIncludesThumbnailCache === true) { return { - annotation: 'inferred path does not have cache', - mediaCachePath: null, + annotation: `adjacent path has cache`, + mediaCachePath: adjacentInferredPath, }; - } else if (inferredIncludesThumbnailCache === null) { + } + + // Throw a very high-priority tantrum if the contained cache exists but + // isn't readable. It's the preferred cache and we can't tell if it's + // available for use or not! + if (wikiCachePath && containedIncludesThumbnailCache === undefined) { return { - annotation: 'inferred path will be created', - mediaCachePath: inferredPath, + annotation: `contained path not readable`, + mediaCachePath: null, }; - } else { + } + + // Throw a secondary tantrum if the adjacent cache exists but + // isn't readable. This is just as big of a problem, but if for + // some reason both the contained and adjacent caches exist, + // the contained one is the one we'd rather have addressed. + if (adjacentIncludesThumbnailCache === undefined) { return { - annotation: 'inferred path not readable', + annotation: `adjacent path not readable`, mediaCachePath: null, }; } + + // Throw a high-priority tantrum if the contained cache exists but is + // missing its cache file, again because it's the more preferred cache. + // Unless we're indicated to regenerate such a missing cache file! + if (containedIncludesThumbnailCache === false) { + if (regenerateMissingThumbnailCache) { + return { + annotation: `contained path will regenerate missing cache`, + mediaCachePath: containedInferredPath, + }; + } else { + return { + annotation: `contained path does not have cache`, + mediaCachePath: null, + }; + } + } + + // Throw a secondary tantrum if the adjacent cache exists but is + // missing its cache file, because it's the less preferred cache. + // Unless we're indicated to regenerate a missing cache file! + if (adjacentIncludesThumbnailCache === false) { + if (regenerateMissingThumbnailCache) { + return { + annotation: `adjacent path will regenerate missing cache`, + mediaCachePath: adjacentInferredPath, + }; + } else { + return { + annotation: `adjacent path does not have cache`, + mediaCachePath: null, + }; + } + } + + // If wikiCachePath was provided and the contained cache just doesn't + // exist yet, we'll create it during this run. + if (wikiCachePath && containedIncludesThumbnailCache === null) { + return { + annotation: `contained path will be created`, + mediaCachePath: containedInferredPath, + }; + } + + // If the adjacent cache doesn't exist, too dang bad! + // We aren't interested in newly creating it, so + // don't count it as an option. + + // Similarly, we've already established mediaPath isn't + // currently doubling as the thumbnail cache, and we won't + // newly start generating thumbnails here either. + + // All options aside struck out, there's no way to continue. + + return { + annotation: `missing wiki cache to create media cache inside`, + mediaCachePath: null, + }; } export async function migrateThumbsIntoDedicatedCacheDirectory({ diff --git a/src/upd8.js b/src/upd8.js index 4b5064a6..e2b65b75 100755 --- a/src/upd8.js +++ b/src/upd8.js @@ -240,7 +240,12 @@ async function main() { }, 'media-cache-path': { - help: `Specify path to media cache directory, including automatically generated thumbnails\n\nThis usually doesn't need to be provided, and will be inferred by adding "-cache" to the end of the media directory`, + help: `Specify path to media cache directory, including automatically generated thumbnails\n\nThis usually doesn't need to be provided, and will be inferred either by loading "media-cache" from --cache-path, or by adding "-cache" to the end of the media directory\n\nAlso may be provided via the HSMUSIC_MEDIA_CACHE environment variable`, + type: 'value', + }, + + 'cache-path': { + help: `Specify path to general cache directory, usually containing generated thumbnails and assorted files reused between builds\n\nRequired for some features and may always be required if you're starting a new workspace\n\nAlso may be provided via the HSMUSIC_CACHE environment varaible`, type: 'value', }, @@ -284,6 +289,11 @@ async function main() { type: 'flag', }, + 'new-thumbs': { + help: `Repair a media cache that's completely missing its index file by starting clean and not reusing any existing thumbnails`, + type: 'flag', + }, + 'skip-file-sizes': { help: `Skips preloading file sizes for images and additional files, which will be left blank in the build`, type: 'flag', @@ -473,6 +483,7 @@ async function main() { const dataPath = cliOptions['data-path'] || process.env.HSMUSIC_DATA; const mediaPath = cliOptions['media-path'] || process.env.HSMUSIC_MEDIA; + const wikiCachePath = cliOptions['cache-path'] || process.env.HSMUSIC_CACHE; const langPath = cliOptions['lang-path'] || process.env.HSMUSIC_LANG; // Can 8e left unset! const thumbsOnly = cliOptions['thumbs-only'] ?? false; @@ -500,6 +511,10 @@ async function main() { logError`${`Expected --media-path option or HSMUSIC_MEDIA to be set`}`; } + if (!wikiCachePath) { + logWarn`No --cache-path option nor HSMUSIC_CACHE set; provide for more features`; + } + if (!dataPath || !mediaPath) { return false; } @@ -761,31 +776,118 @@ async function main() { timeStart: Date.now(), }); + const regenerateMissingThumbnailCache = + cliOptions['new-thumbs'] ?? false; + const {mediaCachePath, annotation: mediaCachePathAnnotation} = await determineMediaCachePath({ mediaPath, + wikiCachePath, + providedMediaCachePath: cliOptions['media-cache-path'] || process.env.HSMUSIC_MEDIA_CACHE, + + regenerateMissingThumbnailCache, + disallowDoubling: stepStatusSummary.migrateThumbnails.status === STATUS_NOT_STARTED, }); + if (regenerateMissingThumbnailCache) { + if ( + mediaCachePathAnnotation !== `contained path will regenerate missing cache` && + mediaCachePathAnnotation !== `adjacent path will regenerate missing cache` + ) { + if (mediaCachePath) { + logError`Determined a media cache path. (${mediaCachePathAnnotation})`; + console.error(''); + logWarn`By using ${'--new-thumbs'}, you requested to generate completely`; + logWarn`new thumbnails, but there's already a ${'thumbnail-cache.json'}`; + logWarn`file where it's expected, within this media cache:`; + logWarn`${path.resolve(mediaCachePath)}`; + console.error(''); + logWarn`If you really do want to completely regenerate all thumbnails`; + logWarn`and not reuse any existing ones, move aside ${'thumbnail-cache.json'}`; + logWarn`and run with ${'--new-thumbs'} again.`; + + Object.assign(stepStatusSummary.determineMediaCachePath, { + status: STATUS_FATAL_ERROR, + annotation: `--new-thumbs provided but regeneration not needed`, + timeEnd: Date.now(), + }); + + return false; + } else { + logError`Couldn't determine a media cache path. (${mediaCachePathAnnotation})`; + console.error(''); + logWarn`You requested to generate completely new thumbnails, but`; + logWarn`the media cache wasn't readable or just couldn't be found.`; + logWarn`Run again without ${'--new-thumbs'} - you should investigate`; + logWarn`what's going on before continuing.`; + + Object.assign(stepStatusSummary.determineMediaCachePath, { + status: STATUS_FATAL_ERROR, + annotation: mediaCachePathAnnotation, + timeEnd: Date.now(), + }); + + return false; + } + } + } + if (!mediaCachePath) { logError`Couldn't determine a media cache path. (${mediaCachePathAnnotation})`; switch (mediaCachePathAnnotation) { - case 'inferred path does not have cache': - logError`If you're certain this is the right path, you can provide it via`; - logError`${'--media-cache-path'} or ${'HSMUSIC_MEDIA_CACHE'}, and it should work.`; + case `contained path does not have cache`: + console.error(''); + logError`You've provided a ${'--cache-path'} or ${'HSMUSIC_CACHE_PATH'},`; + logError`${path.resolve(wikiCachePath)}`; + console.error(''); + logError`It contains a ${'media-cache'} folder, but this folder is`; + logError`missing its ${'thumbnail-cache.json'} file. This means there's`; + logError`no information available to reuse. If you use this cache,`; + logError`hsmusic will generate any existing thumbnails over again.`; + console.error(''); + logError`* Try to see if you can recover or locate a copy of your`; + logError` ${'thumbnail-cache.json'} file and put it back in place;`; + logError`* Or, generate all-new thumbnails with ${'--new-thumbs'}.`; break; - case 'inferred path not readable': + case 'adjacent path does not have cache': + console.error(''); + logError`You have an existing ${'media-cache'} folder next to your media path,`; + logError`${path.resolve(mediaPath)}`; + console.error(''); + logError`The ${'media-cache'} folder is missing its ${'thumbnail-cache.json'}`; + logError`file. This means there's no information available to reuse,`; + logError`and if you use this cache, hsmusic will generate any existing`; + logError`thumbnails over again.`; + console.error(''); + logError`* Try to see if you can recover or locate a copy of your`; + logError` ${'thumbnail-cache.json'} file and put it back in place;`; + logError`* Or, generate all-new thumbnails with ${'--new-thumbs'}.`; + break; + + case `contained path not readable`: + case `adjacent path not readable`: + console.error(''); logError`The folder couldn't be read, which usually indicates`; logError`a permissions error. Try to resolve this, or provide`; logError`a new path with ${'--media-cache-path'} or ${'HSMUSIC_MEDIA_CACHE'}.`; break; - case 'media path not provided': /* unreachable */ + case `missing wiki cache to create media cache inside`: + console.error(''); + logError`It looks like you're starting totally fresh, so please`; + logError`create a ${'cache'} folder and provide it with ${'--cache-path'}`; + logError`or ${'HSMUSIC_CACHE'}. The media cache will automatically be`; + logError`generated inside of this folder!`; + break; + + case `media path not provided`: /* unreachable */ + console.error(''); logError`It seems a ${'--media-path'} (or ${'HSMUSIC_MEDIA'}) wasn't provided.`; logError`Make sure one of these is actually pointing to a path that exists.`; break; @@ -1794,6 +1896,7 @@ async function main() { const webRouteSources = await identifyAllWebRoutes({ mediaCachePath, mediaPath, + wikiCachePath, }); const {aggregate, result} = diff --git a/src/web-routes.js b/src/web-routes.js index ab9633b8..efd86ca1 100644 --- a/src/web-routes.js +++ b/src/web-routes.js @@ -36,6 +36,7 @@ export const allStaticWebRoutes = [ export async function identifyDynamicWebRoutes({ mediaPath, mediaCachePath, + wikiCachePath, }) { const routeFunctions = [ () => Promise.resolve([ -- cgit 1.3.0-6-gf8a5