This adds a button to pornhub channel pages to get links to all videos
// ==UserScript== // @name pornhub.com channel videos link grabber // @description This adds a button to pornhub channel pages to get links to all videos // @version 1.0.1 // @license MIT // @match https://*.pornhub.com/channels/* // @match https://*.pornhubpremium.com/channels/* // @namespace https://sleazyfork.org/users/179893 // @grant GM.xmlHttpRequest // ==/UserScript== (function () { 'use strict' addcss(`.loader { position: absolute; left: 50%; top: 50%; z-index: 200; border: 16px solid #f3f3f3; /* Light grey */ border-top: 16px solid #3498db; /* Blue */ border-radius: 50%; width: 120px; height: 120px; margin: -60px 0 0 -60px; animation: spin 2s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }`) var linkList = [] var currentUrl = window.location.href var paginationBaseUrl = currentUrl var myRegexp = /^(https:\/\/.*.pornhub.*.com\/channels\/)([^\/]*)(\/.*)?$/g var match = myRegexp.exec(currentUrl) if (match) { if (currentUrl.indexOf('/videos') === -1) { paginationBaseUrl += '/videos' } // paginationBaseUrl = match[1] + match[2] + '/videos' console.log('matched: ' + paginationBaseUrl) } var btnContainer = document.querySelector('.rightSide .sectionChannelsWrapper div.title') var linkGrabberBtn = document.createElement('a') linkGrabberBtn.text = 'Get all video links' linkGrabberBtn.onclick = startGettingAllTheLinks linkGrabberBtn.classList.add('greyButton') linkGrabberBtn.classList.add('float-right') btnContainer.appendChild(linkGrabberBtn) function startGettingAllTheLinks () { Promise.resolve(ShowLoader()) // FIXME This doesn't work for some reason .then(getLinks) .then(() => { console.log('found ' + linkList.length + ' links') for (var i = 0; i < linkList.length; i++) { console.log(linkList[i].href) } }) .then(RemoveLoader()) .then(showLinks) } const wait = ms => new Promise((resolve) => setTimeout(resolve, ms)) const addQueryParam = function (url, paramName, paramValue) { if (url.indexOf('?') !== -1) { let newUrl = new URL(url) if (newUrl.searchParams.has(paramName)) { newUrl.searchParams.delete(paramName) } newUrl.searchParams.append(paramName, paramValue) return newUrl.toString() } else { return url + '?' + paramName + '=' + paramValue } } const getLinks = async function (index = 1) { let url = addQueryParam(paginationBaseUrl, 'page', index) let responseData try { responseData = await makeRequest('GET', url) } catch (error) { console.log(error) } if (responseData) { if (responseData.status && responseData.status >= 200 && responseData.status < 300) { extractLinks(responseData.response) console.log('parsed page ' + index) await getLinks(index + 1) } else { switch (responseData.status) { case 404: console.log('404 for page ' + index + '. Looks like ' + (index - 1) + ' is the last page.') break case 429: console.log('Too many requests. I\'ll wait a bit and try again') await wait(1000) await getLinks(index) break default: console.error('Augh, there was an error!', responseData.status, responseData.statusText, index) break } } } else { console.log('now this is unexpected ...') } } function extractLinks (response) { var links = response.querySelectorAll('div.sectionChannelsWrapper ul.videos li .title a') for (var i = 0; i < links.length; i++) { linkList.push(links[i]) } } function makeRequest (method, url) { console.log('new Request ' + method + ' ' + url) return new Promise(function (resolve, reject) { GM.xmlHttpRequest({ method: method, url: url, onload: function (response) { var responseXML = null responseXML = new DOMParser() .parseFromString(response.responseText, 'text/html') resolve({ response: responseXML, status: response.status, statusText: response.statusText }) }, onerror: function (response) { reject(new Error('the request ' + method + ' ' + url + ' failed')) } }) // var xhr = new XMLHttpRequest() // xhr.open(method, url) // xhr.responseType = 'document' // xhr.onload = function () { // resolve({ // response: xhr.response, // status: this.status, // statusText: xhr.statusText, // pageIndex: pageIndex // }) // } // xhr.onerror = function () { // reject(new Error('the request ' + method + ' ' + url + ' failed')) // } // xhr.send() }) } function ShowLoader () { var loader = document.createElement('div') loader.id = 'myVeryOwnCustomLoaderUsingAnUniqueId' loader.className = 'loader' document.body.appendChild(loader) } function RemoveLoader () { var loader = document.getElementById('myVeryOwnCustomLoaderUsingAnUniqueId') if (loader) { loader.parentNode.removeChild(loader) } } function addcss (css) { var head = document.getElementsByTagName('head')[0] var s = document.createElement('style') s.setAttribute('type', 'text/css') if (s.styleSheet) { // IE s.styleSheet.cssText = css } else { // the world s.appendChild(document.createTextNode(css)) } head.appendChild(s) } function showLinks () { var outerModalDiv = document.createElement('div') var innerModalDiv = document.createElement('div') outerModalDiv.id = 'pornstarVidsLinkContainingModalPanel' // use a long id to avoid name conflicts outerModalDiv.style.display = 'block' outerModalDiv.style.position = 'fixed' outerModalDiv.style.zIndex = '100' outerModalDiv.style.paddingTop = '100px' outerModalDiv.style.left = '0' outerModalDiv.style.top = '0' outerModalDiv.style.width = '100%' outerModalDiv.style.height = '100%' outerModalDiv.style.overflow = 'auto' outerModalDiv.style.backgroundColor = 'rgb(0,0,0)' outerModalDiv.style.backgroundColor = 'rgb(0,0,0,0.4)' // add close btn var closeButtonContainer = document.createElement('div') closeButtonContainer.className = 'userButtons' var closeButton = CreateButton('X', null, RemoveOuterModalPanel) closeButton.style.cssFloat = 'right' closeButtonContainer.appendChild(closeButton) innerModalDiv.appendChild(closeButtonContainer) innerModalDiv.style.backgroundColor = '#1b1b1b' innerModalDiv.style.margin = 'auto' innerModalDiv.style.padding = '20px' innerModalDiv.style.border = '1px solid #888' innerModalDiv.style.width = '80%' innerModalDiv.style.color = '#ababab' var instructions1 = document.createElement('p') var instructions2 = document.createElement('p') var instructions3 = document.createElement('p') instructions1.innerHTML = 'Save the links to a local textfile (e.g. "C:\\Temp\\dl\\linklist.txt)" and run youtube-dl whith the -a argument and the path to the linklist.' instructions2.innerHTML = 'youtube-dl -a "C:\\Temp\\dl\\linklist.txt"' instructions3.innerHTML = 'Or just use jDownloader with the linklist' innerModalDiv.appendChild(instructions1) innerModalDiv.appendChild(instructions2) innerModalDiv.appendChild(instructions3) var linkListDiv linkListDiv = document.createElement('div') for (var i = 0; i < linkList.length; i++) { var a = document.createElement('a') var p = document.createElement('p') p.innerHTML = linkList[i].href a.href = linkList[i].href a.download = linkList[i].title a.appendChild(p) linkListDiv.appendChild(a) } innerModalDiv.appendChild(linkListDiv) outerModalDiv.appendChild(innerModalDiv) document.body.appendChild(outerModalDiv) } function CreateButton (text, id, onClickEvent) { var innerbutton = document.createElement('button') innerbutton.innerText = text innerbutton.className = 'buttonBase' innerbutton.style.backgroundColor = '#f90' innerbutton.style.color = '#000' innerbutton.style.fontWeight = '700' innerbutton.display = 'inline-block' var button = document.createElement('div') if (id) button.id = id button.style.padding = '5px 10px' button.style.lineHeight = '1.2em' button.style.borderRadius = '4px' button.onclick = onClickEvent button.appendChild(innerbutton) return button } function RemoveOuterModalPanel () { var toRemove = document.getElementById('pornstarVidsLinkContainingModalPanel') toRemove.parentNode.removeChild(toRemove) } })()