@@ -4,17 +4,20 @@ const { overEvery, isEmpty, eq, has, round, size, get, chain, find, isString } =
4
4
const { isUrl, titleize } = require ( '@metascraper/helpers' )
5
5
const youtubedl = require ( 'youtube-dl' )
6
6
const { promisify } = require ( 'util' )
7
+ const twdown = require ( 'twdown' )
8
+ const { URL } = require ( 'url' )
7
9
const path = require ( 'path' )
8
10
9
11
const getInfo = promisify ( youtubedl . getInfo )
10
12
13
+ const TWITTER_HOSTNAMES = [ 'twitter.com' , 'mobile.twitter.com' ]
14
+
15
+ const isTwitterUrl = url => TWITTER_HOSTNAMES . includes ( new URL ( url ) . hostname )
16
+
17
+ // Local cache for successive calls
11
18
let cachedVideoInfoUrl
12
19
let cachedVideoInfo
13
20
14
- /**
15
- * Get the video info.
16
- * Avoid do more one request for the same URL.
17
- */
18
21
const getVideoInfo = async url => {
19
22
if ( url === cachedVideoInfoUrl ) return cachedVideoInfo
20
23
cachedVideoInfoUrl = url
@@ -24,21 +27,21 @@ const getVideoInfo = async url => {
24
27
} catch ( err ) {
25
28
cachedVideoInfo = { }
26
29
}
27
-
28
30
return cachedVideoInfo
29
31
}
30
32
31
- const isMp4 = format => eq ( get ( format , 'ext' ) , 'mp4' ) || path . extname ( get ( format , 'url' ) ) . startsWith ( '.mp4' )
32
- const isHttp = format => eq ( get ( format , 'protocol' ) , 'http' )
33
- const isHttps = format => eq ( get ( format , 'protocol' ) , 'https' )
34
- const hasAudio = format => has ( format , 'abr' )
33
+ const isMp4 = video =>
34
+ eq ( get ( video , 'ext' ) , 'mp4' ) || path . extname ( get ( video , 'url' ) ) . startsWith ( '.mp4' )
35
+ const isHttp = video => eq ( get ( video , 'protocol' ) , 'http' )
36
+ const isHttps = video => eq ( get ( video , 'protocol' ) , 'https' )
37
+ const hasAudio = video => has ( video , 'abr' )
35
38
36
39
/**
37
40
* Get a Video source quality enough good
38
41
* compatible to be consumed for the browser.
39
42
*/
40
- const getVideoUrl = ( formats , filters = [ ] ) => {
41
- const urls = chain ( formats )
43
+ const getVideoUrl = ( videos , filters = [ ] ) => {
44
+ const urls = chain ( videos )
42
45
. filter ( overEvery ( filters ) )
43
46
. map ( 'url' )
44
47
. value ( )
@@ -51,9 +54,13 @@ const getVideoUrl = (formats, filters = []) => {
51
54
/**
52
55
* Get a URL-like video source.
53
56
*/
54
- const getVideoProvider = async ( { url } ) => {
55
- const { formats } = await getVideoInfo ( url )
56
- const videoUrl = getVideoUrl ( formats , [ isMp4 , isHttps , hasAudio ] ) ||
57
+ const getVideoProvider = getBrowserless => async ( { url } ) => {
58
+ const formats = ! isTwitterUrl ( url )
59
+ ? ( await getVideoInfo ( url ) ) . formats
60
+ : await twdown ( { url, browserless : await getBrowserless ( ) } )
61
+
62
+ const videoUrl =
63
+ getVideoUrl ( formats , [ isMp4 , isHttps , hasAudio ] ) ||
57
64
getVideoUrl ( formats , [ isMp4 , isHttp , hasAudio ] ) ||
58
65
getVideoUrl ( formats , [ isMp4 , isHttps ] ) ||
59
66
getVideoUrl ( formats , [ isMp4 ] ) ||
@@ -90,9 +97,9 @@ const getVideoDate = async ({ url }) => {
90
97
return timestamp && new Date ( timestamp * 1000 ) . toISOString ( )
91
98
}
92
99
93
- module . exports = ( ) => {
100
+ module . exports = ( { getBrowserless } ) => {
94
101
return {
95
- video : getVideoProvider ,
102
+ video : getVideoProvider ( getBrowserless ) ,
96
103
author : getVideoAuthor ,
97
104
publisher : getVideoPublisher ,
98
105
title : getVideoTitle ,
0 commit comments