// ==UserScript== // @name OMPS多人在线同播 // @namespace https://xypp.cc/omps/ // @version 7.3.1 // @description OMPS多人在线同播。脚本默认对所有站点均生效。您可以在TamperMonkey=>管理面板=>OMPS多人在线同播=>设置=>用户排除中修改不想要生效的网站或直接修改源代码 // @author 小鱼飘飘 // @match *://*/* // @icon https://xypp.cc/omps/logo.png // @grant GM_setValue // @grant GM_getValue // @grant unsafeWindow // @grant GM_getTab // @downloadURL https://update.greasyfork.icu/scripts/450545/OMPS%E5%A4%9A%E4%BA%BA%E5%9C%A8%E7%BA%BF%E5%90%8C%E6%92%AD.user.js // @updateURL https://update.greasyfork.icu/scripts/450545/OMPS%E5%A4%9A%E4%BA%BA%E5%9C%A8%E7%BA%BF%E5%90%8C%E6%92%AD.meta.js // ==/UserScript== (function () { 'use strict'; const OMPS_VER = "7.3.1"; const DEBUG = false; const URL = "wss://public.xypp.cc:8093"; /** * OMPS 在线多人同播插件 */ //为非油猴的直接运行方式优化 function LOGGER(a) { if (DEBUG) { console.log("[OMPS]" + a) } } var GM_window, GM_get, GM_set; if (typeof unsafeWindow != 'object') GM_window = window; else GM_window = unsafeWindow; if (typeof GM_getValue != 'function') { GM_get = function (key, val) { return GM_window.localStorage.getItem(key) || val; }; GM_set = function (key, val) { return GM_window.localStorage.setItem(key, val); }; } else { GM_get = GM_getValue; GM_set = GM_setValue; } try { document.body.setAttribute("data-omps-injected", "true"); } catch (e) { } function getTextSafe(text) { var tmpElement = document.createElement("div"); if (tmpElement.textContent !== null) tmpElement.textContent = text; else tmpElement.innerText = text; var retText = tmpElement.innerHTML; return retText; } var ws; var video; var currentDomain = GM_window.location.host; var isTopWindow = false; var topCode = "", topUrl = "", topTitle = ""; var frameWindowObjs = []; var oneKeySetting = {} var trustKey = GM_get("trust", "none"); var initFinish = false; var lastTime = -1; var pauseAt = -1; var pauseUser = ""; var globalCount = 24; var timerCount = 0; var toastCnt = 0; var tmpLocalDelay; var localDelay; var currentRate = 1; var baseSpeed = 1; var tipSpeed = true; var initTime; var videoCode = ""; var HASH = {}; var videoTitle = ""; var videoUrl = ""; var errConnBreak = false; var errConnBreak_reconn = 10; var errConnBreak_tryCnt = 0; var isPausedShowList = false; var autoJoinCountDown = -1; var videoInterValId = -1; const css = `.omps-friend .item:hover{text-decoration: underline;cursor: pointer;}.omps-friend .item:hover::after{content: "→";}.omps-friend{position:absolute;top:140px;left:0}.omps-friend .item{height:30px;border-radius:0 40px 40px 0;background-color:rgba(0,0,0,0.772);padding-left:10px;padding-right:5px;line-height:25px;font-size:15px;color:white;max-width:300px;margin-top:3px}.omps-friend .item.offline{opacity:.5;text-decoration:line-through}.omps-tip{position:absolute;top:100px;height:40px;border-radius:0 40px 40px 0;left:-300px;background-color:rgba(0,0,0,0.772);padding-left:10px;line-height:35px;color:white;max-width:100%;z-index:10000000;transition:all 1s;opacity:0}.omps-tip.open{left:0;opacity:1}.omps-tip div{display:inline;font-size:20px}.omps-tip .btn{color:black;background-color:#fff;border-radius:30px;padding:5px 7px;margin-right:6px;line-height:13px;box-shadow:grey 1px 1px 1px;text-align:center;display:inline-block;vertical-align:middle}.omps-tip .btn svg,.omps-chat-input svg,.omps-chat svg,.omps-friend svg{height:20px;width:20px}.omps-tip .btn svg path{fill:black}.omps-tip .btn:hover{background-color:#dcdcdc}.omps-tip .btn:active{box-shadow:inset gray 1px 1px 1px}.omps-tip .btn span{display:none}.omps-tip .btn:hover span{animation:showLabelAnim 1s linear 1s 1;animation-fill-mode:forwards;max-width:0;overflow-x:hidden;word-break:keep-all;margin:0;display:unset;padding:0;line-height:20px;overflow-y:clip;float:right}@keyframes showLabelAnim{0%{max-width:0}100%{max-width:200px}}.omps-config{font-size:18px;z-index:10000003;max-width:calc(100% - 100px);position:absolute;top:50px;width:600px;left:50px;background-color:rgba(0,0,0,0.772);border-radius:10px;color:white;padding:20px;max-height:calc(100% - 100px);overflow:auto}.omps-config input{display:block;outline:0;border:0;border-radius:5px;color:black;background-color:rgba(255,255,255,0.772)}.omps-config input:active{box-shadow:skyblue 0 0 5px 3px}.omps-config input:focus{box-shadow:skyblue 0 0 3px 1px}.omps-config input.i{height:30px;width:300px;max-width:100%}.omps-config input.c{height:20px;width:20px;-webkit-appearance:checkbox!important;appearance:checkbox!important}.omps-config label{display:block;text-decoration:underline}.omps-config small{display:block;color:lightgray}.omps-config .btn{color:black;background-color:#fff;border-radius:30px;padding:5px 10px;margin-right:10px;box-shadow:grey 1px 1px 1px;text-align:center;display:inline-block;vertical-align:middle}.omps-config .btn:hover{background-color:#dcdcdc}.omps-config .btn:active{box-shadow:inset gray 1px 1px 1px}.omps-chat{font-size:18px;position:absolute;top:5px;right:0;overflow-x:hidden;max-width:100%;width:400px}.omps-chat .item{border-radius:40px 0 0 40px;background-color:rgba(0,0,0,0.772);color:white;max-width:calc(100% - 30px);z-index:10000000;transition:all 1s;opacity:0;transform:translateX(100%);position:relative;margin-top:2px;max-height:0;line-break:loose;word-break:break-all;white-space:normal;overflow:hidden}.omps-chat .item.open{transform:translateX(0%);opacity:1;max-height:400px;padding:10px}.omps-chat .item .name{text-decoration:underline;font-weight:bold}.omps-chat .item .delay{font-size:x-small;font-style:italic;position:absolute;top:1px;right:1px}.omps-chat-input{white-space: nowrap;position:absolute;bottom:50px;right:100px;width:calc(100% - 200px);background-color:rgba(0,0,0,0.772);height:40px}.omps-chat-input{font-size:18px;position:absolute;bottom:150px;right:100px;width:calc(100% - 200px);background-color:rgba(0,0,0,0.772);border-radius:50px;padding:10px 30px;opacity:0;transition:all .5s}.omps-chat-input.tipPos{animation:omps-tipPos 1s ease-in-out 0s infinite}.omps-chat-input:hover,.omps-chat-input.inputActive{opacity:1}@keyframes omps-tipPos{0%{opacity:0}50%{opacity:.5}100%{opacity:0}}.omps-chat-input input{background-color:rgba(0,0,0,0);display:inline-block;outline:0;border:rgba(255,255,255,0.497) 1px solid;border-radius:5px;width:calc(100% - 100px);height:100%;color:white}.omps-chat-input input:active{box-shadow:skyblue 0 0 5px 3px}.omps-chat-input input:focus{box-shadow:skyblue 0 0 3px 1px}.omps-chat-input .btn,.omps-chat .btn{color:black;background-color:#fff;border-radius:30px;padding:5px 10px;margin-right:10px;box-shadow:grey 1px 1px 1px;text-align:center;display:inline-block;vertical-align:middle}.omps-chat-input .btn svg path,.omps-chat .btn svg path{fill:black}.omps-chat-input .btn:hover,.omps-chat .btn:hover{background-color:#dcdcdc}.omps-chat-input .btn:active,.omps-chat .btn:active{box-shadow:inset gray 1px 1px 1px}.omps-chat .btn{padding: 2px 5px;font-size: 16px;}.omps-tipLine{position: absolute;top: 100px;height: 40px;border-radius:5px;left: 0px;width: 3px;background-color: white;z-index: 10000001;transition:background-color 0.5s;}.omps-tipLineMask{position: absolute;top: 100px;height: 40px;border-radius:5px;left: 0px;width: 20px;background-color: transparent;z-index: 9999999;}`; function OMPS_INFO() { if (isTopWindow) console.log(` ____ __ _______ _____ / __ \\/ |/ / __ \\/ ___/ / / / / /|_/ / /_/ /\\__ \\ / /_/ / / / / ____/___/ / \\____/_/ /_/_/ /____/ OMPS在线同播插件Ver${OMPS_VER} https://xypp.cc/omps/ `); } const lang = { join: "检测到视频,点击进入联机放映", reconfigure: "重新填写配置", quickJoin: "快速加入", toQuickJoin: ["将在", "秒后加入放映厅", ""], quit: "关闭", setting: { userId: { label: "用户名", tip: "其他用户看到您的名字。请不要使用短用户名防止重名" }, group: { label: "加入的房间ID", tip: "该房间ID用于同步进度。与您的朋友输入相同的房间ID且您和朋友正在观看相同的视频时,你们的进度将会被同步" }, autoJoin: { label: "自动加入", tip: "勾选后,下次检测到视频将会自动进入上次填写的群组" }, silent: { label: "静默模式", tip: "仅会提示您失去同步消息,不会提示您成功消息" }, friend: { label: "始终显示好友", tip: "常驻显示左侧的好友列表。该列表在不同步/等待好友进度时会自动显示,静默模式下该列表不会显示。" }, showMsgIpt: { label: "显示消息输入框", tip: "在视频下方中央显示一个输入框,这将您允许发送消息" }, hideMsg: { label: "隐藏消息", tip: "隐藏消息,不显示别人发送的消息。注意,打开此功能后,显示消息输入框功能将失效。" }, noRateSync: { label: "不使用变速同步", tip: "变速同步会在您与好友进度略微不同时将快的一方速率降低5%来缓慢而无感的达到同步。关闭此选项后将使用默认的暂停同步方法。" }, saveExit: "保存并关闭", saveJoin: "保存并连接" }, conErr: ["无法连接到服务器,将在", "秒后重连"], conErrNoRty: "无法连接到服务器,点击重连", autoReconn: "自动重连中...", pauseMsg: "的视频太慢了,等他一下...", pauseSlowMsg: "您的视频慢了,房间中其他人正在等您", pauseHandMsg: "房间中其他人正在等您,请手动开始播放", delay: ["延迟达到了", "ms,可能会影响您或其他人的观影体验"], success: "连接成功,延迟", syncSuccess: "同步进度完成", playerJoin: "加入了放映厅", playerLeave: "离开了放映厅", sync: "同步", call: "集合", sendmsg: "发送", exit: "退出", unsync: ["失去同步!最快进度为", "(", "s)"], unsync_top: "失去同步!您是目前的最快进度", videoChange: "视频变更,准备重连", dumplName: "注册失败,与服务器中已连接的用户重名", secondConnect: "因为您的另一个客户端的连接,本页面已与服务器断开连接", tiper: "提示", jumpTip: ["用户[", "]从此视频转向观看‘", "’", "点击前往"], jumpConfirm: ["您即将前往", "跳转到目标网站可能会带来不明的风险,您确定要跳转吗?"], shareUrl: "成功创建了分享链接", share: "分享", waitTop: ["分享失败", "等待顶层页面响应,请稍后"], streamVideo: "这是一个直播视频,已关闭进度同步", usingSpeedControl: "正在使用调速同步,这将调整您的视频播放速度" } //===========设置相关============ var setting = { autoJoin: "", userId: "", group: "", silent: "", friend: "", showMsgIpt: "", hideMsg: "", noRateSync: "", }; //导入设置(TamporMonkey/LocalStorage) for (const settingItem in setting) { if (Object.hasOwnProperty.call(setting, settingItem)) { const defaultVal = setting[settingItem]; setting[settingItem] = GM_get(settingItem, defaultVal); } } var openedSettingPanel; //[函数]打开设置面板 function openSettingPanel() { if (openedSettingPanel) { closeSettingPanel(); } var el = document.createElement("div"); el.className = "omps-config"; el.id = "omps-config"; el.onclick = noPop; el.innerHTML = msgMaker.settingPanel(); document.body.appendChild(el); openedSettingPanel = el; } function saveSettings() { var el = document.getElementById("omps-config"); if (!el) return; GM_set("userId", document.getElementById("omps-input-name").value); GM_set("group", document.getElementById("omps-input-group").value); GM_set("autoJoin", document.getElementById("omps-input-quick").checked ? "1" : ""); GM_set("silent", document.getElementById("omps-input-silent").checked ? "1" : ""); GM_set("friend", document.getElementById("omps-input-friend").checked ? "1" : ""); GM_set("showMsgIpt", document.getElementById("omps-input-showMsgIpt").checked ? "1" : ""); GM_set("hideMsg", document.getElementById("omps-input-hideMsg").checked ? "1" : ""); GM_set("noRateSync", document.getElementById("omps-input-noRateSync").checked ? "1" : ""); setting.userId = document.getElementById("omps-input-name").value; setting.group = document.getElementById("omps-input-group").value; setting.autoJoin = document.getElementById("omps-input-quick").checked ? "1" : ""; setting.silent = document.getElementById("omps-input-silent").checked ? "1" : ""; setting.friend = document.getElementById("omps-input-friend").checked ? "1" : ""; setting.showMsgIpt = document.getElementById("omps-input-showMsgIpt").checked ? "1" : ""; setting.hideMsg = document.getElementById("omps-input-hideMsg").checked ? "1" : ""; setting.noRateSync = document.getElementById("omps-input-noRateSync").checked ? "1" : ""; } function closeSettingPanel() { var el = openedSettingPanel; if (el && el.parentNode) { el.parentNode.removeChild(el); } } //===========设置相关============ var msgMaker = { settingPanel: () => {//设置面板 return ` ${lang.setting.userId.tip}
${lang.setting.group.tip}
${lang.setting.autoJoin.tip}
${lang.setting.silent.tip}
${lang.setting.friend.tip}
${lang.setting.showMsgIpt.tip}
${lang.setting.hideMsg.tip}
${lang.setting.noRateSync.tip}

${lang.setting.saveExit} ${lang.setting.saveJoin} `; }, settingButtons: () => { return ` ${lang.reconfigure} ${lang.share} ${lang.exit} ${lang.quit} `; }, joinAsk: (hasConfigured) => { return `${lang.join} ${lang.reconfigure} ${lang.quickJoin} ${lang.quit} `; }, joinPrep: (restCount) => { return `${lang.toQuickJoin[0]}${parseInt(restCount)}${lang.toQuickJoin[1]}${setting.group}${lang.toQuickJoin[2]} ${lang.reconfigure} ${lang.quickJoin} ${lang.quit} `; }, success: (code, delv, group) => { return `${getTextSafe(group)} ${lang.success}${getTextSafe(delv)} ${msgMaker.settingButtons()} `; }, conErr: () => { return `${lang.conErr}`; }, pauseMsg: (pu) => { return `${pu}${lang.pauseMsg}`; }, pauseFor: (isPaused) => { return `${isPaused ? lang.pauseHandMsg : lang.pauseSlowMsg}`; }, playFail: () => { return `${lang.playFail}`; }, delayTip: (delv) => { return `${lang.delay[0]}${getTextSafe(delv)}${lang.delay[1]}`; }, syncSuccess: () => { return lang.syncSuccess; }, playerJoin: (pname) => { return `${lang.playerJoin}`; }, playerLeave: (pname) => { return `${lang.playerLeave}`; }, unsync: (target, deltaTime) => { return `${lang.unsync[0]}${getTextSafe(target)}${lang.unsync[1]}${getTextSafe(deltaTime)}${lang.unsync[2]} ${lang.sync} ${lang.call} ` }, unsync_top: () => { return `${lang.unsync_top} ${lang.call} ` }, chat_input: () => { return ` ${lang.sendmsg} `; }, chat_block: (name, msg, time) => { return ` ${getTextSafe(name)}  :  ${getTextSafe(msg)} ${time} `; }, error: (msg, arg) => { if (msg == "E_USERNAME_DUMPL") { return ` ${lang.dumplName} ${lang.reconfigure} ${lang.quit} `; } else if (msg == "E_SECOND_CONN") { return `${lang.secondConnect} ${lang.reconfigure} ${lang.quickJoin} ${lang.quit} `; } else if (msg == "E_ERR_NET") { let _tipTxt = `${lang.conErr[0]}${arg}${lang.conErr[1]}`; if (!arg) _tipTxt = lang.conErrNoRty; return `${_tipTxt} ${lang.reconfigure} ${lang.quickJoin} ${lang.quit} `; } }, userJump: (name, title, url) => { if (title.length > 80) { title = title.substr(0, 80) + "..."; } var urlDat = ""; try { urlDat = window.btoa(url) } catch (e) { } var urlBtn = `
${lang.jumpTip[3]}
`; if (!urlDat) urlBtn = "【发生错误,无法跳转】"; return ` ${getTextSafe(name)}  :  ${lang.jumpTip[0]}${getTextSafe(name)}${lang.jumpTip[1]}${getTextSafe(title)}${lang.jumpTip[2]}${urlBtn} ` }, share_url: (url) => { return ` ${lang.shareUrl}
` }, } // 向服务器发送数据 var sendDat = (data) => { ws.send(JSON.stringify(data)); } //=============页内通讯===================== function registeInPageMessage() { GM_window.addEventListener("message", inPageOnMessage); if (GM_window !== GM_window.top) { isTopWindow = false; GM_window.top.postMessage({ fromApp: "OMPS", action: "reg" }, "*"); } else { isTopWindow = true; topUrl = GM_window.location.href; topTitle = GM_window.document.title; OMPS_INFO(); } } function inPageOnMessage(event) { var data = event.data; if ((data.fromApp || "") != "OMPS") return; if (data.action == "reg") { event.source.postMessage({ fromApp: "OMPS", action: "setCode", url: topUrl, title: topTitle, code: getVideoCode(), }, "*"); if (oneKeySetting.exist) event.source.postMessage({ fromApp: "OMPS", action: "onekey", data: oneKeySetting, }, "*"); frameWindowObjs.push(event.source) } else if (data.action == "setHash") { GM_window.location.hash = event.data.val; } else if (data.action == "setCode") { topCode = data.code; topUrl = data.url; topTitle = data.title; LOGGER("[OMPS]TOP_CODE Received:" + topCode); detecVideoCode(); } else if (data.action == "onekey") { oneKeySetting = data.data; applyOneKeySetting(); } else if (data.action == "jumpUrl") { if (confirm(lang.jumpConfirm[0] + data.url + '\n' + lang.jumpConfirm[1])) { GM_window.location.href = data.url; } } } //=============视频识别代码计算============== function videoCodeOutdateEventRegist_title() { try { var titleEl = document.getElementsByTagName("title")[0]; var MutationObserver = window.MutationObserver || window.WebKitMutationObserver; var MutationObserverConfig = { childList: true, subtree: true, characterData: true }; var observer = new MutationObserver(function (mutations) { LOGGER("视频检测到标题变化"); topUrl = GM_window.location.href; topTitle = GM_window.document.title; for (let i = 0; i < frameWindowObjs.length; i++) { frameWindowObjs[i].postMessage({ fromApp: "OMPS", action: "setCode", url: topUrl, title: topTitle, code: getVideoCode(), }, "*"); } detecVideoCode(); }); observer.observe(titleEl, MutationObserverConfig); } catch (ignore) { } } //检测VideoCode是否过期 function detecVideoCode() { if (!initFinish) return; let ovideoCode = videoCode; calcVideoCode(video, function () { if (videoCode != ovideoCode) { showToast(lang.videoChange, 1, 1); cleanUpVideo(); showTip(); } else setTimeout(detecVideoCode, 5000); }); } //计算当前视频页面对应的VideoCode function getVideoCode() { var orgCode = GM_window.document.title; var tmpVideoCode = HASH.md5(orgCode); tmpVideoCode = tmpVideoCode.toUpperCase(); return tmpVideoCode; } //更新VideoCode function calcVideoCode(_video, callBack, count = 0) { globalCount = 0; if (count > 10) { LOGGER("[OMPS]TOP_CODE Recv. timeout"); video = null; globalCount = 24; videoCode = ""; testVideo(); return; } let t_videoCode = getVideoCode(); if (!isTopWindow) { if (!topCode) { LOGGER("[OMPS]STILL WAITING TOP"); return setTimeout(calcVideoCode, 1000, _video, callBack, (count || 0) + 1); } else { videoUrl = topUrl; videoTitle = topTitle; t_videoCode = HASH.md5(t_videoCode + topCode); } } else { videoUrl = GM_window.location.href; videoTitle = GM_window.document.title; } videoCode = t_videoCode.toUpperCase(); callBack(); } //视频调速 function setVideoSpeed(speed) { currentRate = speed; if (video.playbackRate != speed) video.playbackRate = speed; } //==========TOAST提示条==================== var toastElement; var toastTipLine; var toastTipLineMask; function showToast(text, time, imp) { if (time === undefined) time = -1; if (time != -1 && setting.silent && !imp) return hideToast(); if (!toastElement) { toastElement = document.createElement("div"); toastElement.className = "omps-tip" toastElement.onclick = noPop; video.parentNode.appendChild(toastElement); } toastCnt = time; toastElement.innerHTML = "
" + text + "
"; toastElement.style.display = "block"; setTimeout(() => { toastElement.classList.add("open"); }, 100); } function hideToast(fh) { if (fh) { if (toastElement) toastElement.style.display = "none"; toastCnt = 0; } else { if (toastElement) toastElement.classList.remove("open"); } } function appendTipLine() { if (toastTipLine && toastTipLine.parentNode) { try { toastTipLine.parentNode.removeChild(toastTipLine) } catch (ignore) { }; } if (toastTipLineMask && toastTipLineMask.parentNode) { try { toastTipLineMask.parentNode.removeChild(toastTipLineMask) } catch (ignore) { }; } toastTipLine = document.createElement("div"); toastTipLine.className = "omps-tipLine"; toastTipLine.onmouseenter = toastTipLine.ontouchstart = function () { showToast(msgMaker.settingButtons(), 3, true); } toastTipLine.onclick = noPop; video.parentNode.appendChild(toastTipLine); toastTipLineMask = document.createElement("div"); toastTipLineMask.className = "omps-tipLineMask"; toastTipLineMask.onclick = function (event) { showToast(msgMaker.settingButtons(), 5, true); noPop(event) }; video.parentNode.appendChild(toastTipLineMask); } //==========加入流程=========== function ableToDirectJoin() { var lastJoinTime = parseInt(GM_get("lastTime", 0)); var lastJoinDomain = GM_get("lastDomain", ""); if (Date.now() - lastJoinTime < 60 * 1000 && lastJoinDomain == currentDomain) { LOGGER("[OMPS]:判断自动跳转视频,自动加入已生效"); return true; } return false; } function updateDirectJoin() { GM_set("lastTime", Date.now()); GM_set("lastDomain", currentDomain); } function showTip() { //STYLE_A 加入目标window,使得视频播放器中样式可用 LOGGER("[OMPS]:已找到视频"); var el = document.createElement("style"); el.innerHTML = css; el.className = "OMPS_CSS"; video.parentNode.appendChild(el); //B 在大window中加入样式 el = document.createElement("style"); el.innerHTML = css; el.className = "OMPS_CSS"; document.body.appendChild(el); if (video.duration === Infinity) { showToast(lang.streamVideo, 3); return; } if (ableToDirectJoin() || setting.oneKeyJoin) { setting.oneKeyJoin = true; ompsEventRouter("create"); } else if (setting.autoJoin && setting.userId && setting.group) { autoJoinCountDown = 3; showToast(msgMaker.joinPrep(autoJoinCountDown)); } else showToast(msgMaker.joinAsk(), 20, 1); } var getVideosNextIframeList; //获取视频 function testVideo() { if (globalCount <= 0) { return; } globalCount--; try { document.body.setAttribute("data-omps-injected", "true"); video = null var videos = document.getElementsByTagName("video"); for (let i = 0; i < videos.length; i++) { var v = videos[i]; if (!video && document.body.offsetWidth < v.offsetWidth * 3) { video = v; break; } } if (getVideosNextIframeList) { for (let i = 0; i < getVideosNextIframeList.length; i++) { try { var frameBody = getVideosNextIframeList[i].contentDocument.body; if (frameBody.getAttribute("data-omps-injected") == "true") continue; if (frameBody.getAttribute("data-omps-injected") != "waitParent") { frameBody.setAttribute("data-omps-injected", "waitParent"); globalCount++; continue; } getVideosNextIframeList[i].contentWindow.ompsEventRouter = ompsEventRouter; var videos = getVideosNextIframeList[i].contentDocument.getElementsByTagName("video"); for (let i = 0; i < videos.length; i++) { var v = videos[i]; if (!video && frameBody.offsetWidth < v.offsetWidth * 3) { video = v; break; } } } catch (e) { }; } } getVideosNextIframeList = document.getElementsByTagName("iframe"); } catch (e) { video = null; } if (video) { calcVideoCode(video, showTip); } else if (globalCount > 0) setTimeout(testVideo, 5000); } //连接网络 function initNetwork() { videoTitle = topTitle; videoUrl = topUrl; if (trustKey == "none") { trustKey = HASH.md5(Date.now() + "--" + parseInt(Math.random() * 1000000)); GM_set("trust", trustKey); } if (ws) { cleanUpVideo(); } createTimer(); ws = new WebSocket(URL); ws.onmessage = wsMsg; ws.onopen = () => setTimeout(() => { sendDat({ type: "reg", user: setting.userId, group: videoCode + "-" + setting.group, trust: trustKey, title: videoTitle, url: videoUrl }); }, 1000); ws.onclose = ws.onerror = () => { cleanUpVideo(); errConnBreak = true; if (errConnBreak_tryCnt > 5) showToast(msgMaker.error("E_ERR_NET", false), -1, 10); else showToast(msgMaker.error("E_ERR_NET", parseInt(errConnBreak_reconn / 5)), -1, 10); } } //=========前端暴露函数============= var ompsEventRouter = GM_window.ompsEventRouter = function (name, event) { if (name == "config") { openSettingPanel(); } else if (name == "create") { hideToast(); if (setting.group && setting.userId) initNetwork(); else openSettingPanel(); } else if (name == "call") { sendDat({ type: "call", time: video.currentTime * 1000 + localDelay }); } else if (name == "sync") { sendDat({ type: "sync" }); } else if (name == "hide") { hideToast(); } else if (name == "saveExit") { saveSettings(); closeSettingPanel(); } else if (name == "saveLink") { saveSettings(); if (setting.group && setting.userId) { hideToast(); initNetwork(); closeSettingPanel(); } } else if (name == "sendMsg") { sendMsg(); } else if (name == "share") { createShare(); } else if (name == "jump") { if (isTopWindow) { if (confirm(lang.jumpConfirm[0] + GM_window.atob(event.currentTarget.getAttribute('data-url')) + '\n' + lang.jumpConfirm[1])) { GM_window.location = GM_window.atob(event.currentTarget.getAttribute('data-url')); } } else { GM_window.top.postMessage({ fromApp: "OMPS", action: "jumpUrl", url: window.atob(event.currentTarget.getAttribute('data-url')) }, '*'); } } else if (name == "exit") { cleanUpVideo(); showToast(msgMaker.joinAsk(), 20, 1); } else if (name == "noAuto") { autoJoinCountDown = -1; showToast(msgMaker.joinAsk(), 20, 1); } } var noPop = GM_window.omps_noPop = function (event) { event.stopPropagation(); } //=========好友列表================ var friends = {}, friendCountTick = 0, friCtr; function jumpToFriend(event) { noPop(event); var friid = event.currentTarget.getAttribute("data-friend-name"); if (friends[friid].tick != friendCountTick) return; video.currentTime = (friends[friid].time - friends[setting.userId].time + lastTime) / 1000; } function addFriendItem(name) { var el = document.createElement("div"); friCtr.appendChild(el); el.setAttribute("data-friend-name", name); el.onclick = jumpToFriend; friends[name] = { tick: friendCountTick, element: el, time: 0 } } function removeFriendItem(name) { friCtr.removeChild(friends[name].element); delete friends[name]; } function updateFriendList(list) { if (!friCtr) { friCtr = document.createElement("div"); friCtr.className = "omps-friend" video.parentNode.appendChild(friCtr); } friendCountTick++; for (let i = 0; i < list.length; i++) { if (!friends[list[i].name]) addFriendItem(list[i].name); friends[list[i].name].time = list[i].time; if (list[i].name == setting.userId) friends[list[i].name].element.innerText = list[i].name + "(你) "; else { var strSig = ""; if (list[i].time > lastTime) { strSig = "+"; } friends[list[i].name].element.innerText = list[i].name + "(" + strSig + ((list[i].time - lastTime) / 1000).toFixed(1) + "s) "; } friends[list[i].name].tick = friendCountTick; } for (const friid in friends) { if (Object.hasOwnProperty.call(friends, friid)) { if (friends[friid].tick != friendCountTick) { friends[friid].element.className = "item offline"; if (friendCountTick - friends[friid].tick > 2) { removeFriendItem(friid); } } else { friends[friid].element.className = "item"; } } } } function hideFriendList() { if (friCtr) { friCtr.parentNode.removeChild(friCtr); friCtr = undefined; friends = {}; friendCountTick = 0; } } //========消息==================== var msgCtr, msgInput; function addMsgInput() { var msgInputE = document.createElement("div"); msgInputE.className = "omps-chat-input tipPos"; msgInputE.innerHTML = msgMaker.chat_input(); msgInputE.onclick = noPop; video.parentNode.appendChild(msgInputE); msgInput = msgInputE.getElementsByTagName("input")[0]; setTimeout(() => msgInputE.classList.remove("tipPos"), 3000); } function createMsg(from, msg, time) { if (from == setting.userId) time = "你"; else if (time > 0) time = "+" + time + "s"; else time += "s"; createMsgRaw(msgMaker.chat_block(from, msg, time)) } function createMsgRaw(msg, delTime) { if (!msgCtr) { msgCtr = document.createElement("div"); msgCtr.className = "omps-chat"; video.parentNode.appendChild(msgCtr); } var el = document.createElement("div") el.className = "item"; el.innerHTML = msg msgCtr.appendChild(el); var hideTime = Math.max(Math.min(8000, msg.length * 400), 3000); if (delTime) hideTime = delTime; setTimeout(() => el.className = "item open", 200); setTimeout(() => el.className = "item", hideTime + 200); setTimeout(() => msgCtr.removeChild(el), hideTime + 600); } function sendMsg() { var text = msgInput.value; msgInput.value = ""; if (!text) return; sendDat({ type: "msg", time: parseInt(video.currentTime * 1000), msg: text }) } //=======分享URL==================== function createShare() { if (!topUrl && !isTopWindow) { createMsg(lang.waitTop[0], lang.waitTop[1], 4000); return; } var url = topUrl + "#&OMPS_room=" + setting.group + "&OMPS_join"; createMsgRaw(msgMaker.share_url(url), 10000); } function parseShareHash() { try { //一键加入放映厅等功能 var oneKeyCommand = GM_window.location.hash; if (oneKeyCommand.charAt(0) == "#") oneKeyCommand = oneKeyCommand.slice(1); if (oneKeyCommand) { LOGGER("[OMPS]:正在处理一键链接参数"); oneKeySetting.exist = true; var newHashLst = []; var commandList = oneKeyCommand.split("&"); for (let i = 0; i < commandList.length; i++) { var cmd = commandList[i].split("="); if (cmd.length == 2 && cmd[0] == "OMPS_room") { oneKeySetting.group = cmd[1]; } else if (cmd.length == 1 && cmd[0] == "OMPS_join") { oneKeySetting.oneKeyJoin = true; } else { newHashLst.push(cmd.join("=")); } } GM_window.location.hash = newHashLst.join("&"); } } catch (e) { LOGGER(`[OMPS][ERROR]:${e}`); } applyOneKeySetting(); for (let i = 0; i < frameWindowObjs.length; i++) { frameWindowObjs[i].postMessage({ fromApp: "OMPS", action: "onekey", data: oneKeySetting, }, "*"); } } function applyOneKeySetting() { if (oneKeySetting.exist) { if (oneKeySetting.group) setting.group = oneKeySetting.group; if (oneKeySetting.oneKeyJoin) setting.oneKeyJoin = true; if (video) { if (setting.group && setting.userId) { initNetwork(); } else if (oneKeyJoin) { openSettingPanel(); } } } } //=======全局计时器与事件分支=========== function cleanUpVideo() {//【退出放映厅】清理计时器,断开连接 try { ws.onclose = ws.onerror = () => { }; } catch (e) { } try { ws.close(); } catch (e) { } initFinish = false; clearInterval(videoInterValId); if (toastTipLine && toastTipLine.parentNode) { try { toastTipLine.parentNode.removeChild(toastTipLine) } catch (ignore) { }; } if (toastTipLineMask && toastTipLineMask.parentNode) { try { toastTipLineMask.parentNode.removeChild(toastTipLineMask) } catch (ignore) { }; } hideFriendList(); } function createTimer() {//创建视频计时器(200ms) var reportPtns = 0; videoInterValId = setInterval(() => { reportPtns++; if (reportPtns == 100) reportPtns = 0; if (initFinish) { timerCount++; timerCount %= 40; if (timerCount % 20 == 0) { if ((setting.friend || (isPausedShowList && !setting.silent))) { sendDat({ type: "list" }); } else hideFriendList(); } if (timerCount == 0) { tmpLocalDelay = Date.now(); sendDat({ type: "delay" }); } var currentTime = parseInt(video.currentTime * 1000); if ( (timerCount % 2 == 0 && currentTime != lastTime) ) { sendDat({ type: "ping", time: currentTime + localDelay }); lastTime = currentTime; } if (currentRate != video.playbackRate) { setVideoSpeed(video.playbackRate); sendDat({ type: "speed", speed: currentRate }); } if (pauseAt != -1 && video.currentTime >= pauseAt) { video.pause(); showToast(msgMaker.pauseMsg(pauseUser)); isPausedShowList = true; pauseAt = -1; } } }, 200); } setInterval(() => {//全局事件计时器(Toast计时,重连计时等) if (toastCnt > 0) { if (--toastCnt == 0) { hideToast(); setTimeout(() => { if (toastCnt == 0) hideToast(1); }, 1000); } } if (errConnBreak && errConnBreak_reconn > 0) { if (errConnBreak_tryCnt > 5) { errConnBreak_reconn = -1; } errConnBreak_reconn--; if (errConnBreak_reconn == 0) { errConnBreak_tryCnt++; errConnBreak = false; errConnBreak_reconn = errConnBreak_tryCnt * 10 + 2; ompsEventRouter('create'); showToast(lang.autoReconn, 3); } } if (autoJoinCountDown > 0) { autoJoinCountDown--; if (autoJoinCountDown == 0) { hideToast(); ompsEventRouter("create"); } else { showToast(msgMaker.joinPrep(autoJoinCountDown)); } } }, 1000); var wsMsg = (e) => {//WEBSOCKET事件响应 var data = JSON.parse(e.data), time, realTime; LOGGER(data); if (data.time) { time = (data.time + localDelay) / 1000; realTime = (data.time) / 1000; } switch (data.type) { case "init": errConnBreak = false; errConnBreak_tryCnt = 0; errConnBreak_reconn = 10; if (data.time != 0) { initTime = data.time; } else initTime = 0; localDelay = 0; pauseAt = -1; tmpLocalDelay = Date.now(); sendDat({ type: "delay" }); isPausedShowList = false; appendTipLine(); toastTipLine.style.backgroundColor = "#a5d6a7"; try { video.play(); hideToast(); } catch (e) { showToast(msgMaker.playFail()); } break; case "delay": updateDirectJoin(); localDelay = parseInt((Date.now() - tmpLocalDelay) / 2); if (initTime != 0) { video.currentTime = (initTime + 3 * localDelay) / 1000; initTime = 0; } if (localDelay > 600) { toastTipLine.style.backgroundColor = "#fff59d"; showToast(msgMaker.delayTip(localDelay), 2); } else { toastTipLine.style.backgroundColor = "#a5d6a7"; } if (!initFinish) { setTimeout(() => { initFinish = true; detecVideoCode(); var delayTime = 5; if (setting.autoJoin || setting.oneKeyJoin) delayTime = 12; showToast(msgMaker.success(videoCode, localDelay, setting.group), delayTime, 1); }, 1000); if (setting.showMsgIpt) { addMsgInput(); } } break; case "pause": //服务器发送Pause指令 // 指令包含: // 切换时间戳 toastTipLine.style.backgroundColor = "#ffab91"; if (video.currentTime >= time) { isPausedShowList = true; video.pause(); showToast(msgMaker.pauseMsg(data.foru)); } else { pauseAt = time; pauseUser = data.foru; if (pauseUser == setting.userId) { showToast(msgMaker.pauseFor(video.paused)); } } break; case "play": //服务器发送Play指令 pauseAt = -1; isPausedShowList = false; if (video.currentTime > time) video.currentTime = time; setVideoSpeed(baseSpeed); toastTipLine.style.backgroundColor = "#a5d6a7"; try { video.play(); showToast(msgMaker.syncSuccess(), 1); } catch (e) { showToast(msgMaker.playFail()); } break; case "join": createMsg(data.user, msgMaker.playerJoin(data.user), 0); break; case "leave": createMsg(data.user, msgMaker.playerLeave(data.user), 0); break; case "unsync": toastTipLine.style.backgroundColor = "#ffab91"; isPausedShowList = true; let deltaTime = (time - video.currentTime).toFixed(1); if (deltaTime >= 0) deltaTime = "+" + deltaTime; if (data.target != setting.userId) showToast(msgMaker.unsync(data.target, deltaTime)); else showToast(msgMaker.unsync_top()); pauseAt = -1; break; case "sync": toastTipLine.style.backgroundColor = "#a5d6a7"; video.currentTime = (realTime); if (data.wait) { video.pause(); } pauseAt = -1; showToast(msgMaker.syncSuccess(), 1); isPausedShowList = false; break; case "error": cleanUpVideo(); showToast(msgMaker.error(data.msg), -1, 10); isPausedShowList = false; break; case "list": if (setting.friend || (isPausedShowList && !setting.silent)) { updateFriendList(data.list); } break; case "msg": if (!setting.hideMsg) { createMsg(data.name, data.msg, parseFloat(realTime - video.currentTime).toFixed(1)); } break; case "userJmp": if (data.title && !setting.hideMsg) { createMsgRaw(msgMaker.userJump(data.name, data.title, data.url), 10000); } break; case "slow": toastTipLine.style.backgroundColor = "#fff59d"; if (setting.noRateSync) break; if (video.currentTime >= time) { if (tipSpeed && !setting.silent) { showToast(lang.usingSpeedControl, 4); tipSpeed = false; } setVideoSpeed(baseSpeed * 0.95); } else { setVideoSpeed(baseSpeed); } break; case "noslow": toastTipLine.style.backgroundColor = "#a5d6a7"; setVideoSpeed(baseSpeed); break; case "speed": baseSpeed = data.speed; setVideoSpeed(baseSpeed); break; default: break; } } //=======MD5 JS======== !function (n) { "use strict"; function d(n, t) { var r = (65535 & n) + (65535 & t); return (n >> 16) + (t >> 16) + (r >> 16) << 16 | 65535 & r } function f(n, t, r, e, o, u) { return d((u = d(d(t, n), d(e, u))) << o | u >>> 32 - o, r) } function l(n, t, r, e, o, u, c) { return f(t & r | ~t & e, n, t, o, u, c) } function g(n, t, r, e, o, u, c) { return f(t & e | r & ~e, n, t, o, u, c) } function v(n, t, r, e, o, u, c) { return f(t ^ r ^ e, n, t, o, u, c) } function m(n, t, r, e, o, u, c) { return f(r ^ (t | ~e), n, t, o, u, c) } function c(n, t) { var r, e, o, u; n[t >> 5] |= 128 << t % 32, n[14 + (t + 64 >>> 9 << 4)] = t; for (var c = 1732584193, f = -271733879, i = -1732584194, a = 271733878, h = 0; h < n.length; h += 16)c = l(r = c, e = f, o = i, u = a, n[h], 7, -680876936), a = l(a, c, f, i, n[h + 1], 12, -389564586), i = l(i, a, c, f, n[h + 2], 17, 606105819), f = l(f, i, a, c, n[h + 3], 22, -1044525330), c = l(c, f, i, a, n[h + 4], 7, -176418897), a = l(a, c, f, i, n[h + 5], 12, 1200080426), i = l(i, a, c, f, n[h + 6], 17, -1473231341), f = l(f, i, a, c, n[h + 7], 22, -45705983), c = l(c, f, i, a, n[h + 8], 7, 1770035416), a = l(a, c, f, i, n[h + 9], 12, -1958414417), i = l(i, a, c, f, n[h + 10], 17, -42063), f = l(f, i, a, c, n[h + 11], 22, -1990404162), c = l(c, f, i, a, n[h + 12], 7, 1804603682), a = l(a, c, f, i, n[h + 13], 12, -40341101), i = l(i, a, c, f, n[h + 14], 17, -1502002290), c = g(c, f = l(f, i, a, c, n[h + 15], 22, 1236535329), i, a, n[h + 1], 5, -165796510), a = g(a, c, f, i, n[h + 6], 9, -1069501632), i = g(i, a, c, f, n[h + 11], 14, 643717713), f = g(f, i, a, c, n[h], 20, -373897302), c = g(c, f, i, a, n[h + 5], 5, -701558691), a = g(a, c, f, i, n[h + 10], 9, 38016083), i = g(i, a, c, f, n[h + 15], 14, -660478335), f = g(f, i, a, c, n[h + 4], 20, -405537848), c = g(c, f, i, a, n[h + 9], 5, 568446438), a = g(a, c, f, i, n[h + 14], 9, -1019803690), i = g(i, a, c, f, n[h + 3], 14, -187363961), f = g(f, i, a, c, n[h + 8], 20, 1163531501), c = g(c, f, i, a, n[h + 13], 5, -1444681467), a = g(a, c, f, i, n[h + 2], 9, -51403784), i = g(i, a, c, f, n[h + 7], 14, 1735328473), c = v(c, f = g(f, i, a, c, n[h + 12], 20, -1926607734), i, a, n[h + 5], 4, -378558), a = v(a, c, f, i, n[h + 8], 11, -2022574463), i = v(i, a, c, f, n[h + 11], 16, 1839030562), f = v(f, i, a, c, n[h + 14], 23, -35309556), c = v(c, f, i, a, n[h + 1], 4, -1530992060), a = v(a, c, f, i, n[h + 4], 11, 1272893353), i = v(i, a, c, f, n[h + 7], 16, -155497632), f = v(f, i, a, c, n[h + 10], 23, -1094730640), c = v(c, f, i, a, n[h + 13], 4, 681279174), a = v(a, c, f, i, n[h], 11, -358537222), i = v(i, a, c, f, n[h + 3], 16, -722521979), f = v(f, i, a, c, n[h + 6], 23, 76029189), c = v(c, f, i, a, n[h + 9], 4, -640364487), a = v(a, c, f, i, n[h + 12], 11, -421815835), i = v(i, a, c, f, n[h + 15], 16, 530742520), c = m(c, f = v(f, i, a, c, n[h + 2], 23, -995338651), i, a, n[h], 6, -198630844), a = m(a, c, f, i, n[h + 7], 10, 1126891415), i = m(i, a, c, f, n[h + 14], 15, -1416354905), f = m(f, i, a, c, n[h + 5], 21, -57434055), c = m(c, f, i, a, n[h + 12], 6, 1700485571), a = m(a, c, f, i, n[h + 3], 10, -1894986606), i = m(i, a, c, f, n[h + 10], 15, -1051523), f = m(f, i, a, c, n[h + 1], 21, -2054922799), c = m(c, f, i, a, n[h + 8], 6, 1873313359), a = m(a, c, f, i, n[h + 15], 10, -30611744), i = m(i, a, c, f, n[h + 6], 15, -1560198380), f = m(f, i, a, c, n[h + 13], 21, 1309151649), c = m(c, f, i, a, n[h + 4], 6, -145523070), a = m(a, c, f, i, n[h + 11], 10, -1120210379), i = m(i, a, c, f, n[h + 2], 15, 718787259), f = m(f, i, a, c, n[h + 9], 21, -343485551), c = d(c, r), f = d(f, e), i = d(i, o), a = d(a, u); return [c, f, i, a] } function i(n) { for (var t = "", r = 32 * n.length, e = 0; e < r; e += 8)t += String.fromCharCode(n[e >> 5] >>> e % 32 & 255); return t } function a(n) { var t = []; for (t[(n.length >> 2) - 1] = void 0, e = 0; e < t.length; e += 1)t[e] = 0; for (var r = 8 * n.length, e = 0; e < r; e += 8)t[e >> 5] |= (255 & n.charCodeAt(e / 8)) << e % 32; return t } function e(n) { for (var t, r = "0123456789abcdef", e = "", o = 0; o < n.length; o += 1)t = n.charCodeAt(o), e += r.charAt(t >>> 4 & 15) + r.charAt(15 & t); return e } function r(n) { return unescape(encodeURIComponent(n)) } function o(n) { return i(c(a(n = r(n)), 8 * n.length)) } function u(n, t) { return function (n, t) { var r, e = a(n), o = [], u = []; for (o[15] = u[15] = void 0, 16 < e.length && (e = c(e, 8 * n.length)), r = 0; r < 16; r += 1)o[r] = 909522486 ^ e[r], u[r] = 1549556828 ^ e[r]; return t = c(o.concat(a(t)), 512 + 8 * t.length), i(c(u.concat(t), 640)) }(r(n), r(t)) } function t(n, t, r) { return t ? r ? u(t, n) : e(u(t, n)) : r ? o(n) : e(o(n)) } "function" == typeof define && define.amd ? define(function () { return t }) : "object" == typeof module && module.exports ? module.exports = t : n.md5 = t }(HASH); GM_window.onload = () => { try { document.body.setAttribute("data-omps-injected", "true"); } catch (e) { } } registeInPageMessage(); parseShareHash(); videoCodeOutdateEventRegist_title(); testVideo(); })();