1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
#!/bin/node
var cp = require('child_process');
var { parseStringPromise } = require('xml2js');
var fs = require('fs');
var { URLSearchParams } = require('url');
var axios = require("axios");
var puppeteer = require("puppeteer");
var cheerio = require("cheerio");
var url = process.argv[2];
if (!url) {
console.error("no input link!");
process.exit(1);
}
function getDLURLs(streaminfo, baseURL) {
var highestQuality = streaminfo.Representation.pop().$;
var toDownload = streaminfo.SegmentTemplate[0];
var urls = [];
var time = 0;
urls.push(toDownload.$.initialization.replace("$RepresentationID$", highestQuality.id));
toDownload.SegmentTimeline[0].S.forEach(segment => {
var repeat = Number(segment.$.r || 0) + 1;
for(let i = 0; i < repeat; i++) {
urls.push(toDownload.$.media
.replace("$RepresentationID$", highestQuality.id)
.replace("$Time$", time));
time += Number(segment.$.d);
}
});
urls = urls.map(url => baseURL + '/' + url);
return urls;
}
async function dlURLs(urls, filename) {
var file = fs.createWriteStream(filename);
for (let i = 0; i < urls.length; i++) {
console.log("downloading " + urls[i] + "...");
var res = await axios({
method: 'get',
url: urls[i],
responseType: 'arraybuffer'
});
file.write(res.data);
}
file.close();
}
(async () => {
var browser = await puppeteer.launch();
var page = await browser.newPage();
await page.goto(url, { waitUntil: 'networkidle2' });
var content = await page.content();
await browser.close();
var $ = cheerio.load(content);
var src = $("iframe").attr("src");
var res = await axios.get(src);
var tokenId = res.data.toString().match(/(?<!var\s+?)tokenId\s+?=\s+?'(.+?)'/s)[1];
url = "https://start-player.npo.nl/embed/" + tokenId;
var videojs = res.data.toString().match(/^\s+var\s+?video\s+?=.+$/m).toString().trim();
var video; eval(videojs);
var streaminfoURL = new URLSearchParams();
streaminfoURL.set("profile", "dash-widevine");
streaminfoURL.set("quality", "npo");
streaminfoURL.set("tokenId", tokenId);
streaminfoURL.set("streamType", "broadcast");
streaminfoURL.set("isYospace", "0");
streaminfoURL.set("videoAgeRating", JSON.stringify(video.age_rating));
streaminfoURL.set("isChromecast", "0");
streaminfoURL.set("mobile", "0");
streaminfoURL.set("ios", "0");
var streaminfo = await axios.get(`https://start-player.npo.nl/video/${video.id}/streams?` + streaminfoURL.toString());
var filename = video.title.toString().replace(/\s+/g, "-") + "_" +
video.id.replace(/\s+/g, '-').toLowerCase() + "_" +
"s" + video.seasonNumber.toString().padStart(2, '0') +
"e" + video.episodeNumber.toString().padStart(2, '0');
var streamSrc = streaminfo.data.stream.src;
var xmlString = await axios.get(streamSrc);
var baseURL = streamSrc.replace(/\/stream\.mpd$/, '');
var xml = await parseStringPromise(xmlString.data);
var videoStream = xml.MPD.Period[0].AdaptationSet.find(s => s.$.contentType == 'video');
await dlURLs(getDLURLs(videoStream, baseURL), filename + '.mp4v');
var audioStream = xml.MPD.Period[0].AdaptationSet.find(s => s.$.contentType == 'audio');
await dlURLs(getDLURLs(audioStream, baseURL), filename + '.mp4a');
cp.execSync(`ffmpeg -i ${filename}.mp4v -i ${filename}.mp4a -map 0:0 -map 1:0 ${filename}.mp4`);
fs.rmSync(filename + '.mp4v');
fs.rmSync(filename + '.mp4a');
})();
|