feat: 增强人群画像批量下载功能,支持自定义文件名
在现有的人群画像批量下载功能中,新增了自定义文件名的选项,用户可以根据需求设置下载文件的名称。这一改进提升了用户的灵活性和体验,同时保持了原有功能的稳定性。
This commit is contained in:
parent
5c45d3523e
commit
805d3037e6
430
douyin/douyinVideoDownload.user.js
Normal file
430
douyin/douyinVideoDownload.user.js
Normal file
@ -0,0 +1,430 @@
|
|||||||
|
// ==UserScript==
|
||||||
|
// @name 抖音视频下载助手
|
||||||
|
// @namespace https://github.com/scriptCat/
|
||||||
|
// @version 1.0.0
|
||||||
|
// @description 抖音视频下载助手,监听视频详情API并提供下载功能
|
||||||
|
// @author wangxi
|
||||||
|
// @match https://www.douyin.com/*
|
||||||
|
// @grant GM_addStyle
|
||||||
|
// @grant GM_download
|
||||||
|
// @connect douyin.com
|
||||||
|
// @connect *
|
||||||
|
// @require https://cdn.jsdelivr.net/npm/sweetalert2@11
|
||||||
|
// @run-at document-start
|
||||||
|
// @license MIT
|
||||||
|
// ==/UserScript==
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// 配置
|
||||||
|
const CONFIG = {
|
||||||
|
enabled: true
|
||||||
|
};
|
||||||
|
|
||||||
|
// 当前视频信息
|
||||||
|
let currentVideoInfo = null;
|
||||||
|
|
||||||
|
// 简化工具函数
|
||||||
|
function showToast(message, type = 'info') {
|
||||||
|
const Toast = Swal.mixin({
|
||||||
|
toast: true,
|
||||||
|
position: 'top-end',
|
||||||
|
showConfirmButton: false,
|
||||||
|
timer: 2000
|
||||||
|
});
|
||||||
|
Toast.fire({ icon: type, title: message });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 下载视频
|
||||||
|
function downloadVideo() {
|
||||||
|
if (!currentVideoInfo) {
|
||||||
|
showToast('没有可下载的视频', 'error');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filename = `${currentVideoInfo.videoId}.mp4`;
|
||||||
|
console.log('[抖音下载] 下载链接:', currentVideoInfo.downloadUrl);
|
||||||
|
console.log('[抖音下载] 文件名:', filename);
|
||||||
|
|
||||||
|
updateFloatingButton('downloading');
|
||||||
|
|
||||||
|
// 使用fetch下载并创建blob链接
|
||||||
|
fetch(currentVideoInfo.downloadUrl, {
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36',
|
||||||
|
'Referer': 'https://www.douyin.com/',
|
||||||
|
'Accept': '*/*'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => {
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`HTTP error! status: ${response.status}`);
|
||||||
|
}
|
||||||
|
return response.blob();
|
||||||
|
})
|
||||||
|
.then(blob => {
|
||||||
|
// 创建下载链接
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = filename;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
showToast('下载成功', 'success');
|
||||||
|
updateFloatingButton('ready');
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.error('[抖音下载] 下载失败:', error);
|
||||||
|
showToast('下载失败', 'error');
|
||||||
|
updateFloatingButton('ready');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建悬浮按钮
|
||||||
|
function createFloatingButton() {
|
||||||
|
// 检查是否已存在
|
||||||
|
if (document.querySelector('.douyin-download-float')) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const floatingButton = document.createElement('div');
|
||||||
|
floatingButton.className = 'douyin-download-float';
|
||||||
|
floatingButton.innerHTML = `
|
||||||
|
<div class="douyin-download-btn" title="下载视频">
|
||||||
|
<svg class="douyin-download-icon" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
|
||||||
|
</svg>
|
||||||
|
<span class="douyin-download-text">下载</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 添加样式
|
||||||
|
GM_addStyle(`
|
||||||
|
.douyin-download-float {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 100px;
|
||||||
|
right: 30px;
|
||||||
|
z-index: 9999;
|
||||||
|
user-select: none;
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-float:hover {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-float.dragging {
|
||||||
|
transition: none !important;
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: linear-gradient(135deg, #ff6b6b 0%, #ff8e8e 100%);
|
||||||
|
color: white;
|
||||||
|
border-radius: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
box-shadow: 0 4px 12px rgba(255, 107, 107, 0.3);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
min-width: 80px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-btn:hover {
|
||||||
|
background: linear-gradient(135deg, #ff5252 0%, #ff7979 100%);
|
||||||
|
box-shadow: 0 6px 16px rgba(255, 107, 107, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-btn:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-btn.disabled {
|
||||||
|
background: linear-gradient(135deg, #d1d5db 0%, #9ca3af 100%);
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-btn.downloading {
|
||||||
|
background: linear-gradient(135deg, #f59e0b 0%, #fbbf24 100%);
|
||||||
|
box-shadow: 0 4px 12px rgba(245, 158, 11, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-icon {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-icon.spinning {
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-text {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 吸附效果 */
|
||||||
|
.douyin-download-float.snap-right {
|
||||||
|
right: 0;
|
||||||
|
border-radius: 25px 0 0 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.douyin-download-float.snap-left {
|
||||||
|
left: 0;
|
||||||
|
border-radius: 0 25px 25px 0;
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
|
||||||
|
// 添加到页面
|
||||||
|
document.body.appendChild(floatingButton);
|
||||||
|
|
||||||
|
// 设置拖拽功能
|
||||||
|
setupDragAndSnap(floatingButton);
|
||||||
|
|
||||||
|
// 设置点击事件
|
||||||
|
const downloadBtn = floatingButton.querySelector('.douyin-download-btn');
|
||||||
|
downloadBtn.addEventListener('click', () => {
|
||||||
|
if (downloadBtn.classList.contains('disabled')) return;
|
||||||
|
|
||||||
|
// 点击反馈
|
||||||
|
downloadBtn.style.transform = 'scale(0.9)';
|
||||||
|
setTimeout(() => {
|
||||||
|
downloadBtn.style.transform = '';
|
||||||
|
}, 150);
|
||||||
|
|
||||||
|
downloadVideo();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置拖拽和吸附功能
|
||||||
|
function setupDragAndSnap(floatingButton) {
|
||||||
|
let isDragging = false;
|
||||||
|
let startX, startY, startLeft, startTop;
|
||||||
|
|
||||||
|
floatingButton.addEventListener('mousedown', (e) => {
|
||||||
|
isDragging = true;
|
||||||
|
startX = e.clientX;
|
||||||
|
startY = e.clientY;
|
||||||
|
|
||||||
|
const rect = floatingButton.getBoundingClientRect();
|
||||||
|
startLeft = rect.left;
|
||||||
|
startTop = rect.top;
|
||||||
|
|
||||||
|
floatingButton.classList.add('dragging');
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mousemove', (e) => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
const deltaX = e.clientX - startX;
|
||||||
|
const deltaY = e.clientY - startY;
|
||||||
|
|
||||||
|
let newLeft = startLeft + deltaX;
|
||||||
|
let newTop = startTop + deltaY;
|
||||||
|
|
||||||
|
// 限制在视窗内
|
||||||
|
const maxX = window.innerWidth - floatingButton.offsetWidth;
|
||||||
|
const maxY = window.innerHeight - floatingButton.offsetHeight;
|
||||||
|
|
||||||
|
newLeft = Math.max(0, Math.min(newLeft, maxX));
|
||||||
|
newTop = Math.max(0, Math.min(newTop, maxY));
|
||||||
|
|
||||||
|
// 设置位置
|
||||||
|
floatingButton.style.left = newLeft + 'px';
|
||||||
|
floatingButton.style.top = newTop + 'px';
|
||||||
|
floatingButton.style.right = 'auto';
|
||||||
|
floatingButton.style.bottom = 'auto';
|
||||||
|
});
|
||||||
|
|
||||||
|
document.addEventListener('mouseup', () => {
|
||||||
|
if (!isDragging) return;
|
||||||
|
|
||||||
|
isDragging = false;
|
||||||
|
floatingButton.classList.remove('dragging');
|
||||||
|
|
||||||
|
// 吸附效果
|
||||||
|
const rect = floatingButton.getBoundingClientRect();
|
||||||
|
const windowWidth = window.innerWidth;
|
||||||
|
const centerX = rect.left + rect.width / 2;
|
||||||
|
|
||||||
|
// 移除吸附类
|
||||||
|
floatingButton.classList.remove('snap-left', 'snap-right');
|
||||||
|
|
||||||
|
if (centerX < windowWidth / 2) {
|
||||||
|
// 吸附到左边
|
||||||
|
floatingButton.style.left = '0px';
|
||||||
|
floatingButton.style.right = 'auto';
|
||||||
|
floatingButton.classList.add('snap-left');
|
||||||
|
} else {
|
||||||
|
// 吸附到右边
|
||||||
|
floatingButton.style.right = '0px';
|
||||||
|
floatingButton.style.left = 'auto';
|
||||||
|
floatingButton.classList.add('snap-right');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新悬浮按钮状态
|
||||||
|
function updateFloatingButton(status = 'ready') {
|
||||||
|
const floatingButton = document.querySelector('.douyin-download-float');
|
||||||
|
if (!floatingButton) return;
|
||||||
|
|
||||||
|
const downloadBtn = floatingButton.querySelector('.douyin-download-btn');
|
||||||
|
const icon = floatingButton.querySelector('.douyin-download-icon');
|
||||||
|
const text = floatingButton.querySelector('.douyin-download-text');
|
||||||
|
|
||||||
|
if (!downloadBtn || !icon || !text) return;
|
||||||
|
|
||||||
|
// 移除所有状态类
|
||||||
|
downloadBtn.classList.remove('disabled', 'downloading');
|
||||||
|
icon.classList.remove('spinning');
|
||||||
|
|
||||||
|
switch (status) {
|
||||||
|
case 'ready':
|
||||||
|
if (currentVideoInfo) {
|
||||||
|
downloadBtn.title = `下载视频: ${currentVideoInfo.title}`;
|
||||||
|
text.textContent = '下载';
|
||||||
|
icon.innerHTML = '<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>';
|
||||||
|
} else {
|
||||||
|
downloadBtn.classList.add('disabled');
|
||||||
|
downloadBtn.title = '等待视频加载...';
|
||||||
|
text.textContent = '等待';
|
||||||
|
icon.innerHTML = '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'downloading':
|
||||||
|
downloadBtn.classList.add('downloading');
|
||||||
|
downloadBtn.title = '正在下载...';
|
||||||
|
text.textContent = '下载中';
|
||||||
|
icon.classList.add('spinning');
|
||||||
|
icon.innerHTML = '<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/>';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 早期初始化
|
||||||
|
function earlyInit() {
|
||||||
|
// 简化的早期初始化
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从页面提取视频信息
|
||||||
|
function extractVideoInfoFromPage() {
|
||||||
|
// 直接从HTML video/source标签提取
|
||||||
|
extractFromVideoTags();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 从HTML video/source标签提取下载链接
|
||||||
|
function extractFromVideoTags() {
|
||||||
|
// 直接使用完整选择器获取第一个source
|
||||||
|
const firstSource = document.querySelector("xg-video-container video source:first-child");
|
||||||
|
|
||||||
|
if (firstSource) {
|
||||||
|
const src = firstSource.src || firstSource.getAttribute('src');
|
||||||
|
|
||||||
|
if (src) {
|
||||||
|
const videoId = extractVideoIdFromPage();
|
||||||
|
|
||||||
|
currentVideoInfo = {
|
||||||
|
videoId: videoId || 'unknown',
|
||||||
|
vid: 'vid',
|
||||||
|
downloadUrl: src,
|
||||||
|
title: '抖音视频'
|
||||||
|
};
|
||||||
|
|
||||||
|
updateFloatingButton();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从页面提取视频ID
|
||||||
|
function extractVideoIdFromPage() {
|
||||||
|
// 方法1:从URL提取
|
||||||
|
const urlMatch = window.location.href.match(/\/video\/(\d+)/);
|
||||||
|
if (urlMatch) return urlMatch[1];
|
||||||
|
|
||||||
|
// 方法2:从指定CSS选择器的链接提取
|
||||||
|
const linkElement = document.querySelector('#user_detail_element > div > div._1PChJilq > div > div > div._akC5KCF.W6aLyAOC > div > div.UTSD8keU.bVgnIvTC > ul > li:nth-child(4) > div > a');
|
||||||
|
if (linkElement && linkElement.href) {
|
||||||
|
const linkMatch = linkElement.href.match(/\/video\/(\d+)/);
|
||||||
|
if (linkMatch) return linkMatch[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 界面初始化
|
||||||
|
function uiInit() {
|
||||||
|
// 创建悬浮按钮
|
||||||
|
createFloatingButton();
|
||||||
|
|
||||||
|
// 提取视频信息(带重试机制)
|
||||||
|
function tryExtractVideo(retries = 5) {
|
||||||
|
extractVideoInfoFromPage();
|
||||||
|
if (!currentVideoInfo && retries > 0) {
|
||||||
|
setTimeout(() => tryExtractVideo(retries - 1), 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tryExtractVideo();
|
||||||
|
|
||||||
|
// 监听页面变化
|
||||||
|
let currentUrl = location.href;
|
||||||
|
new MutationObserver(() => {
|
||||||
|
// 检查URL变化
|
||||||
|
if (location.href !== currentUrl) {
|
||||||
|
currentUrl = location.href;
|
||||||
|
currentVideoInfo = null;
|
||||||
|
updateFloatingButton();
|
||||||
|
setTimeout(() => tryExtractVideo(3), 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查新video标签
|
||||||
|
if (!currentVideoInfo) {
|
||||||
|
setTimeout(extractFromVideoTags, 500);
|
||||||
|
}
|
||||||
|
}).observe(document, {
|
||||||
|
subtree: true,
|
||||||
|
childList: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 立即执行早期初始化
|
||||||
|
earlyInit();
|
||||||
|
|
||||||
|
// 等待页面准备好后初始化界面
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
setTimeout(uiInit, 500);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
setTimeout(uiInit, 500);
|
||||||
|
}
|
||||||
|
})();
|
||||||
Loading…
x
Reference in New Issue
Block a user