Skip to content

Commit 0c62bfd

Browse files
committed
fix(sources): use ytdl-core to fetch youtube video info
fixes #16
1 parent 06c19b4 commit 0c62bfd

File tree

6 files changed

+52
-33
lines changed

6 files changed

+52
-33
lines changed

background/SizedMap.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* @param {!Array.<Object>|string} list
77
* @param {number} ttl
88
*/
9-
export default class {
9+
export default class SizedMap {
1010
constructor(limit, list, ttl) {
1111
this.limit = limit;
1212
this._ttl = ttl;

background/sources/videos/twitch.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export default {
8181
},
8282
};
8383
},
84-
ttl: 1000 * 60 * 30 // 30min
84+
ttl: 1000 * 60 * 60 // 1hr
8585
},
8686
headers: { 'Authorization': `OAuth ${token}` },
8787
});

background/sources/videos/youtube.js

Lines changed: 47 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,67 @@
11
import * as util from '../../util.js';
2+
import SizedMap from '../../SizedMap.js';
23

4+
import ytdl from '../../../lib/ytdl-core.min.js';
5+
const ytdlCache = new SizedMap(100, 'cache-youtube', 1000 * 60 * 60); // 1hr
6+
7+
8+
// ytdl-core requires the User-Agent header to be empty to scrape the watch
9+
// page correctly. XMLHttpRequest doesn't allow removing this header,
10+
// so we have to use the `webRequest` API.
11+
// Removing the header from the list of headers doesn't work either,
12+
// we have to set it to a blank value.
13+
chrome.webRequest.onBeforeSendHeaders.addListener((details) => {
14+
let header = details.requestHeaders.find(h => h.name === 'User-Agent');
15+
if (header) { header.value = ''; }
16+
return { requestHeaders: details.requestHeaders };
17+
}, {
18+
urls: [
19+
'https://www.youtube.com/watch?v=*',
20+
'https://www.youtube.com/get_video_info*'
21+
],
22+
types: ['xmlhttprequest'],
23+
}, ['blocking', 'requestHeaders']);
324

425
export default {
526
patterns: [
627
'*://www.youtube.com/watch?v=*',
728
'*://m.youtube.com/watch?v=*',
829
'*://youtu.be/*',
30+
'*://music.youtube.com/watch?v=*',
31+
'*://gaming.youtube.com/watch?v=*',
932
],
1033
getVideo: async (url) => {
11-
const r = /(?:v=|youtu\.be\/)([^?&$]+)/.exec(url);
12-
let id;
13-
if (r) {
14-
id = r[1];
15-
} else {
16-
throw Error('Could not get video ID of URL: ' + url);
34+
const id = ytdl.getURLVideoID(url);
35+
if (ytdlCache.has(id)) {
36+
return id;
1737
}
18-
const meta = await util.ajax('https://www.youtube.com/get_video_info', {
19-
data: { ps: 'default', gl: 'US', hl: 'en', video_id: id },
20-
cache: {
21-
transform: (response) => {
22-
if (response.status === 'fail') {
23-
throw Error('failed to get video info: ' + response.reason);
24-
}
25-
return {
26-
length: parseInt(response.length_seconds, 10),
27-
title: response.title,
28-
views: parseInt(response.view_count, 10),
29-
user: { name: response.author },
30-
};
31-
},
32-
ttl: 1000 * 60 * 30 // 30 min
33-
},
34-
});
35-
return {
38+
const info = await ytdl.getBasicInfo(url);
39+
const meta = {
3640
// Canonical YouTube URL.
37-
url: 'https://www.youtube.com/watch?v=' + id,
41+
url: info.video_url,
42+
length: parseInt(info.length_seconds, 10),
43+
title: info.title,
44+
views: parseInt(info.view_count, 10),
45+
user: {
46+
url: info.author.channel_url,
47+
name: info.author.name,
48+
thumbnail: info.author.avatar,
49+
verified: info.author.verified,
50+
},
3851

3952
// Using medium quality gives a screenshot without black bars.
40-
thumbnail: 'https://i.ytimg.com/vi/' + id +
53+
thumbnail: 'https://i.ytimg.com/vi/' + info.video_id +
4154
'/mqdefault.jpg?custom=true&w=196&h=110&stc=true&jpg444=true&' +
4255
'jpgq=90&sp=68',
4356

44-
game: null,
45-
...meta,
57+
game: info.media && info.media.game ? {
58+
name: info.media.game,
59+
url: info.media.game_url,
60+
thumbnail: info.media.image,
61+
} : null,
4662
};
63+
ytdlCache.push(id, meta);
64+
return meta;
4765
},
4866
getAllVideos: async () => {
4967
const body = await util.ajax('https://www.youtube.com/feed/subscriptions', {
@@ -110,8 +128,7 @@ export default {
110128
url: videoUrl,
111129
thumbnail,
112130
title: item.title.simpleText,
113-
desc:
114-
item.descriptionSnippet && item.descriptionSnippet.simpleText,
131+
desc: item.descriptionSnippet && item.descriptionSnippet.simpleText,
115132
length: length ? util.timeToSeconds(length.simpleText) : null,
116133
views: views ? parseInt(views.replace(/,/g, ''), 10) : null,
117134
timestamp,

background/util.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,6 @@ const request = async (url, opts) => {
7171
const response = await fetch(url, {
7272
credentials: 'include',
7373
redirect: 'follow',
74-
cache: 'force-cache',
7574
headers: opts.headers,
7675
});
7776
ajax.active--;

lib/ytdl-core.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

manifest.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
"https://t.co/",
2121
"contextMenus",
2222
"cookies",
23+
"webRequest",
24+
"webRequestBlocking",
2325
"notifications",
2426
"storage"
2527
],

0 commit comments

Comments
 (0)