/**--------------------- * @name tykayn Rangement * @description Rangement sorts and rename files depending on their exif data * @contact contact@cipherbliss.com --------------------- */ /** --------------------- libs --------------------- */ import fs from 'node-fs' import minimist from 'minimist' import log from 'loglevel' import path from 'node:path' /** --------------------- custom utilities and configuration --------------------- */ import rangement_instance from './conf/configs.js' import { TestFindFormattedDate, TestScreenShotIsFoundAndRenamed, TestTagsAreDetectedInFileName } from './utils/testFunctions.mjs' import finder from './utils/finder.mjs' let mini_arguments log.setLevel(rangement_instance.log_level) log.info(' ') function parseArguments () { mini_arguments = minimist(process.argv.slice(2)) log.debug('arguments', mini_arguments) } /** * if there is no original file name free text into the new name, append it to the free text part * @param originalFileName * @param fileMixedNewName */ function addOriginalFileNameIfMissing (originalFileName, fileMixedNewName) { if (!fileMixedNewName.includes(originalFileName)) { let properties = finder.destructurateFileName(fileMixedNewName) return properties.freeText + ' ' + originalFileName } else { return fileMixedNewName } } function renameFile (originalFileName, fileMixedNewName) { if (rangement_instance.keepOriginalNameInRename) { fileMixedNewName = addOriginalFileNameIfMissing(originalFileName, fileMixedNewName) } fs.rename(originalFileName, fileMixedNewName, function (err) { log.info('name changed', fileMixedNewName) if (err) { log.info('rename ERROR: ' + err) } else { // otherRenames.push(originalFileName) otherRenames.push(fileMixedNewName) finder.statistics['filesModified']++ } }) } export function makeFileNameFromProperties (fileProperties) { let tagPlace = '' log.info('fileProperties.tags', fileProperties.tags) if (fileProperties.tags.length && rangement_instance.keepTags) { tagPlace = ' ' + rangement_instance.tagSectionSeparator + ' ' + fileProperties.tags.join(rangement_instance.tagSeparator) } console.log('fileProperties.dateStampExif', fileProperties.dateStampExif) let newName = '' + fileProperties.dateStampExif + ' ' + (rangement_instance.keepFreeText ? fileProperties.freeText : '') + tagPlace + fileProperties.extension if (rangement_instance.replaceUnderscoreWithSpaces) { newName = newName.replace('_', ' ') } newName = finder.cleanSpaces(newName) return newName } let otherRenames = [] function shouldWeChangeName (structureForFile) { log.info(' ______ shouldWeChangeName ', structureForFile.fileNameOriginal) let newName = makeFileNameFromProperties(structureForFile) log.debug('newName', newName) if (structureForFile.fileNameOriginal !== newName && !otherRenames.includes(newName)) { log.info('\n ancien nom :', structureForFile.fileNameOriginal) log.info(' nouveau nom:', newName) if (!mini_arguments['dry-run']) { renameFile(structureForFile.fullPath, structureForFile.folderPath + newName) } else { log.info('no renaming for real, this is a dry run') finder.statistics['filesNotModified']++ } } else { log.info(' rien à changer') } } /** * guess file name on one file which is not a directory * @param fullPath */ function guessFileNameOnOnefile (fullPath) { log.info('go guess file name on file: ', fullPath) fs.stat(fullPath, (err, stats) => { if (err) { log.error('échec fichier', err) log.error('ce fichier n existe pas: ', fullPath) return } else { let structureForFile = finder.destructurateFileName(fullPath) // examiner les infos exif de chaque fichier pour proposer un nouveau nom if (!structureForFile.dateStampInFileNameOriginal) { log.info(' le nom de fichier "' + structureForFile.freeText + '" ne contient pas de date formatée au début') finder.findExifCreationDate(structureForFile.fullPath) .then(data => { log.info(' ... chercher la date de création : "' + structureForFile.freeText + '"') let foundDate = finder.findEarliestDateInExifData(data) log.info(' =>>>>>>> foundDate : ', foundDate) if (foundDate) { structureForFile.dateStampExif = foundDate } else { log.info('pas de date trouvée dans le nom') } shouldWeChangeName(structureForFile) } , (error) => { log.warn('/////////// Error in reading exif of file: ' + error.message) return '' }) } } }) } let expandedFileList = [] let cwd = path.dirname(process.cwd()) + '/' + path.basename(process.cwd()) console.log('cwd', cwd) function guessFileNameOnAllFilesFromArguments () { // parcourir les fichiers log.debug('liste des fichiers', mini_arguments._) let fileList = mini_arguments._ // test file exists fileList.forEach(fullPath => { log.debug('file list element: ', fullPath) // parcourir les dossiers isFolderOrFile(`${fullPath}`) } ) log.info('expanded file list :', expandedFileList) expandedFileList.forEach(filePath => guessFileNameOnOnefile(filePath)) if (rangement_instance.reportStatistics || mini_arguments.stats) { finder.reportStatistics() } } function readSubdirectories (baseDir) { const newGlob = baseDir fs.readdir(baseDir, (err, files) => { if (err) throw err console.log('readSubdirectories - files', files) files.forEach((subDirOrFile) => { const newFullPath = cwd + '/' + subDirOrFile if (fs.existsSync(newFullPath)) { const s = fs.statSync(newFullPath) if (s.isFile()) { expandedFileList.push(cwd + '/' + subDirOrFile) } } }) }) } function isFolderOrFile (fileName) { const stat = fs.statSync(fileName) if (stat.isDirectory()) { let fileList = readSubdirectories(fileName) console.log('fileList in directory ', fileName, '\n', fileList) if (fileList) { expandedFileList.push(...fileList) } } else if (stat.isFile()) { expandedFileList.push(fileName) } } parseArguments() guessFileNameOnAllFilesFromArguments() // run tests if (rangement_instance.enableTestsLocally) { TestTagsAreDetectedInFileName() TestFindFormattedDate() TestScreenShotIsFoundAndRenamed() }