Greasy Fork is available in English.

youtube记忆画面清晰度

2023/7/27 11:45:08

  1. // ==UserScript==
  2. // @name youtube记忆画面清晰度
  3. // @namespace Violentmonkey Scripts
  4. // @match *://www.youtube.com/watch?v=*
  5. // @match *://www.youtube.com/*
  6. // @match *://*.youtu.be/*
  7. // @match *://www.youtube.com/embed/*
  8. // @match *://www.youtube-nocookie.com/embed/*
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. // @grant GM_registerMenuCommand
  12. // @version 4.43
  13. // @author lazy cat
  14. // @description 2023/7/27 11:45:08
  15. // @run-at document-end
  16. // @license MIT
  17. // @homepageURL https://greasyfork.org/zh-CN/scripts/473553-youtube%E9%BB%98%E8%AE%A4%E5%BC%80%E5%90%AF%E6%9C%80%E9%AB%98%E7%94%BB%E8%B4%A8
  18. // @homepage https://greasyfork.org/zh-CN/scripts/473553-youtube%E9%BB%98%E8%AE%A4%E5%BC%80%E5%90%AF%E6%9C%80%E9%AB%98%E7%94%BB%E8%B4%A8
  19. // ==/UserScript==
  20. function save_loc_px(px) {
  21. console.log('储存数据', px)
  22. GM_setValue('yt_px', px)
  23. }
  24. function get_loc_px() {
  25. console.log('读取数据')
  26. return GM_getValue('yt_px', 'highres')
  27. }
  28. // 设置清晰度
  29. function set_px(px) {
  30. console.log('设置清晰度', px)
  31. if (!px || px === 'none') return
  32. let player = document.querySelector('div[id="movie_player"]')
  33. if (!player) return
  34. player.setPlaybackQualityRange(px)
  35. }
  36. // 获取清晰度列表
  37. function get_px_list() {
  38. let player = document.querySelector('div[id="movie_player"]')
  39. if (!player) return []
  40. return player.getAvailableQualityLevels()
  41. }
  42. // 选择合适清晰度
  43. function select_px() {
  44. let name_list = ['tiny', 'small', 'medium', 'large', 'hd720', 'hd1080', 'hd1440', 'hd2160', 'hd2880', 'highres']
  45. let now_px_list = get_px_list()
  46. if (now_px_list.length === 0) return 'none'
  47. let loc_px = get_loc_px()
  48. if (now_px_list.includes(loc_px)) return loc_px
  49. let now_px_index = name_list.indexOf(now_px_list[0])
  50. let loc_px_index = name_list.indexOf(loc_px)
  51. if (loc_px_index >= now_px_index) return now_px_list[0]
  52. while (loc_px_index > 0) {
  53. loc_px_index--
  54. if (now_px_index.includes(name_list[loc_px_index])) return name_list[loc_px_index]
  55. }
  56. return now_px_list[0]
  57. }
  58. // 按钮回调函数
  59. function callback_save(e) {
  60. if (!e.isTrusted) return
  61. let px = this.innerText.split(' ')[0]
  62. if (px.includes('画质')) return
  63. if (!/\d+p6?0?/.test(px)) return
  64. let px_map = {
  65. '144p': 'tiny',
  66. '240p': 'small',
  67. '360p': 'medium',
  68. '480p': 'large',
  69. '720p': 'hd720',
  70. '1080p': 'hd1080',
  71. '1440p': 'hd1440',
  72. '2160p': 'hd2160',
  73. '2880p': 'hd2880',
  74. '4320p': 'highres',
  75. '144p60': 'tiny',
  76. '240p60': 'small',
  77. '360p60': 'medium',
  78. '480p60': 'large',
  79. '720p60': 'hd720',
  80. '1080p60': 'hd1080',
  81. '1440p60': 'hd1440',
  82. '2160p60': 'hd2160',
  83. '2880p60': 'hd2880',
  84. '4320p60': 'highres',
  85. }
  86. if (!Object.keys(px_map).includes(px)) return
  87. console.log('回调函数触发', this)
  88. save_loc_px(px_map[px])
  89. }
  90. // 清晰度按钮监听器回调函数
  91. function callback_look_button(..._) {
  92. document.querySelectorAll('.ytp-menuitem-label').forEach((e) => {
  93. e.addEventListener('click', callback_save)
  94. })
  95. }
  96. // 清晰度按钮监听器
  97. function look_button_start() {
  98. let button_fater = document.querySelector('div[id="ytp-id-18"]')
  99. if (!button_fater) return
  100. const config = { attributes: true, childList: true, subtree: true }
  101. const obs = new MutationObserver(callback_look_button)
  102. obs.observe(button_fater, config)
  103. }
  104. // 视频标题和选集改变调函数
  105. function callback_look_title() {
  106. console.log('视频改变')
  107. set_px(select_px())
  108. look_button_start()
  109. }
  110. // 视频标题和选集改变监听器
  111. function look_title_start() {
  112. let title = document.querySelector('h1 yt-formatted-string[class="style-scope ytd-watch-metadata"]')?.parentElement
  113. let video_list = document.querySelectorAll('#container>#items')
  114. const config = { attributes: true, childList: true, subtree: true }
  115. const obs = new MutationObserver(callback_look_title)
  116. if (title) obs.observe(title, config)
  117. else return setTimeout(look_title_start, 100) // 防止标题未加载完成
  118. if (video_list.length > 1) obs.observe(video_list[1], config)
  119. }
  120. // 执行一次主要逻辑
  121. function work() {
  122. console.log('主要逻辑执行')
  123. set_px(select_px())
  124. look_button_start()
  125. look_title_start()
  126. // 提供ifame内的支持
  127. if (!in_iframe()) return
  128. let player = document.querySelector('div[id="movie_player"]')
  129. player?.removeEventListener('onStateChange', work)
  130. }
  131. // 等待标题加载完成监听器回调函数
  132. function callback_await_load(_, observer) {
  133. console.log('回调函数执行')
  134. let title = document.querySelector('h1 yt-formatted-string[class="style-scope ytd-watch-metadata"]')
  135. let title_2 = document.querySelector('.ytp-title-text')
  136. if (!title && !title_2) return
  137. observer.disconnect()
  138. work()
  139. // 提供ifame内的支持
  140. if (!in_iframe()) return
  141. let player = document.querySelector('div[id="movie_player"]')
  142. player?.addEventListener('onStateChange', work)
  143. }
  144. // 等待标题加载完成
  145. function await_load() {
  146. console.log('等待加载')
  147. let title = document.querySelector('h1 yt-formatted-string[class="style-scope ytd-watch-metadata"]')
  148. let title_father = document.querySelector('body')
  149. const config = { childList: true, subtree: true }
  150. const obs = new MutationObserver(callback_await_load)
  151. if (title_father && !title) obs.observe(title_father, config)
  152. else work()
  153. }
  154. // 读取是否对网页内嵌视频生效
  155. function get_emb_mod() {
  156. return GM_getValue('emb_mod', true)
  157. }
  158. // 改变内嵌视频生效设置
  159. function change_emb_mod() {
  160. let emb_mod = !get_emb_mod()
  161. GM_setValue('emb_mod', emb_mod)
  162. console.log('设置内嵌视频生效', emb_mod)
  163. location.reload()
  164. }
  165. // 判断是否处于iframe内
  166. function in_iframe() {
  167. return window.self !== window.top
  168. }
  169. // 程序入口
  170. (function () {
  171. let emb_mod = get_emb_mod()
  172. let show_text = '网页内嵌视频生效' + (emb_mod ? '✔️' : '❌')
  173. GM_registerMenuCommand(show_text, change_emb_mod)
  174. if (in_iframe() && !emb_mod) {
  175. console.log('网页内嵌视频禁止生效')
  176. return
  177. }
  178. let now_url = window.location.href
  179. if (now_url.includes('watch?v=')) await_load()
  180. else setTimeout(await_load, 3000)
  181. })()