// ==UserScript== // @name TikCapture – 录制TikTok直播 // @name:fr TikCapture – Enregistrer les Lives TikTok // @namespace https://tikcapture.live // @version 1.0.1 // @description 在TikTok播放器控件中添加"录制直播"按钮,自动填写用户名并跳转至TikCapture.live。 // @description:fr Ajoute un bouton "Enregistrer le live" dans les contrôles du lecteur TikTok. Redirige vers TikCapture.live avec le profil pré-rempli automatiquement. // @author TikCapture // @license MIT // @homepageURL https://tikcapture.live // @supportURL https://tikcapture.live // @match *://www.tiktok.com/*/live* // @match *://www.tiktok.com/live* // @match *://tiktok.com/*/live* // @match *://tiktok.com/live* // @grant none // @run-at document-idle // @scriptcat true // ==/UserScript== (function () { 'use strict'; const TARGET_URL = 'https://tikcapture.live/'; const BUTTON_ID = 'tikcapture-record-btn'; const STYLE_ID = 'tikcapture-style'; // ----------------------------------------------------------- // 注入CSS样式 // ----------------------------------------------------------- function injectStyle() { if (document.getElementById(STYLE_ID)) return; const style = document.createElement('style'); style.id = STYLE_ID; style.textContent = ` #tikcapture-record-btn { display: flex; align-items: center; justify-content: center; height: 36px; padding: 0 14px; margin-bottom: 6px; background-color: #E5253A; color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; font-size: 13px; font-weight: 600; letter-spacing: 0.02em; white-space: nowrap; border: none; border-radius: 6px; cursor: pointer; transition: background-color 0.2s ease, color 0.2s ease, box-shadow 0.2s ease; box-shadow: 0 2px 8px rgba(229, 37, 58, 0.45); outline: none; -webkit-font-smoothing: antialiased; } #tikcapture-record-btn:hover { background-color: #ffffff; color: #E5253A; box-shadow: 0 2px 12px rgba(255, 255, 255, 0.3); } #tikcapture-record-btn:active { transform: scale(0.97); } #tikcapture-record-btn:focus-visible { outline: 2px solid #ffffff; outline-offset: 2px; } `; document.head.appendChild(style); } // ----------------------------------------------------------- // 从当前TikTok URL中提取@用户名 // ----------------------------------------------------------- function getUsername() { const match = window.location.pathname.match(/\/@([^/]+)/); return match ? match[1] : null; } // ----------------------------------------------------------- // 构建跳转到TikCapture的URL // ----------------------------------------------------------- function buildRedirectUrl(username) { if (username) { return `${TARGET_URL}?u=@${encodeURIComponent(username)}`; } return `${TARGET_URL}?u=${encodeURIComponent(window.location.href)}`; } // ----------------------------------------------------------- // 创建按钮元素 // ----------------------------------------------------------- function createButton() { const btn = document.createElement('button'); btn.id = BUTTON_ID; btn.textContent = '录制直播'; btn.setAttribute('aria-label', '使用TikCapture录制TikTok直播'); btn.setAttribute('title', '在TikCapture.live上录制此直播'); btn.addEventListener('click', () => { const url = buildRedirectUrl(getUsername()); window.open(url, '_blank', 'noopener'); }); return btn; } // ----------------------------------------------------------- // 查找TikTok播放器控件容器 // ----------------------------------------------------------- function findControlsContainer() { const selectors = [ '[data-e2e="volume-icon"]', '[data-e2e="fullscreen-icon"]', '[data-e2e="player-settings"]', ]; for (const sel of selectors) { const el = document.querySelector(sel); if (el && el.parentElement) return el.parentElement; } return null; } // ----------------------------------------------------------- // 注入按钮到页面 // ----------------------------------------------------------- function injectButton() { if (document.getElementById(BUTTON_ID)) return; const container = findControlsContainer(); if (!container) return; injectStyle(); const btn = createButton(); container.insertBefore(btn, container.firstChild); console.log('[TikCapture] 按钮注入成功'); } // ----------------------------------------------------------- // 监听DOM变化(TikTok是单页应用) // ----------------------------------------------------------- let injectionAttempts = 0; const MAX_ATTEMPTS = 60; function waitAndInject() { injectButton(); if (document.getElementById(BUTTON_ID)) return; const observer = new MutationObserver(() => { if (document.getElementById(BUTTON_ID)) { observer.disconnect(); return; } injectionAttempts++; if (injectionAttempts > MAX_ATTEMPTS) { observer.disconnect(); return; } injectButton(); }); observer.observe(document.body, { childList: true, subtree: true }); } // ----------------------------------------------------------- // 处理TikTok单页应用导航 // ----------------------------------------------------------- function onNavigate() { const existing = document.getElementById(BUTTON_ID); if (existing) existing.remove(); injectionAttempts = 0; setTimeout(waitAndInject, 1500); } const originalPushState = history.pushState.bind(history); history.pushState = function (...args) { originalPushState(...args); onNavigate(); }; window.addEventListener('popstate', onNavigate); // ----------------------------------------------------------- // 启动脚本 // ----------------------------------------------------------- if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', waitAndInject); } else { waitAndInject(); } })();