// ==UserScript==
// @name 自动下载微信公众号文章中的视频
// @namespace http://tampermonkey.net/
// @version 0.1
// @description 自动下载微信公众号文章视频
// @author Zep
// @match https://mp.weixin.qq.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=qq.com
// @grant GM_log
// @grant GM_xmlhttpRequest
// @grant GM_download
// @grant GM_addStyle
// @require https://lib.baomitu.com/axios/0.27.2/axios.min.js
// @require https://lib.baomitu.com/jquery/1.12.4/jquery.min.js
// @license MIT
// ==/UserScript==
(()=>{
console.log('axios', axios)
function downloadVideo1(url) {
// 这是传统的下载方式
const downloadFileA = unsafeWindow.document.createElement('a')
unsafeWindow.document.body.append(downloadFileA)
downloadFileA.href = url
// http://mpvideo.qpic.cn/0bc3cyabeaaa2maoapqhensfafwdcilaaeqa.f10002.mp4?dis_k=97a32be63138ce2a2f2bde95e491119e&dis_t=1695370394&play_scene=10120&auth_info=Ws+SvC4/Xkm+rqDbJXVtXXtmQlMRLHRjWHgdFWRDLz5hUEkyPwVVHDgFMjpFNlRSbzA=&auth_key=409cd067a1d042d130c471012ecfb8fa&vid=wxv_2829481345448984576&format_id=10002&support_redirect=0&mmversion=false
const filenameArr = url.split('?')[0].split('/')
const filename = filenameArr[filenameArr.length - 1]
downloadFileA.download = filename
downloadFileA.rel = 'noopener noreferrer'
// downloadFileA.click()
unsafeWindow.document.body.removeChild(downloadFileA)
}
function downloadVideo2(url) {
axios({
method: 'get',
url: url,
// 必须显式指明响应类型是一个Blob对象,这样生成二进制的数据,才能通过window.URL.createObjectURL进行创建成功
responseType: 'blob',
}).then((res) => {
if (!res) {
return
}
// 将lob对象转换为域名结合式的url
let blobUrl = window.URL.createObjectURL(res.data)
let link = document.createElement('a')
document.body.appendChild(link)
link.style.display = 'none'
link.href = blobUrl
// 设置a标签的下载属性,设置文件名及格式,后缀名最好让后端在数据格式中返回
const filenameArr = url.split('?')[0].split('/')
const filename = filenameArr[filenameArr.length - 1]
link.download = filename
// 自触发click事件
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(blobUrl);
})
}
function downloadVideo3(url) {
GM_xmlhttpRequest({
url:"url",
method :"GET",
headers: {
// "Content-type": "application/x-www-form-urlencoded",
"Host": "mpvideo.qpic.cn",
},
responseType: 'blob',
onload:function(xhr){
console.log("success", xhr);
// 将lob对象转换为域名结合式的url
// let blobUrl = window.URL.createObjectURL(xhr.response)
var blob = this.response;
var reader = new FileReader();
reader.readAsDataURL(blob); // 转换为base64,可以直接放入a表情href
reader.onload = function (e) {
let link = document.createElement('a')
document.body.appendChild(link)
link.style.display = 'none'
link.href = e.target.result
// 设置a标签的下载属性,设置文件名及格式,后缀名最好让后端在数据格式中返回
const filenameArr = url.split('?')[0].split('/')
const filename = filenameArr[filenameArr.length - 1]
link.download = filename
// 自触发click事件
link.click()
document.body.removeChild(link)
// window.URL.revokeObjectURL(blobUrl);
};
// console.log('blobUrl', blobUrl)
}
});
}
console.log('GM_xmlhttpRequest', GM_xmlhttpRequest)
function promise_GM_download(url,i, filename, options = {}){
return new Promise((resolve, reject) => {
GM_download({
url: url,
name: filename, //不填则自动获取文件名
saveAs: true, //布尔值,显示"保存为"对话框
onerror: function (error) {
//如果下载最终出现错误,则要执行的回调
// console.log(`${filename}下载失败,请重试!`,error)
reject(error)
},
onprogress: (pro) => {
//如果此下载取得了一些进展,则要执行的回调
// console.log(pro.loaded) //文件加载量
// console.log(pro.totalSize) //文件总大小
const pecentage = (pro.loaded / pro.totalSize).toFixed(2) * 100
// console.log('.percentage'+ i, 'percentage', pecentage)
$('.percentage'+ i).text(pecentage + '%')
$('.progress'+ i).val(pecentage)
// console.log(`${filename}下载进度:${pecentage}%`)
},
ontimeout: () => {
//如果此下载由于超时而失败,则要执行的回调
},
onload: () => {
//如果此下载完成,则要执行的回调
// console.log(`${filename}下载成功`)
resolve(`${filename}下载成功`)
},
...options
})
})
}
async function downloadVideo4(url,i, filename) {
try {
const res = await promise_GM_download(url,i, filename,{})
console.log(res)
}catch(err) {
$('.error'+ i).removeClass('hiddenTip')
console.log(err)
}
/* GM_download({
url: url,
name: filename, //不填则自动获取文件名
saveAs: true, //布尔值,显示"保存为"对话框
onerror: function (error) {
//如果下载最终出现错误,则要执行的回调
console.log(`${filename}下载失败,请重试!`,error)
},
onprogress: (pro) => {
//如果此下载取得了一些进展,则要执行的回调
// console.log(pro.loaded) //文件加载量
// console.log(pro.totalSize) //文件总大小
const pecentage = pro.loaded / pro.totalSize * 100
console.log(`${filename}下载进度:${pecentage}%`)
},
ontimeout: () => {
//如果此下载由于超时而失败,则要执行的回调
},
onload: () => {
//如果此下载完成,则要执行的回调
console.log(`${filename}下载成功`)
}
})
*/
}
// 往页面上注入弹窗页面
function addPage() {
GM_addStyle( `
.myPage {
position: fixed;
top: 50%;
right: 0;
padding: 10px;
background: #eee;
min-height: 500px;
width: 500px;
box-sizing: border-box;
display: flex;
flex-direction: column;
border: 2px solid deepskyblue;
transform: translate(0%, -50%);
}
.title {
color: deepskyblue;
font-size: 18px;
margin: 10px 0;
font-weight: 700;
}
.list {
/* display: flex;
flex-direction: column;
flex-wrap: wrap;
align-items: flex-start; */
overflow-x: auto;
height: 400px;
}
.list-title {
font-size: 14px;
}
.list-item {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 5px;
}
.top {
width: 100%;
display: flex;
align-items: flex-start;
justify-content: flex-start;
}
.bottom {
width: 100%;
display: flex;
justify-content: flex-start;
}
.error {
font-size: 12px;
color: red;
width: 100%;
display: flex;
justify-content: flex-start;
align-items: center;
margin: 0 10px;
}
.hiddenTip {
display: none!important;
}
.error-tip {
}
.progress {
margin: 0 10px;
}
.retryBtn {
padding: 5px;
color: #fff;
background-color: #409eff;
border: none;
border-radius: 10%;
font-size: 12px;
}
.line {
width: 100%;
height: 1px;
background-color: #fff;
margin: 10px 0;
}
`)
const div = document.createElement('div')
div.classList.add('myPage');
div.innerHTML = `
<div class="title">自动下载微信公众号文章中的所有视频</div>
<div class="list-title">该页面共存在<span class='count'>10</span>个视频文件:</div>
<div class="list">
</div>
`
document.body.append(div)
}
window.onload = function(){
$('body').on('click', '.retryBtn', function(e) {
let url = e.target.attributes.getNamedItem('data-src').nodeValue
const filename = e.target.attributes.getNamedItem('data-filename').nodeValue
const i = e.target.attributes.getNamedItem('data-i').nodeValue
// 点击事件处理程序
console.log('点击了动态添加的元素', e, url,filename,i);
// url = url.replace('12345', '')
$('.error'+ i).addClass('hiddenTip')
downloadVideo4(url, i, filename)
})
console.log('onload')
addPage()
const videos = window.document.querySelectorAll('video')
$('.count').text(videos.length)
for(let i = 0; i < videos.length; i++) {
let url = videos[i].getAttribute('src')
const filenameArr = url.split('?')[0].split('/')
let filename = i+1 + '-' + filenameArr[filenameArr.length - 1]
const suffixs = filename.split('.')
const suffix = suffixs[suffixs.length - 1]
filename = i+1 + '-' + new Date().getTime() + '.' + suffix
// if(i === 1) {
// url = '12345' + url
// }
$('.list').append(`
<div class="list-item">
<div class="top">
<div>${i+1}.</div>
<div>${filename}:</div>
</div>
<div class="bottom">
<progress class="progress${i+1}" value="0" max="100"></progress>
<div class="percentage${i+1}">0%</div>
</div>
<div class="error hiddenTip error${i+1}">
<div class="error-tip error-tip${i+1}">下载失败,请重试!</div>
<button class="retryBtn" data-src=${url} data-i=${i+1} data-filename=${filename}>重试</button>
</div>
<div class="line"></div>
</div>
`)
downloadVideo4(url, i+1, filename)
}
}
}
)()