Greasy Fork

快手下载

解析快手视频

目前为 2022-01-27 提交的版本。查看 最新版本

// ==UserScript==
// @name         快手下载
// @namespace    http://tampermonkey.net/
// @version      0.1.3
// @description  解析快手视频
// @author       xelicev963
// @run-at       document-start
// @license      MIT License
// @grant        GM_invokeFn
// @include      *://*.kuaishou.com/*
// @inject-into  page
// @require      https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.18.0/js/md5.min.js
// @require      https://cdn.bootcdn.net/ajax/libs/jquery/1.7.2/jquery.min.js
// ==/UserScript==

var metaCache = {};

//hook fetch
var fetchListKeys = ['brilliantData', 'visionSearchPhoto', 'brilliantTypeData', 'gameLiveCardList', 'visionProfilePhotoList']
var originFetch = fetch
unsafeWindow.fetch = async (url, request) => {
  var response = await originFetch(url, request)
  if (url && url.indexOf('graphql') > -1) {
    try {
      var text = await response.text()
      response.text = () => { return new Promise((resolve) => { resolve(text) }) }
      var res = JSON.parse(text)
      for (i in fetchListKeys) {
        var key = fetchListKeys[i];
        if (res?.data[key] && res.data[key].feeds && res.data[key].feeds.length > 0) {
          var feeds = res.data[key].feeds;
          for (var i = 0; i < feeds.length; i++) {
            var itemData = feeds[i].photo;
            itemData.author = feeds[i].author
            metaCache[itemData.id] = itemData
          }
          // console.log(feeds)
        }
      }
    } catch (e) {
      console.error(e)
    }
  }
  return response
}

//hook xhr
var xhrListKeys = ['hotPhotoInfos', 'inspiredAds']
var origOpen = XMLHttpRequest.prototype.open;
XMLHttpRequest.prototype.open = function() {
  this.addEventListener('load', function() {
    if (this.responseType && this.responseType !== "text") return;
    var res = JSON.parse(this.responseText)
    for (i in xhrListKeys) {
      var key = xhrListKeys[i];
      if (res?.data && typeof res.data === 'object' &&
          key in res.data && res.data[key].length > 0) {
        var feeds = res.data[key];
        for (var i = 0; i < feeds.length; i++) {
          var url = new URL(feeds[i].coverThumbnailUrls[0].url);
          var id = url.searchParams.get('clientCacheKey')?.replace('.jpg', '');
          // console.log(id, feeds[i])
          feeds[i].id = id;
          metaCache[id] = feeds[i];
        }
        // console.log(feeds)
      }
    }
  });
  // console.log(arguments)
  origOpen.apply(this, arguments);
};

(function () {

  var getItemByMeta = (meta, pselector='', pclass='') => {
    var id = `mona-${md5(meta.id)}`
    if ($(`#${id}`).length > 0) return null;
    var dom = $(`<div id='${id}' style="z-index:100"></div>`);
    var url = meta.photoUrl;
    meta.title = meta.caption;
    meta.cover = meta.coverUrl;
    meta.filename = `${meta.id}.mp4`;
    let pdom = null;
    if (pselector) pdom = $(pselector)
    if (pdom.length > 0 && pclass) {
      var ps = pdom.parentsUntil(pclass);
      if (ps.length > 0) pdom = $(ps[ps.length - 1])
    }
    return { id, url, dom, pdom, meta }
  };

  var pageParsers = {
    detail: async () => {
      var url = new URL(window.location.href);
      var video_id = url.pathname.replace("/short-video/", '');
      if (video_id in metaCache) {
        var item = getItemByMeta(metaCache[video_id], '.kwai-player-container-video')
        return item ? [item] : [];
      }
      return [];
    },
    list: async () => {
      var items = []
      for (var [id, meta] of Object.entries(metaCache)) {
        var pselector = `img[src*='clientCacheKey=${id}']`
        var item = getItemByMeta(meta, pselector, '.video-card')
        if (item) items.push(item);
      }
      return items
    },
    cc: async () => {
      var items = [];
      for (var [id, meta] of Object.entries(metaCache)) {
        var pselector = `.cc-player-poster[style*='clientCacheKey=${id}']`
        var item = getItemByMeta(meta, pselector, '.common-video-item')
        if (!item) continue;
        item.url = meta.mainMvUrls[0].url;
        item.meta.cover = meta.coverThumbnailUrls[0].url;
        if (item) items.push(item);
      }
      return items
    }
  }

  var getPageParser = () => {
    var url = new URL(window.location.href);
    var paths = url.pathname.split('/');
    var path = paths[1];
    if (path === "short-video") {
      return pageParsers.detail;
    } else if (!path || ["search", "brilliant", "profile"].includes(path)) {
      return pageParsers.list;
    } else if (url.host === 'cc.e.kuaishou.com' &&
              ["/inspiration/ads", "/inspiration/hot"].includes(url.pathname)) {
      return pageParsers.cc;
    } else {
      throw 999;
    }
  }

  var parser = {
    parseItems: async () => {
      var pr = getPageParser()
      if (!pr) return []
      return pr()
    }
  }

  GM_invokeFn("regParser", parser)
})()