|
// ==UserScript== |
|
// @name Google Recorder Export |
|
// @namespace https://recorder.google.com |
|
// @version 2025-06-28 |
|
// @description Export the files from Google Recorder with metadata. |
|
// @author You |
|
// @match https://recorder.google.com/* |
|
// @icon https://www.google.com/s2/favicons?sz=64&domain=google.com |
|
// @grant none |
|
// ==/UserScript== |
|
|
|
function processRecordingElement(recording) { |
|
recording.click(); |
|
const metadata = recording.querySelector('recorder-sidebar-item').shadowRoot.querySelector('.top-section recorder-metadata').shadowRoot.querySelector('.container'); |
|
const nestedMetadata = metadata.querySelector('.subtitle .wrapper .flex-overflow-wrapper'); |
|
const title = metadata.querySelector('.title-wrapper').textContent.trim(); |
|
const recordingLocation = nestedMetadata.querySelector('.location')?.textContent || null; |
|
const date = nestedMetadata.querySelector('span[aria-label]').textContent; |
|
const regex = /\b(1[0-2]|0?[1-9]):[0-5][0-9]\s?(AM|PM)\b/i; // for timestamp |
|
const match = title.match(regex); |
|
let time = null; |
|
if (match) { |
|
time = match[0]; |
|
} |
|
const fullMetadataString = nestedMetadata.parentElement.textContent; |
|
const pathname = window.location.pathname; |
|
const url = window.location.href; |
|
return {title, recordingLocation, time, date, fullMetadataString, pathname, url}; |
|
} |
|
|
|
function processAllMetadata() { |
|
console.log("Google Recorder Exporter Loading..."); |
|
|
|
// drill down through the shadow dom |
|
const items = document.querySelector('recorder-main') |
|
.ki.querySelector('.container recorder-sidebar') |
|
.shadowRoot.querySelector('.container .sidebar recorder-sidebar-items') |
|
.shadowRoot.querySelector('.container .items'); |
|
|
|
const libraryList = items.querySelector('.library-list'); |
|
const loadMoreButton = items.querySelector('.load-more'); |
|
const loadMore = () => loadMoreButton.click(); |
|
const getNumberOfRecordings = () => libraryList.getElementsByClassName("item").length; |
|
|
|
let userConfirmed = confirm("Do you want to load all user recordings? Saying no will just export what you have."); |
|
if (userConfirmed) { |
|
|
|
function loadAllRecordings(delay = 1000) { |
|
let lastAmount = 0; |
|
let currentAmount = getNumberOfRecordings(); |
|
|
|
function checkAndLoad() { |
|
lastAmount = currentAmount; |
|
loadMore(); |
|
|
|
setTimeout(() => { |
|
currentAmount = getNumberOfRecordings(); |
|
if (currentAmount > lastAmount) { |
|
checkAndLoad(); // Keep loading |
|
} else { |
|
console.log("All recordings loaded. Found:", getNumberOfRecordings()); |
|
} |
|
}, delay); |
|
} |
|
checkAndLoad(); |
|
} |
|
loadAllRecordings(); |
|
} |
|
|
|
// now, process the recordings we've loaded: |
|
|
|
const recordingElements = libraryList.getElementsByClassName("item"); |
|
const metadataElements = []; |
|
const iterationDepth = getNumberOfRecordings(); |
|
|
|
function printAll() { |
|
console.log({metadataElements}); |
|
// console.log('stringified:'); |
|
// console.log(JSON.stringify(metadataElements)); |
|
} |
|
|
|
function gatherMetadata(i = 0, delay = 2000) { |
|
console.log({i}); |
|
const metadata = processRecordingElement(recordingElements[i]); |
|
metadataElements.push(metadata); |
|
console.log({metadata}); |
|
printAll(); |
|
|
|
setTimeout(() => { |
|
if (i < iterationDepth) { |
|
gatherMetadata(i + 1); // Keep loading |
|
} else { |
|
console.log("All recordings loaded. Found:", getNumberOfRecordings()); |
|
printAll(); |
|
} |
|
}, delay); |
|
} |
|
|
|
gatherMetadata(); |
|
|
|
|
|
} |
|
|
|
|
|
(function() { |
|
'use strict'; |
|
// wait 5 seconds, then process the metadata |
|
window.addEventListener('load', () => setTimeout(processAllMetadata, 5000)); |
|
})(); |