您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
更換翻唱歌曲、更精確校時
当前为
// ==UserScript== // @name jpmarumaru 更改片源與播放延遲 // @namespace Anong0u0 // @version 0.2.2 // @description 更換翻唱歌曲、更精確校時 // @author Anong0u0 // @match https://www.jpmarumaru.com/tw/* // @icon https://www.google.com/s2/favicons?sz=64&domain=jpmarumaru.com // @grant GM_setValue // @grant GM_getValue // @grant unsafeWindow // @license Beerware // ==/UserScript== const delay = (ms = 0) => {return new Promise((r)=>{setTimeout(r, ms)})} const waitElementLoad = (elementSelector, selectCount = 1, tryTimes = 1, interval = 0) => { return new Promise(async (resolve, reject)=> { let t = 1, result; while(true) { if(selectCount != 1) {if((result = document.querySelectorAll(elementSelector)).length >= selectCount) break;} else {if(result = document.querySelector(elementSelector)) break;} if(tryTimes>0 && ++t>tryTimes) return reject(new Error("Wait Timeout")); await delay(interval); } resolve(result); }) } const id = GM_getValue("id", {}) const songID = document.querySelector("#SongPK").textContent const ytid = document.querySelector("#VideoID").textContent // ===== 更改片源 ====== if (songID in id) waitElementLoad("iframe#divVideo", 1, 0, 10).then((vid)=>{vid.src = vid.src.replace(ytid, id[songID])}) const inp = document.createElement("input") const btn = document.createElement("button") btn.innerText = "確定" btn.style = "font-size: 12px" btn.hidden = true btn.onclick = () => { const t = GM_getValue("id", {}) switch(btn.innerText) { case "更改": t[songID] = inp.value break; case "復原": t[songID] = ytid break; } GM_setValue("id", t) location.reload() } inp.style = "margin-left: 16px;height: 16px;" inp.maxLength = 11 inp.oninput = () => { const value = inp.value if(value.length == 11 && ((value != ytid && value != id[songID]) || (value == ytid && id[songID] != ytid))) { btn.hidden = false btn.innerText = "更改" } else btn.hidden = true } const head = document.querySelector("#header_meta") head.onmouseenter = () => { inp.style.width = "9em" inp.value = id[songID] || ytid if(inp.value != ytid) { btn.hidden = false btn.innerText = "復原" } } head.onmouseleave = () => { inp.style.width = "3em" inp.value = id[songID] && id[songID] != ytid ? "已替換" : "未變更" btn.hidden = true } head.onmouseleave() const ul = document.querySelector("#bread_crumb") ul.append(inp) ul.append(btn) // ===== 更改延遲 ====== const delayNum = GM_getValue("delayNum", {}) const div = document.createElement("div") div.hidden = true div.innerHTML = ` <div class="dropdown-content delayInput"> <span class="minus x10"><<</span> <span class="minus x1"><</span> <div class="s"><input id="delayInput" type="number" value="${delayNum[songID] || 0}" step="0.1"></div> <span class="plus x1">></span> <span class="plus x10">>></span> </div> <style> .delayInput * {box-sizing: border-box;} .delayInput { display:block; overflow:unset; min-width: unset; width: max-content; left: -30px; } .delayInput input { font-size: 14px; height: 34px; background-color: #fff; border: none; float: left; width: 60px; line-height: 32px; text-align: center; font-family: "helveticaneuecyrbold"; padding: 0; } .delayInput input::-webkit-outer-spin-button, .delayInput input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } .delayInput .s {display:inline} .delayInput .s::after { content: "s"; float: left; text-align: center; line-height: 37px; background-color: #fff; right: 69px; position: absolute; height: 0; } .delayInput span { line-height: 33px; font-size: 16px; font-weight: bolder; letter-spacing: -5px; text-align: center; display: block; width: 32px; float: left; height: 34px; cursor: pointer; transition: all 0.3s; padding-right: 4px; } .delayInput span:hover { background-color: #d5d5d5; } </style> ` document.querySelector(":last-child > .dropdown").append(div) const delayInput = document.querySelector("#delayInput") for(const np of ["minus", "plus"]) { const npNum = np=="plus" ? 1 : -1 for(const multiple of ["x1", "x10"]) { const multipleNum = multiple=="x10" ? 1 : 0.1 document.querySelector(`span.${np}.${multiple}`).onclick = () => { delayInput.value = (Number(delayInput.value) + npNum*multipleNum).toFixed(1) delayInput.onchange() } } } Number.prototype.getTimeSpan = function() { const hours = String(Math.floor(this / 3600000)).padStart(2, '0'); const minutes = String(Math.floor((this % 3600000) / 60000)).padStart(2, '0'); const seconds = String(Math.floor((this % 60000) / 1000)).padStart(2, '0'); const ms = String((this % 1000)).padStart(3, '0'); return `${hours}:${minutes}:${seconds}:${ms}`; } const originStartTime = json.StartTime, originEndTime = json.EndTime delayInput.onchange = () => { const t = GM_getValue("delayNum", {}) let value = Number(delayInput.value) if (Number.isInteger(value)) { delayInput.value = String(value) } if(value > 0) { json.StartTime = originStartTime.map((e)=>(new Date(`1970-1-1 ${e}+0`).getTime()+value*1000).getTimeSpan()) json.EndTime = originEndTime.map((e)=>(new Date(`1970-1-1 ${e}+0`).getTime()+value*1000).getTimeSpan()) unsafeWindow.LST = 0 } else { json.StartTime = originStartTime json.EndTime = originEndTime unsafeWindow.LST = -value } t[songID] = value GM_setValue("delayNum", t) } delayInput.onchange() const timeBtn = document.querySelector("div:last-child > .dropdown > .dropbtn") timeBtn.addEventListener("click", () => { div.hidden = !div.hidden }) document.addEventListener("click", (e) => { if (!(div.contains(e.target) || timeBtn.contains(e.target))) { div.hidden = true } }); document.querySelector("#LST").remove()