-
-
Save vothanhkiet/64d334d8fa551eeef62773e0c56cab69 to your computer and use it in GitHub Desktop.
Download Videos from https://plus.gopro.com/media-library/
This file contains hidden or 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
| console.error("WARNING: you must check browser download status for failed downloads!"); | |
| // time to wait before starting download in ms, you can change it during the download and changes will take effect | |
| var TIMEOUT_WAIT_DOWNLOAD_VIDEO = 2 * 60 * 1000; | |
| var TIMEOUT_WAIT_DOWNLOAD_PHOTO = 10 * 1000; | |
| // if you want to download only selected files change it to const DOWNLOAD_ONLY = ["GX010709.MP4", "GX010710.MP4"]; | |
| const DOWNLOAD_ONLY = []; | |
| // time to wait for popup initialization (load available download options), increase if it takes more | |
| var TIMEOUT_WAIT_POPUP = 3000; | |
| (function() { | |
| var stats = null; | |
| var stopped = false; | |
| function click(item) { | |
| const event = document.createEvent ('MouseEvents'); | |
| event.initEvent ("click", true, true); | |
| item.dispatchEvent(event); | |
| } | |
| function xpath(context, query) { | |
| return document.evaluate(query, context, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; | |
| } | |
| function startDownload(item) { | |
| // open menu | |
| click(xpath(item.parentElement, "div[contains(@class, 'dropdown')]/button")); | |
| setTimeout(function() { | |
| // find download button, wait for it if necessary | |
| if (stopped) { | |
| console.log(new Date().toISOString(), "Explicitly stopped"); | |
| return; | |
| } | |
| const fullLength = xpath(item.parentElement, "div[@class='dropdown open']//a[contains(@id,' Original quality - Full length')]"); | |
| const originalQuality = xpath(item.parentElement, "div[@class='dropdown open']//a[contains(@id, 'Original quality')]"); | |
| const singleMedia = xpath(item.parentElement, "div[@class='dropdown open']//li[@class='download-menu-item']/a[contains(@id, 'download-single-media')]"); | |
| const download = fullLength || originalQuality || singleMedia; | |
| if (download) { | |
| click(download); | |
| // deselect video: | |
| click(item); | |
| // continue with the rest of the videos: | |
| setTimeout(() => startAll(), isPhoto(getFilename(item)) ? TIMEOUT_WAIT_DOWNLOAD_PHOTO : TIMEOUT_WAIT_DOWNLOAD_VIDEO); | |
| return; | |
| } | |
| console.log(new Date().toISOString(), "Can't download ", getFilename(item), item, ", retrying"); | |
| startDownload(item); | |
| }, TIMEOUT_WAIT_POPUP); | |
| } | |
| function calculateExpected(current) { | |
| const expected = current.photo * (TIMEOUT_WAIT_DOWNLOAD_PHOTO + TIMEOUT_WAIT_POPUP) + (current.edit + current.video) * (TIMEOUT_WAIT_DOWNLOAD_VIDEO + TIMEOUT_WAIT_POPUP); | |
| if (expected < 1000) { | |
| return "1 second"; | |
| } | |
| if (expected < 60 * 1000) { | |
| return `${expected / 1000} seconds`; | |
| } | |
| if (expected < 60 * 60 * 1000) { | |
| return `${(expected / (60 * 1000)).toFixed(2)} minutes`; | |
| } | |
| return `${(expected / (60 * 60 * 1000)).toFixed(2)} hours`; | |
| } | |
| function isPhoto(name) { | |
| return name.toUpperCase().endsWith(".JPG"); | |
| } | |
| function showEstimates() { | |
| const filenames = videos.map(a => getFilename(a)); | |
| const all = filenames.length; | |
| const photo = filenames.filter(a => isPhoto(a)).length; | |
| const edit = filenames.filter(a => !a).length; | |
| const current = { | |
| all: all, | |
| photo: photo, | |
| edit: edit, | |
| video: all - photo - edit, | |
| }; | |
| if (!stats) { | |
| stats = current; | |
| } | |
| const completed = { | |
| all: stats.all - current.all, | |
| photo: stats.photo - current.photo, | |
| edit: stats.edit - current.edit, | |
| video: stats.video - current.video, | |
| }; | |
| const expected = calculateExpected(current); | |
| console.log(new Date().toISOString(), `More than ${expected} expected, completed: ${completed.all}/${stats.all}, photo: ${completed.photo}/${stats.photo}, edit: ${completed.edit}/${stats.edit}, video: ${completed.video}/${stats.video}, you can adjust variables TIMEOUT_WAIT_DOWNLOAD_VIDEO and TIMEOUT_WAIT_DOWNLOAD_PHOTO while script is running`); | |
| } | |
| function startAll() { | |
| showEstimates(); | |
| item = videos.shift(); | |
| if (!item) { | |
| console.log(new Date().toISOString(), "DONE"); | |
| stopPrevious(); | |
| return; | |
| } | |
| console.log(new Date().toISOString(), "Starting", getFilename(item), item); | |
| startDownload(item); | |
| } | |
| function stopPrevious() { | |
| const stopButton = xpath(document, "//button[@id='stopButton']"); | |
| if (stopButton) { | |
| click(stopButton); | |
| } | |
| } | |
| function getFilename(item) { | |
| const label = xpath(item.parentElement.parentElement, "div[@class='filename-overlay']"); | |
| if (!label) { | |
| // it is edit | |
| return ""; | |
| } | |
| return label.textContent; | |
| } | |
| function videosToDownload() { | |
| const selected = []; | |
| const query = document.evaluate("//button[@class='collection-item-menu-button select-button show-button selected']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); | |
| for (let i = 0, length = query.snapshotLength; i < length; ++i) { | |
| selected.push(query.snapshotItem(i)); | |
| } | |
| if (DOWNLOAD_ONLY.length === 0) { | |
| return selected; | |
| } | |
| if (new Set(DOWNLOAD_ONLY).size !== DOWNLOAD_ONLY.length) { | |
| throw new Error("Duplicates in DOWNLOAD_ONLY"); | |
| } | |
| const result = selected.filter(a => DOWNLOAD_ONLY.indexOf(getFilename(a)) !== -1); | |
| const filenames = result.map(a => getFilename(a)); | |
| DOWNLOAD_ONLY.filter(a => filenames.indexOf(a) === -1).forEach(a => console.error(`Can't find ${a}`)); | |
| DOWNLOAD_ONLY.filter(a => filenames.filter(b => b === a).length > 1).forEach(a => console.error(`More than once (${filenames.filter(b => b === a).length}): ${a}`)); | |
| return result; | |
| } | |
| stopPrevious(); | |
| const videos = videosToDownload(); | |
| console.log(new Date().toISOString(), "Going to download: ", videos, videos.map(a => getFilename(a))); | |
| const stopButton = document.createElement("button"); | |
| stopButton.innerHTML = "------~~~~~~[[[[[[ STOP DOWNLOADS ]]]]]]~~~~~~------"; | |
| stopButton.id = "stopButton"; | |
| stopButton.style.position = "fixed"; | |
| stopButton.style.top = 0; | |
| stopButton.style.zIndex = 1016; | |
| stopButton.addEventListener("click", () => { | |
| stopped = true; | |
| stopButton.remove(); | |
| console.error("WARNING: you must check browser download status for failed downloads!"); | |
| }, false); | |
| document.body.appendChild(stopButton); | |
| startAll(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment