你看的时间太长了

You see it too long time.

  1. // ==UserScript==
  2. // @name 你看的时间太长了
  3. // @namespace You see it too long time.
  4. // @version 0.1.1
  5. // @author 稻米鼠
  6. // @description You see it too long time.
  7. // @run-at document-idle
  8. // @homepage https://meta.appinn.net/t/11501
  9. // @supportURL https://meta.appinn.com/t/11501
  10. // @match *://*/*
  11. // @noframes
  12. // @grant GM_getValue
  13. // @grant GM_setValue
  14. // @grant GM_deleteValue
  15. // @grant GM_listValues
  16. // ==/UserScript==
  17.  
  18. // 模式 0:限制总观看时间;模式 1:限制每个网站观看时间
  19. // const youSeeItTooLongTimeMode = 1
  20. // 最大观看时长,单位:分钟
  21. // const howMinutesCanYouWatchIt = 60
  22. /* 以下无需修改 */
  23. const oldLoadFun = window.onload
  24. window.onload = async function(){
  25. oldLoadFun && oldLoadFun()
  26. // 获取当前所需时间对象
  27. const getNow = ()=>{
  28. const nowUTC = new Date()
  29. const now = Number((+nowUTC/1000).toFixed()) - nowUTC.getTimezoneOffset()*60
  30. const day = now - now%86400
  31. return { now, day }
  32. }
  33. // 储存当前数据
  34. const storeData = async (long)=>{
  35. await GM_setValue(window.location.hostname, JSON.stringify({
  36. day: start.day,
  37. long: long
  38. }))
  39. }
  40. // 获取存储的数据
  41. const getData = async (key=window.location.hostname)=>{
  42. if(await GM_getValue(key)){
  43. try {
  44. const temp = JSON.parse(await GM_getValue(key))
  45. // 如果有合理的数据,并且是当天数据
  46. if(temp.day && temp.day === start.day && temp.long){
  47. return temp
  48. }
  49. } catch (error) {}
  50. }
  51. return false
  52. }
  53. // 储存配置数据
  54. const storeConfig = async (mode=1, max=3600)=>{
  55. const config = {
  56. mode: isNaN(mode) ? 1 : mode,
  57. max: isNaN(max) ? 3600 : max
  58. }
  59. await GM_setValue('options', JSON.stringify(config))
  60. return config
  61. }
  62. // 获取配置数据
  63. const getConfig = async ()=>{
  64. if(await GM_getValue('options')){
  65. try {
  66. const temp = JSON.parse(await GM_getValue('options'))
  67. // 如果有合理的数据,并且是当天数据
  68. if(!isNaN(temp.mode) && !isNaN(temp.max)){
  69. return temp
  70. }
  71. } catch (error) {
  72. this.console.log(error)
  73. }
  74. }
  75. return await storeConfig()
  76. }
  77. // 修改网页标题
  78. const changeTitle = title=>{
  79. document.title = title+document.title.replace(/^【[\d:-]+】/i, '')
  80. }
  81. // 两位数字
  82. const dbNum = num=>{
  83. return num<10 ? '0'+num : num
  84. }
  85. // 窗口是否最小化
  86. const isMin = ()=>{
  87. let isWinMin = false;
  88. if (window.outerWidth != undefined) {
  89. isWinMin = window.outerWidth <= 160 && window.outerHeight <= 27;
  90. } else {
  91. isWinMin = window.screenTop < -30000 && window.screenLeft < -30000;
  92. }
  93. return isWinMin;
  94. }
  95. // 处理 hash 指令
  96. const hashCommand = async (hash, options)=>{
  97. if(/^#clear$/i.test(hash)){
  98. await storeData( 0 )
  99. window.location.hash = ''
  100. return
  101. }
  102. if(/^#clearAll$/i.test(hash)){
  103. const keys = await GM_listValues()
  104. for (let key of keys) {
  105. if(key==='options'){ continue }
  106. GM_deleteValue(key);
  107. }
  108. window.location.hash = ''
  109. return
  110. }
  111. if(/^#close$/i.test(hash)){
  112. await storeData( 1.25*options.max )
  113. window.location.hash = ''
  114. return
  115. }
  116. if(/^#add\d+$/i.test(hash)){
  117. let add = +hash.replace(/^#add(\d+)$/, '$1')
  118. add = isNaN(add) ? 0 : add*60
  119. const siteData = await getData()
  120. let historyLong = siteData ? siteData.long : 0
  121. historyLong = historyLong<add ? 0 : historyLong-add
  122. await storeData(historyLong)
  123. window.location.hash = ''
  124. return
  125. }
  126. if(/^#sub\d+$/i.test(hash)){
  127. let sub = +hash.replace(/^#sub(\d+)$/, '$1')
  128. sub = isNaN(sub) ? 0 : sub*60
  129. const siteData = await getData()
  130. let historyLong = siteData ? siteData.long : 0
  131. historyLong = historyLong+sub
  132. await storeData(historyLong)
  133. window.location.hash = ''
  134. return
  135. }
  136. if(/^#mode\d$/i.test(hash)){
  137. let mode = +hash.replace(/^#mode(\d)$/, '$1')
  138. mode = isNaN(mode) ? 1 : (mode===0 ? 0 : 1)
  139. await storeConfig(mode, options.max)
  140. window.location.hash = ''
  141. return
  142. }
  143. if(/^#max\d+$/i.test(hash)){
  144. let max = +hash.replace(/^#max(\d+)$/, '$1')
  145. if(isNaN(max)){ return }
  146. max = 60*max
  147. await storeConfig(options.mode, max)
  148. window.location.hash = ''
  149. return
  150. }
  151. if(/^#maxadd\d+$/i.test(hash)){
  152. let add = +hash.replace(/^#maxadd(\d+)$/, '$1')
  153. if(isNaN(add)){ return }
  154. const max = options.max + 60*add
  155. await storeConfig(options.mode, max)
  156. window.location.hash = ''
  157. return
  158. }
  159. if(/^#maxsub\d+$/i.test(hash)){
  160. let sub = +hash.replace(/^#maxsub(\d+)$/, '$1')
  161. if(isNaN(sub)){ return }
  162. const max = options.max - 60*sub
  163. await storeConfig(options.mode, max>=0 ? max : 0)
  164. window.location.hash = ''
  165. return
  166. }
  167. }
  168. // 时间更新
  169. const refreshTime = async ()=>{
  170. const n = getNow() // 获取当前时间
  171. // 如果页面在后台或者窗口最小化,仅更新起始对象中的最后更新时间
  172. if(document.visibilityState !== 'visible' || isMin()){
  173. start.lastTime = n.now
  174. return
  175. }
  176. // 获取当前配置
  177. const options = await getConfig()
  178. // 检测指令
  179. await hashCommand(window.location.hash, options)
  180. // 如果是当天
  181. const siteData = await getData()
  182. start.historyLong = n.day === start.day
  183. ? (siteData ? siteData.long : 0) + n.now - start.lastTime
  184. : n.now%86400
  185. start.day = n.day
  186. start.lastTime = n.now
  187. await storeData(start.historyLong)
  188.  
  189. let lastTime = options.max
  190. if(options.mode){
  191. lastTime = options.max - start.historyLong
  192. }else{
  193. const keys = await GM_listValues()
  194. for (let key of keys) {
  195. if(key==='options'){ continue }
  196. const m = await getData(key)
  197. if(m.day === start.day){
  198. lastTime -= m.long
  199. }else{
  200. m && GM_deleteValue(key);
  201. }
  202. }
  203. }
  204. if(lastTime < 0){
  205. const alpha = 1 + 4*lastTime/options.max
  206. const opacity = ( alpha>1 ? 1 : ( alpha<0 ? 0 : alpha ) ).toFixed(2)
  207. document.querySelector('body').style.opacity = opacity
  208. if(document.fullscreenElement){// 如果全屏
  209. document.fullscreenElement.style.opacity = opacity
  210. }
  211. if(alpha<0){
  212. if(document.fullscreenElement){ document.exitFullscreen() } // 如果全屏则退出
  213. document.querySelector('html').style.backgroundImage = 'url("https://i.v2ex.co/WJ15n12m.png")'
  214. document.querySelector('html').style.backgroundPosition = 'center top'
  215. document.querySelector('html').style.backgroundRepeat = 'no-repeat'
  216. document.querySelector('html').style.backgroundAttachment = 'fixed'
  217. window.clearInterval(timer)
  218. }
  219. }else{
  220. document.querySelector('body').style.opacity = 1
  221. }
  222. const lastTimeToShow = (lastTime>=0 ? '' : '-')
  223. + dbNum( Math.floor( Math.abs(lastTime)/60) )
  224. + ':'
  225. + dbNum( Math.abs(lastTime)%60 )
  226. changeTitle('【'+lastTimeToShow+'】')
  227. }
  228. // 初始化 启动程序
  229. const start = getNow()
  230. const siteData = await getData()
  231. start.lastTime = start.now
  232. start.historyLong = siteData ? siteData.long : 0
  233. await storeData( start.historyLong )
  234.  
  235. refreshTime()
  236. const timer = window.setInterval(refreshTime, 1e3)
  237. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址