add demo index

This commit is contained in:
Tykayn 2023-07-01 12:35:33 +02:00 committed by tykayn
parent e7ff7f9677
commit 24970b1eb6
12 changed files with 572 additions and 2 deletions

View File

@ -1,3 +1,16 @@
# rangement
# Devine le rangement
scripts nodejs pour ranger ses fichiers selon la méthodo PIM de Karl Voit, avec quelques utilitaires personnels
script qui devine comment renommer des fichiers selon un pattern de date
trouve des infos exif et prend la plus ancienne pour renseigner le nom de fichier.
Inspiré des travaux de Karl Voit et de ses libs python GuessFileName, append2name, move2archive.
# lancement de renommage
`devine mon_fichier.jpg mon_autre_fichier.pdf`
➡️
🎉
## options
* -n , dry-run, ne pas renommer
* --photos-folder, spécifie un dossier pour les photos

6
configs.mjs Normal file
View File

@ -0,0 +1,6 @@
export const tagSeparator = ' '
export const tagSectionSeparator = '--'
export const enableTestsLocally = false
export const reportStatistics = false
export const version = '1.0.0'

244
finder.mjs Normal file
View File

@ -0,0 +1,244 @@
/**
* la classe qui repère des patterns
*/
import { tagSectionSeparator, tagSeparator } from './configs.mjs'
import exifr from 'exifr'
import moment from 'moment'
import path from 'path'
/**
* finds patterns for file name
*/
export default class finder {
static statistics = {
filesModified: 0,
}
static patternsFiles = {
'downloaded_pic': /^\-\w{15}\.jpg/, // FyB8cZnWIAc21rw.jpg
'telegram_pic': /^\-\d{19}_\d{4}/, // -4900281569878475578_1109.jpg
'open_camera': /^IMG_OC_\d{8}/i, // IMG_OC_20230617_092120_3.jpg
'screenshot': /^Screenshot/i, // Screenshot 2023-06-15 at 15-26-04 Instance Panoramax OSM-FR.png
}
static reportStatistics () {
console.log('statistics',
this.statistics)
}
static findScreenshot (inputString) {
return inputString.match(/screenshot/i) || inputString.match(/capture d'écran/i)
}
static findFormattedDate (filepath) {
let match = filepath.match(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/ig)
// console.log('match findFormattedDate', match)
let result = ''
if (match && match[0]) {
result = match[0]
}
return result
}
static findFileExtension (inputString) {
let result = inputString.match(/\.\w{3,4}$/i)
return result
}
/**
* find the section of file name which contains the free text to describe the picture
* @param fileName
* @returns {*|string}
*/
static findFileNameFreeTextPart (fileName) {
fileName = fileName.replace(this.findFileExtension(fileName), '')
let boom = fileName.split(tagSectionSeparator)
if (boom.length) {
let freeTextPart = boom[0].trim()
console.log('freeTextPart', freeTextPart)
return freeTextPart
}
return fileName.trim()
}
/**
* find an array of tags
* @param inputString
* @returns {[]}
*/
static findTagSectionInString (inputString) {
let listOfTags = []
// remove extension
let extensionFile = finder.findFileExtension(inputString)
if (extensionFile) {
extensionFile = extensionFile[0]
} else {
console.log('no extensionFile', extensionFile, inputString)
extensionFile = ''
}
inputString = inputString.replace(extensionFile, '')
// console.log('extensionFile', extensionFile)
if (inputString.includes(tagSectionSeparator)) {
// console.log('inputString', inputString)
if (inputString.length) {
let boom = inputString.split(tagSectionSeparator)
// console.log('boom', boom)
if (boom.length) {
let fileSectionsName = boom.splice(tagSeparator)
listOfTags = [...fileSectionsName[1].trim().split(tagSeparator)]
// console.log('listOfTags', listOfTags)
} else {
console.log('no boom', boom)
}
}
}
return listOfTags
}
static cleanSpaces (inputString) {
return inputString.trim().replace(/ *g/, ' ')
}
static searchAndReplaceInFileName (searchString, replaceString, fileName) {
return this.cleanSpaces(fileName.replace(searchString, replaceString))
}
/**
* search screenshot clues and rename
*/
static searchAndRenameScreenshots (fileName) {
if (finder.findScreenshot(fileName)) {
let tags = this.findTagSectionInString(fileName)
console.log('tags', tags)
if (!tags.includes('screenshot')) {
fileName = this.addTagInFileName('screenshot', fileName)
fileName = this.searchAndReplaceInFileName('Screenshot', '', fileName)
console.log('screenShotMockFileName:', fileName)
return this.cleanSpaces(fileName)
}
console.log('is a screenshot, remove screenshot in name, and add tag screenshot')
} else {
return null
}
}
static addTagInFileName (tagName, fileName) {
let tags = this.findTagSectionInString(fileName)
let firstPart = this.findFileNameFreeTextPart(fileName)
tags.push(tagName)
let uniqueArray = [...new Set(tags)]
let newFileName = firstPart + ' ' + tagSectionSeparator + ' ' + tags.join(tagSeparator)
newFileName = newFileName.replace(/ {*}/, '') + this.findFileExtension(fileName)
return this.cleanSpaces(newFileName)
}
/**
* convertit un nom de fichier en une structure décrivant plusieurs parties correspondant au pattern d'archivage
* @param fullPath
* @returns {{extension: *, dateStamp: string, freeText: (*|string), tags: *[]}}
*/
static destructurateFileName (fullPath) {
let [folderPath, fileNameOriginal] = this.findFolderPath(fullPath)
let dateStampInFileNameOriginal = this.findFormattedDate(fileNameOriginal)
return {
fullPath,
folderPath,
fileNameOriginal,
dateStampInFileNameOriginal,
dateStampExif: '',
freeText: this.findFileNameFreeTextPart(fileNameOriginal),
tags: this.findTagSectionInString(fileNameOriginal),
extension: this.findFileExtension(fileNameOriginal),
}
}
/**
* finds the earliest part in several exif date info
* @param exifData
* @returns {string}
*/
static findEarliestDateInExifData (exifData) {
if (exifData) {
let moments = []
// console.log('exif data : ', exifData) // Do something with your data!
if (exifData.DateTimeOriginal) {
// console.log('image créée le : DateTimeOriginal : ', exifData.DateTimeOriginal) // Do something with your data!
moments.push(exifData.DateTimeOriginal)
}
if (exifData.ModificationDateTime) {
// console.log('image créée le : ModificationDateTime : ', exifData.ModificationDateTime) // Do something with your data!
moments.push(exifData.ModificationDateTime)
}
if (exifData.ModifyDate) {
// console.log('image créée le : ModifyDate : ', exifData.ModifyDate) // Do something with your data!
moments.push(exifData.ModifyDate)
}
if (exifData.FileAccessDateTime) {
moments.push(exifData.FileAccessDateTime)
}
if (exifData.FileInodeChangeDateTime) {
moments.push(exifData.FileInodeChangeDateTime)
}
if (exifData.FileModificationDateTime) {
// console.log('image créée le : FileModificationDateTime : ', exifData.FileModificationDateTime) // Do something with your data!
moments.push(exifData.FileModificationDateTime)
}
if (exifData.CreateDate) {
// console.log('image créée le : CreateDate : ', exifData.CreateDate) // Do something with your data!
moments.push(exifData.CreateDate)
}
moments = moments.map(d => {
let newdate = moment(d)
return newdate
})
let minDate = moment.min(moments)
// console.log('minDate :::::::::', minDate)
console.log('minDate :::::::::', minDate.format('yyyy-MM-DDTHH:mm:ss'))
return minDate.format('yyyy-MM-DDTHH:mm:ss')
} else {
console.log('pas de exif data')
return ''
}
}
/**
* examine plusieurs propriétés exif de date et retourne la plus ancienne
* @param filepath
*/
static async findExifCreationDate (filepath) {
console.log('filepath', filepath)
let dateAlreadyInFileName = finder.findFormattedDate(filepath)
if (dateAlreadyInFileName) {
console.log('------ dateAlreadyInFileName', dateAlreadyInFileName)
}
return await exifr.parse(filepath)
}
static findFolderPath (filePath) {
let folders = filePath.split('/')
let fileName = folders.pop()
folders = filePath.replace(fileName, '')
console.log('\n - folders', folders)
console.log(' - fileName', fileName, '\n')
return [folders, fileName]
}
}

134
index.mjs Normal file
View File

@ -0,0 +1,134 @@
/**---------------------
* @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'
/** ---------------------
custom utilities and configuration
--------------------- */
import { enableTestsLocally, reportStatistics,tagSectionSeparator, tagSeparator } from './configs.mjs'
import {
TestFindFormattedDate,
TestScreenShotIsFoundAndRenamed,
TestTagsAreDetectedInFileName
} from './testFunctions.mjs'
import finder from './finder.mjs'
let mini_arguments
console.log(' ')
function parseArguments () {
mini_arguments = minimist(process.argv.slice(2))
console.log('arguments', mini_arguments)
}
parseArguments()
function renameFile (originalFileName, fileMixedNewName) {
fs.rename(originalFileName, fileMixedNewName, function (err) {
if (err) console.log('rename ERROR: ' + err)
})
}
function appendFileName (fileProperties, newText) {
fileProperties.freeText = finder.cleanSpaces(fileProperties.freeText + ' ' + newText)
return fileProperties
}
function prependFileName (fileProperties, newText) {
fileProperties.freeText = finder.cleanSpaces(newText + ' ' + fileProperties.freeText)
return fileProperties
}
function makeFileNameFromProperties(fileProperties) {
let tagPlace = ''
if (fileProperties.tags.length) {
tagPlace = ' ' + tagSectionSeparator + ' '
}
// return finder.cleanSpaces(fileProperties.dateStamp + ' ' + fileProperties.freeText + tagPlace + fileProperties.tags.join(tagSeparator) + fileProperties.extension).replace(+' ' + tagSectionSeparator + ' ' + '.', '.')
return ''+fileProperties.dateStampExif + ' ' + fileProperties.freeText + tagPlace + fileProperties.tags.join(tagSeparator) + fileProperties.extension
}
function shouldWeChangeName (structureForFile) {
console.log(' ______ allez hop fini la recherche on fait un nouveau nom')
console.log('structureForFile', structureForFile)
let newName = makeFileNameFromProperties(structureForFile)
if (structureForFile.fileNameOriginal !== newName) {
console.log('\n ancien nom :', structureForFile.fileNameOriginal)
// console.log(' nouveau nom:', foundDate +structureForFile.freeText + structureForFile.tags.join(tagSeparator) + structureForFile.extension )
console.log(' nouveau nom:', newName)
} else {
console.log(' rien à changer')
}
}
async function guessFileNameOnAllFilesFromArguments () {
// parcourir les dossiers
// parcourir les fichiers
console.log('liste des fichiers', mini_arguments._)
let fileList = mini_arguments._
fileList.forEach(fullPath => {
let structureForFile = finder.destructurateFileName(fullPath)
// examiner les infos exif de chaque fichier pour proposer un nouveau nom
if (!structureForFile.dateStampInFileNameOriginal) {
console.log(' le nom de fichier ne contient pas de date formatée au début')
finder.findExifCreationDate(structureForFile.fullPath)
.then(data => {
console.log(' ... chercher la date de création')
let foundDate = finder.findEarliestDateInExifData(data)
console.log(' =>>>>>>> foundDate : ', foundDate)
if (foundDate) {
// finder.findEarliestDateInExifData(fullPath).then(response => {
// console.log(' ... trouvée')
// if (response) {
structureForFile.dateStampExif = foundDate
shouldWeChangeName(structureForFile)
// }
// })
} else {
console.log('pas de date trouvée dans le nom')
}
}
,
(error) => {
console.log('/////////// Error in reading exif of file: ' + error.message)
return ''
})
}
}
)
}
guessFileNameOnAllFilesFromArguments()
// run tests
if (enableTestsLocally) {
TestTagsAreDetectedInFileName()
TestFindFormattedDate()
TestScreenShotIsFoundAndRenamed()
}
if (reportStatistics || mini_arguments.stats) {
finder.reportStatistics()
}

22
main.test.js Normal file
View File

@ -0,0 +1,22 @@
import finder from "./finders.mjs";
// const finders = require('./finders.mjs')
describe('rangement file name', () => {
test('detects date in file name', () => {
expect(finder.findFormattedDate('2023-06-23T18.36.47 -- machin bidule.jpg')).toBe('2023-06-23T18.36.47');
});
test('detects file extension in file name', () => {
expect(finder.findFileExtension()('2023-06-23T18.36.47 -- machin bidule.jpg')).toBe('jpg');
});
})
console.log('finders', finder)
test('adding positive numbers is not zero', () => {
for (let a = 1; a < 10; a++) {
for (let b = 1; b < 10; b++) {
expect(a + b).not.toBe(0);
}
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

View File

0
testFiles/une_photo.jpg Normal file
View File

View File

View File

48
testFunctions.mjs Normal file
View File

@ -0,0 +1,48 @@
import finder from './finder.mjs'
const pathFolder = '/home/poule/encrypted/stockage-syncable/photos/a_dispatcher/tout'
const sortingFolder = '/home/poule/encrypted/stockage-syncable/photos/a_dispatcher'
const fileDefinition = {
dateStamp: '',
freeText: '',
tags: [],
extension: '',
}
export function TestScreenShotIsFoundAndRenamed() {
let screenShotMockFileName = 'Screenshot 2023-06-15 at 15-28-21 Instance Panoramax OSM-FR.png'
let screenShotMockFileNameExpected = '2023-06-15 at 15-28-21 Instance Panoramax OSM-FR -- screenshot.png'
let found = finder.searchAndRenameScreenshots(screenShotMockFileName)
console.log('found', found)
if (found === screenShotMockFileNameExpected) {
console.log('TestScreenShotIsFoundAndRenamed : test succès')
} else {
console.log('TestScreenShotIsFoundAndRenamed : FAIL:')
console.log(found)
console.log(screenShotMockFileNameExpected)
}
}
export function TestTagsAreDetectedInFileName() {
let mockFileName = '2023-06-15T10:11:12 -- screeenshot festival.png'
let expectedResult = ['screeenshot', 'festival']
let found = finder.findTagSectionInString(mockFileName)
if (found === expectedResult) {
console.info('Succès')
}
}
export function TestFindFormattedDate() {
let mockFileName = 'Capture d\'écran 2023-06-15T10:11:12.png'
let expectedResult = '2023-06-15T10:11:12'
let found = finder.findFormattedDate(mockFileName)
console.log('foundDate', found, expectedResult)
console.log('foundDate', found)
if (found === expectedResult) {
console.info('Succès')
}
}

View File

@ -0,0 +1,103 @@
import finder from './finders'
import fs from 'node-fs'
/**
----------------------- parties non réalisées -----------------------
* work in progress
// TODO
---------------------------------------------------------------------
**/
function TestDownloadedTelegramPictureRename (fileName) {
let fileProperties = destructurateFileName(fileName)
}
function hasDifferentDateInNameThanExif (fileName) {
let foundDate = finder.findFormattedDate(fileName)
if (foundDate && foundDate != getExifCreationDate(fileName)) {
return true
}
return false
}
function moveToArchive (targetDirectory, fileFullPath) {
// find current directory,
// rename file to move it
}
function getStatisticsOnArchiveFolder (fileFullPath) {
return {
foldersCount: 'TODO',
filesWithoutSemanticName: 'TODO'
}
}
function getControlledVocabularyFromFiles (fileFullPath) {
let controlledVocabulary = ['TODO']
// find all tags
return controlledVocabulary
}
function moveToSortingFolder (fileFullPath) {
return 'TODO'
}
/**
* écrit un nouveau nom de fichier formatté
* @param convertedToName
* @param originalFileName
* @returns {*}
*/
function mixDateNameWithFileName (convertedToName, originalFileName) {
// enlever l'ancien timestamp si il existe
// ajouter en début de nom le nouveau timestamp avec un espace et conserver le reste du nom
return originalFileName
}
function TestMixingName () {
let fileMixedNewName = mixDateNameWithFileName(convertedToName, originalFileName)
console.log('new name', fileMixedNewName)
if (fileMixedNewName !== originalFileName) {
console.log('renommage =>', fileMixedNewName)
// renameFile(originalFileName, fileMixedNewName)
}
}
/**
* obtenir une liste des dossiers uniquement dans le dossier courant
* @param path
* @returns {*}
*/
function getDirectories (path) {
return fs.readdirSync(path).filter(function (file) {
return fs.statSync(path + '/' + file).isDirectory()
})
}
function convertDateToTimeInFileName (inputDate) {
return inputDate.replace(' ', 'T')
}
function testthings(){
// let list = getDirectories(pathFolder)
// console.log('list', list)
let originalFileName = '2015-04-30T09.09.02 -- scan papier.jpg'
let formattedDatePIMBefore = finder.findFormattedDate(originalFileName)
console.log('formattedDatePIMBefore', formattedDatePIMBefore)
let creationDateFound = finder.getExifCreationDate(pathFolder + '/' + originalFileName)
let convertedToName = ''
if (creationDateFound) {
convertedToName = convertDateToTimeInFileName(creationDateFound)
}
console.log('convertedToName', convertedToName)
}