Greasy Fork is available in English.

you-get-fork

视频下载 bilibili acfun 快手 抖音 腾讯视频 youku

  1. // ==UserScript==
  2. // @name you-get-fork
  3. // @description 视频下载 bilibili acfun 快手 抖音 腾讯视频 youku
  4. // @description:en-US fork from [460910],由于官方API变更,CCTV和油管已失效
  5. // @namespace https://greasyfork.org/users/135090
  6. // @version 0.0.2
  7. // @author You
  8. // @run-at document-end
  9. // @match https://www.bilibili.com/video/BV*
  10. // @match https://www.douyin.com/video/*
  11. // @match https://www.kuaishou.com/short-video/*
  12. // @match https://www.acfun.cn/v/ac*
  13. // @match https://v.qq.com/x/*
  14. // @match https://v.youku.com/v_*
  15. // @license MIT
  16. // @icon data:image/png;base64,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
  17. // @grant none
  18. // ==/UserScript==
  19. (() => {
  20. // src/util/index.js
  21. var download = async (blob, fileName) => {
  22. let link = window.URL.createObjectURL(blob);
  23. const a = document.createElement("a");
  24. a.download = `${fileName}`;
  25. a.href = link;
  26. document.body.appendChild(a);
  27. a.click();
  28. document.body.removeChild(a);
  29. window.URL.revokeObjectURL(link);
  30. };
  31. var safetyParse = (str) => {
  32. try {
  33. return JSON.parse(str);
  34. } catch (error) {
  35. return null;
  36. }
  37. };
  38. var getFile = async (url) => {
  39. const res = await fetch(url);
  40. const reader = res.body.getReader();
  41. const contentLength = +res.headers.get("Content-Length");
  42. if (!contentLength) {
  43. const data = await res.arrayBuffer();
  44. return new Uint8Array(data);
  45. }
  46. let receivedLength = 0;
  47. let chunks = [];
  48. while (true) {
  49. const { done, value } = await reader.read();
  50. if (done) {
  51. break;
  52. }
  53. chunks.push(value);
  54. receivedLength += value.length;
  55. console.log(
  56. `fileSize: ${contentLength} %c downloaded ${receivedLength}`,
  57. "background: #222; color: #bada55"
  58. );
  59. }
  60. return new Blob(chunks);
  61. };
  62. var getUrlsByM3u8 = async (url, parser) => {
  63. const urlObj = new URL(url);
  64. urlObj.pathname = urlObj.pathname.split("/").slice(0, -1).join("/");
  65. urlObj.search = "";
  66. const base = urlObj.toString();
  67. const res = await fetch(url);
  68. const data = await res.text();
  69. return data.split("\n").filter((i) => !!i && !i.startsWith("#")).map((i) => {
  70. if (parser) {
  71. return parser(i);
  72. }
  73. return i.startsWith("/") ? `${base}${i}` : `${base}/${i}`;
  74. });
  75. };
  76. var getFiles = (urls, max = 8) => {
  77. let connections = 0;
  78. let files = [];
  79. urls = urls.map((i, index) => ({
  80. ...i,
  81. index
  82. }));
  83. return new Promise((resolve, reject) => {
  84. const getSingleFile = async ({ url, index, ...rest }) => {
  85. if (connections < max) {
  86. try {
  87. connections = connections + 1;
  88. const data = await getFile(url);
  89. connections = connections - 1;
  90. files[index] = data;
  91. if (urls?.length) {
  92. getSingleFile(urls.shift());
  93. } else {
  94. connections === 0 && resolve(files);
  95. }
  96. } catch (error) {
  97. console.log(error);
  98. urls.push({
  99. url,
  100. index,
  101. ...rest
  102. });
  103. }
  104. }
  105. };
  106. new Array(max).fill(0).forEach((i) => {
  107. getSingleFile(urls.shift());
  108. });
  109. });
  110. };
  111.  
  112. // src/module/acfun.js
  113. var getVideoInfo = async () => {
  114. const m3u8FileUrl = safetyParse(window?.pageInfo?.currentVideoInfo?.ksPlayJson)?.adaptationSet?.[0]?.representation?.[0]?.url;
  115. const urls = await getUrlsByM3u8(m3u8FileUrl);
  116. return urls.map((url) => ({ url }));
  117. };
  118. var acfun_default = async () => {
  119. const urls = await getVideoInfo();
  120. const files = await getFiles(urls);
  121. var tname = document.title;
  122. download(new Blob(files), tname+".mpeg");
  123. };
  124.  
  125. // src/module/bilibili.js
  126. var getBilibiliVideoInfo = async () => {
  127. const res = await fetch(window.location.href);
  128. const str = await res.text();
  129. const data = JSON.parse(
  130. str.match(/window.__playinfo__=([\d\D]+?)<\/script>/)[1]
  131. );
  132. const dash = data?.data?.dash;
  133. const video = dash.video.sort((a, b) => b?.width - a?.width)?.[0];
  134. const audio = dash.audio[0];
  135. const btit = document.title.substring(0,document.title.length-14);
  136. return [
  137. {
  138. url: video.baseUrl,
  139. fileName: btit+".m4v"
  140. },
  141. {
  142. url: audio.baseUrl,
  143. fileName: btit+".m4a"
  144. }
  145. ];
  146. };
  147. var bilibili_default = async () => {
  148. const urls = await getBilibiliVideoInfo();
  149. while (urls?.length) {
  150. const { url, fileName } = urls.shift();
  151. const file = await getFile(url);
  152. download(file, fileName);
  153. }
  154. };
  155.  
  156. // src/module/douyin.js
  157. var getDouyinVideoInfo = () => {
  158. const urls = [...document.querySelectorAll("video source")].map((i) => i.src);
  159. var dyvt=document.title.concat(".mp4");
  160. return {
  161. url: urls[0],
  162. fileName: dyvt
  163. };
  164. };
  165. var douyin_default = async () => {
  166. const url = await getDouyinVideoInfo();
  167. const file = await getFile(url.url);
  168. download(file, url.fileName);
  169. };
  170.  
  171. // src/module/kuaishou.js
  172. var getKuaishouVideoInfo = () => {
  173. var ktname = document.title;
  174. const urls = [...document.querySelectorAll("video")].map((i) => i.src);
  175. return {
  176. url: urls[0],
  177. fileName: ktname+".mp4"
  178. };
  179. };
  180. var kuaishou_default = async () => {
  181. const url = await getKuaishouVideoInfo();
  182. const file = await getFile(url.url);
  183. download(file, url.fileName);
  184. };
  185.  
  186. // src/module/qq.js
  187. var proxyhttpBody = null;
  188. var monitor = async () => {
  189. try {
  190. window.__PLAYER__.pluginMsg.emit = new Proxy(
  191. window.__PLAYER__.pluginMsg.emit,
  192. {
  193. apply: (...args) => {
  194. if (args?.[2]?.[0] === "PROXY_HTTP_START" && args?.[2]?.[1]?.vinfoparam) {
  195. console.log(args?.[2]?.[1]);
  196. proxyhttpBody = JSON.stringify(args?.[2]?.[1]);
  197. }
  198. return Reflect.apply(...args);
  199. }
  200. }
  201. );
  202. } catch (error) {
  203. console.log("monitor error");
  204. }
  205. };
  206. var getVideoInfo2 = async () => {
  207. let m3u8FileUrl = null;
  208. if (!m3u8FileUrl) {
  209. let res = await fetch("https://vd6.l.qq.com/proxyhttp", {
  210. method: "post",
  211. body: proxyhttpBody
  212. });
  213. res = await res.json();
  214. if (res?.errCode === 0 && res?.vinfo) {
  215. const data = safetyParse(res?.vinfo);
  216. const m3u8 = data?.vl?.vi?.sort((a, b) => b.vw - a.vw)?.[0]?.ul;
  217. if (m3u8) {
  218. m3u8FileUrl = m3u8.ui[0].url;
  219. }
  220. }
  221. }
  222. const urls = await getUrlsByM3u8(m3u8FileUrl);
  223. return urls.map((url) => ({
  224. url,
  225. fileName: `qqvideo.mp4`
  226. }));
  227. };
  228. if (window.location.host === "v.qq.com") {
  229. monitor();
  230. }
  231. var qq_default = async () => {
  232. const urls = await getVideoInfo2();
  233. const files = await getFiles(urls);
  234. download(new Blob(files), "qqvideo.mp4");
  235. };
  236. /*
  237. // src/module/cctv.js
  238. var cctv_default = async () => {
  239. let data = await fetch(`https://vdn.apps.cntv.cn/api/getHttpVideoInfo.do?pid=${window.guid}`);
  240. data = await data.json();
  241. const m3u8FileUrl =data?.hls_url;
  242. var mbase=m3u8FileUrl?.substring(0,m3u8FileUrl?.lastIndexOf("/")+1);
  243. const urls = await getUrlsByM3u8(m3u8FileUrl, (i) => i);
  244. const files = await getFiles(urls.map((url) => ({ url:mbase+url })));
  245. download(new Blob(files), `${data.title}.mpeg`);
  246. };
  247. */
  248. // src/module/youku.js
  249. var youku_default = async () => {
  250. if (!document.querySelector("meta#you-get-youku")) {
  251. const meta = document.createElement("meta");
  252. meta.httpEquiv = "Content-Security-Policy";
  253. meta.content = "upgrade-insecure-requests";
  254. meta.id = "you-get-youku";
  255. document.querySelector("head").appendChild(meta);
  256. }
  257. const data =window?.videoPlayer?.context?.mediaData?.mediaResource?._model?.streamList;
  258. if (data) {
  259. const m3u8FileUrl = data.sort((a, b) => b.width - a.width)[0].uri.HLS;
  260. const vtitle =window?.videoPlayer?.context?.mediaData?.mediaResource?._model?.video?.title;
  261. const urls = await getUrlsByM3u8(m3u8FileUrl, (i) => i);
  262. const files = await getFiles(urls.map((url) => ({ url })));
  263. document.title=vtitle+"---下载中...";
  264. download(new Blob(files), `${vtitle}.mpeg`);
  265. document.title=vtitle;
  266. }
  267. };
  268.  
  269. // src/module/index.js
  270. var module_default = {
  271. bilibili: bilibili_default,
  272. douyin: douyin_default,
  273. kuaishou: kuaishou_default,
  274. acfun: acfun_default,
  275. qq: qq_default,
  276. //cctv: cctv_default,
  277. youku: youku_default
  278. };
  279.  
  280. // src/index.js
  281. var youget = () => {
  282. const handler = Object.entries(module_default).find(
  283. ([key, fn]) => window.location.host.toLocaleLowerCase().includes(key)
  284. )?.[1];
  285. if (handler) {
  286. handler();
  287. } else {
  288. console.log("not support");
  289. }
  290. };
  291. window.youget = youget;
  292. var bae=document.createElement("a");
  293. bae.style="position:fixed;top:10vh;right:1vw;font-size:2vw;z-index:99;color:#89FF89";
  294. bae.textContent="YouGet";
  295. bae.href="javascript:youget()";
  296. document.body.append(bae);
  297. })();