🏠 返回首頁 

Greasy Fork is available in English.

你看的时间太长了

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