Initial commit

This commit is contained in:
feedthedogs committed 2023-09-02 10:44:15 +10:00
commit fc49fea217
10 files changed
+237

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
node_modules
_metadata
package-lock.json
+32
View File
@@ -0,0 +1,32 @@
# Enable Youtube PC VR in Chromium
I couldn't find a way to watch Youtube VR videos on the PC without paying?
This is a Chromium extension to enable WebXR on Youtube videos that are non-3D and 360 degrees thanks to https://threejs.org/
### Install Instructions - Easy way
1. Open the Edge extension store and download "Enable Youtube PC VR in Edge"
### Install Instructions - Manual way
1. Install a Chromium based browser - Chrome, Edge, Brave etc.
2. Download the project from github and unzip it to a folder
3. Open the extensions in the browser and enable developer mode
4. Click "Load Unpacked" and choose the folder
### Usage Instructions
1. Turn VR on first
2. Start a new browser so that it detects the hardware
3. Open a non-3D 360 video on https://www.youtube.com/vr/
4. Click on the white cardboard icon in the bottom right hand corner of the video
5. Click on the "Enter VR" button in the middle bottom of the video
6. Enjoy
### Troubleshooting
If it says "VR NOT SUPPPORTED" then the browser hasn't detected your hardware
- Make sure your VR is turned on and calibrated to your room
- Make sure you have given Youtube.com permission to access "Virtual reality" in the "site permissions"
- I have tested this on a HTC Vive Cosmos, please let me know if it works on other hardware configurations - it is generic and should work most devices
If you don't see the white cardboard icon in the bottom right hand corner of the youtube.com video then the extension is not installed or disabled
There isn't much error handling, pull requests welcome
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

+110
View File
@@ -0,0 +1,110 @@
import * as THREE from 'three';
import { VRButton } from 'three/addons/webxr/VRButton.js';
let camera, scene, renderer;
// Function to be executed when the cardboard icon is clicked
function handleCardboardIconClick() {
const videoElement = document.querySelectorAll('video')[0]
console.log("clicked cardboard button");
EnableVRVideo(videoElement)
animate();
}
// Function to add the cardboard icon to a video element
function addCardboardIconToVideo() {
// Create a new <img> element for the cardboard icon
const iconImg = document.createElement('img');
const cardboardimg = 'data:application/octet-stream;base64,iVBORw0KGgoAAAANSUhEUgAAADQAAAA0CAYAAADFeBvrAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAANnSURBVGhD7ZhNaxNBGMezMdsEigRa8IUeBMGUCj1JUehFQSwe6hfwoDf9BB700Lsfwp7Egy+oVQqiR6keJAejhEgC6UmkREjMa5vE/9P8DaYzm9lNUkjo/GDYeWb+88zz7OzMbhKyWCwWi8VisUwsDq8KjUZj1nXdGKrtTsvY4CC2SjQa/U27ByWhVCoVXlhYeOg4zi0UF01jl1C73a6jrGez2fuJRKLFdj17e3t3IZ4IEOsdht0lzOv/XON1EljhtYuSEBKXx2wi0MWqW6Fx2zP9UGLVJTTR2ITGnYETwoZM4dh822q1tlCvsnlg4KMmvsSn+Gbz8Ozu7r6GQ08w6Xdorubz+WMcEqrX6/Nof0lJYJrN5gbe/ufpLpTL5SKYYwU+M5RokVg5xJt+CWGCHIKfo1QB/S8o9Q2SeYWh2k8wJHkGPvOUKgydEPpuUqYFyZ5FAEXKjUD7B2MSHK4Fj+BtyhV0CfneQxi/gzv2jqYWfDDmoPtE0wi0WxiToakFCcue2qFpxHdC+FAtYPIiTU+g22bVCLR5Vj2Zmpoqytw0jQRZoelarRal6Ql0s6wa8aPFCkVlbppGgqzQHFZoiaaWSqUyA90lmkagvShjaGrBCi3J3DSNBHoPhcPhtWQy2T2uD4KE72HyUzT/IZs/LUXqnaYO0J6WMTQVZC6wRnMw+p1yAo7ZZ9Vq9STl++CdFMFp9ICSLkjiB/xdTqfTESlSlzZ2d5Gx4oPu9pE5ZC5KtOhOOeX8F1EkElmlqQW+fqK8QZG7fgIrdx13e7HT2wF9dQS6jEfmC5v2wUl5Af4/Qt+zH6H/imQ3Uf2FvnmUVZSDq90D/G+4rnuDph7TCvkFd/c5XSpIH2VDoVuhQHsoCJhvnVWFfn3DcigJIeB0uVx+T1NB+kRDc6QcVkKP4/F4jaaC9ImG5kgZeUIItIw98oSmJ6IRLc2RoSSESVgbDIzfxMmWpemJaERLc2QoCeEIHiojHL2PWDUSRKsDx7qvP0k+8xoY3PFvhULhA00josWYYX6dmmMtlUrT8obG3RPkbe+3bONFd4VufIMX7bKMPeDLVFqI8anESjddPP+sx5f1Il5cx1E1PYIOvreaeFQzsVhM+we6Ccw1gwDPIU4/h5QjPynwDTi6/x0sFovFYrEcCUKhvx967WCqRNtOAAAAAElFTkSuQmCC'
iconImg.src = cardboardimg
iconImg.className = 'ytp-button';
// Attach the onclick event to the cardboard icon
iconImg.onclick = handleCardboardIconClick;
const rightControls = document.querySelector('.ytp-right-controls');
if (rightControls) {
// Append the image to the div element
rightControls.appendChild(iconImg);
}
}
// Create a MutationObserver to watch for new video elements
const observer = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(addedNode => {
// Check if the added node is a video element
if (addedNode instanceof HTMLVideoElement) {
addCardboardIconToVideo(addedNode);
}
});
});
});
// Attach the cardboard icon to each video element
const videoElements = document.querySelectorAll('video');
videoElements.forEach(video => {
addCardboardIconToVideo(video);
});
// Start observing the entire document for changes
observer.observe(document, { childList: true, subtree: true });
function EnableVRVideo(videoElement) {
console.log("enabling VR on " + videoElement);
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 2000 );
camera.layers.enable( 1 ); // render left view when no stereo available
const texture = new THREE.VideoTexture(videoElement);
texture.colorSpace = THREE.SRGBColorSpace;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x101010 );
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
// Create the sphere geometry
const sphereGeometry = new THREE.SphereGeometry(500, 60, 40);
sphereGeometry.scale(-1, 1, 1); // Invert the geometry to match equirectangular projection
// Create the sphere material
const sphereMaterial = new THREE.MeshBasicMaterial({ map: texture });
// Create the video sphere
const videoSphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
scene.add(videoSphere);
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.xr.enabled = true;
renderer.xr.setReferenceSpaceType( 'local' );
const container = document.querySelector('.html5-video-container')
container.appendChild( renderer.domElement );
// clean alerts box and add VR button
const alertsbox = document.querySelector('#alerts')
alertsbox.removeChild(alertsbox.firstChild);
alertsbox.appendChild( VRButton.createButton( renderer ) );
document.querySelector('#VRButton').style.position='relative'
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
renderer.setAnimationLoop( render );
}
function render() {
renderer.render( scene, camera );
}
+33
View File
@@ -0,0 +1,33 @@
{
"name": "Enable Youtube PC VR in Chromium browsers",
"version": "1.0.0",
"description": "Enables PC-VR for non-3D spherical youtube videos - Tested on HTC Vive Cosmos",
"manifest_version": 3,
"author": "feedthedogs",
"content_scripts": [
{
"matches": [ "*://*.youtube.com/*",
"*://*.googlevideo.com/*"],
"js": ["script.js"]
}
],
"permissions": [
"declarativeNetRequest"
],
"declarative_net_request": {
"rule_resources": [
{
"id": "useagent",
"enabled": true,
"path": "rules.json"
}
]
},
"icons": {
"128": "icon.png"
},
"host_permissions": [
"*://*.youtube.com/*",
"*://*.googlevideo.com/*"
]
}
+18
View File
@@ -0,0 +1,18 @@
{
"name": "Enable Youtube PC VR in Chromium",
"version": "1.0.0",
"description": "Enables PC-VR for non-3D spherical youtube videos - Tested on HTC Vive Cosmos",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "feedthedogs",
"license": "ISC",
"dependencies": {
"three": "^0.155.0"
},
"devDependencies": {
"webpack": "^5.88.2",
"webpack-cli": "^5.1.4"
}
}
+20
View File
@@ -0,0 +1,20 @@
[
{
"id" : 1,
"priority": 1,
"action": {
"type": "modifyHeaders",
"requestHeaders": [
{ "header": "User-Agent", "operation": "set", "value": "oculusbrowser" }
]
},
"condition" : {
"urlFilter" : "|http*",
"resourceTypes" : ["script", "main_frame"],
"initiatorDomains": [
"youtube.com",
"googlevideo.com"
]
}
}
]
+2
View File
@@ -0,0 +1,2 @@
/*! For license information please see script.js.LICENSE.txt */
(()=>{"use strict";const t="155",e=1,n=2,i=3,r=100,a=0,s=1,o=2,l=0,c=1,h=2,u=3,d=4,p=5,f=301,m=302,g=306,_=1e3,v=1001,x=1002,M=1003,S=1005,y=1006,E=1008,T=1009,b=1012,A=1014,w=1015,R=1016,C=1020,L=1023,P=1026,U=1027,D=33776,I=33777,N=33778,O=33779,F=36492,z=2300,B=2301,H=2302,V=3001,G="srgb",k="srgb-linear",W="display-p3",X=7680,j="300 es",q=1035,Y=2e3,K=2001;class Z{addEventListener(t,e){void 0===this._listeners&&(this._listeners={});const n=this._listeners;void 0===n[t]&&(n[t]=[]),-1===n[t].indexOf(e)&&n[t].push(e)}hasEventListener(t,e){if(void 0===this._listeners)return!1;const n=this._listeners;return void 0!==n[t]&&-1!==n[t].indexOf(e)}removeEventListener(t,e){if(void 0===this._listeners)return;const n=this._listeners[t];if(void 0!==n){const t=n.indexOf(e);-1!==t&&n.splice(t,1)}}dispatchEvent(t){if(void 0===this._listeners)return;const e=this._listeners[t.type];if(void 0!==e){t.target=this;const n=e.slice(0);for(let e=0,i=n.length;e<i;e++)n[e].call(this,t);t.target=null}}}const J=["00","01","02","03","04","05","06","07","08","09","0a","0b","0c","0d","0e","0f","10","11","12","13","14","15","16","17","18","19","1a","1b","1c","1d","1e","1f","20","21","22","23","24","25","26","27","28","29","2a","2b","2c","2d","2e","2f","30","31","32","33","34","35","36","37","38","39","3a","3b","3c","3d","3e","3f","40","41","42","43","44","45","46","47","48","49","4a","4b","4c","4d","4e","4f","50","51","52","53","54","55","56","57","58","59","5a","5b","5c","5d","5e","5f","60","61","62","63","64","65","66","67","68","69","6a","6b","6c","6d","6e","6f","70","71","72","73","74","75","76","77","78","79","7a","7b","7c","7d","7e","7f","80","81","82","83","84","85","86","87","88","89","8a","8b","8c","8d","8e","8f","90","91","92","93","94","95","96","97","98","99","9a","9b","9c","9d","9e","9f","a0","a1","a2","a3","a4","a5","a6","a7","a8","a9","aa","ab","ac","ad","ae","af","b0","b1","b2","b3","b4","b5","b6","b7","b8","b9","ba","bb","bc","bd","be","bf","c0","c1","c2","c3","c4","c5","c6","c7","c8","c9","ca","cb","cc","cd","ce","cf","d0","d1","d2","d3","d4","d5","d6","d7","d8","d9","da","db","dc","dd","de","df","e0","e1","e2","e3","e4","e5","e6","e7","e8","e9","ea","eb","ec","ed","ee","ef","f0","f1","f2","f3","f4","f5","f6","f7","f8","f9","fa","fb","fc","fd","fe","ff"],Q=Math.PI/180,$=180/Math.PI;function tt(){const t=4294967295*Math.random()|0,e=4294967295*Math.random()|0,n=4294967295*Math.random()|0,i=4294967295*Math.random()|0;return(J[255&t]+J[t>>8&255]+J[t>>16&255]+J[t>>24&255]+"-"+J[255&e]+J[e>>8&255]+"-"+J[e>>16&15|64]+J[e>>24&255]+"-"+J[63&n|128]+J[n>>8&255]+"-"+J[n>>16&255]+J[n>>24&255]+J[255&i]+J[i>>8&255]+J[i>>16&255]+J[i>>24&255]).toLowerCase()}function et(t,e,n){return Math.max(e,Math.min(n,t))}function nt(t,e,n){return(1-n)*t+n*e}function it(t){return 0==(t&t-1)&&0!==t}function rt(t){return Math.pow(2,Math.floor(Math.log(t)/Math.LN2))}function at(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return t/4294967295;case Uint16Array:return t/65535;case Uint8Array:return t/255;case Int32Array:return Math.max(t/2147483647,-1);case Int16Array:return Math.max(t/32767,-1);case Int8Array:return Math.max(t/127,-1);default:throw new Error("Invalid component type.")}}function st(t,e){switch(e.constructor){case Float32Array:return t;case Uint32Array:return Math.round(4294967295*t);case Uint16Array:return Math.round(65535*t);case Uint8Array:return Math.round(255*t);case Int32Array:return Math.round(2147483647*t);case Int16Array:return Math.round(32767*t);case Int8Array:return Math.round(127*t);default:throw new Error("Invalid component type.")}}class ot{constructor(t=0,e=0){ot.prototype.isVector2=!0,this.x=t,this.y=e}get width(){return this.x}set width(t){this.x=t}get height(){return this.y}set height(t){this.y=t}set(t,e){return this.x=t,this.y=e,this}setScalar(t){return this.x=t,this.y=t,this}setX(t){return this.x=t,this}setY(t){return this.y=t,this}setComponent(t,e){switch(t){case 0:this.x=e;break;case 1:this.y=e;break;default:throw new Error("index is out of range: "+t)}return this}getComponent(t){switch(t){case 0:return this.x;case 1:return this.y;default:throw new Error("index is out of range: "+t)}}clone(){return new this.constructor(this.x,this.y)}copy(t){return this.x=t.x,this.y=t.y,this}add(t){return this.x+=t.x,this.y+=t.y,this}addScalar(t){return this.x+=t,this.y+=t,this}addVectors(t,e){return this.x=t.x+e.x,this.y=t.y+e.y,this}addScaledVector(t,e){return this.x+=t.x*e,this.y+=t.y*e,this}sub(t){return this.x-=t.x,this.y-=t.y,this}subScalar(t){return this.x-=t,this.y-=t,this}subVectors(t,e){return this.x=t.x-e.x,this.y=t.y-e.y,this}multiply(t){return this.x*=t.x,this.y*=t.y,this}multiplyScalar(t){return this.x*=t,this.y*=t,this}divide(t){return this.x/=t.x,this.y/=t.y,this}divideScalar(t){return this.multiplyScalar(1/t)}applyMatrix3(t){const e=this.x,n=this.y,i=t.elements;return this.x=i[0]*e+i[3]*n+i[6],this.y=i[1]*e+i[4]*n+i[7],this}min(t){return this.x=Math.min(this.x,t.x),this.y=Math.min(this.y,t.Line truncated
+5
View File
@@ -0,0 +1,5 @@
/**
* @license
* Copyright 2010-2023 Three.js Authors
* SPDX-License-Identifier: MIT
*/
+14
View File
@@ -0,0 +1,14 @@
const path = require('path');
module.exports = {
entry: './index.js',
output: {
filename: 'script.js',
path: __dirname,
},
resolve: {
fallback: {
fs: false
}
}
};