// ==UserScript==
// @name 冲浪助手
// @namespace https://github.com/Shadow-blank/net-tools
// @version 0.1.4
// @description 你是GG还是MM啊
// @author Shadow-blank
// @match *://m.weibo.cn/status/*
// @match *://www.comicat.org/*
// @match *://*.comicat.net/*
// @match *://dick.xfani.com/*
// @match *://live.bilibili.com/*
// @match *://www.bilibili.com/*
// @match *://ngabbs.com/read.php*
// @match *://nga.178.com/read.php*
// @match *://bbs.nga.cn/read.php*
// @icon https://raw.githubusercontent.com/Shadow-blank/net-tools/main/favicon.ico
// @require https://cdn.staticfile.org/jquery/3.4.0/jquery.min.js
// @grant GM_registerMenuCommand
// @grant GM_xmlhttpRequest
// @connect img.nga.178.com
// @license MIT
// ==/UserScript==
(function () {
'use strict'
// 创建style标签
const createStyle = str => $('body').append(`<style>${str}</style>`)
let data = null
const defaultCheck = ['m.weibo.cn', 'comicat.net', 'dick.xfani.com', 'live.bilibili.com', 'www.bilibili.com', 'nga']
const download = (blob, filename) => {
const blobUrl = typeof blob === 'string' ? blob : window.URL.createObjectURL(blob);
// 这里的文件名根据实际情况从响应头或者url里获取
const a = document.createElement('a');
a.href = blobUrl;
a.target = 'block'
a.download = filename;
a.click();
window.URL.revokeObjectURL(blobUrl);
}
const module = {
weibo: {
name: '微博',
children: [
{
key: 'm.weibo.cn',
name: '移动端页面自动跳转到PC页面 屏幕宽度小于980不跳转',
run() {
if (window.screen.width > 980) {
setTimeout(() => {
const userId = document.querySelector('.m-img-box').href.split('/')[4]
const id = location.pathname.split('/')[2]
location.href = `https://weibo.com/${userId}/${id}`
}, 500)
}
}
}
]
},
comicat: {
name: '漫猫',
children: [
{
key: 'comicat.net',
name: '下载按钮位置上升',
run() {
if (location.pathname === '/') {
createStyle(`
table tbody tr td:nth-child(6) {cursor: pointer; user-select: none;}
table tbody tr td:nth-child(6) span:after {
content: url("data:image/svg+xml,%3csvg viewBox='0 0 1024 1024' xmlns='http://www.w3.org/2000/svg' width='13' height='13' %3e%3cpath d='M160 832h704a32 32 0 1 1 0 64H160a32 32 0 1 1 0-64zm384-253.696 236.288-236.352 45.248 45.248L508.8 704 192 387.2l45.248-45.248L480 584.704V128h64v450.304z'/%3e%3c/svg%3e");
display: inline-block;margin-left: 10px; color: #606266;
}`)
document.querySelector('#data_list').addEventListener('click', e => {
if (e.target.classList.value.includes('btl')) {
const magnet = /show-([a-z0-9]+)/.exec(e.target.parentElement.parentElement.innerHTML)[1]
const href = `magnet:?xt=urn:btih:${magnet}&tr=http://open.acgtracker.com:1096/announce`
download(href)
}
})
} else {
const downEle = document.querySelector('#box_download')
downEle.parentElement.insertBefore(downEle, downEle.parentElement.firstElementChild)
}
}
}
]
},
xfani: {
name: '稀饭',
children: [
{
key: 'dick.xfani.com',
name: '取消右键限制',
run() {
['contextmenu', 'click', 'mousedown', 'mouseup', 'selectstart'].forEach(item => clearEvent(item))
function clearEvent(type) {
const onType = 'on' + type
document[onType] = null
document.body && (document.body[onType] = null)
}
}
}
]
},
bilibili: {
name: 'B站',
children: [
{
key: 'www.bilibili.com',
name: '去除首页轮播图 只保留番剧和推荐',
run() {
createStyle(`
.bili-layout > .bili-grid, .recommended-swipe, .eva-banner{display: none!important;}
.bili-video-card.is-rcmd, .bili-layout > .bili-grid:nth-child(9), .bili-layout >.bili-grid:nth-child(1) {display: block!important;} `)
}
},
{
key: 'live.bilibili.com',
name: '直播页面去掉广告 只显示直播',
run() {
let timer = ''
// 推广直播页面进入原始页面
timer = setInterval(() => {
const iframe = document.querySelector('#app iframe')
if (iframe?.src.includes('/blanc/')) {
clearInterval(timer)
location.href = iframe.src.split('?')[0]
}
}, 100)
// 将直播画面居中显示
createStyle(`
.live-room-app.p-relative{overflow: hidden}
.main {padding: 0px}
.player-ctnr.left-container.p-relative.z-player-ctnr{ width: 100%; margin: 10px auto}
.live-room-app .app-content{padding-top: 0;}
.live-room-app .app-content .app-body {width: 100%;}
#iframe-popup-area, #aside-area-vm, #head-info-vm, #gift-control-vm, #sections-vm, #link-footer-vm, #sidebar-vm, #room-ssr-vm {display:none}
section{margin: 0}`)
}
}
]
},
nga: {
name: 'NGA',
children: [
{
key: 'nga',
name: '保存此贴和下载全部图片',
run() {
const tid = Object.fromEntries(new URLSearchParams(location.search)).tid
if (!tid) return
clearAdv()
let href = location.href
// 当前页数
let currentPage = 0
if (!href.includes('currentPage')) href += `&page=${1}`
// 总页数 拿下一页时可能会更新
let totalPage = undefined
// 上一页的最后一条 防止抽楼重复
// let lastTotal = undefined
$('#m_nav .nav .nav_link:nth-of-type(2)').after(`<a class= "nav_link" id="downAllImage" href = "javascript:void(0)"> 图片批量下载 </a>`, )
$('#downAllImage').click(() => {
$('body').append(`<script src="https://cdn.staticfile.org/jszip/3.10.1/jszip.min.js"></script>`)
getDocument(getImage)
})
function getDocument(cb) {
// 避免从第二页开始获取
currentPage++
$.get(href.replace(/page=[0-9]+/g, `page=${currentPage}`), cb)
}
function parseDocument(str) {
// if (currentPage < totalPage) {
// getDocument(currentPage + 1)
// }
}
function isLastPage(str) {
// 最后一页 只有currentPage - 1 没有currentPage + 1
return !str.includes(`page=${currentPage + 1}`)
}
function getImage(str) {
const imgList = [...new Set([...str.matchAll(/\[img\]\.([^\[]+)[/img]/g)].map(item => `${item[1]}g`))]
imgList.forEach(url => GM_xmlhttpRequest({
method: 'GET',
responseType: 'blob',
url:`https://img.nga.178.com/attachments${url}`,
onload(e){
download(e.response, url)
}
}))
!isLastPage(str) && getDocument(getImage)
}
function clearAdv() {
setTimeout(()=> {
document.querySelectorAll('img[onload^="__INSECTOB"]').forEach(item => {
item.parentElement.parentElement.remove()
})
}, 200)
}
}
}
]
}
}
// 初始化控制面板
const controlPanel = {
status: 0, //0 不存在 1存在
show() {
$('#control-panel').css('display', 'block')
},
hidden() {
$('#control-panel').css('display', 'none')
},
save() {
}
}
// GM_registerMenuCommand('控制面板', initControlPanel)
const initData = () => {
let value = JSON.parse(localStorage.getItem('netToolsData'))
if (!value) {
value = {
checked: getDefaultCheckValue()
}
}
data = value
function getDefaultCheckValue() {
const result = []
for (const key in module) {
const children = module[key].children.map(item => ({
name: item.name,
key: item.key
}))
const value = defaultCheck.filter(value => children.find(item => item.key === value))
result.push({
name: module[key].name,
key,
children,
value
})
}
return result
}
}
function initModule() {
const host = location.host
data.checked.forEach(item => {
const {children, value, key} = item
if (host.includes(key) && value.length) {
const currData = children.find(item => host.includes(item.key) && value.includes(item.key)) || {}
const currentModule = module[key].children.find(item => item.key === currData.key)
return currentModule && currentModule.run()
}
})
}
function initControlPanel() {
const control = createControlPanel()
const style = `
<style>
#control-panel{
display: none;
padding: 15px;
position: fixed;
top: 50%;
left: 50%;
background: white;
transform: translate(-50%, -50%);
z-index: 99999;
box-shadow: 0px 0px 12px rgba(0, 0, 0, .12);
border-radius: 4px;
color: #606266
}
.control-panel-save{
margin-top: 20px;
float: right;
line-height: 1;
height: 32px;
cursor: pointer;
color: #ffffff;
text-align: center;
box-sizing: border-box;
outline: none;
transition: .1s;
font-weight: 500;
user-select: none;
background-color: #409eff;
border: 1px solid #409eff;
padding: 8px 15px;
font-size: 14px;
border-radius: 4px;
}
.control-panel-save:hover{
border-color: #79bbff;
background-color: #79bbff;
}
.control-panel-close{
width: 21px;
cursor: pointer;
float: right;
color: #606266;
}
.el-checkbox {
color: #606266;
font-weight: 500;
font-size: 14px;
position: relative;
cursor: pointer;
display: inline-flex;
align-items: center;
white-space: nowrap;
user-select: none;
margin-right: 30px;
height: 32px;
}
.el-checkbox__input {
white-space: nowrap;
cursor: pointer;
outline: none;
display: inline-flex;
position: relative;
}
.el-checkbox__original {
opacity: 0;
outline: none;
position: absolute;
margin: 0;
width: 0;
height: 0;
z-index: -1;
}
.el-checkbox__input.is-checked .el-checkbox__inner {
background-color: #409eff;
border-color: #409eff;
}
.el-checkbox__inner {
display: inline-block;
position: relative;
border: 1px solid #dcdfe6;
border-radius: 2px;
box-sizing: border-box;
width: 14px;
height: 14px;
z-index: 1;
transition: border-color .25s cubic-bezier(.71,-.46,.29,1.46),background-color .25s cubic-bezier(.71,-.46,.29,1.46),outline .25s cubic-bezier(.71,-.46,.29,1.46);
}
.el-checkbox__input.is-checked .el-checkbox__inner:after {
transform: rotate(45deg) scaleY(1);
}
.el-checkbox__inner:after {
box-sizing: content-box;
content: "";
border: 1px solid #fff;
border-left: 0;
border-top: 0;
height: 7px;
left: 4px;
position: absolute;
top: 1px;
transform: rotate(45deg) scaleY(0);
width: 3px;
transition: transform .15s ease-in .05s;
transform-origin: center;
}
.el-checkbox__input.is-checked+.el-checkbox__label {
color: #409eff;
}
.el-checkbox__label {
display: inline-block;
padding-left: 8px;
line-height: 1;
font-size: 14px;
}
</style>`
$('body').append(control).append(style)
$('.control-panel-close').click(controlPanel.hidden)
$('.control-panel-save').click(controlPanel.save)
$('#control-panel .select-wrap').click(e => {
const ele = e.originalEvent.target
if (ele.type === 'checkbox') {
ele.parentElement.classList.toggle('is-checked')
ele.parentElement.nextElementSibling.classList.toggle('is-checked')
const labelClassList = ele.parentElement.parentElement.classList
for (let i = 0; i < data.checked.length; i++) {
const selectKey = ele.dataset.key
const {key, value} = data.checked[i]
if (selectKey.includes(key)) {
const index = value.findIndex(item => item === selectKey)
if (index > 0) {
value.splice(index, 1)
labelClassList.remove('is-checked')
} else {
value.push(selectKey)
// if (value.length === length) {
// labelClassList.add('is-checked')
// }
}
}
}
}
})
controlPanel.status = 1
controlPanel.show()
function createControlPanel() {
const menu = []
for (const key in module) {
menu.push({
name: module[key].name,
key: key,
children: module[key].children.map(item => ({name: item.name, key: item.key}))
})
}
return (`
<div id="control-panel">
<div class="control-panel-close">
<svg viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor" d="M764.288 214.592 512 466.88 259.712 214.592a31.936 31.936 0 0 0-45.12 45.12L466.752 512 214.528 764.224a31.936 31.936 0 1 0 45.12 45.184L512 557.184l252.288 252.288a31.936 31.936 0 0 0 45.12-45.12L557.12 512.064l252.288-252.352a31.936 31.936 0 1 0-45.12-45.184z"></path>
</svg>
</div>
<div class="select-wrap">
${createSelect()}
</div>
<div class="control-panel-save">保存</div>
</div>
`)
}
function createSelect() {
return data.checked.map(item =>
`<div>
${getCheck(item, item.value.length === item.children.length, 0)}
</br>
${item.children.map(_item => getCheck(_item, item.value.includes(_item.key), 1)).join('')}
</div>`
).join('')
}
function getCheck(item, checked, level) {
return `
<label class="el-checkbox" style="padding-left: ${18 * level}px;">
<span class="el-checkbox__input${checked ? ' is-checked' : ''}" >
<input class="el-checkbox__original" type="checkbox" data-key="${item.key}">
<span class="el-checkbox__inner""></span>
</span>
<span class="el-checkbox__label" data-key="${item.key}">${item.name}</span>
</label>
`
}
}
;(function () {
initData()
initModule()
})()
})()