WWW.YOUINFO.SITE
标签聚合 厂长

/tag/厂长

IT之家 · 2026-05-22 13:02:36+08:00 · tech

IT之家 5 月 22 日消息,在小米 YU7 GT 发布会后,小米集团董事长、CEO 雷军,小米集团副总裁、汽车部 CTO 胡峥楠,小米汽车副总裁宋钢,小米汽车首席测试车手任周灿与媒体对话。 宋钢表示,小米汽车成立之初,业内就开始关注。随着后来跟雷总和小米团队的沟通,深刻感受到了团队对于汽车的热爱和执着,奠定了要加入小米的决心。 宋钢认为, 小米跟特斯拉非常像,不管是从文化上还是从追求极致、追求技术的角度上都非常像 。小米的强项在于文化的开放,整个团队做正确的事情,追求极致,追求在行业里做到最佳,让每个人能够发挥出 120% 的能力,这是非常吸引自己的地方,也是奠定了小米在汽车领域能够取得非常辉煌成就的基础。 谈及自己负责的智能制造领域,宋钢表示,加入小米汽车之后,非常欣喜地看到整个生产交付体系相较于一年前有了非常好的提升。 后续希望把生产效率做进一步提升 ,把先进的制造理念、制造体系和制造技术能够带到小米,以及小米的供应链。 ▲ 小米汽车工厂 雷军还分享了宋钢入职的小故事:“2024 年底我看到宋总从特斯拉离职,就已经安排过同事联系他, 只不过当时宋总已经加入了远景 ,不过我们也没有放弃, 最终经过了一年半不间断的说服,宋总终于入职小米了 。” 据IT之家此前报道,宋钢曾担任特斯拉上海工厂厂长, 今年 4 月出任小米汽车副总裁、参谋长 。此次也是他加入小米后首次对外亮相。

LinuxDo 最新话题 · 2026-05-15 17:44:25+08:00 · tech

一直使用 厂长资源 看电影连续剧,还挺高清的,但是现在发现他的观影历史不能正常使用了 因此用ai撸了个油猴脚本,代替他的观影记录,也分享给大家 // ==UserScript== // @name 厂长资源 观影历史记录增强版 // @namespace https://czzyv.com/ // @version 1.0.0 // @description 为 厂长资源 影视站增加观影历史、播放进度记录、并支持从历史新窗口打开后自动跳转到上次播放时间 // @author wg5945 // @match *://czzyv.com/* // @match *://*.czzyv.com/* // @match *://plala.py1080p.com/* // @match *://*.plala.py1080p.com/* // @grant none // @run-at document-end // ==/UserScript== (function () { 'use strict'; /** * ================================================================ * 配置 * ================================================================ */ // 在 CONFIG 对象中添加新配置 const CONFIG = { // 主站域名 SITE_HOSTS: [ 'czzyv.com' ], // 播放器 iframe 域名 PLAYER_HOSTS: [ 'plala.py1080p.com' ], // 观影历史 localStorage key STORAGE_KEY: 'CZzyv_Watch_History_v1', // 新窗口恢复播放进度用的 localStorage key RESUME_STORAGE_KEY: 'CZzyv_Watch_History_Resume_Target_v1', // 历史记录最大数量 MAX_HISTORY: 200, // 播放页地址规则 PLAY_PAGE_REG: /\/v_play\/[^/]+\.html/i, // 保存进度间隔(毫秒) SAVE_INTERVAL: 10000, // 定时记录间隔(毫秒)- 新增 RECORD_INTERVAL: 10000, // iframe 向外层页面上报播放进度 IFRAME_PROGRESS_MESSAGE: 'CZ_HISTORY_IFRAME_PROGRESS', // 外层页面向 iframe 发送恢复进度指令 IFRAME_RESUME_MESSAGE: 'CZ_HISTORY_IFRAME_RESUME', // iframe 恢复完成后通知外层页面 IFRAME_RESUME_ACK_MESSAGE: 'CZ_HISTORY_IFRAME_RESUME_ACK', // 恢复进度容错秒数 RESUME_TOLERANCE: 3, // 刚进入页面时防止 0 秒覆盖历史进度的保护时间 RESUME_PROTECT_MS: 15000, // 向 iframe 发送恢复指令的间隔 RESUME_DISPATCH_INTERVAL: 800, // 最多发送恢复指令次数,防止影响后续正常播放 RESUME_DISPATCH_MAX_COUNT: 15, // 恢复进度数据有效期 RESUME_EXPIRE_MS: 10 * 60 * 1000, // 定时记录间隔(毫秒) RECORD_INTERVAL: 10000, // iframe 上报进度间隔(毫秒)- 新增 IFRAME_REPORT_INTERVAL: 10000, // 定时记录间隔,毫秒 RECORD_INTERVAL: 10000, // iframe 上报进度间隔,毫秒 IFRAME_REPORT_INTERVAL: 10000, // 小于这个秒数认为还没有真正开始播放 MIN_VALID_PROGRESS_SECONDS: 3, // 是否禁止 0 秒进度覆盖已有历史 PREVENT_ZERO_PROGRESS_OVERWRITE: true, // 是否跳过未开始播放时创建历史记录 SKIP_ZERO_PROGRESS_RECORD: true, }; let lastSaveAt = 0; let latestIframeProgress = null; let pendingResumeTarget = null; let pendingResumeStartAt = 0; let pendingResumeDone = false; let resumeDispatchTimer = null; let resumeDispatchCount = 0; /** * ================================================================ * 通用工具函数 * ================================================================ */ function hostMatches(hostList) { const host = location.hostname; return hostList.some(item => host === item || host.endsWith('.' + item)); } function isMainSiteHost() { return hostMatches(CONFIG.SITE_HOSTS); } function isPlayerHost() { return hostMatches(CONFIG.PLAYER_HOSTS); } function isInIframe() { return window.self !== window.top; } function isPlayPage() { return CONFIG.PLAY_PAGE_REG.test(location.pathname); } function normalizeUrl(url) { try { const u = new URL(url, location.href); u.hash = ''; return u.href; } catch (e) { return String(url || '').split('#')[0]; } } function parseTimeToSeconds(text) { if (!text) return 0; const arr = String(text) .trim() .split(':') .map(num => parseInt(num, 10)) .filter(num => !Number.isNaN(num)); if (arr.length === 2) { return arr[0] * 60 + arr[1]; } if (arr.length === 3) { return arr[0] * 3600 + arr[1] * 60 + arr[2]; } return 0; } function formatSeconds(seconds) { seconds = Math.floor(Number(seconds) || 0); const h = Math.floor(seconds / 3600); const m = Math.floor((seconds % 3600) / 60); const s = seconds % 60; if (h > 0) { return `${h}:${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`; } return `${m}:${String(s).padStart(2, '0')}`; } function parseProgressText(text) { if (!text) return null; const cleanText = String(text).replace(/\s+/g, ' ').trim(); const match = cleanText.match( /(\d{1,2}:\d{2}(?::\d{2})?)\s*\/\s*(\d{1,2}:\d{2}(?::\d{2})?)/ ); if (!match) return null; const currentText = match[1]; const durationText = match[2]; return { currentTime: parseTimeToSeconds(currentText), duration: parseTimeToSeconds(durationText), progressText: `${currentText} / ${durationText}` }; } function escapeHtml(str) { return String(str || '') .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } /** * 支持读取 Shadow DOM 内部节点。 */ function querySelectorAllDeep(selector, root = document) { const result = []; try { result.push(...root.querySelectorAll(selector)); } catch (e) {} let all = []; try { all = Array.from(root.querySelectorAll('*')); } catch (e) {} for (const el of all) { if (el.shadowRoot) { result.push(...querySelectorAllDeep(selector, el.shadowRoot)); } } return result; } /** * ================================================================ * 播放进度读取 * ================================================================ */ function getProgressFromVideo() { const videos = querySelectorAllDeep('video', document); const video = videos && videos.length ? videos[0] : null; if (!video) { return { currentTime: 0, duration: 0, progressText: '' }; } const currentTime = Number.isFinite(video.currentTime) ? video.currentTime : 0; const duration = Number.isFinite(video.duration) ? video.duration : 0; if (!currentTime && !duration) { return { currentTime: 0, duration: 0, progressText: '' }; } return { currentTime, duration, progressText: duration ? `${formatSeconds(currentTime)} / ${formatSeconds(duration)}` : formatSeconds(currentTime) }; } function getProgressFromCurrentDocument() { const selectors = [ '.art-control.art-control-time[data-index="30"]', '.art-control.art-control-time', '.art-controls-left .art-control-time', '.art-control-time', '[class*="art-control-time"]' ]; for (const selector of selectors) { const nodes = querySelectorAllDeep(selector, document); for (const node of nodes) { const text = node.textContent || node.innerText || node.getAttribute('aria-label') || ''; const result = parseProgressText(text); if (result && result.progressText) { return result; } } } const leftControls = querySelectorAllDeep('.art-controls-left', document); for (const node of leftControls) { const result = parseProgressText(node.textContent || node.innerText || ''); if (result && result.progressText) { return result; } } return getProgressFromVideo(); } /** * ================================================================ * iframe 播放器内部逻辑 * * 脚本运行在播放器 iframe 页面时: * 1. 定时读取 video / ArtPlayer 控件进度 * 2. 上报给外层主站页面 * 3. 接收外层页面发来的恢复进度指令 * ================================================================ */ let iframeActiveSeekTimer = null; let iframeActiveSeekTarget = 0; let iframeSeekAttemptCount = 0; let iframeLastResumeKey = ''; let iframeResumeFinished = false; function sendIframeResumeAck(seconds) { try { window.top.postMessage({ type: CONFIG.IFRAME_RESUME_ACK_MESSAGE, currentTime: seconds, href: location.href, time: Date.now() }, '*'); } catch (e) {} } function reportIframeProgressNow() { const progress = getProgressFromCurrentDocument(); if (!progress || !progress.progressText) { return; } try { window.top.postMessage({ type: CONFIG.IFRAME_PROGRESS_MESSAGE, progress, href: location.href, time: Date.now() }, '*'); } catch (e) {} } function startIframeSeek(seconds, autoPlay = true) { seconds = Number(seconds) || 0; if (seconds <= 0) return; const resumeKey = `${Math.floor(seconds)}`; // 同一个恢复时间只处理一次,避免正常播放后被反复拉回旧进度。 if (iframeResumeFinished && iframeLastResumeKey === resumeKey) { return; } iframeLastResumeKey = resumeKey; iframeResumeFinished = false; iframeActiveSeekTarget = seconds; iframeSeekAttemptCount = 0; if (iframeActiveSeekTimer) { clearInterval(iframeActiveSeekTimer); iframeActiveSeekTimer = null; } function doSeek() { iframeSeekAttemptCount++; const videos = querySelectorAllDeep('video', document); if (!videos.length) { if (iframeSeekAttemptCount >= 60) { clearInterval(iframeActiveSeekTimer); iframeActiveSeekTimer = null; } return; } let finished = false; for (const video of videos) { try { let target = iframeActiveSeekTarget; const duration = Number.isFinite(video.duration) ? video.duration : 0; if (duration > 0 && target >= duration - 2) { target = Math.max(0, duration - 5); } const current = Number(video.currentTime) || 0; // 只允许从前往后恢复,不允许播放后再被拉回旧时间。 if (current < target - CONFIG.RESUME_TOLERANCE) { video.currentTime = target; } else { finished = true; } if (autoPlay) { const p = video.play && video.play(); if (p && typeof p.catch === 'function') { p.catch(() => {}); } } const after = Number(video.currentTime) || 0; if (after >= target - CONFIG.RESUME_TOLERANCE) { finished = true; } } catch (e) {} } reportIframeProgressNow(); if (finished || iframeSeekAttemptCount >= 60) { iframeResumeFinished = true; if (iframeActiveSeekTimer) { clearInterval(iframeActiveSeekTimer); iframeActiveSeekTimer = null; } sendIframeResumeAck(iframeActiveSeekTarget); } } doSeek(); iframeActiveSeekTimer = setInterval(doSeek, 500); setTimeout(doSeek, 300); setTimeout(doSeek, 1000); setTimeout(doSeek, 2000); } function startIframeProgressReporter() { let lastText = ''; function reportProgress() { const progress = getProgressFromCurrentDocument(); if (!progress || !progress.progressText) { return; } if (progress.progressText === lastText) { return; } lastText = progress.progressText; try { window.top.postMessage({ type: CONFIG.IFRAME_PROGRESS_MESSAGE, progress, href: location.href, time: Date.now() }, '*'); } catch (e) {} } window.addEventListener('message', function (event) { const data = event.data; if (!data || data.type !== CONFIG.IFRAME_RESUME_MESSAGE) { return; } const seconds = Number(data.currentTime) || 0; if (seconds > 0) { startIframeSeek(seconds, data.autoPlay !== false); } }); setInterval(reportProgress, CONFIG.IFRAME_REPORT_INTERVAL); // 改为使用配置参数 const observer = new MutationObserver(() => { reportProgress(); }); if (document.body) { observer.observe(document.body, { childList: true, subtree: true, characterData: true }); } document.addEventListener('loadedmetadata', function (e) { if ( e.target && e.target.tagName === 'VIDEO' && iframeActiveSeekTarget > 0 && !iframeResumeFinished ) { startIframeSeek(iframeActiveSeekTarget, true); } }, true); document.addEventListener('canplay', function (e) { if ( e.target && e.target.tagName === 'VIDEO' && iframeActiveSeekTarget > 0 && !iframeResumeFinished ) { startIframeSeek(iframeActiveSeekTarget, true); } }, true); setTimeout(reportProgress, 500); setTimeout(reportProgress, 1500); setTimeout(reportProgress, 3000); } // 当前脚本如果运行在播放器 iframe 中,只执行 iframe 逻辑。 if (isInIframe() && isPlayerHost()) { startIframeProgressReporter(); return; } // 非主站页面不执行外层历史记录逻辑。 if (!isMainSiteHost()) { return; } /** * ================================================================ * 历史记录读写 * ================================================================ */ function getHistory() { try { const raw = localStorage.getItem(CONFIG.STORAGE_KEY); const list = raw ? JSON.parse(raw) : []; return Array.isArray(list) ? list : []; } catch (e) { console.error('[观影历史] 读取失败', e); return []; } } function saveHistory(list) { try { localStorage.setItem( CONFIG.STORAGE_KEY, JSON.stringify(list.slice(0, CONFIG.MAX_HISTORY)) ); } catch (e) { console.error('[观影历史] 保存失败', e); } } /** * 新窗口打开后也要恢复进度,因此这里使用 localStorage, * 而不是 sessionStorage。 */ function saveResumeTarget(item) { if (!item || !item.url) return; const currentTime = Number(item.currentTime) || 0; if (currentTime <= 0) return; try { localStorage.setItem(CONFIG.RESUME_STORAGE_KEY, JSON.stringify({ url: item.url, currentTime, duration: Number(item.duration) || 0, title: item.title || '', time: Date.now() })); } catch (e) {} } function loadResumeTarget() { try { const raw = localStorage.getItem(CONFIG.RESUME_STORAGE_KEY); if (!raw) return null; const data = JSON.parse(raw); if (!data || !data.url || !(Number(data.currentTime) > 0)) { return null; } if (Date.now() - (Number(data.time) || 0) > CONFIG.RESUME_EXPIRE_MS) { clearResumeTarget(); return null; } if (normalizeUrl(data.url) !== normalizeUrl(location.href)) { return null; } return { url: data.url, currentTime: Number(data.currentTime) || 0, duration: Number(data.duration) || 0, title: data.title || '', time: Number(data.time) || Date.now() }; } catch (e) { return null; } } function clearResumeTarget() { try { localStorage.removeItem(CONFIG.RESUME_STORAGE_KEY); } catch (e) {} } /** * ================================================================ * 恢复播放进度 * ================================================================ */ function stopResumeDispatcher() { pendingResumeDone = true; clearResumeTarget(); if (resumeDispatchTimer) { clearInterval(resumeDispatchTimer); resumeDispatchTimer = null; } } function sendResumeMessageToIframes() { if (!pendingResumeTarget || pendingResumeDone) return; const currentTime = Number(pendingResumeTarget.currentTime) || 0; if (currentTime <= 0) return; const iframes = document.querySelectorAll('iframe'); for (const iframe of iframes) { try { iframe.contentWindow.postMessage({ type: CONFIG.IFRAME_RESUME_MESSAGE, currentTime, duration: Number(pendingResumeTarget.duration) || 0, url: pendingResumeTarget.url, autoPlay: true, time: Date.now() }, '*'); } catch (e) {} } } function startResumeDispatcher() { if (!isPlayPage()) return; const target = loadResumeTarget(); if (!target || !(target.currentTime > 0)) return; pendingResumeTarget = target; pendingResumeStartAt = Date.now(); pendingResumeDone = false; resumeDispatchCount = 0; function dispatch() { if (!pendingResumeTarget || pendingResumeDone) { stopResumeDispatcher(); return; } resumeDispatchCount++; sendResumeMessageToIframes(); // 只在进入页面初期尝试恢复,避免影响后续正常播放。 if (resumeDispatchCount >= CONFIG.RESUME_DISPATCH_MAX_COUNT) { stopResumeDispatcher(); } } dispatch(); if (resumeDispatchTimer) { clearInterval(resumeDispatchTimer); } resumeDispatchTimer = setInterval(dispatch, CONFIG.RESUME_DISPATCH_INTERVAL); setTimeout(dispatch, 300); setTimeout(dispatch, 1000); setTimeout(dispatch, 2000); } /** * ================================================================ * 外层页面接收 iframe 消息 * ================================================================ */ function bindIframeProgressMessage() { window.addEventListener('message', function (event) { const data = event.data; if (!data) return; if (data.type === CONFIG.IFRAME_RESUME_ACK_MESSAGE) { stopResumeDispatcher(); return; } if (data.type !== CONFIG.IFRAME_PROGRESS_MESSAGE) { return; } if (!data.progress || !data.progress.progressText) { return; } latestIframeProgress = { currentTime: data.progress.currentTime || 0, duration: data.progress.duration || 0, progressText: data.progress.progressText, time: Date.now(), iframeHref: data.href || '' }; if ( pendingResumeTarget && !pendingResumeDone && latestIframeProgress.currentTime >= pendingResumeTarget.currentTime - CONFIG.RESUME_TOLERANCE ) { stopResumeDispatcher(); } recordCurrentPlayPage(false); // 改为 false,让它走节流逻辑 }); } function getProgress() { const pageProgress = getProgressFromCurrentDocument(); if (pageProgress && pageProgress.progressText) { return pageProgress; } if (latestIframeProgress && latestIframeProgress.progressText) { return latestIframeProgress; } return getProgressFromVideo(); } /** * ================================================================ * 标题、集数识别 * ================================================================ */ function getMovieTitle() { const ptitLink = document.querySelector('.mi_cont .paycon h3.ptit a, h3.ptit a'); if (ptitLink && ptitLink.textContent.trim()) { return ptitLink.textContent.trim(); } return document.title .replace(/第\s*\d+\s*集/g, '') .replace(/在线观看.*$/g, '') .replace(/免费播放.*$/g, '') .replace(/在线播放.*$/g, '') .replace(/[-_].*$/g, '') .replace(/\s+/g, ' ') .trim() || location.href; } function getEpisodeText() { const ptitEpisode = document.querySelector('.mi_cont .paycon h3.ptit span, h3.ptit span'); if (ptitEpisode && ptitEpisode.textContent.trim()) { return ptitEpisode.textContent.trim(); } const currentEpisode = document.querySelector('.juji_list .pbplay'); if (currentEpisode && currentEpisode.textContent.trim()) { return `第${currentEpisode.textContent.trim()}集`; } return ''; } /** * ================================================================ * 保存当前播放页进度 * ================================================================ */ function recordCurrentPlayPage(force = false) { if (!isPlayPage()) return; const now = Date.now(); if (!force && now - lastSaveAt < CONFIG.SAVE_INTERVAL) { return; } const progress = getProgress(); const currentTime = Number(progress.currentTime) || 0; const duration = Number(progress.duration) || 0; let list = getHistory(); const currentUrl = location.href; const oldItem = list.find(item => item.url === currentUrl); const oldCurrentTime = oldItem ? Number(oldItem.currentTime) || 0 : 0; /** * ================================================================ * 0 秒进度保护 * * 页面刚打开但还没播放时,播放器通常会上报: * currentTime = 0 * * 如果历史里已经有这一集的有效进度,不能让 0 秒覆盖掉旧进度。 * 如果历史里没有这一集,也不要在未播放时创建一条 0 秒记录。 * ================================================================ */ const minValidSeconds = Number(CONFIG.MIN_VALID_PROGRESS_SECONDS) || 3; const isZeroLikeProgress = currentTime < minValidSeconds; const oldHasValidProgress = oldCurrentTime >= minValidSeconds; if (isZeroLikeProgress) { // 已有有效历史进度时,禁止被 0 秒或接近 0 秒覆盖 if ( CONFIG.PREVENT_ZERO_PROGRESS_OVERWRITE && oldItem && oldHasValidProgress ) { return; } // 没有历史记录时,页面刚打开但未播放,不创建 0 秒记录 if ( CONFIG.SKIP_ZERO_PROGRESS_RECORD && !oldItem ) { return; } } // 恢复初期避免播放器短暂上报 0 秒覆盖已有历史。 if ( pendingResumeTarget && !pendingResumeDone && now - pendingResumeStartAt < CONFIG.RESUME_PROTECT_MS && pendingResumeTarget.currentTime > 5 && currentTime < pendingResumeTarget.currentTime - CONFIG.RESUME_TOLERANCE ) { return; } lastSaveAt = now; const movieTitle = getMovieTitle(); const episodeText = getEpisodeText(); const displayTitle = episodeText ? `${movieTitle} ${episodeText}` : movieTitle; const item = { url: currentUrl, title: displayTitle, movieTitle, episodeText, currentTime, duration, progressText: progress.progressText || '', time: now, host: location.hostname, pathname: location.pathname }; list = list.filter(x => x.url !== item.url); list.unshift(item); saveHistory(list); renderHistoryList(); } function deleteHistory(url) { const list = getHistory().filter(item => item.url !== url); saveHistory(list); renderHistoryList(); } function clearHistory() { if (!confirm('确定要清空全部观影历史吗?')) return; saveHistory([]); renderHistoryList(); } /** * ================================================================ * UI 样式 * ================================================================ */ function addStyle() { const style = document.createElement('style'); style.textContent = ` #czzyv-history-btn { position: fixed; right: 30px; top: 10px; z-index: 999999; width: 34px; /* was 76px */ height: 34px; /* was 34px */ border-radius: 50%; /* was 18px — now a circle */ background: #ff6500; color: #fff; cursor: pointer; box-shadow: 0 4px 14px rgba(0,0,0,.25); user-select: none; display: flex; align-items: center; justify-content: center; } #czzyv-history-btn:hover { background: #ff7a22; } #czzyv-history-panel { position: fixed; right: 20px; top: 64px; width: 420px; max-width: calc(100vw - 40px); height: 560px; max-height: calc(100vh - 90px); background: #ffffff; color: #333; z-index: 999999; border-radius: 10px; box-shadow: 0 8px 32px rgba(0,0,0,.35); display: none; overflow: hidden; font-size: 14px; } #czzyv-history-panel.czzyv-show { display: block; } .czzyv-history-header { height: 48px; background: #20232a; color: #fff; display: flex; align-items: center; justify-content: space-between; padding: 0 14px; box-sizing: border-box; } .czzyv-history-header strong { font-size: 16px; } .czzyv-history-actions { display: flex; align-items: center; gap: 8px; } .czzyv-history-actions button { border: none; background: #444b57; color: #fff; border-radius: 4px; cursor: pointer; padding: 4px 8px; font-size: 12px; } .czzyv-history-actions button:hover { background: #5a6473; } #czzyv-history-list { height: calc(100% - 48px); overflow-y: auto; padding: 8px; box-sizing: border-box; background: #f5f6f7; } .czzyv-history-empty { color: #888; text-align: center; padding: 70px 10px; } .czzyv-history-item { background: #fff; border-radius: 8px; margin-bottom: 8px; padding: 10px; box-sizing: border-box; box-shadow: 0 1px 5px rgba(0,0,0,.08); } .czzyv-history-title { color: #222; font-weight: 600; font-size: 15px; line-height: 1.45; margin-bottom: 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .czzyv-history-progress-text { color: #666; font-size: 13px; line-height: 1.8; } .czzyv-progress-wrap { height: 6px; background: #e5e5e5; border-radius: 10px; margin: 8px 0; overflow: hidden; } .czzyv-progress-bar { height: 100%; background: #ff6500; width: 0%; } .czzyv-history-op { margin-top: 8px; display: flex; gap: 8px; } .czzyv-history-op a, .czzyv-history-op button { font-size: 12px; border: none; text-decoration: none; cursor: pointer; border-radius: 4px; padding: 5px 10px; } .czzyv-history-op a { background: #ff6500; color: #fff; } .czzyv-history-op button { background: #eee; color: #555; } .czzyv-history-op a:hover { background: #ff7a22; } .czzyv-history-op button:hover { background: #ddd; } `; document.head.appendChild(style); } /** * ================================================================ * UI 创建与渲染 * ================================================================ */ function createUI() { if (document.querySelector('#czzyv-history-btn')) return; const btn = document.createElement('div'); btn.id = 'czzyv-history-btn'; btn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <circle cx="12" cy="12" r="10"/> <polyline points="12 6 12 12 16 14"/> </svg>`; btn.title = '观影历史'; const panel = document.createElement('div'); panel.id = 'czzyv-history-panel'; panel.innerHTML = ` <div class="czzyv-history-header"> <strong>观影历史</strong> <div class="czzyv-history-actions"> <button id="czzyv-history-clear">清空</button> <button id="czzyv-history-close">关闭</button> </div> </div> <div id="czzyv-history-list"></div> `; document.body.appendChild(btn); document.body.appendChild(panel); // 鼠标移入按钮时显示面板 btn.addEventListener('mouseenter', function () { panel.classList.add('czzyv-show'); renderHistoryList(); }); // 鼠标移出按钮时,如果不在面板上则隐藏 btn.addEventListener('mouseleave', function () { setTimeout(() => { if (!panel.matches(':hover') && !btn.matches(':hover')) { panel.classList.remove('czzyv-show'); } }, 100); }); // 鼠标移出面板时隐藏 panel.addEventListener('mouseleave', function () { setTimeout(() => { if (!panel.matches(':hover') && !btn.matches(':hover')) { panel.classList.remove('czzyv-show'); } }, 100); }); // 阻止面板内部点击事件冒泡 panel.addEventListener('click', function (e) { e.stopPropagation(); }); // 点击页面其他地方关闭面板 document.addEventListener('click', function () { panel.classList.remove('czzyv-show'); }); panel.querySelector('#czzyv-history-close').addEventListener('click', function () { panel.classList.remove('czzyv-show'); }); panel.querySelector('#czzyv-history-clear').addEventListener('click', clearHistory); renderHistoryList(); } function renderHistoryList() { const box = document.querySelector('#czzyv-history-list'); if (!box) return; const list = getHistory(); if (!list.length) { box.innerHTML = `<div class="czzyv-history-empty">暂无观影历史</div>`; return; } box.innerHTML = list.map((item, index) => { const progress = item.duration > 0 ? Math.min(100, Math.max(0, item.currentTime / item.duration * 100)) : 0; const progressText = item.progressText || '未记录进度'; return ` <div class="czzyv-history-item"> <div class="czzyv-history-title" title="${escapeHtml(item.title)}"> ${escapeHtml(item.title)} </div> <div class="czzyv-progress-wrap"> <div class="czzyv-progress-bar" style="width:${progress}%"></div> </div> <div class="czzyv-history-progress-text"> 进度:${escapeHtml(progressText)} </div> <div class="czzyv-history-op"> <a href="${escapeHtml(item.url)}" target="_blank" rel="noopener noreferrer" data-action="continue" data-index="${index}">继续播放</a> <button data-url="${escapeHtml(item.url)}" data-action="delete">删除</button> </div> </div> `; }).join(''); box.querySelectorAll('a[data-action="continue"]').forEach(link => { link.addEventListener('click', function (e) { e.stopPropagation(); const index = parseInt(this.getAttribute('data-index'), 10); const item = getHistory()[index]; if (!item) return; // 新窗口打开前保存恢复目标,新窗口读取后自动跳转进度。 saveResumeTarget(item); }); }); box.querySelectorAll('button[data-action="delete"]').forEach(btn => { btn.addEventListener('click', function (e) { e.stopPropagation(); const url = this.getAttribute('data-url'); deleteHistory(url); }); }); } /** * ================================================================ * 页面事件绑定 * ================================================================ */ function bindVideoEvents() { const video = document.querySelector('video'); if (!video || video.__czzyvHistoryBound) return; video.__czzyvHistoryBound = true; // 播放开始时记录,延迟 1 秒确保播放器稳定 video.addEventListener('play', () => { setTimeout(() => recordCurrentPlayPage(true), 1000); }); // 暂停时立即记录 video.addEventListener('pause', () => { recordCurrentPlayPage(true); }); // 播放结束时立即记录 video.addEventListener('ended', () => { recordCurrentPlayPage(true); }); // timeupdate 节流处理,每 10 秒记录一次 let lastUpdateTime = 0; video.addEventListener('timeupdate', () => { const now = Date.now(); if (now - lastUpdateTime >= 10000) { lastUpdateTime = now; recordCurrentPlayPage(false); } }); // 元数据加载完成时记录 video.addEventListener('loadedmetadata', () => { setTimeout(() => recordCurrentPlayPage(true), 1000); }); // 用户拖动进度条后记录新位置 video.addEventListener('seeked', () => { recordCurrentPlayPage(true); }); } function observePageChange() { const observer = new MutationObserver(() => { bindVideoEvents(); if (isPlayPage()) { recordCurrentPlayPage(false); } }); observer.observe(document.body, { childList: true, subtree: true, characterData: true }); } function startIntervalRecord() { setInterval(() => { if (isPlayPage()) { recordCurrentPlayPage(true); } }, CONFIG.RECORD_INTERVAL); // 改为使用 CONFIG.RECORD_INTERVAL } function bindBeforeUnload() { window.addEventListener('beforeunload', () => { recordCurrentPlayPage(true); }); window.addEventListener('pagehide', () => { recordCurrentPlayPage(true); }); } /** * ================================================================ * 初始化 * ================================================================ */ function init() { bindIframeProgressMessage(); addStyle(); createUI(); if (isPlayPage()) { startResumeDispatcher(); setTimeout(() => recordCurrentPlayPage(true), 1000); setTimeout(() => recordCurrentPlayPage(true), 3000); setTimeout(() => recordCurrentPlayPage(true), 6000); } bindVideoEvents(); observePageChange(); startIntervalRecord(); bindBeforeUnload(); } init(); })(); 2 个帖子 - 2 位参与者 阅读完整话题

IT之家 · 2026-05-08 09:50:51+08:00 · tech

5 月 8 日上午消息,新浪科技独家获悉,小米汽车近日迎来人事调整,汽车部副总裁宋钢分管生产制造部、智能制造部、体系运营部。 资料显示,宋钢曾在特斯拉任职,2018 年加入特斯拉上海超级工厂,历任生产制造高级总监、生产制造副总裁及上海工厂厂长,并主导建立了特斯拉上海储能超级工厂。2024 年,他加入远景能源担任集成供应链高级副总裁;今年 4 月,宋钢出任小米集团汽车部副总裁、参谋长。 此次宋钢分管生产制造部、智能制造部、体系运营部,则是干起了在特斯拉的老本行,他将向集团 CEO、汽车部总裁雷军直接汇报,同时宋钢继续兼任汽车部参谋长。

www.ithome.com · 2026-04-17 17:55:00+08:00 · tech

IT之家 4 月 17 日消息,据 36 氪报道,4 月 17 日,小米集团通过内部邮件宣布: 胡峥楠 出任小米集团副总裁、汽车部 CTO 宋钢 出任小米集团汽车部副总裁、参谋长 报道称这是小米汽车自 2021 年宣布成立以来 首次设立 CTO 岗位 。两位新任高管在汽车行业均有超过二十年的从业积累,履历横跨传统车企与新能源赛道。 IT之家查询获悉,胡峥楠 1997 年进入汽车行业,至今已有近三十年从业积累,早在 2021 年,胡峥楠便已加入顺为资本担任投资合伙人,并以小米汽车顾问身份参与早期工作。从 SU7 上市发布会、2024 北京车展,到 SU7 Ultra 挑战纽北赛道的现场,胡峥楠均有公开露面。 ▲ 小米官网管理层团队页面显示,胡峥楠已被列为副总裁 据蓝鲸科技本月初报道,曾主导特斯拉上海超级工厂建设与产能爬坡的核心人物 —— 原特斯拉上海工厂生产制造副总裁宋钢,将入职小米汽车。宋钢曾在通用汽车和福特汽车任职, 2018 年加入特斯拉 ,担任特斯拉上海超级工厂生产制造副总裁。