|
// ==UserScript== |
|
// @name Plays.tv - Download JSON data & cURL Script for Uploads |
|
// @namespace https://github.com/Decicus |
|
// @match https://plays.tv/i/uploaded |
|
// @license MIT |
|
// @version 1.1.2 |
|
// @author Decicus |
|
// @updateUrl https://gist.github.com/Decicus/d3bb76d309336d778d1b1f6237b9855f/raw/playstv-curl-json-export.user.js |
|
// @require https://cdn.jsdelivr.net/npm/[email protected]/build/global/luxon.min.js |
|
// ==/UserScript== |
|
|
|
/** |
|
* This script (unfortunately) requires you to have cURL (https://curl.haxx.se/) installed (somehow) |
|
* and then also being able to use it via the terminal. |
|
* |
|
* Once you've downloaded the script ("Download cURL Script"), I recommend moving the script into an empty directory |
|
* as all the files will be downloaded in the same directory (generally speaking) |
|
* |
|
* ======================================== [Linux] ======================================== |
|
* Assuming you're familiar with the terminal, you can use your package manager. Package is usually just called `curl` |
|
* After curl is installed, in the terminal, make sure you're in the correct directory (`cd /home/alex/Downloads` for instance) |
|
* |
|
* Then run: `sh PlaysTV_SomeRandomTextHere.sh` and it should start downloading all the videos/images. |
|
* You can usually type `sh PlaysTV`, hit TAB and the terminal will auto-complete the filename for you. |
|
* |
|
* ======================================== [Windows] ======================================== |
|
* I recommend using git bash. cURL is included when you install git bash (apparently): https://git-scm.com/download |
|
* Once installed, open git bash and navigate to where you downloaded the script. |
|
* |
|
* In my example it's just in my user's "Downloads" directory. Since git bash automatically opens the user directory |
|
* you can just type `cd Downloads` (usually) |
|
* |
|
* Once you're in the correct directory, run `bash PlaysTV_RandomTextHere.sh` |
|
* You can usually type `bash PlaysTV`, hit TAB and git bash will auto-complete the filename for you. |
|
* |
|
* ======================================== [JSON] ======================================== |
|
* The JSON file ("Download JSON" button) is mainly to preserve metadata (video statistics like views and such) and you can safely ignore |
|
* that unless you're actually interested in saving said metadata. |
|
*/ |
|
|
|
async function getUserId() { |
|
const response = await fetch('https://plays.tv/ws/test?_orbitalapp=1', { |
|
credentials: 'include', |
|
}); |
|
|
|
const data = await response.json(); |
|
|
|
/** |
|
* Ignore the public user ID and return null. |
|
*/ |
|
if (data.msg === 'Public user id') { |
|
console.log(data); |
|
return null; |
|
} |
|
|
|
return data.data; |
|
} |
|
|
|
function filterFilename(input) { |
|
/** |
|
* Ghetto-rigged based on: https://github.com/sindresorhus/filename-reserved-regex/blob/master/index.js |
|
* Thanks, Sindre. |
|
*/ |
|
const filenameReserved = /[<>:"\/\\|?*\x00-\x1F]/g; |
|
const filenameReservedWin = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])$/i; |
|
|
|
input = input.replace(filenameReserved, '-'); |
|
input = input.replace(filenameReservedWin, '-'); |
|
return input; |
|
} |
|
|
|
async function getVideos(userId) { |
|
const url = `https://plays.tv/playsapi/feedsys/v1/userfeed/${userId}/uploaded?limit=10000`; |
|
const response = await fetch(url, { |
|
credentials: 'include', |
|
}); |
|
|
|
const data = await response.json(); |
|
return data; |
|
} |
|
|
|
async function getCurl(videos) { |
|
const dt = luxon.DateTime; |
|
|
|
const media = videos.items.map((vid) => { |
|
const game = vid.gameTitle || vid.gameId || 'Unknown'; |
|
const {created, description, downloadUrl, posterUrl, feedId, type} = vid; |
|
const ext = type === 'video' ? 'mp4' : 'jpg'; |
|
|
|
const createdDate = dt.fromMillis(created); |
|
const date = createdDate.toFormat('y-MM-dd_ttt'); |
|
|
|
let basename = `${game}_${description}_${date}.${feedId}`; |
|
// Escape single quotes |
|
basename = filterFilename(basename); |
|
|
|
const finalName = `${basename}.${ext}`; |
|
const thumbnailName = `${basename}.thumbnail.jpg`; |
|
|
|
return { |
|
game, |
|
downloadUrl, |
|
posterUrl, |
|
filename: finalName, |
|
thumbnail: thumbnailName, |
|
}; |
|
}); |
|
|
|
const commands = []; |
|
|
|
for (const file of media) { |
|
const { downloadUrl, filename, posterUrl, thumbnail } = file; |
|
|
|
/** |
|
* Video / screenshot |
|
*/ |
|
commands.push(`echo "Downloading ${filename}"`); |
|
commands.push(`curl -L -o "${filename}" '${downloadUrl}'`); |
|
|
|
/** |
|
* Thumbnail |
|
*/ |
|
commands.push(`echo "Downloading ${thumbnail}"`); |
|
commands.push(`curl -L -o "${thumbnail}" '${posterUrl}'`); |
|
} |
|
|
|
return commands.join('\n'); |
|
} |
|
|
|
async function run() { |
|
console.log('[Loaded] Plays.TV - Download JSON for Uploads'); |
|
|
|
const userId = await getUserId(); |
|
|
|
if (!userId) { |
|
return; |
|
} |
|
|
|
const videos = await getVideos(userId); |
|
const data = new Blob([JSON.stringify(videos)], { |
|
type: 'application/json', |
|
}); |
|
|
|
const curlCmds = await getCurl(videos); |
|
const curlData = new Blob([curlCmds], { |
|
type: 'text/plain', |
|
}); |
|
|
|
const file = window.URL.createObjectURL(data); |
|
const curlFile = window.URL.createObjectURL(curlData); |
|
|
|
const banner = document.querySelector('.sunset'); |
|
|
|
const learnMore = banner.querySelector('.learn-more'); |
|
|
|
const button = document.createElement('a'); |
|
button.appendChild(document.createTextNode('Download JSON')); |
|
button.className = learnMore.className.replace('learn-more', 'download-json'); |
|
button.href = file; |
|
button.download = `PlaysTV_${userId}.json`; |
|
|
|
const curlBtn = document.createElement('a'); |
|
curlBtn.appendChild(document.createTextNode('Download cURL script')); |
|
curlBtn.className = learnMore.className.replace('learn-more', 'download-curl'); |
|
curlBtn.href = curlFile; |
|
curlBtn.download = `PlaysTV_cURL_${userId}.sh`; |
|
|
|
learnMore.insertAdjacentElement('afterend', button); |
|
learnMore.insertAdjacentElement('afterend', curlBtn); |
|
} |
|
|
|
window.addEventListener('load', async () => { |
|
const interval = setInterval(async () => { |
|
const banner = document.querySelector('.sunset'); |
|
if (!banner) |
|
{ |
|
console.log('[Plays.TV Videos JSON & cURL Script] Banner does not exist... Continuing interval.'); |
|
return; |
|
} |
|
|
|
clearInterval(interval); |
|
await run(); |
|
}, 500); |
|
}); |