注意:在保存之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。
?_=1
来访问最新页面。https://zh.moegirl.org.cn/User:Leranjun/js/UtatenSearch.js?_=1
"use strict"; $(() => (async () => { await mw.loader.using("ext.gadget.site-lib"); const searchTitle = wgULS("UtaTen搜索", "UtaTen搜尋"); if (mw.config.get("wgPageName") !== "Special:UtatenSearch") { if (mw.config.get("wgNamespaceNumber") === 0 && !$(".photrans")[0]) { mw.util.addPortletLink("p-tb", `/Special:UtatenSearch?song=${encodeURIComponent(mw.config.get("wgTitle"))}`, searchTitle, "ca-lr-utaten", wgULS("搜索UtaTen.com的歌词并转换为Photrans格式", "搜尋UtaTen.com的歌詞並轉換為Photrans格式")); } return; } $("#firstHeading").text(searchTitle); $("#mw-content-text").html(`<p>${wgULS("正在加载", "正在載入")}……</p>`); document.title = searchTitle + document.title.replace(/^.*?( - .*?)$/, "$1"); await mw.loader.using(["mediawiki.Uri", "oojs-ui"]); const fetchHTML = async (url) => $($.parseHTML((await $.getJSON(`https://api.allorigins.win/get?url=${encodeURIComponent(url)}`)).contents)); class UtaTenWindow extends OO.ui.ProcessDialog { static static = $.extend(Object.create(super.static), { name: "lr-utaten", title: searchTitle, actions: [ { action: "cancel", label: "取消", flags: ["safe", "close", "destructive"], modes: "query", }, { action: "continue", label: wgULS("继续", "繼續"), flags: ["primary", "progressive"], modes: "query", }, { action: "back", label: "返回", flags: ["safe", "back"], modes: ["select", "result"], }, { action: "close", label: wgULS("关闭", "關閉"), flags: ["primary", "progressive"], modes: "result", }, ], }); constructor(config) { // Parent constructor super(config); this.prepopContent = config.data.prepopContent; } initialize() { // Parent method super.initialize(); this.queryPanel = new OO.ui.PanelLayout({ scrollable: false, expanded: false, padded: true, }); this.selectPanel = new OO.ui.PanelLayout({ scrollable: false, expanded: false, padded: true, }); this.resultPanel = new OO.ui.PanelLayout({ scrollable: false, expanded: false, padded: true, }); const labels = { artist_name: "歌手", song: `歌曲(${wgULS("请使用日文原标题", "請使用日文原標題")})`, beginning: wgULS("歌词开头", "歌詞開頭"), body: wgULS("歌词", "歌詞"), lyricist: wgULS("作词", "作詞"), composer: "作曲", sub_title: wgULS("副标题", "副標題"), tag: wgULS("标签", "標籤"), }; this.fields = Object.entries(labels).reduce((acc, [key]) => { acc[key] = new OO.ui.TextInputWidget({ value: this.prepopContent[key] || "", }); return acc; }, {}); this.queryPanel.$element.append(Object.entries(this.fields).map(([key, field], index) => { const layout = new OO.ui.FieldLayout(field, { label: labels[key], align: "top", }); layout.$element.css({ width: "50%", display: "inline-block", "box-sizing": "border-box", }); if (!(index % 2)) { layout.$element.css("padding-right", "1em"); } if (index === 1) { layout.$element.css("margin-top", 0); } return layout.$element; })); this.stackLayout = new OO.ui.StackLayout({ items: [this.queryPanel, this.selectPanel, this.resultPanel], }); this.$body.append(this.stackLayout.$element); } getBodyHeight() { return this.stackLayout.getCurrentItem().$element.outerHeight(true); } getSetupProcess(data) { return super.getSetupProcess(data).next(() => { this.actions.setMode("query"); this.stackLayout.setItem(this.queryPanel); }, this); } getReadyProcess(data) { return super.getReadyProcess(data) .next(() => { this.fields.artist_name.focus(); }, this); } getActionProcess(action) { const dfd = $.Deferred(); if (action === "cancel") { return new OO.ui.Process(() => { this.close({ action: action }); }, this); } else if (action === "continue") { return new OO.ui.Process(() => { this.selectPanel.$element.empty(); this.search().then(() => { this.actions.setMode("select"); this.stackLayout.setItem(this.selectPanel); this.updateSize(); dfd.resolve(); }).catch((e) => { dfd.reject(new OO.ui.Error(e)); }); return dfd.promise(); }, this); } else if (action === "back") { const cur = this.stackLayout.getCurrentItem(); if (cur === this.selectPanel) { this.actions.setMode("query"); this.stackLayout.setItem(this.queryPanel); this.updateSize(); } else if (cur === this.resultPanel) { this.actions.setMode("select"); this.stackLayout.setItem(this.selectPanel); this.updateSize(); } } else if (action === "close") { return new OO.ui.Process(() => { this.close({ action: action }); }, this); } // Fallback to parent handler return super.getActionProcess(action); } async search() { const $panel = this.selectPanel.$element; const query = Object.entries(this.fields).map(([key, field]) => `${key === "song" ? "title" : key}=${field.getValue()}`).join("/"); const searchURL = `https://utaten.com/search/${query}/`; const res = await fetchHTML(searchURL); if (res.find(".noItem")[0]) { $panel.html(`<div class="errorbox">${wgULS("无搜索结果", "無搜尋結果")}。</div>`); return; } const table = res.find(".searchResult__title").closest("table"); const results = table.find("tr").map((_, ele) => { const self = $(ele); if (!self.find(".searchResult__title")[0]) { return; } const title = self.find(".searchResult__title").text().trim(); const src = self.find(".searchResult__title a").attr("href"); const artist = self.find(".searchResult__name").text().trim(); const lyrics = self.find(".lyricList__beginning").text().trim(); return { title, src, artist, lyrics }; }).get(); const resTable = $(`<table class="wikitable" style="width:100%;"><tr><th>歌曲</th><th>${wgULS("歌词开头", "歌詞開頭")}</th></tr></table>`); results.forEach((result) => { $("<tr>").append(`<td>${result.title}<br />by ${result.artist}</td><td>${result.lyrics}</td>`).on("click", () => { this.getLyrics(/lyric\/(.*?)\//.exec(result.src)[1]); }).css("cursor", "pointer").on("mouseover", (e) => { $(e.currentTarget).css("background-color", "#eaecf0"); }).on("mouseout", (e) => { $(e.currentTarget).css("background-color", ""); }).appendTo(resTable); }); const manualID = new OO.ui.TextInputWidget(); $panel.empty().append(`<p>${wgULS("请选择您所寻找的歌曲", "請選擇您所尋找的歌曲")}:</p>`, resTable, new OO.ui.FieldsetLayout({ items: [ new OO.ui.FieldLayout(manualID, { label: wgULS("未找到结果?请手动输入歌词ID", "未找到結果?請手動輸入歌詞ID"), align: "top", }), new OO.ui.FieldLayout(new OO.ui.ButtonWidget({ label: wgULS("搜索", "搜尋"), icon: "search", flags: ["progressive"], }).on("click", () => { const id = manualID.getValue(); if (!id) { return; } this.getLyrics(id); })), ], }).$element); } async getLyrics(id) { const $panel = this.resultPanel.$element; $panel.html("<p>正在获取歌词……</p>"); this.actions.setMode("result"); this.stackLayout.setItem(this.resultPanel); this.updateSize(); const res = await fetchHTML(`https://utaten.com/lyric/${id}/`); const raw = res.find(".hiragana").first().html(); if (!raw) { $panel.html(`<div class="errorbox">${wgULS("无法找到歌词,请检查", "無法找到歌詞,請檢查")}ID ${id} ${wgULS("是否正确", "是否正確")}。</div>`); this.updateSize(); return; } const replaced = raw.replace(/\n|\r/g, "").replace(/<br>/g, "\n").replace(/<span class="ruby"><span class="rb">(.*?)<\/span><span class="rt">(.*?)<\/span><\/span>/g, (_, o, r) => o.trim() && r.trim() ? `{{Photrans|${o.trim()}|${r.trim()}}}` : o.trim()).trim(); const decoded = $("<textarea/>").html(replaced).text(); // Decode html entities $panel.empty().append(`<p>${wgULS("转换成功!以下为Photrans歌词", "轉換成功!以下為Photrans歌詞")}:</p>`, new OO.ui.MultilineTextInputWidget({ value: decoded, autosize: true, maxRows: 30, }).$element.css("max-width", "100%")); this.updateSize(); } } const $body = $(document.body); const windowManager = new OO.ui.WindowManager(); $body.append(windowManager.$element); const utatenDialog = new UtaTenWindow({ size: "full", data: { prepopContent: new mw.Uri(location.href).query, }, }); windowManager.addWindows([utatenDialog]); windowManager.openWindow(utatenDialog); $("#mw-content-text").empty().append(new OO.ui.ButtonWidget({ label: wgULS("打开搜索窗口", "開啟搜尋視窗"), flags: ["primary", "progressive"], }).on("click", () => { windowManager.openWindow(utatenDialog); }).$element); })());