|
/** |
|
* Resumable Google Drive Copier (Bypasses the 6-minute limit) |
|
*/ |
|
|
|
function startOrResumeCopy() { |
|
const sourceFolderId = 'fdsfdsfsdfs'; // update ID |
|
const targetSharedDriveId = 'fdsfsfsdfsd'; // update ID |
|
|
|
const userProps = PropertiesService.getUserProperties(); |
|
let queue = []; |
|
|
|
// 1. Check if we have a saved queue from a previous timeout |
|
const savedQueue = userProps.getProperty('COPY_QUEUE'); |
|
|
|
if (savedQueue) { |
|
queue = JSON.parse(savedQueue); |
|
Logger.log("π Waking up! Resuming from saved queue. Folders left in queue: " + queue.length); |
|
} else { |
|
// Start fresh |
|
queue.push({ source: sourceFolderId, target: targetSharedDriveId, path: "ROOT" }); |
|
Logger.log("π Starting a fresh copy..."); |
|
} |
|
|
|
const startTime = Date.now(); |
|
const MAX_RUN_TIME = 4.5 * 60 * 1000; // 4.5 minutes (leaves a safe buffer) |
|
|
|
// 2. Process the Queue |
|
while (queue.length > 0) { |
|
|
|
// Check the clock before processing the next folder |
|
if (Date.now() - startTime > MAX_RUN_TIME) { |
|
Logger.log("β οΈ Approaching 5 minutes. Saving state and scheduling continuation..."); |
|
userProps.setProperty('COPY_QUEUE', JSON.stringify(queue)); |
|
setupTrigger(); |
|
return; // Exit safely before Google kills the script |
|
} |
|
|
|
const currentTask = queue.shift(); // Get the first item in the list |
|
Logger.log(`π Processing: ${currentTask.path}`); |
|
|
|
try { |
|
const sourceFolder = DriveApp.getFolderById(currentTask.source); |
|
const targetFolder = DriveApp.getFolderById(currentTask.target); |
|
|
|
// A. Copy Files in this folder |
|
const files = sourceFolder.getFiles(); |
|
while (files.hasNext()) { |
|
const file = files.next(); |
|
try { |
|
file.makeCopy(file.getName(), targetFolder); |
|
Logger.log(` βββ π Copied: ${file.getName()}`); |
|
} catch (e) { |
|
Logger.log(` βββ β [ERROR] File copy failed: ${file.getName()} - ${e.message}`); |
|
} |
|
} |
|
|
|
// B. Find Subfolders and add them to the queue for later |
|
const subfolders = sourceFolder.getFolders(); |
|
while (subfolders.hasNext()) { |
|
const subfolder = subfolders.next(); |
|
try { |
|
const newTargetFolder = targetFolder.createFolder(subfolder.getName()); |
|
queue.push({ |
|
source: subfolder.getId(), |
|
target: newTargetFolder.getId(), |
|
path: currentTask.path + "/" + subfolder.getName() |
|
}); |
|
} catch (e) { |
|
Logger.log(` βββ β [ERROR] Folder creation failed: ${subfolder.getName()} - ${e.message}`); |
|
} |
|
} |
|
} catch (e) { |
|
Logger.log(`β [ERROR] Could not access folder in path: ${currentTask.path}`); |
|
} |
|
} |
|
|
|
// 3. If the loop finishes naturally, the queue is empty. We are completely done! |
|
Logger.log("β
COPY COMPLETE! Cleaning up triggers and memory."); |
|
userProps.deleteProperty('COPY_QUEUE'); |
|
deleteTrigger(); |
|
} |
|
|
|
|
|
/** |
|
* Sets a time-driven trigger to run the script again in 1 minute. |
|
*/ |
|
function setupTrigger() { |
|
deleteTrigger(); // Ensure no duplicate triggers exist |
|
ScriptApp.newTrigger('startOrResumeCopy') |
|
.timeBased() |
|
.after(1 * 60 * 1000) // 1 minute from now |
|
.create(); |
|
} |
|
|
|
|
|
/** |
|
* Deletes any existing triggers so they don't run endlessly. |
|
*/ |
|
function deleteTrigger() { |
|
const triggers = ScriptApp.getProjectTriggers(); |
|
for (const trigger of triggers) { |
|
if (trigger.getHandlerFunction() === 'startOrResumeCopy') { |
|
ScriptApp.deleteTrigger(trigger); |
|
} |
|
} |
|
} |
|
|
|
/** |
|
* Utility function to clear the queue and delete triggers manually if needed. |
|
*/ |
|
function resetCopyQueue() { |
|
PropertiesService.getUserProperties().deleteProperty('COPY_QUEUE'); |
|
deleteTrigger(); |
|
Logger.log("π§Ή Queue cleared and triggers deleted."); |
|
} |