Greasy Fork

Greasy Fork is available in English.

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

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

当前为 2025-05-26 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         抖音视频提取器(简洁版)
// @namespace    http://tampermonkey.net/
// @version      1.1.6
// @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('author-card-user-video-like');
            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);
    }
})();