🏠 Home 

你看的时间太长了

You see it too long time.

// ==UserScript==
// @name 你看的时间太长了
// @namespace You see it too long time.
// @version 0.1.1
// @author 稻米鼠
// @description You see it too long time.
// @run-at document-idle
// @homepage https://meta.appinn.net/t/11501
// @supportURL https://meta.appinn.com/t/11501
// @match *://*/*
// @noframes
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_listValues
// ==/UserScript==
// 模式 0:限制总观看时间;模式 1:限制每个网站观看时间
// const youSeeItTooLongTimeMode = 1
// 最大观看时长,单位:分钟
// const howMinutesCanYouWatchIt = 60
/* 以下无需修改 */
const oldLoadFun = window.onload
window.onload = async function(){
oldLoadFun && oldLoadFun()
// 获取当前所需时间对象
const getNow = ()=>{
const nowUTC = new Date()
const now = Number((+nowUTC/1000).toFixed()) - nowUTC.getTimezoneOffset()*60
const day = now - now%86400
return { now, day }
}
// 储存当前数据
const storeData = async (long)=>{
await GM_setValue(window.location.hostname, JSON.stringify({
day: start.day,
long: long
}))
}
// 获取存储的数据
const getData = async (key=window.location.hostname)=>{
if(await GM_getValue(key)){
try {
const temp = JSON.parse(await GM_getValue(key))
// 如果有合理的数据,并且是当天数据
if(temp.day && temp.day === start.day && temp.long){
return temp
}
} catch (error) {}
}
return false
}
// 储存配置数据
const storeConfig = async (mode=1, max=3600)=>{
const config = {
mode: isNaN(mode) ? 1 : mode,
max: isNaN(max) ? 3600 : max
}
await GM_setValue('options', JSON.stringify(config))
return config
}
// 获取配置数据
const getConfig = async ()=>{
if(await GM_getValue('options')){
try {
const temp = JSON.parse(await GM_getValue('options'))
// 如果有合理的数据,并且是当天数据
if(!isNaN(temp.mode) && !isNaN(temp.max)){
return temp
}
} catch (error) {
this.console.log(error)
}
}
return await storeConfig()
}
// 修改网页标题
const changeTitle = title=>{
document.title = title+document.title.replace(/^【[\d:-]+】/i, '')
}
// 两位数字
const dbNum = num=>{
return num<10 ? '0'+num : num
}
// 窗口是否最小化
const isMin = ()=>{
let isWinMin = false;
if (window.outerWidth != undefined) {
isWinMin = window.outerWidth <= 160 && window.outerHeight <= 27;
} else {
isWinMin = window.screenTop < -30000 && window.screenLeft < -30000;
}
return isWinMin;
}
// 处理 hash 指令
const hashCommand = async (hash, options)=>{
if(/^#clear$/i.test(hash)){
await storeData( 0 )
window.location.hash = ''
return
}
if(/^#clearAll$/i.test(hash)){
const keys = await GM_listValues()
for (let key of keys) {
if(key==='options'){ continue }
GM_deleteValue(key);
}
window.location.hash = ''
return
}
if(/^#close$/i.test(hash)){
await storeData( 1.25*options.max )
window.location.hash = ''
return
}
if(/^#add\d+$/i.test(hash)){
let add = +hash.replace(/^#add(\d+)$/, '$1')
add = isNaN(add) ? 0 : add*60
const siteData = await getData()
let historyLong = siteData ? siteData.long : 0
historyLong = historyLong<add ? 0 : historyLong-add
await storeData(historyLong)
window.location.hash = ''
return
}
if(/^#sub\d+$/i.test(hash)){
let sub = +hash.replace(/^#sub(\d+)$/, '$1')
sub = isNaN(sub) ? 0 : sub*60
const siteData = await getData()
let historyLong = siteData ? siteData.long : 0
historyLong = historyLong+sub
await storeData(historyLong)
window.location.hash = ''
return
}
if(/^#mode\d$/i.test(hash)){
let mode = +hash.replace(/^#mode(\d)$/, '$1')
mode = isNaN(mode) ? 1 : (mode===0 ? 0 : 1)
await storeConfig(mode, options.max)
window.location.hash = ''
return
}
if(/^#max\d+$/i.test(hash)){
let max = +hash.replace(/^#max(\d+)$/, '$1')
if(isNaN(max)){ return }
max = 60*max
await storeConfig(options.mode, max)
window.location.hash = ''
return
}
if(/^#maxadd\d+$/i.test(hash)){
let add = +hash.replace(/^#maxadd(\d+)$/, '$1')
if(isNaN(add)){ return }
const max = options.max + 60*add
await storeConfig(options.mode, max)
window.location.hash = ''
return
}
if(/^#maxsub\d+$/i.test(hash)){
let sub = +hash.replace(/^#maxsub(\d+)$/, '$1')
if(isNaN(sub)){ return }
const max = options.max - 60*sub
await storeConfig(options.mode, max>=0 ? max : 0)
window.location.hash = ''
return
}
}
// 时间更新
const refreshTime = async ()=>{
const n = getNow()  // 获取当前时间
// 如果页面在后台或者窗口最小化,仅更新起始对象中的最后更新时间
if(document.visibilityState !== 'visible' || isMin()){
start.lastTime = n.now
return
}
// 获取当前配置
const options = await getConfig()
// 检测指令
await hashCommand(window.location.hash, options)
// 如果是当天
const siteData = await getData()
start.historyLong = n.day === start.day
? (siteData ? siteData.long : 0) + n.now - start.lastTime
: n.now%86400
start.day = n.day
start.lastTime = n.now
await storeData(start.historyLong)
let lastTime = options.max
if(options.mode){
lastTime = options.max - start.historyLong
}else{
const keys = await GM_listValues()
for (let key of keys) {
if(key==='options'){ continue }
const m = await getData(key)
if(m.day === start.day){
lastTime -= m.long
}else{
m && GM_deleteValue(key);
}
}
}
if(lastTime < 0){
const alpha = 1 + 4*lastTime/options.max
const opacity = ( alpha>1 ? 1 : ( alpha<0 ? 0 : alpha ) ).toFixed(2)
document.querySelector('body').style.opacity = opacity
if(document.fullscreenElement){// 如果全屏
document.fullscreenElement.style.opacity = opacity
}
if(alpha<0){
if(document.fullscreenElement){ document.exitFullscreen() } // 如果全屏则退出
document.querySelector('html').style.backgroundImage = 'url("https://i.v2ex.co/WJ15n12m.png")'
document.querySelector('html').style.backgroundPosition = 'center top'
document.querySelector('html').style.backgroundRepeat = 'no-repeat'
document.querySelector('html').style.backgroundAttachment = 'fixed'
window.clearInterval(timer)
}
}else{
document.querySelector('body').style.opacity = 1
}
const lastTimeToShow = (lastTime>=0 ? '' : '-')
+ dbNum( Math.floor( Math.abs(lastTime)/60) )
+ ':'
+ dbNum( Math.abs(lastTime)%60 )
changeTitle('【'+lastTimeToShow+'】')
}
// 初始化 启动程序
const start = getNow()
const siteData = await getData()
start.lastTime = start.now
start.historyLong = siteData ? siteData.long : 0
await storeData( start.historyLong )
refreshTime()
const timer = window.setInterval(refreshTime, 1e3)
}