Skip to content

Instantly share code, notes, and snippets.

@mozurin
Last active December 28, 2020 06:56
Show Gist options
  • Save mozurin/0c3bc302b1106f1adb7d31e616c7df9b to your computer and use it in GitHub Desktop.
Save mozurin/0c3bc302b1106f1adb7d31e616c7df9b to your computer and use it in GitHub Desktop.
Tampermonkey script that extracts hi-res MP4 movie file URLs from tweet permalink page.
// ==UserScript==
// @name Twitter video extractor
// @namespace https://dislife.com/
// @version 0.0.3
// @description Extract hi-res MP4 movie file URLs from tweet permalink page
// @author mizuki@mozurin.com
// @match https://twitter.com/*/status/*
// @grant none
// @run-at document-body
// ==/UserScript==
(function() {
'use strict';
const injectCode = function ()
{
const APP_NAME = 'Twitter video extractor';
const APP_ID = 'TwitterVideoExtractor';
const MENU_TEXT = 'Extract video file URLs';
const NOTFOUND_TEXT = 'Video files are not found.';
function log(msg)
{
console.log('[' + APP_ID + '] ' + msg);
}
// video information extractor
let jsonTree;
function statusFetched(response)
{
jsonTree = (
typeof response == 'string'?
JSON.parse(response) : response
);
}
function parseAndShowList()
{
if (!jsonTree) {
log('Tweet JSON tree not fetched. Updating the script update might be required.');
return;
}
if (document.getElementById(APP_ID)) {
log('Video list box is already open.');
return;
}
const foundVideos = [];
for (const tweet of Object.values(jsonTree.globalObjects.tweets)) {
if (!tweet.extended_entities || !tweet.extended_entities.media) {
continue;
}
for (const media of tweet.extended_entities.media) {
if (media.type != 'video') {
continue;
}
foundVideos.push(
{
thumbnail: media.media_url_https,
url: media.video_info.variants.sort(
(a, b) => (b.bitrate? b.bitrate : 0) - (a.bitrate? a.bitrate : 0)
)[0].url,
}
);
}
}
// build and show popup
const parserBox = document.createElement('div');
let innerSrc = `
<div id="${APP_ID}" style="position: fixed; left: 3em; right: 3em; top: 3em;
bottom: 3em; background: white; border: solid gray 1px;">
<p style="background: #348; color: white; margin: 0;
padding: 0.5em; padding-left: 3em; overflow: hidden; line-height: 3em;">
${APP_NAME}
<button type="button" style="float: right; height: 3em;"
onclick="document.getElementById('${APP_ID}').remove();">
close
</button>
</p>
`;
if (foundVideos.length < 1) {
innerSrc += `
<p style="color: red; font-size: 120%; font-weight: bold; text-align: center;">
${NOTFOUND_TEXT}
</p>
`;
} else {
innerSrc += `
<div style="overflow: hidden scroll; position: absolute; top: 4em;
bottom: 0; width: 100%;">
`;
for (const video of foundVideos) {
innerSrc += `
<a href="${video.url}">
<img src="${video.thumbnail}" style="height: 20em; width: auto;
margin: 1em; float: left;" />
</a>
`;
}
innerSrc += '</div>';
}
innerSrc += '</div>';
parserBox.innerHTML = innerSrc;
document.body.appendChild(parserBox.children[0]);
}
// append contextmenu
window.addEventListener(
'DOMContentLoaded',
() => {
const menuId = document.body.getAttribute('contextmenu');
let menu;
if (menuId) {
menu = document.getElementById(menuId);
} else {
menu = document.createElement('menu');
menu.setAttribute('id', 'gmscript-menu');
menu.setAttribute('type', 'context');
document.body.appendChild(menu);
document.body.setAttribute('contextmenu', 'gmscript-menu');
}
const item = document.createElement('menuitem');
item.textContent = MENU_TEXT;
item.addEventListener('click', parseAndShowList, false);
menu.appendChild(item);
},
false
);
// install hook into XMLHttpRequest
const originXHR = window.XMLHttpRequest;
window.XMLHttpRequest = function ()
{
const xhr = new originXHR(...arguments);
const originOpen = xhr.open;
xhr.open = function (method, url)
{
if (
url.match(/\/api\/2\/timeline\/conversation\/\d+\.json/) ||
url.match(/\/api\/2\/rux\.json/)
) {
this.addEventListener(
'readystatechange',
function ()
{
if (this.readyState != XMLHttpRequest.DONE) {
return;
}
statusFetched(this.response);
},
false
);
}
return originOpen.apply(this, arguments);
};
return xhr;
};
Object.setPrototypeOf(window.XMLHttpRequest, originXHR);
};
const script = document.createElement('script');
script.innerHTML = '(' + injectCode.toString() + ')();';
document.body.appendChild(script);
})();
@coo11
Copy link

coo11 commented Dec 28, 2020

It doesn't work in my chrome and shows “Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'unsafe-inline'”

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment