|
1 | 1 | 'use strict'
|
2 | 2 |
|
3 |
| -const { isUrl } = require('@metascraper/helpers') |
| 3 | +const { round, size, get, chain, find, isString } = require('lodash') |
| 4 | +const { isUrl, titleize } = require('@metascraper/helpers') |
4 | 5 | const youtubedl = require('youtube-dl')
|
5 | 6 | const { promisify } = require('util')
|
6 | 7 | const path = require('path')
|
7 | 8 |
|
8 |
| -const getVideoInfo = promisify(youtubedl.getInfo) |
| 9 | +const getInfo = promisify(youtubedl.getInfo) |
| 10 | + |
| 11 | +let cachedVideoInfoUrl |
| 12 | +let cachedVideoInfo |
9 | 13 |
|
10 | 14 | /**
|
11 |
| - * Get a Video source quality not too high |
| 15 | + * Get the video info. |
| 16 | + * Avoid do more one request for the same URL. |
12 | 17 | */
|
13 |
| -const getVideoUrl = ({formats}) => { |
14 |
| - const urls = formats |
15 |
| - .filter(item => |
16 |
| - item.protocol === 'https' && |
17 |
| - (item.ext === 'mp4' || |
18 |
| - path.extname(item.url).startsWith('.mp4')) |
19 |
| - ) |
20 |
| - .map(item => item.url) |
21 |
| - |
22 |
| - const index = Math.round(urls.length / 2) - 1 |
23 |
| - return urls[index] |
24 |
| -} |
25 |
| - |
26 |
| -const getVideoProvider = async url => { |
| 18 | +const getVideoInfo = async url => { |
| 19 | + if (url === cachedVideoInfoUrl) return cachedVideoInfo |
27 | 20 | try {
|
28 |
| - const info = await getVideoInfo(url) |
29 |
| - const videoUrl = getVideoUrl(info) |
30 |
| - return isUrl(videoUrl) && videoUrl |
| 21 | + const info = await getInfo(url) |
| 22 | + cachedVideoInfoUrl = url |
| 23 | + cachedVideoInfo = info |
| 24 | + return info |
31 | 25 | } catch (err) {
|
32 |
| - return false |
| 26 | + return {} |
33 | 27 | }
|
34 | 28 | }
|
35 | 29 |
|
| 30 | +const isMp4 = format => format.ext === 'mp4' || path.extname(format.url).startsWith('.mp4') |
| 31 | +const isHttp = format => format.protocol === 'https' || format.protocol === 'http' |
| 32 | + |
| 33 | +/** |
| 34 | + * Get a Video source quality enough good |
| 35 | + * compatible to be consumed for the browser. |
| 36 | + */ |
| 37 | +const getVideoUrl = formats => { |
| 38 | + const urls = chain(formats) |
| 39 | + .filter(format => isHttp(format) && isMp4(format)) |
| 40 | + .map('url') |
| 41 | + .value() |
| 42 | + |
| 43 | + const index = round(size(urls) / 2) - 1 |
| 44 | + return get(urls, index) |
| 45 | +} |
| 46 | + |
| 47 | +/** |
| 48 | + * Get a URL-like video source. |
| 49 | + */ |
| 50 | +const getVideoProvider = async ({url}) => { |
| 51 | + const { formats } = await getVideoInfo(url) |
| 52 | + const videoUrl = getVideoUrl(formats) |
| 53 | + return isUrl(videoUrl) && videoUrl |
| 54 | +} |
| 55 | + |
| 56 | +/** |
| 57 | + * Get the Author of the video. |
| 58 | + */ |
| 59 | +const getVideoAuthor = async ({url}) => { |
| 60 | + const { uploader, creator, uploader_id: uploaderId } = await getVideoInfo(url) |
| 61 | + const author = find([creator, uploader, uploaderId], str => ( |
| 62 | + isString(str) && !isUrl(str, {relative: false}) |
| 63 | + )) |
| 64 | + return author && titleize(author, {removeBy: true}) |
| 65 | +} |
| 66 | + |
| 67 | +const getVideoPublisher = async ({url}) => { |
| 68 | + const { extractor_key: extractorKey } = await getVideoInfo(url) |
| 69 | + return isString(extractorKey) && extractorKey |
| 70 | +} |
| 71 | + |
| 72 | +const getVideoTitle = async ({url}) => { |
| 73 | + const { title: mainTitle, alt_title: secondaryTitle } = await getVideoInfo(url) |
| 74 | + const title = find([mainTitle, secondaryTitle], isString) |
| 75 | + return title && titleize(title) |
| 76 | +} |
| 77 | + |
| 78 | +const getVideoDate = async ({url}) => { |
| 79 | + const { timestamp } = await getVideoInfo(url) |
| 80 | + return timestamp && new Date(timestamp * 1000).toISOString() |
| 81 | +} |
| 82 | + |
36 | 83 | module.exports = () => {
|
37 | 84 | return {
|
38 |
| - video: ({url}) => getVideoProvider(url) |
| 85 | + video: getVideoProvider, |
| 86 | + author: getVideoAuthor, |
| 87 | + publisher: getVideoPublisher, |
| 88 | + title: getVideoTitle, |
| 89 | + date: getVideoDate |
39 | 90 | }
|
40 | 91 | }
|
41 | 92 |
|
|
0 commit comments