Repository: coderzhaoziwei/yande-re-chinese-patch Branch: main Commit: 4b5c888921b3 Files: 31 Total size: 141.2 KB Directory structure: gitextract_unlq4rb5/ ├── .editorconfig ├── .gitignore ├── .vscode/ │ └── custom.code-snippets ├── bundle/ │ ├── index.js │ └── index.user.js ├── config/ │ └── rollup.config.js ├── package.json ├── readme.md ├── readme_main.md ├── script/ │ ├── action.js │ ├── bundle.sh │ └── replace.js ├── source/ │ ├── app.js │ ├── browse.js │ ├── data/ │ │ ├── footers.json │ │ ├── menus.json │ │ └── tags.json │ ├── hotkey.js │ ├── html/ │ │ ├── body.html │ │ ├── head.html │ │ └── options.html │ ├── index.js │ ├── options.js │ ├── post.js │ ├── style/ │ │ └── fix.css │ ├── style.js │ └── translate.js └── temp/ ├── css_text_loader.js ├── index.js ├── index.user.js └── webpack.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # 编辑器插件 EditorConfig 的配置文件 root = true [*] charset = utf-8 # 字符集 indent_style = space # 缩进样式 indent_size = 2 # 缩进尺寸 trim_trailing_whitespace = true # 清理行尾空白 end_of_line = lf # 换行符 insert_final_newline = true # 文末插入空白行 ================================================ FILE: .gitignore ================================================ node_modules yarn-error.log .DS_Store ================================================ FILE: .vscode/custom.code-snippets ================================================ { "/* 单行注释 */": { "scope": "javascript,typescript,css", "prefix": "ll", "body": ["/* ${1:comments} */"], "description": "创建单行注释", }, "/* 多行注释 */": { "scope": "javascript,typescript", "prefix": "lll", "body": ["/**", " * ${1:comments}", " */"], "description": "创建多行注释", }, "console.log()": { "scope": "javascript,typescript", "prefix": "con", "body": ["console.log($1)", ""], }, "导入模块": { "scope": "javascript,typescript", "prefix": "imp", "body": ["import $1 from '$1'", ""], }, } ================================================ FILE: bundle/index.js ================================================ // ==UserScript== // @name Yande.re 简体中文 // @namespace com.coderzhaoziwei.yandere // @version 2.1.47 // @author Coder Zhao coderzhaoziwei@outlook.com // @description 中文标签 | 界面优化 | 高清大图 | 键盘翻页 | 流体布局 // @homepage https://greasyfork.org/scripts/421970 // @license MIT // @match https://yande.re/* // @exclude https://yande.re/forum/* // @match https://konachan.com/* // @exclude https://konachan.com/forum/* // @match https://konachan.net/* // @exclude https://konachan.net/forum/* // @supportURL https://github.com/coderzhaoziwei/yande-re-chinese-patch/issues // @grant GM_download // ==/UserScript== /* eslint-env es2022 */ /* global jQuery:readonly */ /* global Vue:readonly */ /* global Vuetify:readonly */ /* global VueMasonry:readonly */ (function () { 'use strict'; const initStyle = function() { document.head.insertAdjacentHTML("beforeend", ``); }; const initHotKey = function() { window.addEventListener("keyup", function(event) { console.log('keyup:', event.key); if (/^(TEXTAREA|INPUT|SELECT|BUTTON)$/.test(document.activeElement.tagName)) return const prev = document.querySelector(".pagination>.previous_page") || jQuery("li:contains('Previous') a[href]")[0]; if (prev && (event.key == "ArrowLeft" || event.key == "a" || event.key == "A")) { prev.click(); return event.preventDefault() } const next = document.querySelector(".pagination>.next_page") || jQuery("li:contains('Next') a[href]")[0]; if (next && (event.key == "ArrowRight" || event.key === "d" || event.key == "D")) { next.click(); return event.preventDefault() } const show = document.querySelector("#png") || document.querySelector("#highres"); if (show && (event.key === "s" || event.key === "S")) { show.click(); return event.preventDefault() } const where = jQuery("li:contains('Source:') a")[0]; if (where && (event.key === "w" || event.key === "W")) { where.click(); return event.preventDefault() } }); const sidebar = document.querySelector("#post-list > div.sidebar") || document.querySelector("#post-view > div.sidebar"); if (sidebar) { sidebar.insertAdjacentHTML("beforeend", "
" + "
快捷键说明
" + "
上一页:A / ←
" + "
下一页:D / →
" + "
显示当前作品原图:S
" + "
显示当前作品来源:W
" + "
"); } }; class Post { constructor(data) { if (typeof data !== "object") data = {}; this.id = data.id || 0; this.score = data.score || 0; this.tags = data.tags || ""; this.source = data.source || ""; this.author = data.author || ""; this.creatorId = data.creator_id || 0; this.createdAt = data.created_at || 0; this.updatedAt = data.updated_at || 0; this.rating = data.rating || "s"; this.fileUrl = data.file_url || ""; this.fileExt = data.file_ext || ""; this.fileSize = data.file_size || 0; this.width = data.width || 0; this.height = data.height || 0; this.jpegUrl = data.jpeg_url || ""; this.jpegSize = data.jpeg_file_size || 0; this.jpegWidth = data.jpeg_width || 0; this.jpegHeight = data.jpeg_height || 0; this.sampleUrl = data.sample_url; this.sampleSize = data.sample_file_size || 0; this.sampleWidth = data.sample_width || 0; this.sampleHeight = data.sample_height || 0; this.previewUrl = data.preview_url; this.previewWidth = data.actual_preview_width || 0; this.previewHeight = data.actual_preview_height || 0; this.favorite = false; } get isRatingS() { return this.rating === "s" } get isRatingQ() { return this.rating === "q" } get isRatingE() { return this.rating === "e" } get aspectRatio() { return this.width / this.height } getSizeText(size) { if (size > 1024 * 1024) { return (size / (1024 * 1024)).toFixed(2) + "MB" } if (size > 1024) { return (size / 1024).toFixed(2) + "KB" } return (size).toFixed(2) + "B" } get sampleSizeText() { return this.getSizeText(this.sampleSize) } get sampleDownloadText() { return `下载缩略图 ${this.sampleWidth}×${this.sampleHeight} [${this.sampleSizeText}]` } get sampleDownloadName() { return `${location.hostname}.${this.id}.${this.sampleWidth}x${this.sampleHeight}`.replace(/\./g, "_") } get jpegSizeText() { return this.getSizeText(this.jpegSize) } get jpegDownloadText() { return `下载高清图 ${this.jpegWidth}×${this.jpegHeight} [${this.jpegSizeText}]` } get jpegDownloadName() { return `${location.hostname}.${this.id}.${this.jpegWidth}x${this.jpegHeight}`.replace(/\./g, "_") } get fileSizeText() { return this.getSizeText(this.fileSize) } get fileDownloadText() { return `下载原文件 ${this.width}×${this.height} [${this.fileSizeText}] ${this.fileExt.toUpperCase()}` } get fileDownloadName() { return `${location.hostname}.${this.id}.${this.width}x${this.height}`.replace(/\./g, "_") } get createdTime() { const date = new Date(this.createdAt * 1000); return `${date.toLocaleDateString()} ${date.toLocaleTimeString("en-DE")}` } get updatedTime() { const date = new Date(this.updatedAt * 1000); return `${date.toLocaleDateString()} ${date.toLocaleTimeString("en-DE")}` } get sourceUrl() { if (/^https:\/\/i\.pximg\.net\/img-original\/img\/[\d\/]{19}\/([\d]{1,})_p[\d]{1,}\.(jpg|png)$/.test(this.source)) { const pid = RegExp.$1; return `https://pixiv.net/artworks/${pid}` } return this.source } } const App = { template: "#app-template", data() { return { showDrawer: false, showImageSelected: false, showImageInfo: true, showRatingQ: JSON.parse(localStorage.getItem("showRatingQ") || "true"), showRatingE: JSON.parse(localStorage.getItem("showRatingE") || "false"), imageList: [], imageSelectedIndex: 0, imageSelectedDetail: {}, params: new URLSearchParams(location.search), requestState: false, requestStop: false, innerWidth: window.innerWidth, innerHeight: window.innerHeight, imageCountInRow: JSON.parse(localStorage.getItem("imageCountInRow") || "3"), imageQualityHigh: JSON.parse(localStorage.getItem("imageQualityHigh") || "false"), showFavoriteSuccess: false, } }, computed: { isMobile() { try { return this.$vuetify.breakpoint.mobile } catch(error) { return false } }, title() { return `${this.imageList.length} Posts` }, version() { return GM_info.script.version }, imageSelected() { return this.imageList[this.imageSelectedIndex] || new Post() }, imageSelectedWidth() { const width = parseInt(Math.min(this.innerWidth * 0.9, this.imageSelected.sampleWidth)); const height = Math.min(this.innerHeight * 0.9, this.imageSelected.sampleHeight); const width2 = parseInt(height * this.imageSelected.aspectRatio); return Math.min(width, width2) }, imageSelectedHeight() { const width = Math.min(this.innerWidth * 0.9, this.imageSelected.sampleWidth); const height = parseInt(Math.min(this.innerHeight * 0.9, this.imageSelected.sampleHeight)); const height2 = parseInt(width / this.imageSelected.aspectRatio); return Math.min(height, height2) }, }, watch: { showRatingQ(value) { localStorage.setItem("showRatingQ", JSON.stringify(value)); }, showRatingE(value) { localStorage.setItem("showRatingE", JSON.stringify(value)); }, imageCountInRow(value) { localStorage.setItem("imageCountInRow", JSON.stringify(value)); }, imageQualityHigh(value) { localStorage.setItem("imageQualityHigh", JSON.stringify(value)); }, showFavoriteSuccess(value) { console.log('showFavoriteSuccess: ', value); }, showImageSelected(value) { if (!value) { this.imageSelectedDetail = {}; return } this.getPostDetail(this.imageSelected.id).then(res => { if (!res) return this.imageSelectedDetail = res; }); } }, methods: { async request() { this.requestState = true; const url = location.origin + location.pathname + ".json?" + this.params.toString(); const response = await new Promise(resolve => { console.log(url); jQuery.get(url, data => resolve(data)); }); if (response instanceof Array && response.length > 0) { window.history.pushState("", "", location.pathname + "?" + this.params.toString()); response.forEach(item => this.imageList.push(new Post(item))); const page = Number(this.params.get("page")) || 1; this.params.set("page", page + 1); setTimeout(() => (this.requestState = false), 1000); } else { this.requestStop = true; } }, download(src, filename) { const match = src.match(/[.](?png|jpg|jpeg)$/); if (match) { const extension = match.groups.extension; GM_download(src, filename + "." + extension); } else { GM_download(src, filename); } }, onFavorite(id) { $.ajax({ method: 'POST', url: "https://yande.re/post/vote.json", beforeSend: xhr => xhr.setRequestHeader('x-csrf-token', window.csrfToken), data: { id, score: 3 }, success: data => { if (data.success === true) { this.imageList[this.imageSelectedIndex].favorite = true; this.imageSelectedDetail.favorite = true; } }, }); }, async getPostDetail(id) { try { if (!id) return const response = await fetch(`/post.json?api_version=2&tags=id:${id}&include_tags=1&include_votes=1`); const result = await response.json(); return { favorite: result.votes[id] == 3, artist: Object.keys(result.tags).find(k => result.tags[k] == 'artist') } } catch (error) { console.log('getPostDetail error:', error); } } }, mounted() { const timeInterval = setInterval(() => { if (this.requestStop === true) { clearInterval(timeInterval); return } const scrollTop = document.documentElement.scrollTop; const scrollHeight = document.documentElement.scrollHeight; const height = window.innerHeight; if (scrollTop + height >= scrollHeight * 0.75) { if (this.requestState === false) { this.request(); } } }, 1000); window.addEventListener("resize", () => { this.innerWidth = window.innerWidth; this.innerHeight = window.innerHeight; }); }, }; async function enterBrowseMode() { function getScript(url) { return new Promise(resolve => jQuery.getScript(url, () => resolve())) } await getScript("https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js"); await getScript("https://cdn.jsdelivr.net/npm/vuetify@2.5.0/dist/vuetify.min.js"); await getScript("https://cdn.jsdelivr.net/npm/vue-masonry-css@1.0.3/dist/vue-masonry.min.js"); await getScript("https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"); window.csrfToken = jQuery('[name="csrf-token"]').attr('content'); document.head.innerHTML = `[{ path: "source/html/head.html" }]`; document.body.innerHTML = `[{ path: "source/html/body.html" }]`; Vue.use(VueMasonry); new Vue({ vuetify: new Vuetify({ theme: { dark: true }, }), render: h => h(App) }).$mount("#app"); } const onChangeLeftBar = function() { const value = Boolean(document.getElementById("showLeftBar").selectedIndex); localStorage.setItem("showLeftBar", JSON.stringify(value)); const element = document.querySelector("#post-list > .sidebar"); element.setAttribute("show-left-bar", value); console.log("showLeftBar", value); }; const onChangeRatingE = function() { const value = Boolean(document.getElementById("showRatingE").selectedIndex); localStorage.setItem("showRatingE", JSON.stringify(value)); const elementList = document.querySelectorAll(".javascript-hide"); elementList.forEach(element => element.setAttribute("show-rating-e", value)); console.log("showRatingE", value); }; const onChangeImageHD = function() { const index = document.getElementById("showImageHD").selectedIndex; const elementList = document.querySelectorAll("#post-list-posts > li > .inner"); elementList.forEach(element => element.setAttribute("show-image-hd", index)); localStorage.setItem("showImageHD", JSON.stringify(index)); console.log("showImageHD", index); }; let taskArray = []; let maxLoadingSampleNum = 4; let doLoadSampleUrl = () => { let loadingNum = 0; let loadSampleUrl = () => { if (taskArray.length == 0) return loadingNum++; let { element, sampleUrl } = taskArray.shift(); element.onerror = () => { element.src = sampleUrl; }; element.onload = () => { loadingNum--; }; element.src = sampleUrl; }; setInterval(() => { if (taskArray.length == 0) return let needloadNum = maxLoadingSampleNum - loadingNum; while (needloadNum--) { loadSampleUrl(); } }, 1000); }; const initOptions = function() { if (/^\/user\/show\/[\d]{1,}/.test(location.pathname)) return if (document.getElementById("post-list-posts") === null) return document.getElementById("post-list-posts").insertAdjacentHTML("beforebegin", `[{ path: "source/html/options.html" }]`); const imageList = document.querySelectorAll("img.preview"); const samples = JSON.parse(localStorage.getItem("sample_urls")); imageList.forEach(element => { if (/\/post\/show\/([\d]{1,})/.test(element.nextElementSibling.innerText)) { const id = RegExp.$1; const sampleUrl = samples[id]; if (sampleUrl !== undefined) { element.src = sampleUrl; } } }); doLoadSampleUrl(); document.getElementById("showLeftBar").addEventListener("change", onChangeLeftBar); document.getElementById("showRatingE").addEventListener("change", onChangeRatingE); document.getElementById("showImageHD").addEventListener("change", onChangeImageHD); const showLeftBar = JSON.parse(localStorage.getItem("showLeftBar") || "true"); const showRatingE = JSON.parse(localStorage.getItem("showRatingE") || "true"); const showImageHD = JSON.parse(localStorage.getItem("showImageHD") || "0"); document.getElementById("showLeftBar").selectedIndex = showLeftBar; document.getElementById("showRatingE").selectedIndex = showRatingE; document.getElementById("showImageHD").selectedIndex = showImageHD; onChangeLeftBar(); onChangeRatingE(); onChangeImageHD(); document.getElementById("enterBrowseMode").addEventListener("click", enterBrowseMode); }; const tags = [{ path: "source/data/tags.json" }]; const menus = [{ path: "source/data/menus.json" }]; const footers = [{ path: "source/data/footers.json" }]; const translateTags = function() { const elementList = Array.from(document.getElementsByTagName("a")); elementList.forEach(element => { const href = element.getAttribute("href"); if (typeof href === "string" && /^\/post\?tags=(\S+)$/.test(href)) { const en = RegExp.$1; const cn = tags[en]; if (cn) { element.innerText = `[${cn}]${en.replace(/_/g, " ")}`; } } }); }; const translateMenus = function() { const mainMenuList = Array.from(document.querySelectorAll("#main-menu>ul>li>a")); const subMenuList = Array.from(document.querySelectorAll("ul.submenu>li>a")); const elementList = [...mainMenuList, ...subMenuList]; elementList.forEach(element => { if (element.getAttribute("href") === "#") return const en = element.innerText; const cn = menus[en]; if (cn) { element.innerText = cn; } }); }; const translateNotice = function() { const elementList = Array.from(document.querySelectorAll(".status-notice")); elementList.forEach(element => { console.log(element.innerHTML); element.innerHTML = element.innerHTML .replace(/^[\s]+This image has been resized. Click on the /, "这张图片已经被压缩,单击侧边栏中的") .replace(/View larger version/, "显示高清图") .replace(/ link in the sidebar for a high-quality version./, "可以获取更高质量的版本。") .replace(/Hide this message<\/a>\./, "不再提醒") .replace(/This post belongs to a /, "这张图片从属于一个") .replace(/parent post<\/a>\./, "相关父作品。") .replace(/This post has /, "这张图片从属于一个") .replace(/child posts<\/a>\. \(post #/, "作品集。相关子作品:") .replace(/a child post<\/a>\. \(post #/, "作品集。相关子作品:") .replace(/<\/a>, | \)/, ""); }); }; const translateButtons = function() { [ ['#highres-show', 'View larger version', '显示高清图'], ['#highres', 'Download larger version', '下载高清图'], ['#png', 'Download PNG', '下载 PNG 图'], ['li#add-to-favs>a', 'Add to favorites', '添加收藏'], ['li#set-avatar>a', 'Set avatar', '设置头像'], ['h4>a.js-posts-show-edit-tab', 'Edit', '编辑'], ['h4>a.js-posts-show-comments-tab', 'Respond', '评论'], ['.pagination>.previous_page', '← Previous', '上一页'], ['.pagination>.next_page', 'Next →', '下一页'], ].forEach(data => { const [selector, en, cn] = data; const element = document.querySelector(selector); if (element) { element.innerText = element.innerText.replace(en, cn); } }); }; const translateFooters = function() { const elementList = Array.from(document.querySelectorAll('#subnavbar>li>a')); elementList.forEach(element => { const en = element.innerText; const cn = footers[en]; if (cn) { element.innerText = cn; } }); }; const initTranslate = function() { translateTags(); translateMenus(); translateNotice(); translateButtons(); translateFooters(); }; jQuery(document).ready(function() { initStyle(); initHotKey(); initOptions(); initTranslate(); if (document.cookie.includes('locale=zh_CN') === false) { document.cookie = "locale=zh_CN"; location.href = location.href; } }); }()); ================================================ FILE: bundle/index.user.js ================================================ // ==UserScript== // @name Yande.re 简体中文 // @namespace com.coderzhaoziwei.yandere // @version 2.1.47 // @author Coder Zhao coderzhaoziwei@outlook.com // @description 中文标签 | 界面优化 | 高清大图 | 键盘翻页 | 流体布局 // @homepage https://greasyfork.org/scripts/421970 // @license MIT // @match https://yande.re/* // @exclude https://yande.re/forum/* // @match https://konachan.com/* // @exclude https://konachan.com/forum/* // @match https://konachan.net/* // @exclude https://konachan.net/forum/* // @supportURL https://github.com/coderzhaoziwei/yande-re-chinese-patch/issues // @grant GM_download // ==/UserScript== /* eslint-env es2022 */ /* global jQuery:readonly */ /* global Vue:readonly */ /* global Vuetify:readonly */ /* global VueMasonry:readonly */ (function () { 'use strict'; const initStyle = function() { document.head.insertAdjacentHTML("beforeend", ``); }; const initHotKey = function() { window.addEventListener("keyup", function(event) { console.log('keyup:', event.key); if (/^(TEXTAREA|INPUT|SELECT|BUTTON)$/.test(document.activeElement.tagName)) return const prev = document.querySelector(".pagination>.previous_page") || jQuery("li:contains('Previous') a[href]")[0]; if (prev && (event.key == "ArrowLeft" || event.key == "a" || event.key == "A")) { prev.click(); return event.preventDefault() } const next = document.querySelector(".pagination>.next_page") || jQuery("li:contains('Next') a[href]")[0]; if (next && (event.key == "ArrowRight" || event.key === "d" || event.key == "D")) { next.click(); return event.preventDefault() } const show = document.querySelector("#png") || document.querySelector("#highres"); if (show && (event.key === "s" || event.key === "S")) { show.click(); return event.preventDefault() } const where = jQuery("li:contains('Source:') a")[0]; if (where && (event.key === "w" || event.key === "W")) { where.click(); return event.preventDefault() } }); const sidebar = document.querySelector("#post-list > div.sidebar") || document.querySelector("#post-view > div.sidebar"); if (sidebar) { sidebar.insertAdjacentHTML("beforeend", "
" + "
快捷键说明
" + "
上一页:A / ←
" + "
下一页:D / →
" + "
显示当前作品原图:S
" + "
显示当前作品来源:W
" + "
"); } }; class Post { constructor(data) { if (typeof data !== "object") data = {}; this.id = data.id || 0; this.score = data.score || 0; this.tags = data.tags || ""; this.source = data.source || ""; this.author = data.author || ""; this.creatorId = data.creator_id || 0; this.createdAt = data.created_at || 0; this.updatedAt = data.updated_at || 0; this.rating = data.rating || "s"; this.fileUrl = data.file_url || ""; this.fileExt = data.file_ext || ""; this.fileSize = data.file_size || 0; this.width = data.width || 0; this.height = data.height || 0; this.jpegUrl = data.jpeg_url || ""; this.jpegSize = data.jpeg_file_size || 0; this.jpegWidth = data.jpeg_width || 0; this.jpegHeight = data.jpeg_height || 0; this.sampleUrl = data.sample_url; this.sampleSize = data.sample_file_size || 0; this.sampleWidth = data.sample_width || 0; this.sampleHeight = data.sample_height || 0; this.previewUrl = data.preview_url; this.previewWidth = data.actual_preview_width || 0; this.previewHeight = data.actual_preview_height || 0; this.favorite = false; } get isRatingS() { return this.rating === "s" } get isRatingQ() { return this.rating === "q" } get isRatingE() { return this.rating === "e" } get aspectRatio() { return this.width / this.height } getSizeText(size) { if (size > 1024 * 1024) { return (size / (1024 * 1024)).toFixed(2) + "MB" } if (size > 1024) { return (size / 1024).toFixed(2) + "KB" } return (size).toFixed(2) + "B" } get sampleSizeText() { return this.getSizeText(this.sampleSize) } get sampleDownloadText() { return `下载缩略图 ${this.sampleWidth}×${this.sampleHeight} [${this.sampleSizeText}]` } get sampleDownloadName() { return `${location.hostname}.${this.id}.${this.sampleWidth}x${this.sampleHeight}`.replace(/\./g, "_") } get jpegSizeText() { return this.getSizeText(this.jpegSize) } get jpegDownloadText() { return `下载高清图 ${this.jpegWidth}×${this.jpegHeight} [${this.jpegSizeText}]` } get jpegDownloadName() { return `${location.hostname}.${this.id}.${this.jpegWidth}x${this.jpegHeight}`.replace(/\./g, "_") } get fileSizeText() { return this.getSizeText(this.fileSize) } get fileDownloadText() { return `下载原文件 ${this.width}×${this.height} [${this.fileSizeText}] ${this.fileExt.toUpperCase()}` } get fileDownloadName() { return `${location.hostname}.${this.id}.${this.width}x${this.height}`.replace(/\./g, "_") } get createdTime() { const date = new Date(this.createdAt * 1000); return `${date.toLocaleDateString()} ${date.toLocaleTimeString("en-DE")}` } get updatedTime() { const date = new Date(this.updatedAt * 1000); return `${date.toLocaleDateString()} ${date.toLocaleTimeString("en-DE")}` } get sourceUrl() { if (/^https:\/\/i\.pximg\.net\/img-original\/img\/[\d\/]{19}\/([\d]{1,})_p[\d]{1,}\.(jpg|png)$/.test(this.source)) { const pid = RegExp.$1; return `https://pixiv.net/artworks/${pid}` } return this.source } } const App = { template: "#app-template", data() { return { showDrawer: false, showImageSelected: false, showImageInfo: true, showRatingQ: JSON.parse(localStorage.getItem("showRatingQ") || "true"), showRatingE: JSON.parse(localStorage.getItem("showRatingE") || "false"), imageList: [], imageSelectedIndex: 0, imageSelectedDetail: {}, params: new URLSearchParams(location.search), requestState: false, requestStop: false, innerWidth: window.innerWidth, innerHeight: window.innerHeight, imageCountInRow: JSON.parse(localStorage.getItem("imageCountInRow") || "3"), imageQualityHigh: JSON.parse(localStorage.getItem("imageQualityHigh") || "false"), showFavoriteSuccess: false, } }, computed: { isMobile() { try { return this.$vuetify.breakpoint.mobile } catch(error) { return false } }, title() { return `${this.imageList.length} Posts` }, version() { return GM_info.script.version }, imageSelected() { return this.imageList[this.imageSelectedIndex] || new Post() }, imageSelectedWidth() { const width = parseInt(Math.min(this.innerWidth * 0.9, this.imageSelected.sampleWidth)); const height = Math.min(this.innerHeight * 0.9, this.imageSelected.sampleHeight); const width2 = parseInt(height * this.imageSelected.aspectRatio); return Math.min(width, width2) }, imageSelectedHeight() { const width = Math.min(this.innerWidth * 0.9, this.imageSelected.sampleWidth); const height = parseInt(Math.min(this.innerHeight * 0.9, this.imageSelected.sampleHeight)); const height2 = parseInt(width / this.imageSelected.aspectRatio); return Math.min(height, height2) }, }, watch: { showRatingQ(value) { localStorage.setItem("showRatingQ", JSON.stringify(value)); }, showRatingE(value) { localStorage.setItem("showRatingE", JSON.stringify(value)); }, imageCountInRow(value) { localStorage.setItem("imageCountInRow", JSON.stringify(value)); }, imageQualityHigh(value) { localStorage.setItem("imageQualityHigh", JSON.stringify(value)); }, showFavoriteSuccess(value) { console.log('showFavoriteSuccess: ', value); }, showImageSelected(value) { if (!value) { this.imageSelectedDetail = {}; return } this.getPostDetail(this.imageSelected.id).then(res => { if (!res) return this.imageSelectedDetail = res; }); } }, methods: { async request() { this.requestState = true; const url = location.origin + location.pathname + ".json?" + this.params.toString(); const response = await new Promise(resolve => { console.log(url); jQuery.get(url, data => resolve(data)); }); if (response instanceof Array && response.length > 0) { window.history.pushState("", "", location.pathname + "?" + this.params.toString()); response.forEach(item => this.imageList.push(new Post(item))); const page = Number(this.params.get("page")) || 1; this.params.set("page", page + 1); setTimeout(() => (this.requestState = false), 1000); } else { this.requestStop = true; } }, download(src, filename) { const match = src.match(/[.](?png|jpg|jpeg)$/); if (match) { const extension = match.groups.extension; GM_download(src, filename + "." + extension); } else { GM_download(src, filename); } }, onFavorite(id) { $.ajax({ method: 'POST', url: "https://yande.re/post/vote.json", beforeSend: xhr => xhr.setRequestHeader('x-csrf-token', window.csrfToken), data: { id, score: 3 }, success: data => { if (data.success === true) { this.imageList[this.imageSelectedIndex].favorite = true; this.imageSelectedDetail.favorite = true; } }, }); }, async getPostDetail(id) { try { if (!id) return const response = await fetch(`/post.json?api_version=2&tags=id:${id}&include_tags=1&include_votes=1`); const result = await response.json(); return { favorite: result.votes[id] == 3, artist: Object.keys(result.tags).find(k => result.tags[k] == 'artist') } } catch (error) { console.log('getPostDetail error:', error); } } }, mounted() { const timeInterval = setInterval(() => { if (this.requestStop === true) { clearInterval(timeInterval); return } const scrollTop = document.documentElement.scrollTop; const scrollHeight = document.documentElement.scrollHeight; const height = window.innerHeight; if (scrollTop + height >= scrollHeight * 0.75) { if (this.requestState === false) { this.request(); } } }, 1000); window.addEventListener("resize", () => { this.innerWidth = window.innerWidth; this.innerHeight = window.innerHeight; }); }, }; async function enterBrowseMode() { function getScript(url) { return new Promise(resolve => jQuery.getScript(url, () => resolve())) } await getScript("https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js"); await getScript("https://cdn.jsdelivr.net/npm/vuetify@2.5.0/dist/vuetify.min.js"); await getScript("https://cdn.jsdelivr.net/npm/vue-masonry-css@1.0.3/dist/vue-masonry.min.js"); await getScript("https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"); window.csrfToken = jQuery('[name="csrf-token"]').attr('content'); document.head.innerHTML = ` Yande.re 简体中文 `; document.body.innerHTML = `
`; Vue.use(VueMasonry); new Vue({ vuetify: new Vuetify({ theme: { dark: true }, }), render: h => h(App) }).$mount("#app"); } const onChangeLeftBar = function() { const value = Boolean(document.getElementById("showLeftBar").selectedIndex); localStorage.setItem("showLeftBar", JSON.stringify(value)); const element = document.querySelector("#post-list > .sidebar"); element.setAttribute("show-left-bar", value); console.log("showLeftBar", value); }; const onChangeRatingE = function() { const value = Boolean(document.getElementById("showRatingE").selectedIndex); localStorage.setItem("showRatingE", JSON.stringify(value)); const elementList = document.querySelectorAll(".javascript-hide"); elementList.forEach(element => element.setAttribute("show-rating-e", value)); console.log("showRatingE", value); }; const onChangeImageHD = function() { const index = document.getElementById("showImageHD").selectedIndex; const elementList = document.querySelectorAll("#post-list-posts > li > .inner"); elementList.forEach(element => element.setAttribute("show-image-hd", index)); localStorage.setItem("showImageHD", JSON.stringify(index)); console.log("showImageHD", index); }; let taskArray = []; let maxLoadingSampleNum = 4; let doLoadSampleUrl = () => { let loadingNum = 0; let loadSampleUrl = () => { if (taskArray.length == 0) return loadingNum++; let { element, sampleUrl } = taskArray.shift(); element.onerror = () => { element.src = sampleUrl; }; element.onload = () => { loadingNum--; }; element.src = sampleUrl; }; setInterval(() => { if (taskArray.length == 0) return let needloadNum = maxLoadingSampleNum - loadingNum; while (needloadNum--) { loadSampleUrl(); } }, 1000); }; const initOptions = function() { if (/^\/user\/show\/[\d]{1,}/.test(location.pathname)) return if (document.getElementById("post-list-posts") === null) return document.getElementById("post-list-posts").insertAdjacentHTML("beforebegin", `
`); const imageList = document.querySelectorAll("img.preview"); const samples = JSON.parse(localStorage.getItem("sample_urls")); imageList.forEach(element => { if (/\/post\/show\/([\d]{1,})/.test(element.nextElementSibling.innerText)) { const id = RegExp.$1; const sampleUrl = samples[id]; if (sampleUrl !== undefined) { element.src = sampleUrl; } } }); doLoadSampleUrl(); document.getElementById("showLeftBar").addEventListener("change", onChangeLeftBar); document.getElementById("showRatingE").addEventListener("change", onChangeRatingE); document.getElementById("showImageHD").addEventListener("change", onChangeImageHD); const showLeftBar = JSON.parse(localStorage.getItem("showLeftBar") || "true"); const showRatingE = JSON.parse(localStorage.getItem("showRatingE") || "true"); const showImageHD = JSON.parse(localStorage.getItem("showImageHD") || "0"); document.getElementById("showLeftBar").selectedIndex = showLeftBar; document.getElementById("showRatingE").selectedIndex = showRatingE; document.getElementById("showImageHD").selectedIndex = showImageHD; onChangeLeftBar(); onChangeRatingE(); onChangeImageHD(); document.getElementById("enterBrowseMode").addEventListener("click", enterBrowseMode); }; const tags = { "4koma": "四格漫画", "5-toubun_no_hanayome": "五等分的新娘", "anal": "肛交", "angel": "天使", "angel_beats!": "Angel Beats!", "animal_ears": "兽耳", "anthropomorphization": "拟人化", "anus": "肛门露出", "areola": "乳晕", "arknights": "明日方舟", "armor": "盔甲/装甲", "artist_revision": "画师修改", "ass": "臀部", "ass_grab": "持股/捏臀", "atelier": "炼金工房系列", "autographed": "亲笔签名", "azur_lane": "碧蓝航线", "bakemonogatari": "化物语", "bandages": "绷带", "bandaid": "创可贴/绷带", "bang_dream!": "BanG Dream!", "baseball": "棒球", "basketball": "篮球", "bathing": "沐浴", "benghuai_xueyuan": "崩坏学园", "bike_shorts": "自行车短裤", "bikini": "比基尼", "bikini_armor": "比基尼装甲/轻薄盔甲", "bikini_top": "比基尼乳罩", "black_rock_shooter": "黑岩射手", "blood": "血腥", "bloomers": "灯笼裤/宽松短裤", "blue_archive": "碧蓝档案", "bodysuit": "紧身衣裤", "boku_wa_tomodachi_ga_sukunai": "我的朋友很少", "bondage": "束缚", "bottomless": "下身露出", "bra": "乳罩", "breast_grab": "握乳", "breast_hold": "托乳", "breasts": "乳", "bukkake": "颜射", "bunny_ears": "兔耳", "bunny_girl": "兔女郎", "buruma": "运动短裤", "business_suit": "西装/职业服", "calendar": "日历", "cameltoe": "阴户凸显", "card": "卡牌", "card_captor_sakura": "魔卡少女樱", "censored": "有码", "cg": "CG/计算机动画", "chainsaw": "电锯", "character_design": "角色设计", "cheerleader": "啦啦队队员", "chibi": "Q版", "chinadress": "旗袍", "choujigen_game_neptune": "超次元游戏海王星", "christmas": "圣诞", "cleavage": "乳沟", "code_geass": "反叛的鲁路修", "condom": "避孕套", "corset": "(束腰)紧身内衣", "cosplay": "角色扮演", "cream": "奶油", "cropped": "裁剪图", "crossdress": "变装", "crossover": "作品联动/混合同人", "cum": "精液", "cunnilingus": "品玉/舔阴", "dakimakura": "抱枕", "darling_in_the_franxx": "DARLING in the FRANXX", "date_a_live": "约会大作战", "detexted": "去字图片", "devil": "魔鬼/恶魔", "digital_version": "数字版", "dildo": "假阳具", "disc_cover": "光盘封面", "dress": "连衣裙", "dress_shirt": "衬衫", "duplicate": "重复图片", "elf": "精灵", "endcard": "片尾插图", "erect_nipples": "乳尖", "expression": "角色展示/立绘", "extreme_content": "极端", "eyepatch": "眼罩", "fairy": "精灵/小精灵", "fate/kaleid_liner_prisma_illya": "Fate/kaleid liner 魔法少女☆伊莉雅", "feet": "足", "fellatio": "口交", "final_fantasy": "最终幻想", "final_fantasy_vii": "最终幻想 VII", "final_fantasy_xiv": "最终幻想 14", "fingering": "指交", "fire_emblem": "火焰纹章", "fire_emblem_heroes": "火焰之纹章:英雄云集", "fire_emblem_kakusei": "火焰之纹章:觉醒", "fire_emblem_three_houses": "火焰之纹章:风花雪月", "fishnets": "鱼网袜", "fixed": "修改", "footjob": "足交", "fundoshi": "褌/兜裆布", "futanari": "扶她", "game_cg": "游戏CG", "gangbang": "乱交", "garter": "袜带", "garter_belt": "吊袜腰带", "genderswap": "性转", "genshin_impact": "原神", "girls_frontline": "少女前线", "girls_und_panzer": "少女与战车", "gochuumon_wa_usagi_desu_ka?": "请问您今天要来点兔子吗?", "gothic_lolita": "哥特式洛丽塔", "granblue_fantasy": "碧蓝幻想", "guitar": "吉他", "gun": "枪炮", "gundam": "高达", "guro": "猎奇", "halloween": "万圣节前夜", "handjob": "打手枪", "headphones": "耳机", "heels": "高跟鞋", "heterochromia": "虹膜异色", "hibike!_euphonium": "吹响吧!上低音号", "highschool_dxd": "恶魔高校D×D", "honkai_impact": "崩坏 3", "horns": "角", "index_page": "索引页面", "infinite_stratos": "IS/无限斯特拉托斯", "inumimi": "犬耳", "japanese_clothes": "日式服装", "k-on!": "轻音少女", "kaguya-sama_wa_kokurasetai_~tensai-tachi_no_renai_zunousen~": "辉夜大小姐想让我告白~天才们的恋爱头脑战~", "kantai_collection": "舰队 Collection", "kemono_friends": "兽娘动物园", "kimetsu_no_yaiba": "鬼灭之刃", "kimono": "和服", "kitsune": "狐狸", "kobayashi-san_chi_no_maid_dragon": "小林家的龙女仆", "kono_subarashii_sekai_ni_shukufuku_wo!": "为美好的世界献上祝福!", "lactation": "泌乳", "landscape": "风景画", "league_of_legends": "英雄联盟", "leotard": "紧身连衣裤", "line_art": "线条画", "lingerie": "贴身内衣", "little_busters!": "Little Busters!", "loli": "萝莉", "lolita_fashion": "洛丽塔", "love_live!_nijigasaki_high_school_idol_club": "Love Live! 虹咲学园学园偶像同好会", "lucky_star": "幸运星", "maebari": "前貼り/遮盖私处", "mahou_shoujo_lyrical_nanoha": "魔法少女奈叶", "mahou_shoujo_lyrical_nanoha_strikers": "魔法少女奈叶 StrikerS", "maid": "女仆", "male": "男性", "masturbation": "自摸/手淫", "mecha": "机甲", "mecha_musume": "机甲娘", "megane": "眼镜", "megaten": "女神转生系列", "mermaid": "美人鱼", "miko": "巫女", "monochrome": "单色", "monster": "怪物", "monster_girl": "怪物女孩", "monster_musume_no_iru_nichijou": "魔物娘的相伴日常", "naked": "裸体", "naked_apron": "裸体围裙", "naked_cape": "裸体披风", "naked_ribbon": "裸体丝带", "neko": "猫", "nekomimi": "猫耳", "neon_genesis_evangelion": "新世纪福音战士", "nier_automata": "尼尔:自动人形", "nijisanji": "彩虹社", "ninja": "忍者", "nipple_slip": "露点", "nipples": "乳头", "no_bra": "无乳罩", "nopan": "无胖次", "nun": "修女", "nurse": "护士", "official_watermark": "官方水印", "onsen": "温泉", "open_shirt": "衬衫敞开", "ore_no_imouto_ga_konnani_kawaii_wake_ga_nai": "我的妹妹哪有这么可爱!", "overalls": "工装连衣裤", "overwatch": "守望先锋", "paizuri": "乳交", "pajama": "睡衣", "panties": "内裤", "pantsu": "胖次", "panty_pull": "胖次脱下", "pantyhose": "吊带袜", "parody": "仿拟/谐拟", "partial_scan": "局部扫描", "pasties": "乳贴", "pee": "尿尿", "penguin": "企鹅", "penis": "阴茎", "photo": "照片/现实背景", "photoshop": "PS 改图", "pirate": "海盗", "pointy_ears": "尖耳朵", "pokemon": "精灵宝可梦", "possible_duplicate": "可能重复", "pregnant": "孕妇", "pretty_cure": "光之美少女", "princess_connect": "公主连结", "princess_connect!_re:dive": "公主连结 Re:Dive", "profile_page": "角色资料页", "pubic_hair": "阴毛", "puella_magi_madoka_magica": "魔法少女小圆", "pussy": "阴户", "pussy_juice": "妹汁", "queen's_blade": "女王之刃", "raw_scan": "扫描原图", "re_zero_kara_hajimeru_isekai_seikatsu": "Re:从零开始的异世界生活", "robe": "长袍/礼服/睡袍", "saenai_heroine_no_sodatekata": "路人女主的养成方法", "sailor_moon": "美少女战士", "sake": "日本清酒", "sample": "样品图", "sarashi": "晒し/缠胸布", "school_swimsuit": "学校泳衣", "see_through": "透视", "seifuku": "制服", "selfie": "自拍", "senran_kagura": "闪乱神乐", "sex": "性交", "sheets": "床单", "shimapan": "条纹胖次", "shirt_lift": "衬衫掀起", "shota": "正太", "silhouette": "剪影/暗色轮廓/体形", "sketch": "素描", "skirt_lift": "裙摆掀起", "sling_bikini": "吊带比基尼", "smoking": "吸烟", "soccer": "足球", "sono_bisque_doll_wa_koi_wo_suru": "更衣人偶坠入爱河", "spy_x_family": "间谍过家家", "ssss.gridman": "SSSS.古立特", "stick_poster": "海报", "stockings": "长筒袜", "strike_witches": "强袭魔女", "string_panties": "细绳胖次", "summer_dress": "夏装", "suzumiya_haruhi_no_yuuutsu": "凉宫春日的忧郁", "sweater": "毛衣", "swimsuits": "泳衣", "sword": "刀剑", "sword_art_online": "刀剑神域", "symmetrical_docking": "乳乳相接", "tagme": "标签", "tail": "兽尾", "tan_lines": "日晒线", "tattoo": "文身", "tennis": "网球", "tentacles": "触手", "text": "文本", "the_idolm@ster": "偶像大师", "the_idolm@ster_cinderella_girls": "偶像大师灰姑娘女孩", "the_idolm@ster_million_live!": "偶像大师百万现场", "the_idolm@ster_shiny_colors": "偶像大师闪耀色彩", "thighhighs": "过膝袜", "thong": "丁字裤", "to_aru_kagaku_no_railgun": "某科学的超电磁炮", "to_aru_majutsu_no_index": "魔法禁书目录", "to_heart_(series)": "To Heart 系列", "to_heart_2": "To Heart 2", "to_love_ru": "出包王女", "to_love_ru_darkness": "出包王女 Darkness", "topless": "上身露出", "torn_clothes": "破衣", "touhou": "东方", "towel": "浴巾", "translated": "文字已翻译(英文)", "transparent_png": "背景透明", "trap": "伪娘", "tribadism": "磨豆腐/交叉体位", "tutorial": "教程", "uma_musume_pretty_derby": "赛马娘", "umbrella": "伞", "uncensored": "无码", "underboob": "南半球", "underwear": "内衣", "undressing": "脱衣", "uniform": "制服", "valentine": "情人节", "vibrator": "跳蛋", "wa_maid": "和风女仆", "waitress": "女侍", "wallpaper": "壁纸", "wardrobe_malfunction": "走光", "weapon": "武器", "wedding_dress": "婚纱", "wet": "湿身", "wet_clothes": "湿衣", "wings": "翅膀", "witch": "女巫", "xenoblade": "异度神剑", "xenoblade_chronicles_2": "异度神剑 2", "yahari_ore_no_seishun_lovecome_wa_machigatteiru.": "我的青春恋爱喜剧果然有问题", "yaoi": "蔷薇/男同", "yukata": "浴衣", "yuri": "百合", "zhanjianshaonv": "战舰少女" }; const menus = { "My Account": "账户", "Posts": "作品", "Comments": "评论", "Notes": "笔记", "Artists": "画师", "Tags": "标签", "Forum": "论坛", "Help": "帮助", "More »": "更多>>", "New Mail": "新消息", "My Profile": "我的资料", "My Mail": "我的消息", "My Favorites": "我的收藏", "Settings": "设置", "Change Password": "修改密码", "Logout": "退出登录", "View Posts": "浏览作品", "Search Posts": "搜索作品", "Upload": "上传", "Random": "随机浏览", "Popular": "热门", "Image Search": "搜索图片", "History": "历史", "View Comments": "浏览评论", "Search Comments": "搜索评论", "View Notes": "浏览笔记", "Search Notes": "搜索笔记", "View Artists": "浏览画师", "Search Artists": "搜索画师", "Create": "创建", "View Tags": "浏览标签", "Search Tags": "搜索标签", "Aliases": "别名", "Implications": "含义", "View Pools": "浏览 Pools", "Search Pools": "搜索 Pools", "Create New Pool": "创建 Pool", "View Wiki Index": "浏览 Wiki 主页", "Search Wiki": "搜索 Wiki", "Create New Page": "创建新页面", "Mark All Read": "全部标记已读" } ; const footers = { "List": "首页", "Browse": "翻阅", "Upload": "上传", "Random": "随机", "Popular": "热门", "Image Search": "寻图", "History": "历史", "Help": "帮助" } ; const translateTags = function() { const elementList = Array.from(document.getElementsByTagName("a")); elementList.forEach(element => { const href = element.getAttribute("href"); if (typeof href === "string" && /^\/post\?tags=(\S+)$/.test(href)) { const en = RegExp.$1; const cn = tags[en]; if (cn) { element.innerText = `[${cn}]${en.replace(/_/g, " ")}`; } } }); }; const translateMenus = function() { const mainMenuList = Array.from(document.querySelectorAll("#main-menu>ul>li>a")); const subMenuList = Array.from(document.querySelectorAll("ul.submenu>li>a")); const elementList = [...mainMenuList, ...subMenuList]; elementList.forEach(element => { if (element.getAttribute("href") === "#") return const en = element.innerText; const cn = menus[en]; if (cn) { element.innerText = cn; } }); }; const translateNotice = function() { const elementList = Array.from(document.querySelectorAll(".status-notice")); elementList.forEach(element => { console.log(element.innerHTML); element.innerHTML = element.innerHTML .replace(/^[\s]+This image has been resized. Click on the /, "这张图片已经被压缩,单击侧边栏中的") .replace(/View larger version/, "显示高清图") .replace(/ link in the sidebar for a high-quality version./, "可以获取更高质量的版本。") .replace(/Hide this message<\/a>\./, "不再提醒") .replace(/This post belongs to a /, "这张图片从属于一个") .replace(/parent post<\/a>\./, "相关父作品。") .replace(/This post has /, "这张图片从属于一个") .replace(/child posts<\/a>\. \(post #/, "作品集。相关子作品:") .replace(/a child post<\/a>\. \(post #/, "作品集。相关子作品:") .replace(/<\/a>, | \)/, ""); }); }; const translateButtons = function() { [ ['#highres-show', 'View larger version', '显示高清图'], ['#highres', 'Download larger version', '下载高清图'], ['#png', 'Download PNG', '下载 PNG 图'], ['li#add-to-favs>a', 'Add to favorites', '添加收藏'], ['li#set-avatar>a', 'Set avatar', '设置头像'], ['h4>a.js-posts-show-edit-tab', 'Edit', '编辑'], ['h4>a.js-posts-show-comments-tab', 'Respond', '评论'], ['.pagination>.previous_page', '← Previous', '上一页'], ['.pagination>.next_page', 'Next →', '下一页'], ].forEach(data => { const [selector, en, cn] = data; const element = document.querySelector(selector); if (element) { element.innerText = element.innerText.replace(en, cn); } }); }; const translateFooters = function() { const elementList = Array.from(document.querySelectorAll('#subnavbar>li>a')); elementList.forEach(element => { const en = element.innerText; const cn = footers[en]; if (cn) { element.innerText = cn; } }); }; const initTranslate = function() { translateTags(); translateMenus(); translateNotice(); translateButtons(); translateFooters(); }; jQuery(document).ready(function() { initStyle(); initHotKey(); initOptions(); initTranslate(); if (document.cookie.includes('locale=zh_CN') === false) { document.cookie = "locale=zh_CN"; location.href = location.href; } }); }()); ================================================ FILE: config/rollup.config.js ================================================ import { version } from "../package.json" import cleanup from "rollup-plugin-cleanup" import json from "@rollup/plugin-json" const banner = `// ==UserScript== // @name Yande.re 简体中文 // @namespace com.coderzhaoziwei.yandere // @version ${ version } // @author Coder Zhao coderzhaoziwei@outlook.com // @description 中文标签 | 界面优化 | 高清大图 | 键盘翻页 | 流体布局 // @homepage https://greasyfork.org/scripts/421970 // @license MIT // @match https://yande.re/* // @exclude https://yande.re/forum/* // @match https://konachan.com/* // @exclude https://konachan.com/forum/* // @match https://konachan.net/* // @exclude https://konachan.net/forum/* // @supportURL https://github.com/coderzhaoziwei/yande-re-chinese-patch/issues // @grant GM_download // ==/UserScript== /* eslint-env es2022 */ /* global jQuery:readonly */ /* global Vue:readonly */ /* global Vuetify:readonly */ /* global VueMasonry:readonly */ ` export default { input: "source/index.js", output: { file: "bundle/index.js", format: "iife", banner, }, plugins: [ cleanup(), json() ], } ================================================ FILE: package.json ================================================ { "name": "yande-re-chinese-patch", "version": "2.1.47", "author": "Coder Zhao", "description": "Yande.re Chinese Patch | Y 站简体中文补丁", "repository": "https://github.com/coderzhaoziwei/yande.re-chinese-patch.git", "license": "MIT", "scripts": { "bundle": "sh script/bundle.sh" }, "devDependencies": { "@rollup/plugin-json": "^4.1.0", "rollup": "^2.47.0", "rollup-plugin-cleanup": "^3.2.1" } } ================================================ FILE: readme.md ================================================ ![version][img-version] ![license][img-license] ![stars][img-stars] ![cover][img-cover] # 目录 - [前言](#前言) - [预览](#预览) - [功能](#功能) - [标签翻译](#标签翻译) - [操作优化](#操作优化) - [浏览模式](#浏览模式) - [安装](#安装) - [安装浏览器插件](#安装浏览器插件) - [安装脚本程序](#安装脚本程序) - [常见问题](#常见问题) - [Q: 安卓手机如何安装 Tampermonkey?](#q-安卓手机如何安装-tampermonkey) - [相关链接](#相关链接) - [开源许可](#开源许可) - [附:标签翻译表](#附标签翻译表) # 前言 最初,我在浏览 [yande.re 站](https://yande.re)(以下简称 Y 站)的时候,遇到了一些难懂的标签名。在使用搜索引擎多方查询之后,我才终于明白个别晦涩单词的含义,可是过了一段时间再次遇到,又忘记了。然后我就敲了一个脚本程序,自动遍历页面中的标签名,添加中文翻译。为了让与我有同样困扰的其他朋友节约时间,我将脚本程序发布到了脚本网站 [Greasy Fork](https://greasyfork.org/) 上。 后来,有朋友提议添加更优化的操作方式,于是陆续增加了显示大图、键盘翻页等功能。再后来有一天,我躺在床上用手机登陆了 Y 站,网站无法适配移动端屏幕实在是太特么难受了,于是我就敲了响应式布局的浏览模式,手机可以自动加载图片资源,单屏一滑到底。 终于,舒服了。 - https://yande.re - ~~https://oreno.imouto.us (Y 站镜像,无需魔法上网)~~(已失效) - https://konachan.com (K 站已兼容) - https://konachan.net (K 站安全模式,.net 域名下默认隐藏成人内容) # 预览 - 样式优化,高清图源,尺寸自选。 ![1](https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/source/img/1.png) - 流体布局,自动加载,一屏到底。 ![2](https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/source/img/2.png) - 单图预览,聚合详情,一键下载。 ![3](https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/source/img/3.png) - 显隐自如,更多功能,欢迎体验。 ![4](https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/source/img/4.png) # 功能 ## 标签翻译 翻译了 Y 站出现频率较高的 100 多个标签,详细内容请查看最下方的标签翻译表。欢迎校正或补充。 ## 操作优化 - 对于 Y 站默认隐藏的成人内容,提供了显示或者隐藏的选项。 - 网页左侧的边栏,提供了显示或者隐藏的选项。 - 图源默认自动替换为高清资源,并提供了 1 ~ 4 倍尺寸的选项。 - 快捷键 - 上一页:A / ← - 下一页:D / → - 显示当前作品原图:S - 显示当前作品来源:W ## 浏览模式 浏览任意图片列表时,可以进入浏览模式。 # 安装 ## 安装浏览器插件 首先你需要为你的浏览器安装一个用户脚本管理器,推荐使用插件 [Tampermonkey](https://www.tampermonkey.net/)。 > 主流浏览器有 Chrome、Microsoft Edge、Firefox、Safari 等,如果你的浏览器无法安装 Tampermonkey,那么也就无法安装此脚本程序。 ## 安装脚本程序 使用浏览器直接访问 [Greasy Fork - Yande.re 简体中文 - 主页](https://greasyfork.org/scripts/421970),点击安装即可。 如果你没有 Greasy Fork 的账号,访问以上链接可能会提示你:`此脚本不再在本网站上匿名可用。请登录并检查您的 Greasy Fork 账号设置。` 因为此脚本程序涉及成人内容,所以在 Greasy Fork 站点必须登录才可以浏览或安装。如果你不想注册成为 Greasy Fork 的用户,可以访问成人脚本站点 [Sleasy Fork](https://sleazyfork.org/scripts/421970) 直接安装,或者直接获取[仓库文件](https://github.com/coderzhaoziwei/yande-re-chinese-patch/raw/main/index.user.js)来安装。 # 常见问题 ## Q: 安卓手机如何安装 Tampermonkey? 我推荐安卓 Yandex 浏览器,可以直接安装 Chrome 插件。当你发现谷歌市场的 Tampermonkey 显示不兼容时,点击 Yandex 菜单栏,切换为桌面模式即可。 其他安卓浏览器也可以的,Firefox、Kiwi、Iceraven 等自行尝试。 # 相关链接 [Github 仓库](https://github.com/coderzhaoziwei/yande-re-chinese-patch) | [Greasy Fork 脚本主页](https://greasyfork.org/scripts/421970) | [封面原图 Yande#388833](https://yande.re/post/show/388833) # 开源许可 MIT # 附:标签翻译表 ||English|简体中文| |:-:|:-|:-| |1|4koma|四格漫画| |2|5-toubun no hanayome|五等分的新娘| |3|anal|肛交| |4|angel|天使| |5|angel beats!|Angel Beats!| |6|animal ears|兽耳| |7|anthropomorphization|拟人化| |8|anus|肛门露出| |9|areola|乳晕| |10|arknights|明日方舟| |11|armor|盔甲/装甲| |12|artist revision|画师修改| |13|ass|臀部| |14|ass grab|持股/捏臀| |15|atelier|炼金工房系列| |16|autographed|亲笔签名| |17|azur lane|碧蓝航线| |18|bakemonogatari|化物语| |19|bandages|绷带| |20|bandaid|创可贴/绷带| |21|bang dream!|BanG Dream!| |22|baseball|棒球| |23|basketball|篮球| |24|bathing|沐浴| |25|benghuai xueyuan|崩坏学园| |26|bike shorts|自行车短裤| |27|bikini|比基尼| |28|bikini armor|比基尼装甲/轻薄盔甲| |29|bikini top|比基尼乳罩| |30|black rock shooter|黑岩射手| |31|blood|血腥| |32|bloomers|灯笼裤/宽松短裤| |33|blue archive|碧蓝档案| |34|bodysuit|紧身衣裤| |35|boku wa tomodachi ga sukunai|我的朋友很少| |36|bondage|束缚| |37|bottomless|下身露出| |38|bra|乳罩| |39|breast grab|握乳| |40|breast hold|托乳| |41|breasts|乳| |42|bukkake|颜射| |43|bunny ears|兔耳| |44|bunny girl|兔女郎| |45|buruma|运动短裤| |46|business suit|西装/职业服| |47|calendar|日历| |48|cameltoe|阴户凸显| |49|card|卡牌| |50|card captor sakura|魔卡少女樱| |51|censored|有码| |52|cg|CG/计算机动画| |53|chainsaw|电锯| |54|character design|角色设计| |55|cheerleader|啦啦队队员| |56|chibi|Q版| |57|chinadress|旗袍| |58|choujigen game neptune|超次元游戏海王星| |59|christmas|圣诞| |60|cleavage|乳沟| |61|code geass|反叛的鲁路修| |62|condom|避孕套| |63|corset|(束腰)紧身内衣| |64|cosplay|角色扮演| |65|cream|奶油| |66|cropped|裁剪图| |67|crossdress|变装| |68|crossover|作品联动/混合同人| |69|cum|精液| |70|cunnilingus|品玉/舔阴| |71|dakimakura|抱枕| |72|darling in the franxx|DARLING in the FRANXX| |73|date a live|约会大作战| |74|detexted|去字图片| |75|devil|魔鬼/恶魔| |76|digital version|数字版| |77|dildo|假阳具| |78|disc cover|光盘封面| |79|dress|连衣裙| |80|dress shirt|衬衫| |81|duplicate|重复图片| |82|elf|精灵| |83|endcard|片尾插图| |84|erect nipples|乳尖| |85|expression|角色展示/立绘| |86|extreme content|极端| |87|eyepatch|眼罩| |88|fairy|精灵/小精灵| |89|fate/kaleid liner prisma illya|Fate/kaleid liner 魔法少女☆伊莉雅| |90|feet|足| |91|fellatio|口交| |92|final fantasy|最终幻想| |93|final fantasy vii|最终幻想 VII| |94|final fantasy xiv|最终幻想 14| |95|fingering|指交| |96|fire emblem|火焰纹章| |97|fire emblem heroes|火焰之纹章:英雄云集| |98|fire emblem kakusei|火焰之纹章:觉醒| |99|fire emblem three houses|火焰之纹章:风花雪月| |100|fishnets|鱼网袜| |101|fixed|修改| |102|footjob|足交| |103|fundoshi|褌/兜裆布| |104|futanari|扶她| |105|game cg|游戏CG| |106|gangbang|乱交| |107|garter|袜带| |108|garter belt|吊袜腰带| |109|genderswap|性转| |110|genshin impact|原神| |111|girls frontline|少女前线| |112|girls und panzer|少女与战车| |113|gochuumon wa usagi desu ka?|请问您今天要来点兔子吗?| |114|gothic lolita|哥特式洛丽塔| |115|granblue fantasy|碧蓝幻想| |116|guitar|吉他| |117|gun|枪炮| |118|gundam|高达| |119|guro|猎奇| |120|halloween|万圣节前夜| |121|handjob|打手枪| |122|headphones|耳机| |123|heels|高跟鞋| |124|heterochromia|虹膜异色| |125|hibike! euphonium|吹响吧!上低音号| |126|highschool dxd|恶魔高校D×D| |127|honkai impact|崩坏 3| |128|horns|角| |129|index page|索引页面| |130|infinite stratos|IS/无限斯特拉托斯| |131|inumimi|犬耳| |132|japanese clothes|日式服装| |133|k-on!|轻音少女| |134|kaguya-sama wa kokurasetai ~tensai-tachi no renai zunousen~|辉夜大小姐想让我告白~天才们的恋爱头脑战~| |135|kantai collection|舰队 Collection| |136|kemono friends|兽娘动物园| |137|kimetsu no yaiba|鬼灭之刃| |138|kimono|和服| |139|kitsune|狐狸| |140|kobayashi-san chi no maid dragon|小林家的龙女仆| |141|kono subarashii sekai ni shukufuku wo!|为美好的世界献上祝福!| |142|lactation|泌乳| |143|landscape|风景画| |144|league of legends|英雄联盟| |145|leotard|紧身连衣裤| |146|line art|线条画| |147|lingerie|贴身内衣| |148|little busters!|Little Busters!| |149|loli|萝莉| |150|lolita fashion|洛丽塔| |151|love live! nijigasaki high school idol club|Love Live! 虹咲学园学园偶像同好会| |152|lucky star|幸运星| |153|maebari|前貼り/遮盖私处| |154|mahou shoujo lyrical nanoha|魔法少女奈叶| |155|mahou shoujo lyrical nanoha strikers|魔法少女奈叶 StrikerS| |156|maid|女仆| |157|male|男性| |158|masturbation|自摸/手淫| |159|mecha|机甲| |160|mecha musume|机甲娘| |161|megane|眼镜| |162|megaten|女神转生系列| |163|mermaid|美人鱼| |164|miko|巫女| |165|monochrome|单色| |166|monster|怪物| |167|monster girl|怪物女孩| |168|monster musume no iru nichijou|魔物娘的相伴日常| |169|naked|裸体| |170|naked apron|裸体围裙| |171|naked cape|裸体披风| |172|naked ribbon|裸体丝带| |173|neko|猫| |174|nekomimi|猫耳| |175|neon genesis evangelion|新世纪福音战士| |176|nier automata|尼尔:自动人形| |177|nijisanji|彩虹社| |178|ninja|忍者| |179|nipple slip|露点| |180|nipples|乳头| |181|no bra|无乳罩| |182|nopan|无胖次| |183|nun|修女| |184|nurse|护士| |185|official watermark|官方水印| |186|onsen|温泉| |187|open shirt|衬衫敞开| |188|ore no imouto ga konnani kawaii wake ga nai|我的妹妹哪有这么可爱!| |189|overalls|工装连衣裤| |190|overwatch|守望先锋| |191|paizuri|乳交| |192|pajama|睡衣| |193|panties|内裤| |194|pantsu|胖次| |195|panty pull|胖次脱下| |196|pantyhose|吊带袜| |197|parody|仿拟/谐拟| |198|partial scan|局部扫描| |199|pasties|乳贴| |200|pee|尿尿| |201|penguin|企鹅| |202|penis|阴茎| |203|photo|照片/现实背景| |204|photoshop|PS 改图| |205|pirate|海盗| |206|pointy ears|尖耳朵| |207|pokemon|精灵宝可梦| |208|possible duplicate|可能重复| |209|pregnant|孕妇| |210|pretty cure|光之美少女| |211|princess connect|公主连结| |212|princess connect! re:dive|公主连结 Re:Dive| |213|profile page|角色资料页| |214|pubic hair|阴毛| |215|puella magi madoka magica|魔法少女小圆| |216|pussy|阴户| |217|pussy juice|妹汁| |218|queen's blade|女王之刃| |219|raw scan|扫描原图| |220|re zero kara hajimeru isekai seikatsu|Re:从零开始的异世界生活| |221|robe|长袍/礼服/睡袍| |222|saenai heroine no sodatekata|路人女主的养成方法| |223|sailor moon|美少女战士| |224|sake|日本清酒| |225|sample|样品图| |226|sarashi|晒し/缠胸布| |227|school swimsuit|学校泳衣| |228|see through|透视| |229|seifuku|制服| |230|selfie|自拍| |231|senran kagura|闪乱神乐| |232|sex|性交| |233|sheets|床单| |234|shimapan|条纹胖次| |235|shirt lift|衬衫掀起| |236|shota|正太| |237|silhouette|剪影/暗色轮廓/体形| |238|sketch|素描| |239|skirt lift|裙摆掀起| |240|sling bikini|吊带比基尼| |241|smoking|吸烟| |242|soccer|足球| |243|sono bisque doll wa koi wo suru|更衣人偶坠入爱河| |244|spy x family|间谍过家家| |245|ssss.gridman|SSSS.古立特| |246|stick poster|海报| |247|stockings|长筒袜| |248|strike witches|强袭魔女| |249|string panties|细绳胖次| |250|summer dress|夏装| |251|suzumiya haruhi no yuuutsu|凉宫春日的忧郁| |252|sweater|毛衣| |253|swimsuits|泳衣| |254|sword|刀剑| |255|sword art online|刀剑神域| |256|symmetrical docking|乳乳相接| |257|tagme|标签| |258|tail|兽尾| |259|tan lines|日晒线| |260|tattoo|文身| |261|tennis|网球| |262|tentacles|触手| |263|text|文本| |264|the idolm@ster|偶像大师| |265|the idolm@ster cinderella girls|偶像大师灰姑娘女孩| |266|the idolm@ster million live!|偶像大师百万现场| |267|the idolm@ster shiny colors|偶像大师闪耀色彩| |268|thighhighs|过膝袜| |269|thong|丁字裤| |270|to aru kagaku no railgun|某科学的超电磁炮| |271|to aru majutsu no index|魔法禁书目录| |272|to heart (series)|To Heart 系列| |273|to heart 2|To Heart 2| |274|to love ru|出包王女| |275|to love ru darkness|出包王女 Darkness| |276|topless|上身露出| |277|torn clothes|破衣| |278|touhou|东方| |279|towel|浴巾| |280|translated|文字已翻译(英文)| |281|transparent png|背景透明| |282|trap|伪娘| |283|tribadism|磨豆腐/交叉体位| |284|tutorial|教程| |285|uma musume pretty derby|赛马娘| |286|umbrella|伞| |287|uncensored|无码| |288|underboob|南半球| |289|underwear|内衣| |290|undressing|脱衣| |291|uniform|制服| |292|valentine|情人节| |293|vibrator|跳蛋| |294|wa maid|和风女仆| |295|waitress|女侍| |296|wallpaper|壁纸| |297|wardrobe malfunction|走光| |298|weapon|武器| |299|wedding dress|婚纱| |300|wet|湿身| |301|wet clothes|湿衣| |302|wings|翅膀| |303|witch|女巫| |304|xenoblade|异度神剑| |305|xenoblade chronicles 2|异度神剑 2| |306|yahari ore no seishun lovecome wa machigatteiru.|我的青春恋爱喜剧果然有问题| |307|yaoi|蔷薇/男同| |308|yukata|浴衣| |309|yuri|百合| |310|zhanjianshaonv|战舰少女| [img-version]: https://img.shields.io/github/package-json/v/coderzhaoziwei/yande-re-chinese-patch?style=flat-square [img-license]: https://shields.io/badge/license-MIT-blue?style=flat-square [img-stars]: https://img.shields.io/github/stars/coderzhaoziwei/yande-re-chinese-patch?label=star&style=social [img-cover]: https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/preview.png ================================================ FILE: readme_main.md ================================================ ![version][img-version] ![license][img-license] ![stars][img-stars] ![cover][img-cover] # 目录 - [前言](#前言) - [预览](#预览) - [功能](#功能) - [标签翻译](#标签翻译) - [操作优化](#操作优化) - [浏览模式](#浏览模式) - [安装](#安装) - [安装浏览器插件](#安装浏览器插件) - [安装脚本程序](#安装脚本程序) - [常见问题](#常见问题) - [Q: 安卓手机如何安装 Tampermonkey?](#q-安卓手机如何安装-tampermonkey) - [相关链接](#相关链接) - [开源许可](#开源许可) - [附:标签翻译表](#附标签翻译表) # 前言 最初,我在浏览 [yande.re 站](https://yande.re)(以下简称 Y 站)的时候,遇到了一些难懂的标签名。在使用搜索引擎多方查询之后,我才终于明白个别晦涩单词的含义,可是过了一段时间再次遇到,又忘记了。然后我就敲了一个脚本程序,自动遍历页面中的标签名,添加中文翻译。为了让与我有同样困扰的其他朋友节约时间,我将脚本程序发布到了脚本网站 [Greasy Fork](https://greasyfork.org/) 上。 后来,有朋友提议添加更优化的操作方式,于是陆续增加了显示大图、键盘翻页等功能。再后来有一天,我躺在床上用手机登陆了 Y 站,网站无法适配移动端屏幕实在是太特么难受了,于是我就敲了响应式布局的浏览模式,手机可以自动加载图片资源,单屏一滑到底。 终于,舒服了。 - https://yande.re - ~~https://oreno.imouto.us (Y 站镜像,无需魔法上网)~~(已失效) - https://konachan.com (K 站已兼容) - https://konachan.net (K 站安全模式,.net 域名下默认隐藏成人内容) # 预览 - 样式优化,高清图源,尺寸自选。 ![1](https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/source/img/1.png) - 流体布局,自动加载,一屏到底。 ![2](https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/source/img/2.png) - 单图预览,聚合详情,一键下载。 ![3](https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/source/img/3.png) - 显隐自如,更多功能,欢迎体验。 ![4](https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/source/img/4.png) # 功能 ## 标签翻译 翻译了 Y 站出现频率较高的 100 多个标签,详细内容请查看最下方的标签翻译表。欢迎校正或补充。 ## 操作优化 - 对于 Y 站默认隐藏的成人内容,提供了显示或者隐藏的选项。 - 网页左侧的边栏,提供了显示或者隐藏的选项。 - 图源默认自动替换为高清资源,并提供了 1 ~ 4 倍尺寸的选项。 - 快捷键 - 上一页:A / ← - 下一页:D / → - 显示当前作品原图:S - 显示当前作品来源:W ## 浏览模式 浏览任意图片列表时,可以进入浏览模式。 # 安装 ## 安装浏览器插件 首先你需要为你的浏览器安装一个用户脚本管理器,推荐使用插件 [Tampermonkey](https://www.tampermonkey.net/)。 > 主流浏览器有 Chrome、Microsoft Edge、Firefox、Safari 等,如果你的浏览器无法安装 Tampermonkey,那么也就无法安装此脚本程序。 ## 安装脚本程序 使用浏览器直接访问 [Greasy Fork - Yande.re 简体中文 - 主页](https://greasyfork.org/scripts/421970),点击安装即可。 如果你没有 Greasy Fork 的账号,访问以上链接可能会提示你:`此脚本不再在本网站上匿名可用。请登录并检查您的 Greasy Fork 账号设置。` 因为此脚本程序涉及成人内容,所以在 Greasy Fork 站点必须登录才可以浏览或安装。如果你不想注册成为 Greasy Fork 的用户,可以访问成人脚本站点 [Sleasy Fork](https://sleazyfork.org/scripts/421970) 直接安装,或者直接获取[仓库文件](https://github.com/coderzhaoziwei/yande-re-chinese-patch/raw/main/index.user.js)来安装。 # 常见问题 ## Q: 安卓手机如何安装 Tampermonkey? 我推荐安卓 Yandex 浏览器,可以直接安装 Chrome 插件。当你发现谷歌市场的 Tampermonkey 显示不兼容时,点击 Yandex 菜单栏,切换为桌面模式即可。 其他安卓浏览器也可以的,Firefox、Kiwi、Iceraven 等自行尝试。 # 相关链接 [Github 仓库](https://github.com/coderzhaoziwei/yande-re-chinese-patch) | [Greasy Fork 脚本主页](https://greasyfork.org/scripts/421970) | [封面原图 Yande#388833](https://yande.re/post/show/388833) # 开源许可 MIT # 附:标签翻译表 [[ TAGS ]] [img-version]: https://img.shields.io/github/package-json/v/coderzhaoziwei/yande-re-chinese-patch?style=flat-square [img-license]: https://shields.io/badge/license-MIT-blue?style=flat-square [img-stars]: https://img.shields.io/github/stars/coderzhaoziwei/yande-re-chinese-patch?label=star&style=social [img-cover]: https://cdn.jsdelivr.net/gh/coderzhaoziwei/yande-re-chinese-patch/preview.png ================================================ FILE: script/action.js ================================================ #!/usr/bin/env node const fs = require('fs') const path = require('path') const tagsData = Object() const version = process.env['npm_package_version'] || '' formatTagsFile() generateReadMeFile() function formatTagsFile() { info('正在格式化标签数据') const tagsPath = path.resolve(__dirname, '../source/data/tags.json') const data = JSON.parse(fs.readFileSync(tagsPath)) Object.keys(data).sort().forEach(key => tagsData[key.replace(/ /g, '_')] = data[key]) fs.writeFileSync(tagsPath, JSON.stringify(tagsData, null, 2)) } function generateReadMeFile() { info('正在生成说明文档') const main = fs.readFileSync(path.resolve(__dirname, '../readme_main.md'), 'utf8') const tags = Object.keys(tagsData).reduce((data, key, index) => { const en = key.replace(/_/g, ' ') const cn = tagsData[key] return `${ data }\n|${ index + 1 }|${en}|${cn}|` }, '||English|简体中文|\n|:-:|:-|:-|') fs.writeFileSync(path.resolve(__dirname, '../readme.md'), main.replace('[[ TAGS ]]', tags)) } function info(log) { console.log(`\u001b[35m[${version}] \u001b[34m${log}`) } ================================================ FILE: script/bundle.sh ================================================ # clear echo "\033[2J" # version yarn version --no-git-tag-version --patch node script/action.js # rollup yarn rollup --config config/rollup.config.js node script/replace.js # copy if (type pbcopy >/dev/null 2>&1) then pbcopy < bundle/index.user.js echo "\033[32mcopied \033[1;36mbundle/valkyrie.user.js \033[0;32mto clipboard.\033[0m" fi # end echo "" ================================================ FILE: script/replace.js ================================================ #!/usr/bin/env node const fs = require("fs") const origin = fs.readFileSync("bundle/index.js", "utf8") const result = handler(origin) fs.writeFile("bundle/index.user.js", result, error => { if (error) console.log(error) }) function handler(content) { // [{ path: "source/body.html" }] const regexp = /\[{ path: ([\S]+) }\]/i while (regexp.test(content)) { const path = JSON.parse(RegExp.$1) console.log(path) const replacement = fs.readFileSync(path, "utf8") content = content.replace(regexp, replacement) } return content } ================================================ FILE: source/app.js ================================================ import Post from "./post" const App = { template: "#app-template", data() { return { showDrawer: false, showImageSelected: false, showImageInfo: true, showRatingQ: JSON.parse(localStorage.getItem("showRatingQ") || "true"), showRatingE: JSON.parse(localStorage.getItem("showRatingE") || "false"), imageList: [], imageSelectedIndex: 0, imageSelectedDetail: {}, params: new URLSearchParams(location.search), requestState: false, requestStop: false, innerWidth: window.innerWidth, innerHeight: window.innerHeight, imageCountInRow: JSON.parse(localStorage.getItem("imageCountInRow") || "3"), imageQualityHigh: JSON.parse(localStorage.getItem("imageQualityHigh") || "false"), showFavoriteSuccess: false, } }, computed: { isMobile() { try { return this.$vuetify.breakpoint.mobile } catch(error) { return false } }, title() { return `${this.imageList.length} Posts` }, version() { return GM_info.script.version }, imageSelected() { return this.imageList[this.imageSelectedIndex] || new Post() }, imageSelectedWidth() { const width = parseInt(Math.min(this.innerWidth * 0.9, this.imageSelected.sampleWidth)) const height = Math.min(this.innerHeight * 0.9, this.imageSelected.sampleHeight) const width2 = parseInt(height * this.imageSelected.aspectRatio) return Math.min(width, width2) }, imageSelectedHeight() { const width = Math.min(this.innerWidth * 0.9, this.imageSelected.sampleWidth) const height = parseInt(Math.min(this.innerHeight * 0.9, this.imageSelected.sampleHeight)) const height2 = parseInt(width / this.imageSelected.aspectRatio) return Math.min(height, height2) }, }, watch: { showRatingQ(value) { localStorage.setItem("showRatingQ", JSON.stringify(value)) }, showRatingE(value) { localStorage.setItem("showRatingE", JSON.stringify(value)) }, imageCountInRow(value) { localStorage.setItem("imageCountInRow", JSON.stringify(value)) }, imageQualityHigh(value) { localStorage.setItem("imageQualityHigh", JSON.stringify(value)) }, showFavoriteSuccess(value) { console.log('showFavoriteSuccess: ', value) }, showImageSelected(value) { if (!value) { this.imageSelectedDetail = {} return } this.getPostDetail(this.imageSelected.id).then(res => { if (!res) return this.imageSelectedDetail = res }) } }, methods: { async request() { this.requestState = true const url = location.origin + location.pathname + ".json?" + this.params.toString() const response = await new Promise(resolve => { console.log(url) jQuery.get(url, data => resolve(data)) }) if (response instanceof Array && response.length > 0) { window.history.pushState("", "", location.pathname + "?" + this.params.toString()) response.forEach(item => this.imageList.push(new Post(item))) const page = Number(this.params.get("page")) || 1 this.params.set("page", page + 1) // 延迟 setTimeout(() => (this.requestState = false), 1000) } else { this.requestStop = true } }, download(src, filename) { // 添加文件后缀 const match = src.match(/[.](?png|jpg|jpeg)$/) if (match) { const extension = match.groups.extension GM_download(src, filename + "." + extension) } else { GM_download(src, filename) } }, // 添加收藏 onFavorite(id) { $.ajax({ method: 'POST', url: "https://yande.re/post/vote.json", beforeSend: xhr => xhr.setRequestHeader('x-csrf-token', window.csrfToken), data: { id, score: 3 }, success: data => { if (data.success === true) { this.imageList[this.imageSelectedIndex].favorite = true // 更新收藏状态 this.imageSelectedDetail.favorite = true } }, }) }, async getPostDetail(id) { try { if (!id) return const response = await fetch(`/post.json?api_version=2&tags=id:${id}&include_tags=1&include_votes=1`) const result = await response.json() return { favorite: result.votes[id] == 3, artist: Object.keys(result.tags).find(k => result.tags[k] == 'artist') } } catch (error) { console.log('getPostDetail error:', error) } } }, mounted() { // 自动加载数据 const timeInterval = setInterval(() => { if (this.requestStop === true) { clearInterval(timeInterval) return } const scrollTop = document.documentElement.scrollTop const scrollHeight = document.documentElement.scrollHeight const height = window.innerHeight if (scrollTop + height >= scrollHeight * 0.75) { if (this.requestState === false) { this.request() } } }, 1000) // 记录窗口尺寸 window.addEventListener("resize", () => { this.innerWidth = window.innerWidth this.innerHeight = window.innerHeight }) }, } export default App ================================================ FILE: source/browse.js ================================================ import app from "./app" export async function enterBrowseMode() { function getScript(url) { return new Promise(resolve => jQuery.getScript(url, () => resolve())) } await getScript("https://cdn.jsdelivr.net/npm/vue@2.6.12/dist/vue.min.js") await getScript("https://cdn.jsdelivr.net/npm/vuetify@2.5.0/dist/vuetify.min.js") await getScript("https://cdn.jsdelivr.net/npm/vue-masonry-css@1.0.3/dist/vue-masonry.min.js") await getScript("https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js") window.csrfToken = jQuery('[name="csrf-token"]').attr('content') document.head.innerHTML = `[{ path: "source/html/head.html" }]` document.body.innerHTML = `[{ path: "source/html/body.html" }]` Vue.use(VueMasonry) new Vue({ vuetify: new Vuetify({ theme: { dark: true }, }), render: h => h(app) }).$mount("#app") } ================================================ FILE: source/data/footers.json ================================================ { "List": "首页", "Browse": "翻阅", "Upload": "上传", "Random": "随机", "Popular": "热门", "Image Search": "寻图", "History": "历史", "Help": "帮助" } ================================================ FILE: source/data/menus.json ================================================ { "My Account": "账户", "Posts": "作品", "Comments": "评论", "Notes": "笔记", "Artists": "画师", "Tags": "标签", "Forum": "论坛", "Help": "帮助", "More »": "更多>>", "New Mail": "新消息", "My Profile": "我的资料", "My Mail": "我的消息", "My Favorites": "我的收藏", "Settings": "设置", "Change Password": "修改密码", "Logout": "退出登录", "View Posts": "浏览作品", "Search Posts": "搜索作品", "Upload": "上传", "Random": "随机浏览", "Popular": "热门", "Image Search": "搜索图片", "History": "历史", "View Comments": "浏览评论", "Search Comments": "搜索评论", "View Notes": "浏览笔记", "Search Notes": "搜索笔记", "View Artists": "浏览画师", "Search Artists": "搜索画师", "Create": "创建", "View Tags": "浏览标签", "Search Tags": "搜索标签", "Aliases": "别名", "Implications": "含义", "View Pools": "浏览 Pools", "Search Pools": "搜索 Pools", "Create New Pool": "创建 Pool", "View Wiki Index": "浏览 Wiki 主页", "Search Wiki": "搜索 Wiki", "Create New Page": "创建新页面", "Mark All Read": "全部标记已读" } ================================================ FILE: source/data/tags.json ================================================ { "4koma": "四格漫画", "5-toubun_no_hanayome": "五等分的新娘", "anal": "肛交", "angel": "天使", "angel_beats!": "Angel Beats!", "animal_ears": "兽耳", "anthropomorphization": "拟人化", "anus": "肛门露出", "areola": "乳晕", "arknights": "明日方舟", "armor": "盔甲/装甲", "artist_revision": "画师修改", "ass": "臀部", "ass_grab": "持股/捏臀", "atelier": "炼金工房系列", "autographed": "亲笔签名", "azur_lane": "碧蓝航线", "bakemonogatari": "化物语", "bandages": "绷带", "bandaid": "创可贴/绷带", "bang_dream!": "BanG Dream!", "baseball": "棒球", "basketball": "篮球", "bathing": "沐浴", "benghuai_xueyuan": "崩坏学园", "bike_shorts": "自行车短裤", "bikini": "比基尼", "bikini_armor": "比基尼装甲/轻薄盔甲", "bikini_top": "比基尼乳罩", "black_rock_shooter": "黑岩射手", "blood": "血腥", "bloomers": "灯笼裤/宽松短裤", "blue_archive": "碧蓝档案", "bodysuit": "紧身衣裤", "boku_wa_tomodachi_ga_sukunai": "我的朋友很少", "bondage": "束缚", "bottomless": "下身露出", "bra": "乳罩", "breast_grab": "握乳", "breast_hold": "托乳", "breasts": "乳", "bukkake": "颜射", "bunny_ears": "兔耳", "bunny_girl": "兔女郎", "buruma": "运动短裤", "business_suit": "西装/职业服", "calendar": "日历", "cameltoe": "阴户凸显", "card": "卡牌", "card_captor_sakura": "魔卡少女樱", "censored": "有码", "cg": "CG/计算机动画", "chainsaw": "电锯", "character_design": "角色设计", "cheerleader": "啦啦队队员", "chibi": "Q版", "chinadress": "旗袍", "choujigen_game_neptune": "超次元游戏海王星", "christmas": "圣诞", "cleavage": "乳沟", "code_geass": "反叛的鲁路修", "condom": "避孕套", "corset": "(束腰)紧身内衣", "cosplay": "角色扮演", "cream": "奶油", "cropped": "裁剪图", "crossdress": "变装", "crossover": "作品联动/混合同人", "cum": "精液", "cunnilingus": "品玉/舔阴", "dakimakura": "抱枕", "darling_in_the_franxx": "DARLING in the FRANXX", "date_a_live": "约会大作战", "detexted": "去字图片", "devil": "魔鬼/恶魔", "digital_version": "数字版", "dildo": "假阳具", "disc_cover": "光盘封面", "dress": "连衣裙", "dress_shirt": "衬衫", "duplicate": "重复图片", "elf": "精灵", "endcard": "片尾插图", "erect_nipples": "乳尖", "expression": "角色展示/立绘", "extreme_content": "极端", "eyepatch": "眼罩", "fairy": "精灵/小精灵", "fate/kaleid_liner_prisma_illya": "Fate/kaleid liner 魔法少女☆伊莉雅", "feet": "足", "fellatio": "口交", "final_fantasy": "最终幻想", "final_fantasy_vii": "最终幻想 VII", "final_fantasy_xiv": "最终幻想 14", "fingering": "指交", "fire_emblem": "火焰纹章", "fire_emblem_heroes": "火焰之纹章:英雄云集", "fire_emblem_kakusei": "火焰之纹章:觉醒", "fire_emblem_three_houses": "火焰之纹章:风花雪月", "fishnets": "鱼网袜", "fixed": "修改", "footjob": "足交", "fundoshi": "褌/兜裆布", "futanari": "扶她", "game_cg": "游戏CG", "gangbang": "乱交", "garter": "袜带", "garter_belt": "吊袜腰带", "genderswap": "性转", "genshin_impact": "原神", "girls_frontline": "少女前线", "girls_und_panzer": "少女与战车", "gochuumon_wa_usagi_desu_ka?": "请问您今天要来点兔子吗?", "gothic_lolita": "哥特式洛丽塔", "granblue_fantasy": "碧蓝幻想", "guitar": "吉他", "gun": "枪炮", "gundam": "高达", "guro": "猎奇", "halloween": "万圣节前夜", "handjob": "打手枪", "headphones": "耳机", "heels": "高跟鞋", "heterochromia": "虹膜异色", "hibike!_euphonium": "吹响吧!上低音号", "highschool_dxd": "恶魔高校D×D", "honkai_impact": "崩坏 3", "horns": "角", "index_page": "索引页面", "infinite_stratos": "IS/无限斯特拉托斯", "inumimi": "犬耳", "japanese_clothes": "日式服装", "k-on!": "轻音少女", "kaguya-sama_wa_kokurasetai_~tensai-tachi_no_renai_zunousen~": "辉夜大小姐想让我告白~天才们的恋爱头脑战~", "kantai_collection": "舰队 Collection", "kemono_friends": "兽娘动物园", "kimetsu_no_yaiba": "鬼灭之刃", "kimono": "和服", "kitsune": "狐狸", "kobayashi-san_chi_no_maid_dragon": "小林家的龙女仆", "kono_subarashii_sekai_ni_shukufuku_wo!": "为美好的世界献上祝福!", "lactation": "泌乳", "landscape": "风景画", "league_of_legends": "英雄联盟", "leotard": "紧身连衣裤", "line_art": "线条画", "lingerie": "贴身内衣", "little_busters!": "Little Busters!", "loli": "萝莉", "lolita_fashion": "洛丽塔", "love_live!_nijigasaki_high_school_idol_club": "Love Live! 虹咲学园学园偶像同好会", "lucky_star": "幸运星", "maebari": "前貼り/遮盖私处", "mahou_shoujo_lyrical_nanoha": "魔法少女奈叶", "mahou_shoujo_lyrical_nanoha_strikers": "魔法少女奈叶 StrikerS", "maid": "女仆", "male": "男性", "masturbation": "自摸/手淫", "mecha": "机甲", "mecha_musume": "机甲娘", "megane": "眼镜", "megaten": "女神转生系列", "mermaid": "美人鱼", "miko": "巫女", "monochrome": "单色", "monster": "怪物", "monster_girl": "怪物女孩", "monster_musume_no_iru_nichijou": "魔物娘的相伴日常", "naked": "裸体", "naked_apron": "裸体围裙", "naked_cape": "裸体披风", "naked_ribbon": "裸体丝带", "neko": "猫", "nekomimi": "猫耳", "neon_genesis_evangelion": "新世纪福音战士", "nier_automata": "尼尔:自动人形", "nijisanji": "彩虹社", "ninja": "忍者", "nipple_slip": "露点", "nipples": "乳头", "no_bra": "无乳罩", "nopan": "无胖次", "nun": "修女", "nurse": "护士", "official_watermark": "官方水印", "onsen": "温泉", "open_shirt": "衬衫敞开", "ore_no_imouto_ga_konnani_kawaii_wake_ga_nai": "我的妹妹哪有这么可爱!", "overalls": "工装连衣裤", "overwatch": "守望先锋", "paizuri": "乳交", "pajama": "睡衣", "panties": "内裤", "pantsu": "胖次", "panty_pull": "胖次脱下", "pantyhose": "吊带袜", "parody": "仿拟/谐拟", "partial_scan": "局部扫描", "pasties": "乳贴", "pee": "尿尿", "penguin": "企鹅", "penis": "阴茎", "photo": "照片/现实背景", "photoshop": "PS 改图", "pirate": "海盗", "pointy_ears": "尖耳朵", "pokemon": "精灵宝可梦", "possible_duplicate": "可能重复", "pregnant": "孕妇", "pretty_cure": "光之美少女", "princess_connect": "公主连结", "princess_connect!_re:dive": "公主连结 Re:Dive", "profile_page": "角色资料页", "pubic_hair": "阴毛", "puella_magi_madoka_magica": "魔法少女小圆", "pussy": "阴户", "pussy_juice": "妹汁", "queen's_blade": "女王之刃", "raw_scan": "扫描原图", "re_zero_kara_hajimeru_isekai_seikatsu": "Re:从零开始的异世界生活", "robe": "长袍/礼服/睡袍", "saenai_heroine_no_sodatekata": "路人女主的养成方法", "sailor_moon": "美少女战士", "sake": "日本清酒", "sample": "样品图", "sarashi": "晒し/缠胸布", "school_swimsuit": "学校泳衣", "see_through": "透视", "seifuku": "制服", "selfie": "自拍", "senran_kagura": "闪乱神乐", "sex": "性交", "sheets": "床单", "shimapan": "条纹胖次", "shirt_lift": "衬衫掀起", "shota": "正太", "silhouette": "剪影/暗色轮廓/体形", "sketch": "素描", "skirt_lift": "裙摆掀起", "sling_bikini": "吊带比基尼", "smoking": "吸烟", "soccer": "足球", "sono_bisque_doll_wa_koi_wo_suru": "更衣人偶坠入爱河", "spy_x_family": "间谍过家家", "ssss.gridman": "SSSS.古立特", "stick_poster": "海报", "stockings": "长筒袜", "strike_witches": "强袭魔女", "string_panties": "细绳胖次", "summer_dress": "夏装", "suzumiya_haruhi_no_yuuutsu": "凉宫春日的忧郁", "sweater": "毛衣", "swimsuits": "泳衣", "sword": "刀剑", "sword_art_online": "刀剑神域", "symmetrical_docking": "乳乳相接", "tagme": "标签", "tail": "兽尾", "tan_lines": "日晒线", "tattoo": "文身", "tennis": "网球", "tentacles": "触手", "text": "文本", "the_idolm@ster": "偶像大师", "the_idolm@ster_cinderella_girls": "偶像大师灰姑娘女孩", "the_idolm@ster_million_live!": "偶像大师百万现场", "the_idolm@ster_shiny_colors": "偶像大师闪耀色彩", "thighhighs": "过膝袜", "thong": "丁字裤", "to_aru_kagaku_no_railgun": "某科学的超电磁炮", "to_aru_majutsu_no_index": "魔法禁书目录", "to_heart_(series)": "To Heart 系列", "to_heart_2": "To Heart 2", "to_love_ru": "出包王女", "to_love_ru_darkness": "出包王女 Darkness", "topless": "上身露出", "torn_clothes": "破衣", "touhou": "东方", "towel": "浴巾", "translated": "文字已翻译(英文)", "transparent_png": "背景透明", "trap": "伪娘", "tribadism": "磨豆腐/交叉体位", "tutorial": "教程", "uma_musume_pretty_derby": "赛马娘", "umbrella": "伞", "uncensored": "无码", "underboob": "南半球", "underwear": "内衣", "undressing": "脱衣", "uniform": "制服", "valentine": "情人节", "vibrator": "跳蛋", "wa_maid": "和风女仆", "waitress": "女侍", "wallpaper": "壁纸", "wardrobe_malfunction": "走光", "weapon": "武器", "wedding_dress": "婚纱", "wet": "湿身", "wet_clothes": "湿衣", "wings": "翅膀", "witch": "女巫", "xenoblade": "异度神剑", "xenoblade_chronicles_2": "异度神剑 2", "yahari_ore_no_seishun_lovecome_wa_machigatteiru.": "我的青春恋爱喜剧果然有问题", "yaoi": "蔷薇/男同", "yukata": "浴衣", "yuri": "百合", "zhanjianshaonv": "战舰少女" } ================================================ FILE: source/hotkey.js ================================================ export const initHotKey = function() { window.addEventListener("keyup", function(event) { console.log('keyup:', event.key) // 有输入框被激活时,禁止触发方向键。 if (/^(TEXTAREA|INPUT|SELECT|BUTTON)$/.test(document.activeElement.tagName)) return // 上一页 ← a A const prev = document.querySelector(".pagination>.previous_page") || jQuery("li:contains('Previous') a[href]")[0] if (prev && (event.key == "ArrowLeft" || event.key == "a" || event.key == "A")) { prev.click() return event.preventDefault() } // 下一页 → d D const next = document.querySelector(".pagination>.next_page") || jQuery("li:contains('Next') a[href]")[0] if (next && (event.key == "ArrowRight" || event.key === "d" || event.key == "D")) { next.click() return event.preventDefault() } // 显示 s S const show = document.querySelector("#png") || document.querySelector("#highres") if (show && (event.key === "s" || event.key === "S")) { show.click() return event.preventDefault() } // 来源 w W const where = jQuery("li:contains('Source:') a")[0] if (where && (event.key === "w" || event.key === "W")) { where.click() return event.preventDefault() } }) const sidebar = document.querySelector("#post-list > div.sidebar") || document.querySelector("#post-view > div.sidebar") if (sidebar) { sidebar.insertAdjacentHTML("beforeend", "
" + "
快捷键说明
" + "
上一页:A / ←
" + "
下一页:D / →
" + "
显示当前作品原图:S
" + "
显示当前作品来源:W
" + "
") } } ================================================ FILE: source/html/body.html ================================================
================================================ FILE: source/html/head.html ================================================ Yande.re 简体中文 ================================================ FILE: source/html/options.html ================================================
================================================ FILE: source/index.js ================================================ import { initStyle } from "./style" import { initHotKey } from "./hotkey" import { initOptions } from "./options" import { initTranslate } from "./translate" jQuery(document).ready(function() { initStyle() initHotKey() initOptions() initTranslate() if (document.cookie.includes('locale=zh_CN') === false) { document.cookie = "locale=zh_CN" location.href = location.href } }) ================================================ FILE: source/options.js ================================================ import { enterBrowseMode } from "./browse" export const onChangeLeftBar = function() { const value = Boolean(document.getElementById("showLeftBar").selectedIndex) localStorage.setItem("showLeftBar", JSON.stringify(value)) const element = document.querySelector("#post-list > .sidebar") element.setAttribute("show-left-bar", value) console.log("showLeftBar", value) } export const onChangeRatingE = function() { const value = Boolean(document.getElementById("showRatingE").selectedIndex) localStorage.setItem("showRatingE", JSON.stringify(value)) const elementList = document.querySelectorAll(".javascript-hide") elementList.forEach(element => element.setAttribute("show-rating-e", value)) console.log("showRatingE", value) } export const onChangeImageHD = function() { // 获取 index 值 const index = document.getElementById("showImageHD").selectedIndex // 修改属性 show-image-hd const elementList = document.querySelectorAll("#post-list-posts > li > .inner") elementList.forEach(element => element.setAttribute("show-image-hd", index)) // 缓存 index 值 localStorage.setItem("showImageHD", JSON.stringify(index)) console.log("showImageHD", index) // 设置网格布局宽 // document.querySelector("#post-list-posts").style.gridTemplateColumns = `repeat(auto-fill, ${(index + 1) * 150}px)` } // 网站域名 例如'https://oreno.imouto.us' const origin = window.location.origin let taskArray = [] // 'https://oreno.imouto.us'域名下每秒钟尝试加载的Sample图片数 let maxLoadingSampleNum = 4 let doLoadSampleUrl = () => { let loadingNum = 0 let loadSampleUrl = () => { if (taskArray.length == 0) return loadingNum++ let { element, sampleUrl } = taskArray.shift() element.onerror = () => { element.src = sampleUrl } element.onload = () => { loadingNum-- } element.src = sampleUrl } setInterval(() => { if (taskArray.length == 0) return let needloadNum = maxLoadingSampleNum - loadingNum while (needloadNum--) { loadSampleUrl() } }, 1000) } export const initOptions = function() { // https://yande.re/user/show/507475 if (/^\/user\/show\/[\d]{1,}/.test(location.pathname)) return if (document.getElementById("post-list-posts") === null) return // 插入文档元素 document.getElementById("post-list-posts").insertAdjacentHTML("beforebegin", `[{ path: "source/html/options.html" }]`) // 替换图片元素 const imageList = document.querySelectorAll("img.preview") const samples = JSON.parse(localStorage.getItem("sample_urls")) imageList.forEach(element => { if (/\/post\/show\/([\d]{1,})/.test(element.nextElementSibling.innerText)) { const id = RegExp.$1 const sampleUrl = samples[id] if (sampleUrl !== undefined) { element.src = sampleUrl } } }) doLoadSampleUrl() // 监听 document.getElementById("showLeftBar").addEventListener("change", onChangeLeftBar) document.getElementById("showRatingE").addEventListener("change", onChangeRatingE) document.getElementById("showImageHD").addEventListener("change", onChangeImageHD) // 获取本地记录 const showLeftBar = JSON.parse(localStorage.getItem("showLeftBar") || "true") const showRatingE = JSON.parse(localStorage.getItem("showRatingE") || "true") const showImageHD = JSON.parse(localStorage.getItem("showImageHD") || "0") document.getElementById("showLeftBar").selectedIndex = showLeftBar document.getElementById("showRatingE").selectedIndex = showRatingE document.getElementById("showImageHD").selectedIndex = showImageHD onChangeLeftBar() onChangeRatingE() onChangeImageHD() // 浏览模式 document.getElementById("enterBrowseMode").addEventListener("click", enterBrowseMode) } ================================================ FILE: source/post.js ================================================ /* approver_id: null change: 4106973 frames: [] frames_pending: [] frames_pending_string: "" frames_string: "" has_children: false is_held: false is_note_locked: false is_pending: false is_rating_locked: false is_shown_in_index: true last_commented_at: 0 last_noted_at: 0 parent_id: null status: "active" */ export default class Post { constructor(data) { if (typeof data !== "object") data = {} this.id = data.id || 0 this.score = data.score || 0 this.tags = data.tags || "" this.source = data.source || "" this.author = data.author || "" this.creatorId = data.creator_id || 0 this.createdAt = data.created_at || 0 this.updatedAt = data.updated_at || 0 this.rating = data.rating || "s" // 文件 this.fileUrl = data.file_url || "" this.fileExt = data.file_ext || "" this.fileSize = data.file_size || 0 this.width = data.width || 0 this.height = data.height || 0 // 高清图 this.jpegUrl = data.jpeg_url || "" this.jpegSize = data.jpeg_file_size || 0 this.jpegWidth = data.jpeg_width || 0 this.jpegHeight = data.jpeg_height || 0 // 缩略图 this.sampleUrl = data.sample_url this.sampleSize = data.sample_file_size || 0 this.sampleWidth = data.sample_width || 0 this.sampleHeight = data.sample_height || 0 // 预览图 用作懒加载的占位图 this.previewUrl = data.preview_url this.previewWidth = data.actual_preview_width || 0 this.previewHeight = data.actual_preview_height || 0 this.favorite = false } // 全年龄 safe get isRatingS() { return this.rating === "s" } // 擦边球 questionable get isRatingQ() { return this.rating === "q" } // 成人向 explicit get isRatingE() { return this.rating === "e" } // 长宽比 get aspectRatio() { return this.width / this.height } getSizeText(size) { if (size > 1024 * 1024) { return (size / (1024 * 1024)).toFixed(2) + "MB" } if (size > 1024) { return (size / 1024).toFixed(2) + "KB" } return (size).toFixed(2) + "B" } get sampleSizeText() { return this.getSizeText(this.sampleSize) } get sampleDownloadText() { return `下载缩略图 ${this.sampleWidth}×${this.sampleHeight} [${this.sampleSizeText}]` } get sampleDownloadName() { return `${location.hostname}.${this.id}.${this.sampleWidth}x${this.sampleHeight}`.replace(/\./g, "_") } get jpegSizeText() { return this.getSizeText(this.jpegSize) } get jpegDownloadText() { return `下载高清图 ${this.jpegWidth}×${this.jpegHeight} [${this.jpegSizeText}]` } get jpegDownloadName() { return `${location.hostname}.${this.id}.${this.jpegWidth}x${this.jpegHeight}`.replace(/\./g, "_") } get fileSizeText() { return this.getSizeText(this.fileSize) } get fileDownloadText() { return `下载原文件 ${this.width}×${this.height} [${this.fileSizeText}] ${this.fileExt.toUpperCase()}` } get fileDownloadName() { return `${location.hostname}.${this.id}.${this.width}x${this.height}`.replace(/\./g, "_") } get createdTime() { const date = new Date(this.createdAt * 1000) return `${date.toLocaleDateString()} ${date.toLocaleTimeString("en-DE")}` } get updatedTime() { const date = new Date(this.updatedAt * 1000) return `${date.toLocaleDateString()} ${date.toLocaleTimeString("en-DE")}` } // 解析图源 i.pximg.net => pixiv.net // https://i.pximg.net/img-original/img/2021/05/15/09/41/42/89846615_p0.jpg // https://www.pixiv.net/artworks/89846615 get sourceUrl() { if (/^https:\/\/i\.pximg\.net\/img-original\/img\/[\d\/]{19}\/([\d]{1,})_p[\d]{1,}\.(jpg|png)$/.test(this.source)) { const pid = RegExp.$1 return `https://pixiv.net/artworks/${pid}` } return this.source } } ================================================ FILE: source/style/fix.css ================================================ body { font-size: 12px; padding: 0 0.5rem; } body::-webkit-scrollbar { display: none; width: 0px !important; } /* 标题居中 */ div#header { margin: 0; } div#header > div#title { display: flex; place-content: center; margin: 0 !important; height: fit-content; } div#header > div#title > h2#site-title { display: flex !important; flex-direction: column; } div#header > div#title > h2#site-title > span { font-size: 12px; font-weight: normal; text-align: right; } div#header > div#main-menu { padding: 0 !important; margin: 0 !important; display: flex !important; justify-content: center; font-size: 14px; line-height: 2rem; height: 2rem; } div#header > div#main-menu > ul { margin: 0; } /* 通知 */ .status-notice { text-align: center; } /* 标签前缀 */ li.tag-type-artist a[href^="/post"]:not(.no-browser-link)::before { content: "[画师]"; } li.tag-type-copyright a[href^="/post"]:not(.no-browser-link)::before { content: "[原作]"; } li.tag-type-character a[href^="/post"]:not(.no-browser-link)::before { content: "[角色]"; } li.tag-type-circle a[href^="/post"]:not(.no-browser-link)::before { content: "[公司]"; } /* 图区 */ #post-list { display: flex; flex-direction: row; } #post-list > .sidebar { width: auto; max-width: 200px; flex: 0 0 auto; } #post-list > .content { width: auto; flex: 1 1 auto; } #post-list > div.lsidebar { display: none; } ul#post-list-posts { display: flex; flex-wrap: wrap; gap: 0.5rem; place-content: center; place-items: center; } ul#post-list-posts > li { width: fit-content !important; height: 100%; margin: 0 !important; border: none; } ul#post-list-posts > li > div.inner { width: auto !important; height: fit-content !important; } ul#post-list-posts > li > a.directlink { font-size: 12px; height: 12px; line-height: 12px; margin: 0; padding: 0; overflow: hidden; background: rgb(16, 16, 16); } ul#post-list-posts > li > a.directlink > span.directlink-res { display: inline; } ul#post-list-posts > li > a.directlink > span.directlink-info { display: none; } ul#post-list-posts > li > div.inner > a.thumb { height: auto; } ul#post-list-posts > li > div.inner > a.thumb > img.preview { margin: 0 !important; /* @konachan */ border: none; } /* 分页器 */ div#paginator { padding: 0; } div#paginator > div.pagination { line-height: 2rem; } /* 页脚 */ #content > div:nth-child(2) > div.sidebar { display: none; } #content div.footer { font-size: 14px; margin: 1rem; } /* show-left-bar */ .sidebar[show-left-bar=false] { display: none !important; } /* show-rating-e */ .javascript-hide[show-rating-e=true] { display: block !important; position: relative; } .javascript-hide[show-rating-e=true]::after { content: ""; position: absolute; width: 100%; height: 100%; top: 0; box-shadow: 0px 0px 12px rgb(255, 0, 0) inset; pointer-events: none; } /* show-image-hd */ #post-list-posts > li > .inner[show-image-hd="1"] { zoom: 2; } #post-list-posts > li > .inner[show-image-hd="2"] { zoom: 3; } #post-list-posts > li > .inner[show-image-hd="3"] { zoom: 4; } ================================================ FILE: source/style.js ================================================ export const initStyle = function() { document.head.insertAdjacentHTML("beforeend", ``) } ================================================ FILE: source/translate.js ================================================ const tags = [{ path: "source/data/tags.json" }] const menus = [{ path: "source/data/menus.json" }] const footers = [{ path: "source/data/footers.json" }] // 翻译标签 export const translateTags = function() { const elementList = Array.from(document.getElementsByTagName("a")) elementList.forEach(element => { const href = element.getAttribute("href") if (typeof href === "string" && /^\/post\?tags=(\S+)$/.test(href)) { const en = RegExp.$1 const cn = tags[en] if (cn) { element.innerText = `[${cn}]${en.replace(/_/g, " ")}` } } }) } // 翻译菜单 export const translateMenus = function() { const mainMenuList = Array.from(document.querySelectorAll("#main-menu>ul>li>a")) const subMenuList = Array.from(document.querySelectorAll("ul.submenu>li>a")) const elementList = [...mainMenuList, ...subMenuList] elementList.forEach(element => { if (element.getAttribute("href") === "#") return const en = element.innerText const cn = menus[en] if (cn) { element.innerText = cn } }) } // 翻译提示 export const translateNotice = function() { // EN: This image has been resized. Click on the `View larger version` link in the sidebar // for a high-quality version. Hide this message // CN: 这张图片已经被压缩,单击侧边栏中的 `显示高清图` 可以获取更高质量的版本。不再提醒 // EN: This post belongs to a parent post. // EN: This post has child posts. (post #728160, 746235) // EN: This post has a child post. (post #383703) const elementList = Array.from(document.querySelectorAll(".status-notice")) elementList.forEach(element => { console.log(element.innerHTML) element.innerHTML = element.innerHTML .replace(/^[\s]+This image has been resized. Click on the /, "这张图片已经被压缩,单击侧边栏中的") .replace(/View larger version/, "显示高清图") .replace(/ link in the sidebar for a high-quality version./, "可以获取更高质量的版本。") .replace(/Hide this message<\/a>\./, "不再提醒") /* 相关父作品 */ .replace(/This post belongs to a /, "这张图片从属于一个") .replace(/parent post<\/a>\./, "相关父作品。") /* 相关子作品 */ .replace(/This post has /, "这张图片从属于一个") .replace(/child posts<\/a>\. \(post #/, "作品集。相关子作品:") .replace(/a child post<\/a>\. \(post #/, "作品集。相关子作品:") .replace(/<\/a>, | \)/, "") }) } // 翻译点击 export const translateButtons = function() { [ ['#highres-show', 'View larger version', '显示高清图'], ['#highres', 'Download larger version', '下载高清图'], ['#png', 'Download PNG', '下载 PNG 图'], ['li#add-to-favs>a', 'Add to favorites', '添加收藏'], ['li#set-avatar>a', 'Set avatar', '设置头像'], ['h4>a.js-posts-show-edit-tab', 'Edit', '编辑'], ['h4>a.js-posts-show-comments-tab', 'Respond', '评论'], ['.pagination>.previous_page', '← Previous', '上一页'], ['.pagination>.next_page', 'Next →', '下一页'], ].forEach(data => { const [selector, en, cn] = data const element = document.querySelector(selector) if (element) { element.innerText = element.innerText.replace(en, cn) } }) } // 翻译页脚 export const translateFooters = function() { const elementList = Array.from(document.querySelectorAll('#subnavbar>li>a')) elementList.forEach(element => { const en = element.innerText const cn = footers[en] if (cn) { element.innerText = cn } }) } // 合并 export const initTranslate = function() { translateTags() translateMenus() translateNotice() translateButtons() translateFooters() } ================================================ FILE: temp/css_text_loader.js ================================================ module.exports = function(source) { return 'export default ' + JSON.stringify(source) } ================================================ FILE: temp/index.js ================================================ /** * 默认显示隐藏的作品,并提供可开关的选项。 */ ;(function() { const SET_JS_HIDE = 'set-javascript-hide' const INPUT_HTML = ` ` const target = document.getElementById('post-list-posts') if (target && target.parentNode) { const div = document.createElement('div') target.parentNode.insertBefore(div, target) div.innerHTML = INPUT_HTML // div.setAttribute('style', 'user-select: none; text-align: left;') div.setAttribute('id', 'script-addition') } else return const checkbox = document.getElementById(SET_JS_HIDE) checkbox.checked = JSON.parse(localStorage.getItem(SET_JS_HIDE)) update() checkbox.addEventListener('change', update) function update() { Array.from(document.querySelectorAll('.javascript-hide')).forEach(element => { if (checkbox.checked) element.removeClassName(SET_JS_HIDE) else element.addClassName(SET_JS_HIDE) }) localStorage.setItem(SET_JS_HIDE, checkbox.checked) } })() /** * 默认尺寸 h=150px * 大图模式 h=300px * 高清模式 显示高清大图 parent */ ;(function() { const target = document.getElementById(`script-addition`) if (target) { // const SET_POST_HD = `set-post-hd` // const isHD = Boolean(JSON.parse(localStorage.getItem(SET_POST_HD))) const SET_POST_SIZE = `set-post-size` if (getHDValue() === false) { target.insertAdjacentHTML(`beforeend`, ``) target.insertAdjacentHTML(`beforeend`, ``) target.insertAdjacentHTML(`beforeend`, ``) target.insertAdjacentHTML(`beforeend`, ``) } target.insertAdjacentHTML(`beforeend`, ``) target.insertAdjacentHTML(`beforeend`, ``) const checkbox1 = document.getElementById(`${SET_POST_SIZE}1`) const checkbox2 = document.getElementById(`${SET_POST_SIZE}2`) const checkbox3 = document.getElementById(`${SET_POST_SIZE}3`) update() checkbox1 && checkbox1.addEventListener(`change`, changeValue) checkbox2 && checkbox2.addEventListener(`change`, changeValue) checkbox3.addEventListener(`change`, changeHDValue) function getValue() { return Boolean(JSON.parse(localStorage.getItem(SET_POST_SIZE))) } function changeValue() { const value = getValue() localStorage.setItem(SET_POST_SIZE, JSON.stringify(!value)) location.reload() // 刷新页面 } function getHDValue() { return Boolean(JSON.parse(localStorage.getItem(`set-post-hd`))) } function changeHDValue() { const value = getHDValue() localStorage.setItem(`set-post-hd`, JSON.stringify(!value)) location.reload() // 刷新页面 } function update() { if (getHDValue() === true) { checkbox3.checked = true target.insertAdjacentHTML(`beforeend`, `
高清模式加载缓慢,可能需要等待一段时间。
`) // setTimeout(() => document.getElementById(`script-notice-hd`).remove(), 10000) const imageList = document.querySelectorAll(`img.preview`) imageList.forEach(x => x.src = x.parentNode.parentNode.nextElementSibling.href) document.head.insertAdjacentHTML(`beforeend`, ``) } else if (getValue() === false) { checkbox1.checked = true checkbox2.checked = false checkbox3.checked = false } else { checkbox1.checked = false checkbox2.checked = true checkbox3.checked = false document.head.insertAdjacentHTML(`beforeend`, ``) document.head.insertAdjacentHTML(`beforeend`, ``) } } } })() ================================================ FILE: temp/index.user.js ================================================ // ==UserScript== // @name Yande.re 简体中文 // @namespace com.coderzhaoziwei.yandere // @version 1.0.10 // @author Coder Zhao // @description Y 站简体中文补丁| 显示隐藏作品 | 高清大图模式 | 界面布局优化 | 方向键翻页 | Simplified Chinese patch for Yande.re // @modified 2021/5/7 01:12:31 // @license MIT // @homepage https://greasyfork.org/zh-CN/scripts/421970 // @match https://yande.re/* // @exclude https://yande.re/forum/* // @match https://yande.in/* // @match https://oreno.imouto.us/* // @match https://konachan.com/* // @supportURL https://github.com/coderzhaoziwei/yande-re-chinese-patch/issues // @grant none // ==/UserScript== /******/ (() => { // webpackBootstrap /******/ "use strict"; /******/ var __webpack_modules__ = ({ /***/ 798: /***/ (() => { ;// CONCATENATED MODULE: ./tags.json const tags_namespaceObject = JSON.parse('{"anal":"肛交","angel":"天使","animal_ears":"兽耳","anus":"肛门露出","areola":"乳晕","armor":"盔甲/装甲","artist_revision":"画师修改","ass":"臀部","ass_grab":"持股/捏臀","bandages":"绷带","bathing":"沐浴","bikini":"比基尼","bikini_armor":"比基尼装甲/轻薄盔甲","bikini_top":"比基尼乳罩","blood":"血腥","bloomers":"灯笼裤/宽松短裤","bodysuit":"紧身衣裤","bondage":"束缚","bottomless":"下身露出","bra":"乳罩","breast_grab":"握乳","breast_hold":"托乳","breasts":"乳","bunny_ears":"兔耳","bunny_girl":"兔女郎","buruma":"运动短裤","calendar":"日历","cameltoe":"阴户凸显","censored":"有码","cheerleader":"啦啦队队员","chibi":"Q版","chinadress":"旗袍","christmas":"圣诞","cleavage":"乳沟","cream":"奶油","crossdress":"变装","cum":"精液","dakimakura":"抱枕","digital_version":"数字版","dildo":"假阳具","disc_cover":"光盘封面","dress":"连衣裙","dress_shirt":"衬衫","elf":"精灵","erect_nipples":"乳尖","extreme_content":"极端","eyepatch":"眼罩","feet":"足","fellatio":"口交","fishnets":"鱼网袜","fixed":"修改","footjob":"足交","futanari":"扶她","game_cg":"游戏CG","gangbang":"乱交","garter":"袜带","garter_belt":"吊袜腰带","guitar":"吉他","gun":"枪炮","guro":"猎奇","halloween":"万圣节前夜","handjob":"打手枪","headphones":"耳机","heels":"高跟鞋","heterochromia":"虹膜异色","horns":"角","japanese_clothes":"日式服装","kimono":"和服","kitsune":"狐狸","landscape":"风景画","leotard":"紧身连衣裤","lingerie":"贴身内衣","loli":"萝莉","lolita_fashion":"洛丽塔","maid":"女仆","male":"男性","masturbation":"自摸/手淫","mecha":"机甲","megane":"眼镜","miko":"巫女","monochrome":"单色","naked":"裸体","naked_apron":"裸体围裙","naked_cape":"裸体披风","neko":"猫","nekomimi":"猫耳","nipples":"乳头","no_bra":"无乳罩","nopan":"无胖次","nurse":"护士","onsen":"温泉","open_shirt":"衬衫敞开","paizuri":"乳交","pajama":"睡衣","pantsu":"胖次","panty_pull":"胖次脱下","pantyhose":"吊带袜","partial_scan":"局部扫描","penis":"阴茎","pointy_ears":"尖耳朵","pubic_hair":"阴毛","pussy":"阴户","pussy_juice":"妹汁","school_swimsuit":"学校泳衣","see_through":"透视","seifuku":"制服","sex":"性交","sheets":"床单","shimapan":"条纹胖次","shirt_lift":"衬衫掀起","shota":"正太","sketch":"素描","skirt_lift":"裙摆掀起","stockings":"长筒袜","string_panties":"细绳胖次","sweater":"毛衣","swimsuits":"泳衣","sword":"刀剑","symmetrical_docking":"乳乳相接","tagme":"标签","tail":"兽尾","tan_lines":"日晒线","tattoo":"文身","tentacles":"触手","text":"文本","thighhighs":"过膝袜","thong":"丁字裤","topless":"上身露出","torn_clothes":"破衣","towel":"浴巾","transparent_png":"背景透明","trap":"伪娘","umbrella":"伞","uncensored":"无码","underboob":"南半球/下乳露出","undressing":"脱衣","uniform":"制服","vibrator":"跳蛋","waitress":"女侍","wallpaper":"壁纸","weapon":"武器","wedding_dress":"婚纱","wet":"湿身","wet_clothes":"湿衣","wings":"翅膀","witch":"女巫","yaoi":"蔷薇/男同","yukata":"浴衣","yuri":"百合"}'); ;// CONCATENATED MODULE: ./style.css /* harmony default export */ const style = ("/* 标签前缀 */\nli.tag-type-artist a:nth-child(4)::before {\n content: '[画师]';\n}\nli.tag-type-copyright a:nth-child(4)::before {\n content: '[原作]';\n}\nli.tag-type-character a:nth-child(4)::before {\n content: '[角色]';\n}\nli.tag-type-circle a:nth-child(4)::before {\n content: '[公司]';\n}\n\n/* 字体大小 */\nbody {\n font-size: 12px;\n padding: 12px 4px;\n}\n\n/* 标题居中 */\n#title {\n display: flex;\n justify-content: center;\n margin: 0 0 0 0 !important;\n}\n#site-title {\n display: flex !important;\n}\n#main-menu {\n padding: 0 !important;\n margin: 0 !important;\n display: flex !important;\n justify-content: center;\n}\n\n/* 通知居中 */\n.status-notice {\n text-align: center;\n}\n\n/* 图片区域 */\n#post-list {\n display: flex;\n flex-direction: row;\n}\n#post-list > .sidebar {\n width: auto;\n max-width: 200px;\n flex: 0 0 auto;\n}\n#post-list > .content {\n width: auto;\n flex: 0 1 auto;\n}\n\n#post-list-posts {\n display: flex !important;\n flex-wrap: wrap;\n justify-content: center;\n}\n#post-list-posts > li {\n width: auto !important;\n height: auto !important;\n margin: 0 8px 8px 0 !important; /* 图片区域间距 */\n border: 1px solid rgba(0, 0, 0, 0);\n}\n#post-list-posts > li.javascript-hide:not(.set-javascript-hide) {\n display: block !important;\n position: relative;\n}\n#post-list-posts > li.javascript-hide::after {\n content: \"\";\n position: absolute;\n width: 100%;\n height: 100%;\n top: 0;\n box-shadow: 0px 0px 12px rgb(255, 0, 0) inset;\n pointer-events: none;\n}\n#post-list-posts > li > .inner {\n width: auto !important;\n height: 150px !important;\n display: flex;\n align-items: center;\n}\n#post-list-posts > li > .inner > .thumb {\n height: auto;\n}\n#post-list-posts > li > .largeimg.directlink {\n height: 12px;\n font-size: 12px;\n line-height: 12px;\n padding: 0;\n margin: 2px 0 0 0;\n overflow: hidden;\n}\n\n/* 脚本添加的内容 */\n#script-addition {\n user-select: none;\n /* text-align: right; */\n /* font-weight: 100; */\n padding: 8px 12px;\n display: flex;\n}\n#script-addition > input {\n margin-left: 1em;\n}\n#script-notice-hd {\n margin-left: 0.5em;\n font-weight: bold;\n color: #ee8887;\n}\n\n/* 隐藏浏览器默认显示的滚动条 */\nbody::-webkit-scrollbar {\n display: none;\n width: 0px !important;\n}\n"); ;// CONCATENATED MODULE: ./index.js (function() { const list = Array.from(document.getElementsByTagName('a')) list.forEach(a => { const href = a.getAttribute('href') if (typeof href === 'string' && /^\/post\?tags=(\S+)$/.test(href)) { const en = RegExp.$1 const cn = tags_namespaceObject[en] if (cn) a.innerText = `[${cn}]${en.replace(/_/g, ' ')}` } }) })() /** * 创建 `) } else if (getValue() === false) { checkbox1.checked = true checkbox2.checked = false checkbox3.checked = false } else { checkbox1.checked = false checkbox2.checked = true checkbox3.checked = false document.head.insertAdjacentHTML(`beforeend`, ``) document.head.insertAdjacentHTML(`beforeend`, ``) } } } })() /***/ }) /******/ }); /************************************************************************/ /******/ /******/ // startup /******/ // Load entry module and return exports /******/ // This entry module doesn't tell about it's top-level declarations so it can't be inlined /******/ var __webpack_exports__ = {}; /******/ __webpack_modules__[798](); /******/ /******/ })() ; ================================================ FILE: temp/webpack.config.js ================================================ const path = require('path') const config = require('./package.json') const { BannerPlugin } = require('webpack') const banner = ` ` module.exports = { mode: 'production', entry: './index.js', output: { path: path.resolve(__dirname, ''), filename: `index.user.js`, }, module: { rules: [ { test: /\.css$/, use: './css_text_loader.js' }, ], }, plugins: [ new BannerPlugin({ banner, raw: true, entryOnly: true }), ], optimization: { minimize: true, minimizer: [], }, }