抖音视频提取器(简洁版)

提取抖音用户视频链接,支持正常提取和排序提取功能

// ==UserScript==
// @name         抖音视频提取器(简洁版)
// @namespace    http://tampermonkey.net/
// @version      1.1.8
// @description  提取抖音用户视频链接,支持正常提取和排序提取功能
// @author       qqlcx5
// @match        https://www.douyin.com/user/*
// @match        https://www.douyin.com/search/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=douyin.com
// @grant        GM_setClipboard
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

  /**
   * 存储提取到的视频链接和点赞数
   * Stores the extracted video links and like counts
   */
    let videoLinks = [];

  /**
   * 提取用户主页中的所有视频链接和点赞数
   * Extracts all video links and like counts from the user's profile page
   */
    function extractVideoLinks() {
      // 定义需要提取链接的节点选择器
        const selectors = [
            'div[data-e2e="user-post-list"]',
            'div[data-e2e="user-like-list"]'
        ];
        const videoListContainer = document.querySelector(selectors);

        if (!videoListContainer) {
            console.warn('未找到视频列表元素');
            return;
        }

        const videoAnchorElements = videoListContainer.querySelectorAll('a[href^="/video/"]');
        videoLinks = Array.from(videoAnchorElements).map(anchor => {
            const videoElement = anchor.closest('li');
            const likeCountElement = videoElement ? videoElement.querySelector('.b3Dh2ia8') : null;
            const likeCount = likeCountElement ? parseLikeCount(likeCountElement.textContent) : 0;

            const url = new URL(anchor.href);
            url.searchParams.set('likeCount', likeCount);

            return {
                href: url.toString(),
                likeCount: likeCount
            };
        });

        console.info(`提取到 ${videoLinks.length} 个视频链接`);
    }

  /**
   * 将点赞数文本转换为数字
   * Converts like count text to a number
   * @param {string} text - 点赞数文本 (Like count text)
   * @returns {number} - 转换后的点赞数 (Converted like count)
   */
    function parseLikeCount(text) {
        if (text.includes('万')) {
            return parseFloat(text) * 10000;
        }
        return parseInt(text, 10);
    }

  /**
   * 按点赞数排序视频链接
   * Sorts video links by like count
   * @param {boolean} ascending - 是否升序排序 (Whether to sort in ascending order)
   */
    function sortVideoLinksByLikes(ascending = false) {
        videoLinks.sort((a, b) => {
            return ascending ? a.likeCount - b.likeCount : b.likeCount - a.likeCount;
        });
    }

  /**
   * 复制所有视频链接到剪贴板
   * Copies all video links to the clipboard
   * @param {boolean} shouldSort - 是否按点赞数排序 (Whether to sort by like count)
   */
    function copyAllVideoLinks(shouldSort = false) {
        extractVideoLinks();

        if (videoLinks.length === 0) {
            showNotification('未找到视频链接', 'error');
            return;
        }

        if (shouldSort) {
            sortVideoLinksByLikes();
            showNotification('已按点赞数降序排序', 'info');
        }

        const linksText = videoLinks.map(video => video.href).join('\n');
        GM_setClipboard(linksText);
        showNotification(`已复制 ${videoLinks.length} 个视频链接`, 'success');
    }

  /**
   * 创建并添加悬浮按钮组到页面
   * Creates and adds a floating button group to the page
   */
    function createFloatingButtonGroup() {
        const buttonGroup = document.createElement('div');
        buttonGroup.id = 'floating-button-group';

        Object.assign(buttonGroup.style, {
            position: 'fixed',
            right: '24px',
            bottom: '24px',
            display: 'flex',
            flexDirection: 'column',
            gap: '12px',
            zIndex: '10000',
        });

        const normalButton = createButton('提取链接', '#007AFF', () => {
            copyAllVideoLinks(false);
        });

      // 排序提取按钮
      const sortButton = createButton('点赞数提取', '#FF4D4F', () => {
          copyAllVideoLinks(true); // 排序
        });

      // 添加按钮到按钮组
        buttonGroup.appendChild(normalButton);
        buttonGroup.appendChild(sortButton);

      // 添加按钮组到页面主体
        document.body.appendChild(buttonGroup);
    }

  /**
   * 创建按钮
   * Creates a button
   * @param {string} text - 按钮文字 (Button text)
   * @param {string} color - 按钮背景色 (Button background color)
   * @param {function} onClick - 点击事件 (Click event)
   * @returns {HTMLElement} - 按钮元素 (Button element)
   */
    function createButton(text, color, onClick) {
        const button = document.createElement('button');
        button.textContent = text;

        Object.assign(button.style, {
            padding: '12px 24px',
            backgroundColor: color,
            color: '#FFFFFF',
            border: 'none',
            borderRadius: '12px',
            boxShadow: '0 2px 8px rgba(0, 122, 255, 0.15)',
            cursor: 'pointer',
            fontSize: '15px',
            fontWeight: '500',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
            transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
            WebkitAppearance: 'none',
            margin: '0',
            userSelect: 'none',
            WebkitTapHighlightColor: 'transparent'
        });

        button.addEventListener('mouseenter', () => {
            button.style.transform = 'scale(1.02) translateY(-1px)';
            button.style.boxShadow = '0 4px 12px rgba(0, 122, 255, 0.2)';
            button.style.backgroundColor = '#0066D6';
        });

        button.addEventListener('mouseleave', () => {
            button.style.transform = 'none';
            button.style.boxShadow = '0 2px 8px rgba(0, 122, 255, 0.15)';
            button.style.backgroundColor = color;
        });

        button.addEventListener('mousedown', () => {
            button.style.transform = 'scale(0.98)';
        });

        button.addEventListener('mouseup', () => {
            button.style.transform = 'scale(1.02) translateY(-1px)';
        });

        button.addEventListener('click', onClick);

        return button;
    }

    function showNotification(message, type = 'info') {
        const colors = {
            success: '#34C759',
            error: '#FF3B30',
            info: '#007AFF'
        };

        const notification = document.createElement('div');
        notification.textContent = message;

        Object.assign(notification.style, {
            position: 'fixed',
            bottom: '90px',
            right: '24px',
            backgroundColor: '#FFFFFF',
            color: colors[type],
            padding: '12px 20px',
            borderRadius: '12px',
            boxShadow: '0 4px 16px rgba(0, 0, 0, 0.08)',
            opacity: '0',
            transform: 'translateY(10px)',
            transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)',
            zIndex: '10000',
            fontSize: '15px',
            fontWeight: '500',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
            border: `1px solid ${colors[type]}20`
        });

        document.body.appendChild(notification);

        requestAnimationFrame(() => {
            notification.style.opacity = '1';
            notification.style.transform = 'translateY(0)';
        });

        setTimeout(() => {
            notification.style.opacity = '0';
            notification.style.transform = 'translateY(10px)';
            setTimeout(() => {
                document.body.removeChild(notification);
            }, 300);
        }, 3000);
    }

    function initializeScript() {
        createFloatingButtonGroup();
        console.info('抖音视频链接提取器已启用');
    }

    if (document.readyState === 'complete' || document.readyState === 'interactive') {
        initializeScript();
    } else {
        document.addEventListener('DOMContentLoaded', initializeScript);
    }
})();