├── .gitignore
├── LICENSE
├── README.md
├── Twitch-Auto-Max-Quality.user.js
└── Twitch-Mobile-Web-Source-Quality.user.js
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | .eslintrc
3 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Nomo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Twitch-Auto-Max-Quality
2 |
3 |
7 |
8 | > **Important: As a Korean developer, I no longer plan to actively maintain this script as Twitch has shut down operations in South Korea. Check [this article](https://blog.twitch.tv/en/2023/12/05/an-update-on-twitch-in-korea/) for more information.**
9 |
10 | - Always start playing live video with the quality you want on twitch.tv.
11 | - Prevent automatic change of video quality when tab is disabled.
12 |
13 | ## Install
14 |
15 | ### STEP 1. ScriptManager
16 |
17 | - Firefox - [Tampermonkey](https://addons.mozilla.org/ko/firefox/addon/tampermonkey/)
18 | - Chrome - [Tampermonkey](https://chrome.google.com/webstore/detail/tampermonkey/dhdgffkkebhmkfjojejmpbldmpobfkfo)
19 | - Opera - [Tampermonkey](https://addons.opera.com/extensions/details/tampermonkey-beta/)
20 | - Safari - [Tampermonkey](https://safari.tampermonkey.net/tampermonkey.safariextz)
21 | - Edge - [Tampermonkey](https://microsoftedge.microsoft.com/addons/detail/tampermonkey/iikmkjmpaadaobahmlepeloendndfphd)
22 |
23 | This script may not work properly on script managers other than Tampermonkey.
24 |
25 | ### STEP 2. UserScript
26 |
27 | - [Install](https://raw.githubusercontent.com/nomomo/Twitch-Auto-Max-Quality/master/Twitch-Auto-Max-Quality.user.js) from [https://raw.githubusercontent.com/nomomo/Twitch-Auto-Max-Quality/master/Twitch-Auto-Max-Quality.user.js](https://raw.githubusercontent.com/nomomo/Twitch-Auto-Max-Quality/master/Twitch-Auto-Max-Quality.user.js)
28 |
29 | ## Bug report
30 |
31 | nomotg@gmail.com
32 |
33 | ## Change log
34 |
35 | ### 0.4.1b (2024-03-01)
36 |
37 | - I have set the "Only the quality you want method" to default. New users installing the script will now only see the maximum quality. Existing users will not be affected.
38 |
39 | ### 0.4.1 (2023-03-23)
40 |
41 | - Fixed an issue where 160p was exposed with source quality in certain environments when using the "Only the quality you want method" (Thanks Taskmant!)
42 |
43 | ### 0.4.0 (2023-02-19)
44 |
45 | - The script version update was missing, only the version was updated.
46 |
47 | ### 0.4.0 (2023-02-11)
48 |
49 | - "Only source quality method" is renewed as "Only the quality you want method"
50 | - You can now remove all but the quality you want, not just the source quality. (set it in the settings menu)
51 | - Now again "Simulate settings button method" works fine.
52 | - The script was modified according to some changes in the Twitch player.
53 | - Known bug for "Simulate settings button method": When browsing VOD and CLIP on twitch.tv in some environments, it automatically scrolls down after setting the quality.
54 |
55 | ### 0.3.2 (2022-07-31)
56 |
57 | - Add legacy option to "Only source quality method"
58 | - Type legacy : Removes all other selectable video quality except source quality & show (source) text.
59 | - Fixed an issue where the "Only source quality method" did not work on the VOD page.
60 |
61 | ### 0.3.1 (2022-07-30)
62 |
63 | - Now, when using the "Only source quality method", the text "(source)" is no longer displayed in the quality name. Of course the video quality set by the script is the source quality, even if "(source)" is not displayed.
64 | - Add options to "Only source quality method"
65 | - Type 0 : Removes all other selectable video quality except best quality
66 | - Type 1 : Overwrite all selectable video quality with best quality
67 |
68 | ### 0.2.2 (2022-03-23)
69 |
70 | - "Disable power saving for inactive tabs" feature no longer works with embed Twitch clips to avoid conflicts with ad blocking extensions.
71 |
72 | ### 0.2.1 (2022-03-09)
73 |
74 | - New TAMQ Labs feature: "Disable power saving for inactive tabs (Disable JavaScript Timer Throttling)"
75 | - If you often have problems playing videos in inactive tabs, try using this feature.
76 | - By enabling this option, you can disable some of the power saving features (Javascript Timer Throttling) for inactive tabs supported by Chrome-based browsers.
77 | - This feature may conflict with certain ad filters in the ad blocking extension.
78 |
79 | ### 0.2.0 (2022-03-04)
80 |
81 | - Now "Only source quality method" works on the VOD page as well.
82 | - New feature added: "Apply \'Only source quality method\' to the clip page"
83 | - Removed "Use the Twitch EMBED API in an Iframe" and "Use for live video where url starts with blob" options.
84 | - Korean language is supported in the settings menu. You can select English and Korean(한국어) from the setting menu.
85 |
86 | ### 0.1.2 (2021-09-15)
87 |
88 | - Fixed an issue related to the timeline of VOD (Thanks MonkeyDMax92)
89 |
90 | ### 0.1.1 (2021-09-14)
91 |
92 | - Fixed the script not working properly in Violentmonkey.
93 | - Added exception handling.
94 |
95 | ### 0.1.0 (2021-08-04)
96 |
97 | - New feature added: "Only source quality method"
98 | - Removes all other selectable video quality except source quality. So even if the Twitch player sets the video quality to "Auto", the only selectable "Source quality" is set.
99 | - When this option is enabled, Localstorage modify method and Simulate settings button method are automatically disabled.
100 | - Caution: Enabling this option may conflict with other scripts (eg TwitchAdSolution.), causing problems with video playback or the scripts not working properly. Of course, it might work just fine. If you run into problems, setting the \"Position\" to 1 or the last one in \"Settings\" tab of the script settings menu may solve the problem. If that doesn't work, turn this option off.
101 |
102 | ### 0.0.9 (2021-06-07)
103 |
104 | - Fixes an issue where the script unintentionally works on some clip pages.
105 |
106 | ### 0.0.8 (2021-06-05)
107 |
108 | - Now again "Simulate settings button method" works fine.
109 | - You can now set your preferred video quality. (set it in the settings menu)
110 | - Removed "legacy mode" of "Simulate settings button method"
111 |
112 | ### 0.0.7 (2021-03-10)
113 |
114 | - Removed 'Automatic restart on error' that did not work properly.
115 | - Fixed jquery https related problem. (Thanks sowind)
116 | - Added 'Set the volume when stream starts'. (enable it in the settings menu)
117 |
118 | ### 0.0.6 (2020-10-29)
119 |
120 | - It works better for cases where there is only one video quality.
121 | - User can set initial delay for automatic quality change. If the problem occurs because the script changes the video quality too quickly, increase the delay.
122 |
123 | ### 0.0.5 (2020-05-31)
124 |
125 | - In May 2020, the structure of the Twitch player inserted as an iframe seems to have changed. So I modified the code so that the script works properly again in the Twitch player inserted as an iframe. However, there may be users who still use the old Twitch Player(idk). If 'Simulate settings button method' does not work properly, try turning on "legacy mode" in the settings menu.
126 |
127 | ### 0.0.4 (2019-11-15)
128 |
129 | - Occasionally, video quality is fixed at 720p when watching live with the embed player. In this case, the script will automatically pause the video and try to play again quickly.
130 |
131 | ### 0.0.3 (2019-10-23)
132 |
133 | - This script will be disabled when users watch squad streaming. (This script does not currently support squad streaming page)
134 | - Initial delay (500 ms) is applied when the video quality setting menu is clicked virtually. This will prevent the problem caused by trying to change the video quality too quickly.
135 |
136 | ### 0.0.2 (2019-10-17)
137 |
138 | - Modified to apply to the new layout of the Twitch.
139 |
140 | ### 0.0.1 (2019-10-07)
141 |
142 | - Initial commit
143 |
144 | ## Happy??
145 |
146 |
147 |
--------------------------------------------------------------------------------
/Twitch-Auto-Max-Quality.user.js:
--------------------------------------------------------------------------------
1 | // ==UserScript==
2 | // @name Twitch-Auto-Max-Quality
3 | // @namespace Twitch-Auto-Max-Quality
4 | // @version 0.4.1
5 | // @author Nomo
6 | // @description Always start playing live video with source quality on twitch.tv
7 | // @supportURL https://github.com/nomomo/Twitch-Auto-Max-Quality/issues
8 | // @homepage https://github.com/nomomo/Twitch-Auto-Max-Quality/
9 | // @downloadURL https://raw.githubusercontent.com/nomomo/Twitch-Auto-Max-Quality/master/Twitch-Auto-Max-Quality.user.js
10 | // @updateURL https://raw.githubusercontent.com/nomomo/Twitch-Auto-Max-Quality/master/Twitch-Auto-Max-Quality.user.js
11 | // @include *://*.twitch.tv/*
12 | // @require https://greasemonkey.github.io/gm4-polyfill/gm4-polyfill.js
13 | // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js
14 | // @run-at document-start
15 | // @grant GM.addStyle
16 | // @grant GM_addStyle
17 | // @grant GM.getValue
18 | // @grant GM_getValue
19 | // @grant GM.setValue
20 | // @grant GM_setValue
21 | // @grant GM.deleteValue
22 | // @grant GM_deleteValue
23 | // @grant GM.listValues
24 | // @grant GM_listValues
25 | // @grant GM.info
26 | // @grant GM_info
27 | // @grant GM.xmlHttpRequest
28 | // @grant GM_xmlhttpRequest
29 | // @grant GM.registerMenuCommand
30 | // @grant GM_registerMenuCommand
31 | // @grant GM_getResourceText
32 | // @grant GM.notification
33 | // @grant GM_notification
34 | // @grant GM.addValueChangeListener
35 | // @grant GM_addValueChangeListener
36 | // @grant GM.removeValueChangeListener
37 | // @grant GM_removeValueChangeListener
38 | // @grant unsafeWindow
39 | // ==/UserScript==
40 | // @icon https://raw.githubusercontent.com/nomomo/Twitch-Auto-Max-Quality/master/images/logo.png
41 | /* eslint-disable no-undef */
42 | "use strict";
43 | (async () => {
44 | console.log("[TAMQ] RUNNING TWITCH AUTO MAX QUALITY", document.location.href);
45 | var DEBUG = await GM.getValue("DEBUG", false);
46 |
47 | ////////////////////////////////////////////////////////////////////////////////////
48 | // libs
49 | ////////////////////////////////////////////////////////////////////////////////////
50 | var NOMO_DEBUG = function ( /**/ ) {
51 | if (!DEBUG) return;
52 | var args = arguments, args_length = args.length, args_copy = args;
53 | for (let i = args_length; i > 0; i--) args[i] = args_copy[i - 1];
54 | args[0] = "[TAMQ] ";
55 | args.length = args_length + 1;
56 | console.log.apply(console, args);
57 | };
58 |
59 | /* arrive.js
60 | * v2.4.1
61 | * https://github.com/uzairfarooq/arrive
62 | * MIT licensed
63 | * Copyright (c) 2014-2017 Uzair Farooq
64 | */
65 | // eslint-disable-next-line no-cond-assign, no-unused-vars, no-prototype-builtins
66 | const Arrive = function(e,t,n){"use strict";function r(e,t,n){l.addMethod(t,n,e.unbindEvent),l.addMethod(t,n,e.unbindEventWithSelectorOrCallback),l.addMethod(t,n,e.unbindEventWithSelectorAndCallback);}function i(e){e.arrive=f.bindEvent,r(f,e,"unbindArrive"),e.leave=d.bindEvent,r(d,e,"unbindLeave");}if(e.MutationObserver&&"undefined"!=typeof HTMLElement){var o=0,l=function(){var t=HTMLElement.prototype.matches||HTMLElement.prototype.webkitMatchesSelector||HTMLElement.prototype.mozMatchesSelector||HTMLElement.prototype.msMatchesSelector;return{matchesSelector:function(e,n){return e instanceof HTMLElement&&t.call(e,n);},addMethod:function(e,t,r){var i=e[t];e[t]=function(){return r.length==arguments.length?r.apply(this,arguments):"function"==typeof i?i.apply(this,arguments):n;};},callCallbacks:function(e,t){t&&t.options.onceOnly&&1==t.firedElems.length&&(e=[e[0]]);for(var n,r=0;n=e[r];r++)n&&n.callback&&n.callback.call(n.elem,n.elem);t&&t.options.onceOnly&&1==t.firedElems.length&&t.me.unbindEventWithSelectorAndCallback.call(t.target,t.selector,t.callback);},checkChildNodesRecursively:function(e,t,n,r){for(var i,o=0;i=e[o];o++)n(i,t,r)&&r.push({callback:t.callback,elem:i}),i.childNodes.length>0&&l.checkChildNodesRecursively(i.childNodes,t,n,r);},mergeArrays:function(e,t){var n,r={};for(n in e)e.hasOwnProperty(n)&&(r[n]=e[n]);for(n in t)t.hasOwnProperty(n)&&(r[n]=t[n]);return r;},toElementsArray:function(t){return n===t||"number"==typeof t.length&&t!==e||(t=[t]),t;}};}(),c=function(){var e=function(){this._eventsBucket=[],this._beforeAdding=null,this._beforeRemoving=null;};return e.prototype.addEvent=function(e,t,n,r){var i={target:e,selector:t,options:n,callback:r,firedElems:[]};return this._beforeAdding&&this._beforeAdding(i),this._eventsBucket.push(i),i;},e.prototype.removeEvent=function(e){for(var t,n=this._eventsBucket.length-1;t=this._eventsBucket[n];n--)if(e(t)){this._beforeRemoving&&this._beforeRemoving(t);var r=this._eventsBucket.splice(n,1);r&&r.length&&(r[0].callback=null);}},e.prototype.beforeAdding=function(e){this._beforeAdding=e;},e.prototype.beforeRemoving=function(e){this._beforeRemoving=e;},e;}(),a=function(t,r){var i=new c,o=this,a={fireOnAttributesModification:!1};return i.beforeAdding(function(n){var i,l=n.target;(l===e.document||l===e)&&(l=document.getElementsByTagName("html")[0]),i=new MutationObserver(function(e){r.call(this,e,n);});var c=t(n.options);i.observe(l,c),n.observer=i,n.me=o;}),i.beforeRemoving(function(e){e.observer.disconnect();}),this.bindEvent=function(e,t,n){t=l.mergeArrays(a,t);for(var r=l.toElementsArray(this),o=0;o0?l.checkChildNodesRecursively(n,t,r,o):"attributes"===e.type&&r(i,t,o)&&o.push({callback:t.callback,elem:i}),l.callCallbacks(o,t);});}function r(e,t){return l.matchesSelector(e,t.selector)&&(e._id===n&&(e._id=o++),-1==t.firedElems.indexOf(e._id))?(t.firedElems.push(e._id),!0):!1;}var i={fireOnAttributesModification:!1,onceOnly:!1,existing:!1};f=new a(e,t);var c=f.bindEvent;return f.bindEvent=function(e,t,r){n===r?(r=t,t=i):t=l.mergeArrays(i,t);var o=l.toElementsArray(this);if(t.existing){for(var a=[],s=0;s0&&l.checkChildNodesRecursively(n,t,r,i),l.callCallbacks(i,t);});}function r(e,t){return l.matchesSelector(e,t.selector);}var i={};d=new a(e,t);var o=d.bindEvent;return d.bindEvent=function(e,t,r){n===r?(r=t,t=i):t=l.mergeArrays(i,t),o.call(this,e,t,r);},d;},f=new s,d=new u;t&&i(t.fn),i(HTMLElement.prototype),i(NodeList.prototype),i(HTMLCollection.prototype),i(HTMLDocument.prototype),i(Window.prototype);var h={};return r(f,h,"unbindAllArrive"),r(d,h,"unbindAllLeave"),h;}}(window,"undefined"==typeof jQuery?null:jQuery,void 0);
67 |
68 | /* HackTimer.js by turuslan
69 | * v1.1.0
70 | * https://github.com/turuslan/HackTimer
71 | * MIT licensed
72 | */
73 | // eslint-disable-next-line no-cond-assign
74 | var HackTimerWorker_min_js, HackTimerWorker_min_js_blob;
75 | const disableJavascriptTimer = function() {
76 | if(unsafeWindow["TAMQuseHackTimer"] !== undefined){
77 | return;
78 | }
79 | unsafeWindow["TAMQuseHackTimer"] = true;
80 | HackTimerWorker_min_js = `var f={},p=postMessage,r='hasOwnProperty';onmessage=function(e){var d=e.data,i=d.i,t=d[r]('t')?d.t:0;switch(d.n){case'a':f[i]=setInterval(function(){p(i)},t);break;case'b':if(f[r](i)){clearInterval(f[i]);delete f[i]}break;case'c':f[i]=setTimeout(function(){p(i);if(f[r](i))delete f[i]},t);break;case'd':if(f[r](i)){clearTimeout(f[i]);delete f[i]}break}}`;
81 | HackTimerWorker_min_js_blob = new Blob([HackTimerWorker_min_js], {type: 'application/javascript'});
82 |
83 | // eslint-disable-next-line no-cond-assign
84 | // eslint-disable-next-line no-empty, no-unused-vars
85 | (function(s){var w,f={},o=unsafeWindow,l=console,m=Math,z='postMessage',p=0,r='hasOwnProperty',y=[].slice,x='fail',v=o.Worker;function d(){do{p=0x7FFFFFFF>p?p+1:0;}while(f[r](p));return p;}if(!/MSIE 10/i.test(navigator.userAgent)){try{s=o.URL.createObjectURL(new Blob(["var f={},p=postMessage,r='hasOwnProperty';onmessage=function(e){var d=e.data,i=d.i,t=d[r]('t')?d.t:0;switch(d.n){case'a':f[i]=setInterval(function(){p(i)},t);break;case'b':if(f[r](i)){clearInterval(f[i]);delete f[i]}break;case'c':f[i]=setTimeout(function(){p(i);if(f[r](i))delete f[i]},t);break;case'd':if(f[r](i)){clearTimeout(f[i]);delete f[i]}break}}"]));}catch(e){}}if(typeof(v)!=='undefined'){try{w=new v(s);o.setInterval=function(c,t){var i=d();f[i]={c:c,p:y.call(arguments,2)};w[z]({n:'a',i:i,t:t});return i;};o.clearInterval=function(i){if(f[r](i))delete f[i],w[z]({n:'b',i:i});};o.setTimeout=function(c,t){var i=d();f[i]={c:c,p:y.call(arguments,2),t:!0};w[z]({n:'c',i:i,t:t});return i;};o.clearTimeout=function(i){if(f[r](i))delete f[i],w[z]({n:'d',i:i});};w.onmessage=function(e){var i=e.data,c,n;if(f[r](i)){n=f[i];c=n.c;if(n[r]('t'))delete f[i];}if(typeof(c)=='string')try{c=new Function(c);}catch(k){}if(typeof(c)=='function')c.apply(o,n.p);};}catch(e){l.log(x);}}else l.log(x);})(HackTimerWorker_min_js_blob);//('HackTimerWorker.min.js');
86 | };
87 |
88 | /* GM_setting.js
89 | * Version: May. 19, 2022
90 | * MIT licensed
91 | * https://github.com/nomomo/
92 | * nomotg@gmail.com
93 | * Copyright (c) 2017-2022 NOMO
94 | */
95 | // eslint-disable-next-line
96 | var GM_setting=function(e,t,n){var i,a=void 0,s="",o=[],r={},l={},_={},d={},c=!1,p=function(){if(c){for(var e=arguments,t=e.length,n=e,i=t;i>0;i--)e[i]=n[i-1];e[0]="+[GM_SETTINGS] ",e.length=t+1,console.log.apply(console,e)}},g=(navigator.language||navigator.userLanguage).toLowerCase().substring(0,2),u=g,v="ko",f=!1;const h={en:{title_settings:"Settings",title_reset:"Reset",donate:"Donate",buymeacoffee:"Buy me a coffee",buymeacoffeeDesc:"Support my projects by buying me a coffee! ☕",toonation:"Toonation",button_reset_settings:"Reset Settings",confirm_reset_settings:"Are you sure you want to reset the settings?",complete_reset_settings:"Settings reset complete!",button_reset_settings_all:"Script reset (refresh is required)",confirm_reset_settings_all:"Do you really want to reset script?",complete_reset_settings_all:"Script initialization complete!",auto_saved:"Autosaved: ",err_val_req:"A value must be entered.",err_num_req:"Only numbers can be entered.",err_num_over:"The input value must be a number greater than or equal to : ",err_num_not_more_than:"The input value must be a number less than or equal to: ",err_valid_array_string:"Only English letters, numbers, commas (,) and underscores (_) can be entered.",err_value_empty:"Something for which no value exists, such as an empty value.",err_value_dup:"Duplicate value exists: ",err_value_blank:"There is an item of a space in the string: "},ko:{title_settings:"Settings",title_reset:"Reset",donate:"후원하기",buymeacoffee:"Buy me a coffee 로 커피 한 잔 사주기",buymeacoffeeDesc:"커피 한 잔☕ 으로 프로젝트를 지원해주세요~",toonation:"Toonation 으로 후원하기",button_reset_settings:"Reset Settings",confirm_reset_settings:"진짜 설정을 초기화 할까요?",complete_reset_settings:"설정 초기화 완료!",button_reset_settings_all:"전체 초기화(새로고침 필요)",confirm_reset_settings_all:"진짜 스크립트를 모두 초기화 할까요?",complete_reset_settings_all:"스크립트 초기화 완료!",auto_saved:"자동 저장 됨: ",err_val_req:"반드시 값이 입력되어야 합니다.",err_num_req:"숫자만 입력 가능합니다.",err_num_over:"입력 값은 다음 값 이상의 숫자이어야 합니다. : ",err_num_not_more_than:"입력 값은 다음 값 이하의 숫자이어야 합니다. : ",err_valid_array_string:"영문, 숫자, 콤마(,), 언더바(_) 만 입력 가능합니다.",err_value_empty:"공백 값 등 값이 존재하지 않는 항목이 존재합니다.",err_value_dup:"중복된 값이 존재합니다: ",err_value_blank:"문자열 내 공백이 존재하는 항목이 있습니다: "}};var G=function(e){var t="";if("object"==typeof e){var n=Object.keys(e);if(0===n.length)return t;t=void 0!==e[u]?e[u]:void 0!==e[v]?e[u]:e[n[0]]}else t=e;return t},M=function(e){return void 0!==h[u]?h[u][e]:void 0!==h[v]?h[v][e]:""},m=async function(){""!==s&&await GM.setValue(s,_),t[s]=_,e.each(o,function(e,t){void 0!==l[t]&&void 0!==l[t].change&&l[t].change(_[t])}),o=[]},x=async function(){p("load_"),""!==s&&(_=await GM.getValue(s,_)),_.Lang=await y(),t[s]=_},y=async function(){return u=await GM.getValue("GM_SETTING_LANG",g),p("loadLang_",u),u},b=function(t){d={};var n=e(t);i=n,0!==n.find("#GM_setting_container").length&&n.empty();var s=e(""),o=e(`\n