-
-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to clone a YouTube video element so it can play the same video? #2
Comments
I've managed to intercept the underlying // Execute while the video is playing.
let capturedArgs;
URL._jumpcutterUnchangedCreateObjectURL = URL.createObjectURL;
URL.createObjectURL = function(...args) {
const toReturn = URL._jumpcutterUnchangedCreateObjectURL(...args);
capturedArgs = args;
console.log('gotcha!', args, toReturn);
return toReturn;
}
v = document.querySelector('video');
// Currently, removing `src` from a YouTube video makes it create a new one.
v.src = '';
setTimeout(() => {
v = document.querySelector('video');
v.play();
originalMediaSource = capturedArgs[0];
}, 500); But further attempts of attaching it to a new video did not go well: v2 = document.querySelector('video').cloneNode(true);
v2.src = URL.createObjectURL(originalMediaSource);
setTimeout(() => {
v2.play();
}, 100);
|
One option would be, although probably not really the nicest one, to create a new iFrame with |
When I clone the element I get the same error, however on Firefox you get a more verbose error:
I'm not sure if the security error is real and related to CSP/CORS/etc or if it’s just a generic error that is shown. Another thing on my mind is that YouTube could streaming the media in, via a JavaScript media stream object, directly into the player meaning the blob URL is just a placeholder. However I'm not an expert on media streams so more investigation will be necessary As an alternate solution; YouTube serves audio and video separately you are able to isolate the audio stream and play that in another element. I found another add-on that converts YouTube into an audio only mode. Audio Only YouTube GitHub It looks at the outgoing requests to find one with an audio mime type. It then takes that URL and strips off the URL parameters that provide DASH playback, and you end up with a full audio stream (mp3, m4a, ogg, webm, etc) I'm not entirely sure how caching/bandwidth increase/sync issues would affect this as I presume you would have to have another audio stream downloading at the same time, rather than using the current video. Also other sites that use DASH will do this differently, so maybe not a catch-all solution. |
@zznidar Damn, son. This is genius. Never thought of that.
Will need to think whether it makes sense to try to implement it now or to wait until we have a less workaround-y way. |
Not sure if you meant something more specific than
Yeah, it doesn't sound like it is. Although maybe there's some kind of a standard or a library that everyone uses for this. For YouTube specifically I had an idea of recognizing the current video ID, then utilizing YouTube API to create another video with that ID. But the |
@WofWca good points.
|
There may be a better option, however. If you take a look at this StackOverflow answer for drawing waveform: https://stackoverflow.com/a/67265439 I haven't really dived too deeply into it (it has been on my todo-list for almost half a year now), but at a glance it seems to me that the size of the red columns corresponds to the loudness of the audio at that time. Using this approach, it may be possible to pre-analyse the whole audio track and therefore skip the silent parts completely (and, furthermore, add a margin-before without any distortions). |
Yeah, but to be more precise it's just raw samples. The problem with this approach, however, is that it uses And it also demands that we know the media source and can download it independently of the main |
For a POC, just const createObjectURL = URL.createObjectURL;
URL.createObjectURL = function(...args) {
console.log("createObjectURL", args[0])
const realMediaSource = args[0];
const addSourceBuffer = realMediaSource.addSourceBuffer;
realMediaSource.addSourceBuffer = function(...args) {
console.log("addSourceBuffer", ...args)
const realSourceBuffer = addSourceBuffer.call(this, ...args)
if (!args[0].startsWith("audio")) {
return realSourceBuffer
}
const copyMediaSource = new MediaSource()
let copyBuffer
copyMediaSource.addEventListener("sourceopen", () => {
copyBuffer = copyMediaSource.addSourceBuffer(args[0])
})
const copyAudio = document.createElement("audio")
copyAudio.src = createObjectURL(copyMediaSource)
copyAudio.play()
console.log(copyAudio)
const orgAppendBuffer = realSourceBuffer.appendBuffer
realSourceBuffer.appendBuffer = function(...args) {
console.log("appendBuffer", this, ...args)
if (copyBuffer) {
copyBuffer.appendBuffer(...args)
}
return orgAppendBuffer.call(this, ...args)
}
return realSourceBuffer
}
return createObjectURL(...args);
} |
We've been waiting for your arrival. Man oh man. So all we had to do was intercept one layer deeper? Can this work when the video is already playing though? I'll need to research the Thanks a lot for descending to us! |
I did some reading, and looks like YouTube is using the DASH technique (but I'm not sure if it's 100% compatible). |
I did some more research in regards to #2 (comment), here are some things I found out. I short, my biggest concern is whether we can initialize our extension after the video has already started playing.
|
Aight my dudes, after 4 years in development, hopefully it would have been worth the wait. The prototype is working on YouTube! Cloning.on.YouTube.mp4Here's the build that you can play around with (you'll have to "Load unpacked"): dist-chromium.zip. Built from 2415783. |
Sorta works in Chromium on YouTube now Closes #2 Co-authored-by: Jonas Herzig <[email protected]>
@Johni0702 could you please clarify how this can be done? |
That's not something you can do. That's something the website author would have to have done, since they are the ones who implemented the original I don't know if there's any way to do this post-hoc (and I doubt it) but that shouldn't really be much of an issue because I'd imagine there's a way for addons to run before the page code does (meaning you'd just have to duplicate all sources just in case; assuming the browser doesn't actually do any decoding until you try to play the source, that should hopefully not be an issue; if it turns out it does, then you'd just have to store all the data you need to do it later). So it'd only really ever be an issue when the addon is freshly installed (people would have to refresh the page to properly use it). |
I mean, if they were to have done it, how would it work? |
It would "just work". You could just take the same MediaSource or even URL and use it for multiple video/audio elements. |
I mean, how would the website maker implement it? Sorry XD |
Thanks again for the input! It's extremely satisfying to implement it after all this time! And let's not stop here. If someone has another approach in mind, please share. |
As a solution to #1, we could create another video element that plays the same video, hide it, and shift its playback for just a moment before the original video's
.currentTime
. That way we can effectively read the audio data that is yet to be played by the "main" video.The problem is – I don't know how to clone a YouTube video element so it can play the same track.
This wouldn't work:
Because
So, how do I do it?
Summary of the below comments
The text was updated successfully, but these errors were encountered: