Sleazy Fork is available in English.

Pornhub Video Download Helper

get videos of pornhub.com

  1. // ==UserScript==
  2. // @name Pornhub Video Download Helper
  3. // @namespace http://tampermonkey.net/
  4. // @version 2024-09-28
  5. // @description get videos of pornhub.com
  6. // @author PangPang
  7. // @match *://*.pornhub.com/view_video.php?viewkey=*
  8. // @icon https://www.google.com/s2/favicons?sz=64&domain=pornhub.com
  9. // @grant unsafeWindow
  10. // @grant GM_addStyle
  11. // @grant GM_openInTab
  12. // @license MIT
  13. // @require https://code.jquery.com/jquery-2.1.4.min.js
  14. // ==/UserScript==
  15.  
  16. (function() {
  17. 'use strict';
  18. let isClickedDownloadButton = false;
  19. window.addEventListener('load', function() {
  20.  
  21. const urls = getUrlInfo();
  22. const index = getHighQualityUrlIndex(urls);
  23.  
  24. const videoTitle = getVideoTitle();
  25. const videoFileName = sanitizeFileName(videoTitle + ".mp4");
  26.  
  27. addDownloadButtonToBody();
  28.  
  29. addClickEventToDownloadButton(videoTitle,videoFileName,urls[index].url);
  30. });
  31.  
  32. function getValueByObjStartStr(obj,str){
  33. const arrays = [];
  34. Object.keys(obj).forEach(key => {
  35. if (key.startsWith(str)) {
  36. arrays.push({
  37. key: key,
  38. value: obj[key],
  39. });
  40. }
  41. });
  42. return arrays;
  43. }
  44.  
  45. function getUrlInfo(){
  46. const allVideoUrls = getValueByObjStartStr(unsafeWindow,"flashvars_");
  47.  
  48. if(allVideoUrls.length === 0 || allVideoUrls[0]['value']['mediaDefinitions'].length === 0){
  49. return;
  50. }
  51.  
  52. let remoteAddress = undefined;
  53. allVideoUrls[0]['value']['mediaDefinitions'].forEach(item => {
  54. if(item.remote){
  55. remoteAddress = item.videoUrl;
  56. }else{
  57. return;
  58. }
  59. });
  60.  
  61. let urlInfo = [];
  62. if (remoteAddress) {
  63. $.ajax({
  64. url: remoteAddress,
  65. async: false,
  66. success: (data) => {
  67. if (data && data.length) {
  68. urlInfo = urlInfo.concat(data.map(item => ({
  69. quality: item.quality + '.' + item.format,
  70. url: item.videoUrl
  71. })));
  72. }
  73. }
  74. });
  75. }
  76. return urlInfo;
  77.  
  78. }
  79.  
  80. function getHighQualityUrlIndex(urlArray){
  81. let index = 0;
  82. for(let i = 1; i < urlArray.length; i++){
  83. const currentUrlQuality = parseInt(urlArray[i].quality.split(",")[0]);
  84. const highQualityUrl = parseInt(urlArray[index].quality.split(",")[0]);
  85. console.log(currentUrlQuality+' '+highQualityUrl)
  86. if(currentUrlQuality > highQualityUrl){
  87.  
  88. index = i;
  89. }
  90.  
  91. }
  92. return index;
  93.  
  94. }
  95.  
  96. function getVideoTitle(){
  97. const title = $("span.inlineFree").text();
  98. return title
  99. }
  100.  
  101. function sanitizeFileName(fileName) {
  102. // 定义非法字符的正则表达式
  103. const illegalChars = /[\/\\:*?"<>|]/g;
  104. // 替换非法字符为空字符串
  105. return fileName.replace(illegalChars, '');
  106. }
  107.  
  108. function addDownloadButtonToBody(){
  109. let css = `
  110. #download_video{
  111. display: inline-block;
  112. align-items: center;
  113. display: flex;
  114. z-index:999;
  115. background:red;
  116. border-radius:5px;
  117. cursor:pointer;
  118. font:bold
  119. color:#fff
  120. vertical-align: middle;
  121. }
  122. #download_video span{
  123. display: inline-block;
  124. vertical-align: middle;
  125. }
  126. `
  127. GM_addStyle(css)
  128. const buttonHtml =
  129. `
  130. <div id="download_video"><span class="inlineFree title inlineFree">Download Video</span></div>
  131. `
  132. $("div.suggestToggleAlt ").after(buttonHtml);
  133.  
  134.  
  135.  
  136.  
  137. }
  138.  
  139. function addClickEventToDownloadButton(title,videoName,videoUrl){
  140. $("div#download_video").click(function(){
  141. if(isClickedDownloadButton){
  142. return;
  143. }
  144. isClickedDownloadButton = true;
  145.  
  146. const htmlContent = creatDownloadWindowHtml(title,videoName,videoUrl);
  147. const blob = new Blob([htmlContent], { type: 'text/html' });
  148.  
  149. // 生成一个 URL,指向 Blob 内容
  150. const blobUrl = URL.createObjectURL(blob);
  151. GM_openInTab(blobUrl, { active: false, insert: true });
  152. });
  153.  
  154. }
  155.  
  156. function creatDownloadWindowHtml(title,videoName,videoUrl){
  157.  
  158. return `
  159. <!DOCTYPE html>
  160. <html lang="zh-CN">
  161. <head>
  162. <script src="https://cdn.jsdelivr.net/npm/web-streams-polyfill@2.0.2/dist/ponyfill.min.js"></script>
  163. <script src="https://cdn.jsdelivr.net/npm/streamsaver@2.0.3/StreamSaver.min.js"></script>
  164. <script>
  165. import streamSaver from 'streamsaver'
  166. const streamSaver = require('streamsaver')
  167. const streamSaver = window.streamSaver
  168. </script>
  169. <meta charset="UTF-8" />
  170. <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  171. <title>${title}</title>
  172. <style>
  173. html,
  174. body {
  175. margin: 0;
  176. padding: 0;
  177. background-color: black;
  178. display: flex;
  179. justify-content: center;
  180. align-items: center;
  181. height: 100vh;
  182. overflow: hidden;
  183. }
  184. video {
  185. max-width: 100%;
  186. max-height: 100%;
  187. }
  188. </style>
  189. </head>
  190. <body>
  191. <video id="video" autoplay controls>
  192. <source
  193. src="${videoUrl}"
  194. type="video/mp4"
  195. />
  196. 您的浏览器不支持 HTML5 视频标签。
  197. </video>
  198.  
  199. <script>
  200. // 当视频加载元数据时,调整窗口大小以适应视频
  201. const videoElement = document.getElementById("video");
  202. videoElement.addEventListener("loadedmetadata", () => {
  203. // 获取视频的宽度和高度
  204. const videoWidth = videoElement.videoWidth;
  205. const videoHeight = videoElement.videoHeight;
  206.  
  207. // 调整浏览器窗口大小以适应视频
  208. window.resizeTo(videoWidth, videoHeight);
  209. });
  210. fileDownloadHandle("${videoUrl}","get","${videoName}")
  211.  
  212.  
  213. function fileDownloadHandle(url,method,name,size){
  214. fetch(url,{
  215. method:method,
  216. }).then(res=>{
  217. const fileStream=streamSaver.createWriteStream(name,{
  218. size:res.headers.get("content-length")
  219. })
  220. const readableStream=res.body;
  221. if(window.WritableStream&&readableStream.pipeTo){
  222. return readableStream.pipeTo(fileStream).then(()=> {
  223. //下载完毕自动关闭
  224. window.close();
  225. })
  226. }
  227. window.writer=fileStream.getWriter();
  228. const reader=res.body.getReader();
  229. const pump=()=>reader.read().then(res=>res.done? window.writer.close():window.writer.write(res.value).then(pump))
  230. pump();
  231. })
  232. }
  233.  
  234. </script>
  235. </body>
  236. </html>
  237. `
  238. }
  239. })();