reorganize

This commit is contained in:
Tykayn 2023-06-29 18:58:08 +02:00 committed by tykayn
parent 07b2b0358a
commit c1750aff07
8 changed files with 419 additions and 347 deletions

View File

@ -1,4 +1,16 @@
# rangement
# Devine le rangement
renomme des fichiers selon un pattern de date
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

View File

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

210
rangement/finder.mjs Normal file
View File

@ -0,0 +1,210 @@
/**
* la classe qui repère des patterns
*/
import { tagSectionSeparator, tagSeparator } from './configs.mjs'
import exifr from 'exifr'
import moment from 'moment'
/**
* 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)
// console.log('match findFileExtension', match)
// let result = ''
// if (match && match[0]) {
// result = match[0]
// }
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 fileName
* @returns {{extension: *, dateStamp: string, freeText: (*|string), tags: *[]}}
*/
static destructurateFileName (fileName) {
return {
dateStamp: this.findFormattedDate(fileName),
freeText: this.findFileNameFreeTextPart(fileName),
tags: this.findTagSectionInString(fileName),
extension: this.findFileExtension(fileName),
}
}
/**
* examine plusieurs propriétés exif de date et retourne la plus ancienne
* @param filepath
*/
static findExifCreationDate (filepath) {
console.log('filepath', filepath)
let dateAlreadyInFileName = finder.findFormattedDate(filepath)
console.log('------ dateAlreadyInFileName', dateAlreadyInFileName)
exifr.parse(filepath).then(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.ModifyDate) {
// console.log('image modifiée le : ModifyDate : ', exifData.ModifyDate) // Do something with your data!
moments.push(exifData.ModifyDate)
}
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)
return minDate
} else {
console.log('pas de exif data')
return null
}
}).catch(error => console.log('Error: ' + error.message))
}
}

View File

@ -1,59 +0,0 @@
/**
* la classe qui repère des patterns
*/
import {tagSectionSeparator} from "./configs.mjs";
/**
* finds patterns for file name
*/
export default class finders {
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 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)
// console.log('match findFileExtension', match)
// let result = ''
// if (match && match[0]) {
// result = match[0]
// }
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()
}
}

View File

@ -1,163 +1,42 @@
/**---------------------
* @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 finders from './finders.mjs'
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'
// list.map(folder => {
// getExifCreationDate(filepath)
// })
import exifr from 'exifr'
import moment from 'moment'
import {tagSectionSeparator, tagSeparator, enableTestsLocally} from "./configs.mjs";
let mini_arguments;
console.log(' ')
function parseArguments () {
mini_arguments = minimist(process.argv.slice(2))
// console.log('arguments', mini_arguments)
}
parseArguments()
const pathFolder = '/home/poule/encrypted/stockage-syncable/photos/a_dispatcher/tout'
const sortingFolder = '/home/poule/encrypted/stockage-syncable/photos/a_dispatcher'
// Replace with path to source directory
/**
* 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 getExifCreationDate(filepath) {
console.log('filepath', filepath)
let dateAlreadyInFileName = finders.findFormattedDate(filepath)
console.log('------ dateAlreadyInFileName', dateAlreadyInFileName)
exifr.parse(filepath).then(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.ModifyDate) {
// console.log('image modifiée le : ModifyDate : ', exifData.ModifyDate) // Do something with your data!
moments.push(exifData.ModifyDate)
}
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)
return minDate
} else {
console.log('pas de exif data')
return null
}
}).catch(error => console.log('Error: ' + error.message))
}
let fileSectionsName
function findTagSectionInString(inputString) {
let listOfTags = []
// remove extension
let extensionFile = finders.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) {
fileSectionsName = boom.splice(tagSeparator)
listOfTags = [...fileSectionsName[1].trim().split(tagSeparator)]
console.log('listOfTags', listOfTags)
} else {
console.log('no boom', boom)
}
}
}
return listOfTags
}
function renameFile(originalFileName, fileMixedNewName) {
function renameFile (originalFileName, fileMixedNewName) {
fs.rename(originalFileName, fileMixedNewName, function (err) {
if (err) console.log('rename ERROR: ' + err)
})
}
// let list = getDirectories(pathFolder)
// console.log('list', list)
let originalFileName = '2015-04-30T09.09.02 -- scan papier.jpg'
let formattedDatePIMBefore = finders.findFormattedDate(originalFileName)
console.log('formattedDatePIMBefore', formattedDatePIMBefore)
let creationDateFound = getExifCreationDate(pathFolder + '/' + originalFileName)
let convertedToName = ''
if (creationDateFound) {
convertedToName = convertDateToTimeInFileName(creationDateFound)
}
console.log('convertedToName', convertedToName)
let fileMixedNewName = mixDateNameWithFileName(convertedToName, originalFileName)
console.log('new name', fileMixedNewName)
if (fileMixedNewName !== originalFileName) {
console.log('renommage =>', fileMixedNewName)
// renameFile(originalFileName, fileMixedNewName)
}
function addTagInFileName(tagName, fileName) {
let tags = findTagSectionInString(fileName)
let firstPart = finders.findFileNameFreeTextPart(fileName)
tags.push(tagName)
let uniqueArray = [...new Set(tags)]
let newFileName = firstPart + ' ' + tagSectionSeparator + ' ' + tags.join(tagSeparator)
newFileName = newFileName.replace(/ {*}/, '') + finders.findFileExtension(fileName)
return cleanSpaces(newFileName)
}
const fileDefinition = {
dateStamp: '',
freeText: '',
@ -165,144 +44,32 @@ const fileDefinition = {
extension: '',
}
function destructurateFileName(fileName) {
return {
dateStamp: finders.findFormattedDate(fileName),
freeText: finders.findFileNameFreeTextPart(fileName),
tags: findTagSectionInString(fileName),
extension: finders.findFileExtension(fileName),
}
function makeFileNameFromProperties (fileProperties) {
return finder.cleanSpaces(fileProperties.dateStamp + ' ' + fileProperties.freeText + ' ' + tagSectionSeparator + ' ' + fileProperties.tags.join(tagSeparator) + fileProperties.extension)
}
function cleanSpaces(inputString) {
return inputString.trim().replace(/ *g/, ' ')
}
function makeFileNameFromProperties(fileProperties) {
return cleanSpaces(fileProperties.dateStamp + ' ' + fileProperties.freeText + ' ' + tagSectionSeparator + ' ' + fileProperties.tags.join(tagSeparator) + fileProperties.extension)
}
function appendFileName(fileProperties, newText) {
fileProperties.freeText = cleanSpaces(fileProperties.freeText + ' ' + newText)
function appendFileName (fileProperties, newText) {
fileProperties.freeText = finder.cleanSpaces(fileProperties.freeText + ' ' + newText)
return fileProperties
}
function prependFileName(fileProperties, newText) {
fileProperties.freeText = cleanSpaces(newText + ' ' + fileProperties.freeText)
function prependFileName (fileProperties, newText) {
fileProperties.freeText = finder.cleanSpaces(newText + ' ' + fileProperties.freeText)
return fileProperties
}
function searchAndReplaInFileName(searchString, replaceString, fileName) {
return cleanSpaces(fileName.replace(searchString, replaceString))
}
// getExifCreationDate('/home/poule/encrypted/stockage-syncable/photos/a_dispatcher/2023-06-23T18.36.47 -- machin bidule.jpg')
// findTagSectionInString('2023-06-23T18.36.47 -- machin bidule.jpg')
function searchAndRenameScreenshots(fileName) {
if (finders.findScreenshot(fileName)) {
let tags = findTagSectionInString(fileName)
console.log('tags', tags)
if (!tags.includes('screenshot')) {
fileName = addTagInFileName('screenshot', fileName)
fileName = searchAndReplaInFileName('Screenshot', '', fileName)
console.log('screenShotMockFileName:', fileName)
return cleanSpaces(fileName)
}
console.log('is a screenshot, remove screenshot in name, and add tag screenshot')
} else {
return null
}
}
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 = 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)
}
}
TestScreenShotIsFoundAndRenamed()
/**
* work in progress
*
*/
function TestTagsAreDetectedInFileName() {
let mockFileName = '2023-06-15T10:11:12 -- screeenshot festival.png'
let expectedResult = ['screeenshot', 'festival']
let found = findTagSectionInString(mockFileName)
if (found === expectedResult) {
console.info('Succès')
}
}
function TestFindFormattedDate() {
let mockFileName = 'Capture d\'écran 2023-06-15T10:11:12.png'
let expectedResult = '2023-06-15T10:11:12'
let found = finders.findFormattedDate(mockFileName)
console.log('foundDate', found, expectedResult)
console.log('foundDate', found)
if (found === expectedResult) {
console.info('Succès')
}
}
// run tests
if (enableTestsLocally) {
TestTagsAreDetectedInFileName()
TestFindFormattedDate()
TestScreenShotIsFoundAndRenamed()
}
/**
----------------------- parties non réalisées -----------------------
// TODO
---------------------------------------------------------------------
**/
function TestDownloadedTelegramPictureRename(fileName) {
let fileProperties = destructurateFileName(fileName)
}
function hasDifferentDateInNameThanExif(fileName) {
let foundDate = finders.findFormattedDate(fileName);
if (foundDate && foundDate != getExifCreationDate(fileName)) {
return true;
}
return false;
}
function moveToArchive(targetDirectory, fileFullPath) {
// find current directory,
// rename file to move it
}
function moveToSortingFolder(fileFullPath) {
}
/**
* é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
if (reportStatistics || mini_arguments.stats) {
finder.reportStatistics()
}

View File

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

View File

@ -0,0 +1,38 @@
import finder from './finder.mjs'
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,102 @@
import finder from './finders'
import fs from 'node-fs'
/**
----------------------- parties non réalisées -----------------------
// 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)
}