This repository has been archived by the owner on Jul 22, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 218
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
improve default playback quality of YouTube videos to 1440p (or
vq=
…
…, `quality=` query-string parameter) (fixes issue #1051) (#1052) * tidy up YouTube WebCompat WebExtension (follow up to PR #1047) * automatically upgrade default YouTube video playback quality to HD 1440p (fixes issue #1051); hide `Full screen is unavailable` message (fixes issue #1056) - or via `vq=`/`query=` query-string parameter in URLs for youtube.com and youtube-nocookie.com * Fix SyntaxError * Update to wait until player has resolutions available
- Loading branch information
1 parent
863be49
commit a125258
Showing
4 changed files
with
220 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 5 additions & 2 deletions
7
app/src/main/assets/web_extensions/youtube_webcompat/main.css
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
div.alert-with-button-renderer { | ||
display:none; | ||
/* To hide harmless warnings. */ | ||
.alert-with-button-renderer, .ytm-alert-with-button-renderer, ytm-alert-with-button-renderer, | ||
.ytp-ad-module, ytp-ad-module | ||
.ytp-generic-popup, ytp-generic-popup { | ||
display: none; | ||
} |
205 changes: 197 additions & 8 deletions
205
app/src/main/assets/web_extensions/youtube_webcompat/main.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,197 @@ | ||
// Add meta-viewport | ||
let viewport = document.head.querySelector("meta[name='viewport']"); | ||
if (!viewport) { | ||
viewport = document.createElement("meta"); | ||
viewport.name = "viewport"; | ||
viewport.content = "width=user-width, initial-scale=1"; | ||
document.head.appendChild(viewport); | ||
} | ||
(function () { | ||
// If missing, inject a `<meta name="viewport">` tag to trigger YouTube's mobile layout. | ||
window.addEventListener('load', () => { | ||
let viewport = document.head.querySelector('meta[name="viewport"]'); | ||
if (!viewport) { | ||
viewport = document.createElement('meta'); | ||
viewport.name = 'viewport'; | ||
viewport.content = 'width=device-width, initial-scale=1'; | ||
document.head.appendChild(viewport); | ||
} | ||
}); | ||
|
||
const LOGTAG = '[firefoxreality:webcompat]' | ||
const qs = new URLSearchParams(window.location.search); | ||
let retryTimeout = null; | ||
|
||
function getTruthyQS (key) { | ||
if (!qs || !qs.has(key)) { | ||
return false; | ||
} | ||
const valueLower = (qs.get('key') || '').trim().toLowerCase(); | ||
return valueLower === '' || valueLower === '1' || valueLower === 'true' || valueLower === 'yes' || valueLower === 'on'; | ||
} | ||
|
||
const prefs = { | ||
hd: false, | ||
quality: 1440, | ||
log: qs.get('mozDebug') ? getTruthyQS('mozDebug') : true, | ||
retryAttempts: parseInt(qs.get('retryAttempts') || qs.get('retryattempts') || '10', 10), | ||
retryTimeout: parseInt(qs.get('retryTimeout') || qs.get('retrytimeout') || '500', 10) | ||
}; | ||
|
||
const printLog = String(prefs.log) === 'true'; | ||
|
||
const log = (...args) => printLog && console.log(LOGTAG, ...args); | ||
const logError = (...args) => printLog && console.error(LOGTAG, ...args); | ||
const logWarn = (...args) => printLog && console.warn(LOGTAG, ...args); | ||
|
||
const ytImprover = window.ytImprover = (state, attempts) => { | ||
if (ytImprover.completed) { | ||
return; | ||
} | ||
|
||
if (typeof attempts === 'undefined') { | ||
attempts = 1; | ||
} | ||
if (attempts >= prefs.retryAttempts) { | ||
logError(`Giving up trying to increase resolution after ${prefs.retryAttempts} attempts.`); | ||
return; | ||
} | ||
|
||
let player = document.getElementById('movie_player'); | ||
let reason = 'unknown'; | ||
if (state !== 1) { | ||
reason = 'invalid state'; | ||
} else if (!player) { | ||
reason = 'player not found'; | ||
} else if (!player.wrappedJSObject) { | ||
reason = 'player.wrappedJSObject not found'; | ||
player = null; | ||
} else if (!player.wrappedJSObject.getAvailableQualityLevels) { | ||
reason = 'player.wrappedJSObject.getAvailableQualityLevels not found'; | ||
player = null; | ||
} | ||
|
||
if (!player) { | ||
logWarn(`Cannot find player because ${reason}. attempts: ${attempts}`); | ||
attempts++; | ||
retryTimeout = setTimeout(() => { | ||
ytImprover(state, attempts); | ||
}, prefs.retryTimeout); | ||
return; | ||
} | ||
|
||
player = player.wrappedJSObject; | ||
|
||
const levels = player.getAvailableQualityLevels(); | ||
if (!levels || !levels.length) { | ||
logWarn(`Cannot read 'player.getAvailableQualityLevels()' attempts: ${attempts}`); | ||
attempts++; | ||
retryTimeout = setTimeout(() => { | ||
ytImprover(state, attempts); | ||
}, prefs.retryTimeout); | ||
return; | ||
} | ||
|
||
clearTimeout(retryTimeout); | ||
ytImprover.completed = true; | ||
|
||
prefs.qualities = [ | ||
'highres', 'h2880', 'hd2160', 'hd1440', 'hd1080', 'hd720', 'large', 'medium', 'small', 'tiny', 'auto' | ||
]; | ||
prefs.qualityLabels = { | ||
'4320': 'highres', // 8K / 4320p / QUHD | ||
'2880': 'hd2880', // 5K / 2880p / UHD+ | ||
'2160': 'hd2160', // 4K / 2160p / UHD | ||
'1440': 'hd1440', // 1440p / QHD | ||
'1080': 'hd1080', // 1080p / FHD | ||
'720': 'hd720', // 720p / HD | ||
'480': 'large', // 480p | ||
'360': 'medium', // 360p | ||
'240': 'small', // 240p | ||
'144': 'tiny', // 144p | ||
'0': 'auto' | ||
}; | ||
|
||
const getDesiredQuality = () => { | ||
const qsQuality = (qs.get('vq') || qs.get('quality') || '').trim().toLowerCase(); | ||
if (qsQuality) { | ||
if (qsQuality in prefs.qualityLabels) { | ||
prefs.quality = prefs.qualityLabels[qsQuality]; | ||
} else { | ||
const qsQualityNumber = parseInt(qsQuality, 10); | ||
if (Number.isInteger(qsQualityNumber)) { | ||
prefs.quality = qsQualityNumber; | ||
} else { | ||
prefs.quality = qsQuality; | ||
} | ||
} | ||
} | ||
prefs.quality = String(prefs.quality).toLowerCase(); | ||
if (qsQuality === 'auto' || qsQuality === 'default') { | ||
prefs.quality = 'auto'; | ||
} | ||
if (prefs.quality in prefs.qualityLabels) { | ||
prefs.quality = prefs.qualityLabels[prefs.quality]; | ||
} | ||
return prefs.quality; | ||
}; | ||
|
||
prefs.quality = getDesiredQuality(); | ||
if (prefs.quality === 'auto') { | ||
return log(`Desired quality is fine (${prefs.quality})`); | ||
} | ||
|
||
const currentQuality = player.getPlaybackQuality(); | ||
if (prefs.quality === currentQuality) { | ||
return log(`Current quality is desired quality (${currentQuality})`); | ||
} | ||
|
||
const findBestQuality = increase => { | ||
if (prefs.quality === 'highest' || prefs.quality === 'best' || prefs.quality === 'max' || prefs.quality === 'maximum') { | ||
return levels[0]; | ||
} | ||
if (prefs.quality === 'lowest' || prefs.quality === 'worst' || prefs.quality === 'min' || prefs.quality === 'minimum') { | ||
return levels[levels.length - 1]; | ||
} | ||
if (increase) { | ||
prefs.quality = prefs.qualities[prefs.qualities.indexOf(prefs.quality) - 1] || levels[0]; | ||
} | ||
const index = levels.indexOf(prefs.quality); | ||
if (index !== -1) { | ||
return prefs.quality; | ||
} | ||
return findBestQuality(true); | ||
}; | ||
const newBestQuality = findBestQuality(); | ||
if (currentQuality === newBestQuality) { | ||
return log(`Current quality "${currentQuality}" is the best available quality`); | ||
} | ||
|
||
if (!player.setPlaybackQuality) { | ||
return logError('`player.setPlaybackQuality` not available'); | ||
} | ||
player.setPlaybackQuality(newBestQuality); | ||
|
||
if (!player.setPlaybackQualityRange) { | ||
return logError('`player.setPlaybackQualityRange` not available'); | ||
} | ||
try { | ||
player.setPlaybackQualityRange(newBestQuality, newBestQuality); | ||
} catch (e) { | ||
logError(`Failed to call 'player.setPlaybackQualityRange(${newBestQuality}, ${newBestQuality})' with exception: `, e); | ||
return; | ||
} | ||
|
||
log(`Changed quality from "${currentQuality}" to "${newBestQuality}"`); | ||
}; | ||
|
||
if (window.location.pathname.startsWith('/watch')) { | ||
const onYouTubePlayerReady = window.onYouTubePlayerReady = evt => { | ||
log('`onYouTubePlayerReady` called'); | ||
window.ytImprover(1); | ||
evt.addEventListener('onStateChange', 'ytImprover'); | ||
}; | ||
|
||
window.addEventListener('spfready', () => { | ||
log('`spfready` event fired'); | ||
if (typeof window.ytplayer === 'object' && window.ytplayer.config) { | ||
log('`window.ytplayer.config.args.jsapicallback` set'); | ||
window.ytplayer.config.args.jsapicallback = 'onYouTubePlayerReady'; | ||
} | ||
}); | ||
|
||
ytImprover(1); | ||
} | ||
})(); |
20 changes: 14 additions & 6 deletions
20
app/src/main/assets/web_extensions/youtube_webcompat/manifest.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,22 @@ | ||
{ | ||
"manifest_version": 2, | ||
"name": "YoutubeWebCompat", | ||
"name": "FirefoxRealityYouTubeWebCompat", | ||
"version": "1.0", | ||
"description": "Youtube web compat fixes for Firefox Reality", | ||
"description": "YouTube web-site compatability fixes for Firefox Reality.", | ||
"content_scripts": [ | ||
{ | ||
"matches": ["*://*.youtube.com/*"], | ||
"css": ["main.css"], | ||
"js": ["main.js"], | ||
"run_at": "document_end" | ||
"matches": [ | ||
"*://*.youtube.com/*", | ||
"*://*.youtube-nocookie.com/*" | ||
], | ||
"css": [ | ||
"main.css" | ||
], | ||
"js": [ | ||
"main.js" | ||
], | ||
"run_at": "document_start", | ||
"all_frames": true | ||
} | ||
] | ||
} |