Last active
July 27, 2023 04:10
-
-
Save Fluf22/d5cce49db7bbc0e049b1841639055304 to your computer and use it in GitHub Desktop.
Transfer pictures and videos from unzipped Google Takeout folder to Nextcloud folder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
This program is free software: you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation, either version 3 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program. If not, see <https://www.gnu.org/licenses/>. | |
*/ | |
// IMPROVEMENTS TO DO: | |
// - Everything is synchronous to avoid file copy conflict | |
// - Sadly, readline take over the standard input and block CTRL + C and CTRL + D signals | |
const fs = require('fs'); | |
const readline = require('readline'); | |
const currDir = process.cwd(); // Get current directory path | |
const nextDir = process.argv[2]; // Get nextcloud directory path from arguments | |
const removeEnabled = process.argv.length > 3 && process.argv[3].includes("--remove=") && process.argv[3].split("=")[1] === "true"; // Get --remove option from arguments | |
// If you are not french, you should change this file name | |
// It is a file stored in every photos folder in order to give some informations about it | |
// I use it to know the real photos folder name, which isn't the one that Google use to store your photos | |
const metadataFilename = "métadonnées.json"; | |
// File extensions that will be used as a reference to know which file to transfer | |
// This list is case INSENSITIVE. We will test case in the script | |
const fileExtensionList = [ | |
"jpg", | |
"jpeg", | |
"png", | |
"gif", | |
"mp4" | |
]; | |
// Initiate readline interface | |
const rl = readline.createInterface({ | |
input: process.stdin, | |
output: process.stdout | |
}); | |
// Some logs | |
console.log(`Old files removal is ${removeEnabled ? "" : "not"} enabled`) | |
console.log(`Directories will be copied to "${nextDir}"!`); | |
console.log(`Opening current dir: ${currDir}...`) | |
// Get current directory file list | |
const files = fs.readdirSync(currDir); | |
// Do a loop on this directory list | |
for (let idx1 = 0; idx1 < files.length; ++idx1) { | |
let file = files[idx1]; | |
const stats = fs.lstatSync(`${currDir}/${file}`); // Get informations about this directory | |
// Test if it is really a directory | |
if (stats.isDirectory()) { | |
let realDirName = ""; | |
// Google Photos stores photos from general view in dated directories | |
let isDatedFolder = file.slice(0, 3) === "199" || file.slice(0, 2) === "20"; | |
// Send photos from Google Photos general view to Nextcloud general Photos directory | |
// Otherwise, send photos to Nextcloud Pictures folder, in a subdirectory | |
if (isDatedFolder) { | |
realDirName = "Photos"; | |
} else if (fs.existsSync(`${currDir}/${file}/${metadataFilename}`)) { // Test if metadata filename exists in directory | |
const metadataStr = fs.readFileSync(`${currDir}/${file}/${metadataFilename}`, "utf-8"); // Read the file as a string | |
const metadata = JSON.parse(metadataStr); // Parse it to get JSON | |
if (metadata && metadata.title) { // If Google used a cleaner name than the one you used to name your folder... | |
realDirName = "Pictures/" + metadata.title; // ...then replace it to keep YOUR choice... | |
} else { | |
realDirName = "Pictures/" + file; // ...else, use the currently processed folder name | |
} | |
} else { | |
realDirName = file; // we never should go here... | |
} | |
// Some log | |
console.log(`${file}: Begin images copy to ${isDatedFolder ? "photos folder" : realDirName + " specific folder"}...`); | |
// Get a list of all the photos inside this folder | |
const dirFiles = fs.readdirSync(`${currDir}/${file}`); | |
let createdDir = false; // Shortcut to know if this folder has been created in Nextcloud | |
let imgCount = 0; // Keep an eye on copy progress | |
// Loop over all the photos | |
for (let idx2 = 0; idx2 < dirFiles.length; ++idx2) { | |
let img = dirFiles[idx2]; // The processed photo/video | |
// Only copy photos and videos | |
if (fileExtensionList.includes(img.split(".")[img.split(".").length - 1].toLowerCase())) { | |
// Create subdirectory if it doesn't exist | |
if (!createdDir && !fs.existsSync(`${nextDir}/${realDirName}`)) { | |
fs.mkdirSync(`${nextDir}/${realDirName}`); | |
createdDir = true; | |
} | |
// Test if the processed file already exists in destination folder | |
// Useful if the script fails and you have to execute it again | |
if (!fs.existsSync(`${nextDir}/${realDirName}/${img}`)) { | |
// Comfort logs - overwrite current line on console | |
if (idx2 !== 0) { | |
readline.cursorTo(process.stdout, 0); // move cursor to beginning of line | |
readline.moveCursor(process.stdout, 0, -1); // move cursor to previous line | |
readline.clearLine(process.stdout, 1); // clear line | |
} | |
rl.write(`${img}: copying to ${realDirName}...\n`); // write text | |
// Actually do the copy | |
fs.copyFileSync(`${currDir}/${file}/${img}`, `${nextDir}/${realDirName}/${img}`); | |
imgCount = imgCount + 1; // Copied one more file to destination, away from Google ;) | |
} | |
// Delete file if remove option is enabled and if it exists (useful in case of script failure/re-execution) | |
if (removeEnabled && fs.existsSync(`${currDir}/${file}/${img}`)) { | |
fs.unlinkSync(`${currDir}/${file}/${img}`); | |
} | |
} | |
// Comfort logs | |
if (imgCount > 0 && idx2 === dirFiles.length - 1) { | |
readline.cursorTo(process.stdout, 0); // move cursor to beginning of line | |
readline.moveCursor(process.stdout, 0, -1); // move cursor to previous line | |
readline.clearLine(process.stdout, 1); // clear line | |
rl.write(`Copied ${imgCount} images!\n`); // write text | |
} | |
}; | |
realDirName = ""; // Reset destination directory name | |
} | |
}; | |
rl.close(); // Close readline interface |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment