How to simulate video playing in Netflix without reloading the page? - javascript

I'm trying to write a chrome extension where I want to be able to start playing a Netflix video from the main page without having to navigate to the video URL using something like window.location.href = '...', which would cause the page to reload. I want to do this without reloading the page because the content script of a chrome extension is re-injected every time a page reload happens, which I don't want to happen every time.
For example, if you go to the netflix website and click on a movie, the video will start playing without actually doing a full page reload. This suggests that changes to the DOM are triggered via JavaScript? Does anyone know how this is done?
I tried looking at some of the Javascript code to see if there was a function I could call to trigger some sort of event, but the code was too complicated to understand. So another approach I thought of was to do a fetch with the url of the video that I want to play, and then simply replace the contents of the body element with that of the fetched html. For example:
fetch("https://www.netflix.com/watch/70174779")
.then(function(response) {
response.text().then(function(html) {
var parser = new DOMParser();
var doc = parser.parseFromString(html, "text/html");
document.getElementsByTagName(
"body"
)[0].innerHTML = doc.getElementsByTagName("body")[0].innerHTML;
});
})
.catch(function(err) {
console.log("Fetch Error :-S", err);
});
However, when I run this in the Chrome DevTools console, it just results in an empty page. I don't get any helpful errors either. I also tried replacing the entire html contents:
fetch("https://www.netflix.com/watch/70174779")
.then(function(response) {
response.text().then(function(html) {
document.open();
document.write(html);
document.close();
});
})
.catch(function(err) {
console.log("Fetch Error :-S", err);
});
But this also didn't work, and I get a bunch of errors in the console.

Related

Browser history broken when using push state

I have the following code in my application which tries to retrieve some data from a URL and then update the browser with the new URL.
const response = await fetch(url);
if (response.ok) {
...
window.history.pushState({ loaded: true }, null, url);
}
Now every time I click to re-fetch the data (with a new URL) the URL in the browser is updated successfully. However if I click back it does nothing unless I click enough times for it to go back to the referred page.
My first fix was to change the code above to the following:
const response = await fetch(url);
if (response.ok) {
...
if (window.history.state && window.history.state.loaded)
window.history.pushState({ loaded: true }, null, url);
else
window.history.replaceState({ loaded: true }, null);
}
Now when I click back after it initially loads it goes back to the referred page as I simply replace the state and don't push anything to the history stack, before I would have had to click back twice. However I still have the issue that when I re-fetch the data it will now push to the history stack and nothing happens when I click back.
I thought the easiest thing would be to reload the page when I click back and the data has loaded. Therefore I tried the following:
window.addEventListener('popstate', e => {
if (e.state && e.state.loaded)
window.location.href = window.location.pathname + window.location.search;
});
However now I can't seem to go back to the referred page. This is because even though I reload the page (in the "popstate" event listener) when it fetches the data it says the state has loaded.
I thought this would be null after the reload but since it's not I thought I would force the browser to make sure the initial state is null when it first loads:
window.addEventListener('load', e => {
window.history.replaceState(null, null);
});
However whilst this initially appeared to work I found this was only because I had the developer tools running (with caching disabled).
I'm sure I'm missing something basic as this seems to be quite a common problem. I'd appreciate it if someone could show me what I am doing wrong.
I've found that changing my fetch request to the following seems to work:
await fetch(url, { cache: 'no-store' });
Alternatively adding an addional query string parameter to the fetched url also works.
I'll leave this open for a little bit incase someone has a better solution.

How to print pdf from server after button click in Angular 2+

On my page I have button that should download concrete pdf file from backend and open the printing window. I tried answers here that included some blob stuff. It did not work. Tried to change route and embed the file in the HTML and after files would be downloaded to call window.print() on the page, but page was blank. Tried also printJS, but wasn't able to make it work, since it kept showing printJS is not a part of onclick function or something like that. Any advice would be helpful.
The only solution I came up with was to do it like this:
printPdf(){
this.network.getPdfHash()
.pipe(take(1))
.subscribe((res) => {
//res === url to the location of the PDF
let win = window.open(res, "_blank");
win.focus();
win.addEventListener(
"load",
() => {
setTimeout(() => {
//to give time for the browser to load the pdf
win.window.print();
}, 250);
},
true
);
});
}

Call content script on URL change

I'm new to Chrome extensions. I know there's lots of similar questions and info out there, but none of it seems to address this problem.
I need my content script to execute on every page that matches *://*.youtube.com/watch?v=*.
I've tried using the above value and *://*.youtube.com/* as the match property, but neither works supposedly due to the way YouTube handles requests. I've also tried using the onhashchange event, but of course YouTube doesn't use anchors in their URLs. I've read about webRequest, but I don't need the function to be called when somebody is scrolling through the comments and the page loads more comments.
All I need is a way to call my content script when the URL changes. How exactly can I accomplish this?
Additionally, I cannot load the content script at document_start because the extension scrapes the HTML and parses it.
I had the same issue and this is what I did. I added a background script to listen to all changes
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.url) {
chrome.tabs.sendMessage(tabId, {
message: 'hello',
url: changeInfo.url
});
}
});
Then in my content script, I listen to it and reload the URL
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if (request.message === 'hello') {
const { url } = request;
chrome.location.replace(url);
}
});

Retrieve html content of a page several seconds after it's loaded

I'm coding a script in nodejs to automatically retrieve data from an online directory.
Knowing that I had never done this, I chose javascript because it is a language I use every day.
I therefore from the few tips I could find on google use request with cheerios to easily access components of dom of the page.
I found and retrieved all the necessary information, the only missing step is to recover the link to the next page except that the one is generated 4 seconds after loading of page and link contains a hash so that this step Is unavoidable.
What I would like to do is to recover dom of page 4-5 seconds after its loading to be able to recover the link
I looked on the internet, and much advice to use PhantomJS for this manipulation, but I can not get it to work after many attempts with node.
This is my code :
#!/usr/bin/env node
require('babel-register');
import request from 'request'
import cheerio from 'cheerio'
import phantom from 'node-phantom'
phantom.create(function(err,ph) {
return ph.createPage(function(err,page) {
return page.open(url, function(err,status) {
console.log("opened site? ", status);
page.includeJs('http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js', function(err) {
//jQuery Loaded.
//Wait for a bit for AJAX content to load on the page. Here, we are waiting 5 seconds.
setTimeout(function() {
return page.evaluate(function() {
var tt = cheerio.load($this.html())
console.log(tt)
}, function(err,result) {
console.log(result);
ph.exit();
});
}, 5000);
});
});
});
});
but i get this error :
return ph.createPage(function (page) {
^
TypeError: ph.createPage is not a function
Is what I am about to do is the best way to do what I want to do? If not what is the simplest way? If so, where does my error come from?
If You dont have to use phantomjs You can use nightmare to do it.
It is pretty neat library to solve problems like yours, it uses electron as web browser and You can run it with or without showing window (You can also open developer tools like in Google Chrome)
It has only one flaw if You want to run it on server without graphical interface that You must install at least framebuffer.
Nightmare has method like wait(cssSelector) that will wait until some element appears on website.
Your code would be something like:
const Nightmare = require('nightmare');
const nightmare = Nightmare({
show: true, // will show browser window
openDevTools: true // will open dev tools in browser window
});
const url = 'http://hakier.pl';
const selector = '#someElementSelectorWitchWillAppearAfterSomeDelay';
nightmare
.goto(url)
.wait(selector)
.evaluate(selector => {
return {
nextPage: document.querySelector(selector).getAttribute('href')
};
}, selector)
.then(extracted => {
console.log(extracted.nextPage); //Your extracted data from evaluate
});
//this variable will be injected into evaluate callback
//it is required to inject required variables like this,
// because You have different - browser scope inside this
// callback and You will not has access to node.js variables not injected
Happy hacking!

Communicating between Chrome DevTools and content script in extension

(I have already read this and it didn't work, and I've done a lot of searching and experimentation to no avail.)
I am writing a Chrome extension (BigConsole) with the goal of building a better Console tab for the Chrome developer tools. This means I would like to execute user-input code in the context of the page with access to the DOM and other variables on the page. To do this, the communication is structured as follows:
devtools creates a panel where the user writes code
When the user wants to execute code from the panel, the panel sends a message to a background script with the code
The background script receives the message/code from panel and passes it on to the content script which is injected into the page
The content script receives the message/code from the background script and injects a script element into the page which then runs the code
The result of the script on the page is then posted back to the content script with window.postMessage
The content script listens for the message/result from the page and passes it on to the background script
The background script receives the message/result from the content script and passes it on to the panel
The panel receives the message/result from the background script and inserts it into the log of results
Whew.
Right now, when the user tries to run the code, nothing happens. I put a bunch of console.log()s into the code but nothing appears in the console. My main question is, what have I done wrong here with the message passing that results in nothing happening? Alternatively, I would love to be told that I am making this way too complicated and there is a better way of doing things. Simplified code below...
panel.js:
window.onload = function() {
var port = chrome.runtime.connect({name: "Eval in context"});
// Add the eval'd response to the console when the background page sends it back
port.onMessage.addListener(function (msg) {
addToConsole(msg, false);
});
document.getElementById('run').addEventListener('click', function() {
var s = document.getElementById('console').value;
try {
// Ask the background page to ask the content script to inject a script
// into the DOM that can finally eval `s` in the right context.
port.postMessage(s);
// Outputting `s` to the log in the panel works here,
// but console.log() does nothing, and I can't observe any
// results of port.postMessage
}
catch(e) {}
});
};
background.js:
chrome.runtime.onConnect.addListener(function (port) {
// Listen for message from the panel and pass it on to the content
port.onMessage.addListener(function (message) {
// Request a tab for sending needed information
chrome.tabs.query({'active': true,'currentWindow': true}, function (tabs) {
// Send message to content script
if (tab) {
chrome.tabs.sendMessage(tabs[0].id, message);
}
});
});
// Post back to Devtools from content
chrome.runtime.onMessage.addListener(function (message, sender) {
port.postMessage(message);
});
});
content.js:
// Listen for the content to eval from the panel via the background page
chrome.runtime.onMessage.addListener(function (message, sender) {
executeScriptInPageContext(message);
});
function executeScriptInPageContext(m) { alert(m); }
As pointed out by Alex, here's a typo in your code which prevents it from working.
Drop your current code and use chrome.devtools.inspectedWindow.eval to directly run the code and parse the results. This simplifies your complicated logic to:
devtools creates a panel where the user writes code
devtools runs code
devtools handles result
PS. There is a way to manipulate the existing console, but I recommend against using it, unless it's for personal use. Two different ways to do this are shown in this answer.

Categories