快手下载

下载快手视频

  1. // ==UserScript==
  2. // @name 快手下载
  3. // @namespace http://tampermonkey.net/
  4. // @version 0.5.1
  5. // @description 下载快手视频
  6. // @author delamingo
  7. // @run-at document-start
  8. // @license MIT License
  9. // @grant GM_invokeFn
  10. // @include *://*.kuaishou.com/*
  11. // @inject-into page
  12. // @require https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.18.0/js/md5.min.js
  13. // @require https://cdn.bootcdn.net/ajax/libs/jquery/1.7.2/jquery.min.js
  14. // @dev 9999
  15. // ==/UserScript==
  16.  
  17. const metaCache = {};
  18.  
  19. //hook fetch
  20. const fetchListKeys = ['brilliantData', 'visionSearchPhoto', 'brilliantTypeData', 'gameLiveCardList', 'visionProfilePhotoList']
  21. const originFetch = fetch
  22. unsafeWindow.fetch = async (url, request) => {
  23. const response = await originFetch(url, request)
  24. if (url && url.indexOf('graphql') > -1) {
  25. try {
  26. const text = await response.text()
  27. response.text = () => { return new Promise((resolve) => { resolve(text) }) }
  28. const res = JSON.parse(text)
  29. for (i in fetchListKeys) {
  30. const key = fetchListKeys[i];
  31. if (res?.data[key] && res.data[key].feeds && res.data[key].feeds.length > 0) {
  32. const feeds = res.data[key].feeds;
  33. for (var i = 0; i < feeds.length; i++) {
  34. var itemData = feeds[i].photo;
  35. itemData.author = feeds[i].author
  36. metaCache[itemData.id] = itemData
  37. }
  38. // console.log(feeds)
  39. }
  40. }
  41. } catch (e) {
  42. console.error(e)
  43. }
  44. }
  45. return response
  46. }
  47.  
  48. //hook xhr
  49. const xhrListKeys = ['hotPhotoInfos', 'inspiredAds']
  50. var origOpen = XMLHttpRequest.prototype.open;
  51. XMLHttpRequest.prototype.open = function() {
  52. this.addEventListener('load', function() {
  53. if (this.responseType && this.responseType !== "text") return;
  54. const res = JSON.parse(this.responseText)
  55. for (i in xhrListKeys) {
  56. const key = xhrListKeys[i];
  57. if (res?.data && typeof res.data === 'object' &&
  58. key in res.data && res.data[key].length > 0) {
  59. const feeds = res.data[key];
  60. for (var i = 0; i < feeds.length; i++) {
  61. const url = new URL(feeds[i].coverThumbnailUrls[0].url);
  62. const id = url.searchParams.get('clientCacheKey')?.replace('.jpg', '');
  63. // console.log(id, feeds[i])
  64. feeds[i].id = id;
  65. metaCache[id] = feeds[i];
  66. }
  67. // console.log(feeds)
  68. }
  69. }
  70. });
  71. // console.log(arguments)
  72. origOpen.apply(this, arguments);
  73. };
  74.  
  75. (function () {
  76.  
  77. var getItemByMeta = (meta, pselector='', pclass='') => {
  78. const id = `mona-${md5(meta.id)}`
  79. if ($(`#${id}`).length > 0) return null;
  80. const dom = $(`<div id='${id}' style="z-index:100"></div>`);
  81. const url = meta.photoUrl;
  82. meta.title = meta.caption;
  83. meta.cover = meta.coverUrl;
  84. meta.filename = `${meta.id}.mp4`;
  85. let pdom = null;
  86. if (pselector) pdom = $(pselector)
  87. if (pdom.length > 0 && pclass) {
  88. const ps = pdom.parentsUntil(pclass);
  89. if (ps.length > 0) pdom = $(ps[ps.length - 1])
  90. }
  91. return { id, url, dom, pdom, meta }
  92. };
  93.  
  94. var pageParsers = {
  95. detail: async () => {
  96. const url = new URL(window.location.href);
  97. const video_id = url.pathname.replace("/short-video/", '');
  98. if (video_id in metaCache) {
  99. const item = getItemByMeta(metaCache[video_id], '.kwai-player-container-video')
  100. return item ? [item] : [];
  101. }
  102. return [];
  103. },
  104. list: async () => {
  105. const items = []
  106. for (const [id, meta] of Object.entries(metaCache)) {
  107. const pselector = `img[src*='clientCacheKey=${id}']`
  108. const item = getItemByMeta(meta, pselector, '.video-card')
  109. if (item) items.push(item);
  110. }
  111. return items
  112. },
  113. cc: async () => {
  114. const items = [];
  115. for (const [id, meta] of Object.entries(metaCache)) {
  116. const pselector = `.cc-player-poster[style*='clientCacheKey=${id}']`
  117. const item = getItemByMeta(meta, pselector, '.common-video-item')
  118. if (!item) continue;
  119. item.url = meta.mainMvUrls[0].url;
  120. item.meta.cover = meta.coverThumbnailUrls[0].url;
  121. if (item) items.push(item);
  122. }
  123. return items
  124. }
  125. }
  126.  
  127. var getPageParser = () => {
  128. const url = new URL(window.location.href);
  129. const paths = url.pathname.split('/');
  130. const path = paths[1];
  131. if (path === "short-video") {
  132. return pageParsers.detail;
  133. } else if (!path || ["search", "brilliant", "profile"].includes(path)) {
  134. return pageParsers.list;
  135. } else if (url.host === 'cc.e.kuaishou.com' &&
  136. ["/inspiration/ads", "/inspiration/hot"].includes(url.pathname)) {
  137. return pageParsers.cc;
  138. } else {
  139. throw 999;
  140. }
  141. }
  142.  
  143. var parser = {
  144. parseItems: async () => {
  145. const pr = getPageParser()
  146. if (!pr) return []
  147. return pr()
  148. }
  149. }
  150.  
  151. GM_invokeFn("regParser", parser)
  152. })()

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址