UNPKG

google-photos-migrate

Version:

A tool to fix EXIF data and recover filenames from a Google Photos takeout.

87 lines (86 loc) 3.63 kB
import { basename, dirname } from 'path'; import { supportedExtensions } from '../config/extensions.js'; import { NoMetaFileError } from '../meta/NoMetaFileError.js'; import { WrongExtensionError } from '../meta/apply-meta-errors.js'; import { applyMetaFile } from '../meta/apply-meta-file.js'; import { findMetaFile } from '../meta/find-meta-file.js'; import { readMetaTitle } from '../meta/read-meta-title.js'; import { InvalidExtError } from './InvalidExtError.js'; import { saveToDir } from './save-to-dir.js'; function applyExt(path, from, to) { return path.slice(0, path.length - from.length) + to; } async function renameExt(migCtx, file, from, to) { const destDir = dirname(file.path); const saveBase = applyExt(basename(file.path), from, to); file.path = await saveToDir(file.path, destDir, migCtx, true, saveBase); return file.path; } function getOutExtRename(ext, metaTitle) { const renames = (ext.aliases ?? []).filter((x) => typeof x === 'object'); return renames.find((r) => metaTitle.endsWith(r.suffix)); } export async function migrateMediaFile(originalPath, migCtx) { const mediaFileInfo = { originalPath, path: originalPath, }; const ext = supportedExtensions.reduce((longestMatch, cur) => { if (!originalPath.endsWith(cur.suffix)) return longestMatch; if (longestMatch === null) return cur; return cur.suffix.length > longestMatch.suffix.length ? cur : longestMatch; }, null); if (!ext) { mediaFileInfo.path = await saveToDir(originalPath, migCtx.errorDir, migCtx); return new InvalidExtError(mediaFileInfo); } const jsonPath = await findMetaFile(originalPath, ext, migCtx); if (!jsonPath) { mediaFileInfo.path = await saveToDir(originalPath, migCtx.errorDir, migCtx); return new NoMetaFileError(mediaFileInfo); } mediaFileInfo.jsonPath = jsonPath; const metaBase = (await readMetaTitle(mediaFileInfo)) ?? basename(mediaFileInfo.path); const outExtRename = getOutExtRename(ext, metaBase ?? basename(mediaFileInfo.path)); const outBase = outExtRename ? applyExt(metaBase, outExtRename.suffix, outExtRename.out) : metaBase; mediaFileInfo.path = await saveToDir(originalPath, migCtx.outputDir, migCtx, false, outBase); const mediaFile = { ...mediaFileInfo, ext, jsonPath, }; const err = await applyMetaFile(mediaFile, migCtx); return handleApplyMetaErr(mediaFile, migCtx, err, outExtRename); } async function handleApplyMetaErr(mediaFile, migCtx, err, outExtRename) { if (err) { if (err instanceof WrongExtensionError) { if (migCtx.skipCorrections) { return mediaFile; } if (outExtRename) { migCtx.warnLog(`Extension ${err.currentExt} should be ${err.actualExt}, but was forcibly set: ${mediaFile.path}`); return mediaFile; } await renameExt(migCtx, mediaFile, err.currentExt, err.actualExt); migCtx.warnLog(`Renamed wrong extension ${err.currentExt} to ${err.actualExt}: ${mediaFile.path}`); err = await applyMetaFile(mediaFile, migCtx); if (!err) { return mediaFile; } } const savedPaths = await Promise.all([ saveToDir(mediaFile.path, migCtx.errorDir, migCtx, true), saveToDir(mediaFile.jsonPath, migCtx.errorDir, migCtx), ]); mediaFile.path = savedPaths[0]; return err; } return mediaFile; }