Allow returning page if transcript extraction failed
| @@ -22,59 +22,15 @@ const visitHeaders = { | ||
| 22 | 22 | 'Sec-Fetch-User': '?1', |
| 23 | 23 | }; |
| 24 | 24 | |
| 25 | -router.post('/serpapi', jsonParser, async (request, response) => { | |
| 26 | - try { | |
| 27 | - const key = readSecret(request.user.directories, SECRET_KEYS.SERPAPI); | |
| 28 | - | |
| 29 | - if (!key) { | |
| 30 | - console.log('No SerpApi key found'); | |
| 31 | - return response.sendStatus(400); | |
| 32 | - } | |
| 33 | - | |
| 34 | - const { query } = request.body; | |
| 35 | - const result = await fetch(`https://serpapi.com/search.json?q=${encodeURIComponent(query)}&api_key=${key}`); | |
| 36 | - | |
| 37 | - console.log('SerpApi query', query); | |
| 38 | - | |
| 39 | - if (!result.ok) { | |
| 40 | - const text = await result.text(); | |
| 41 | - console.log('SerpApi request failed', result.statusText, text); | |
| 42 | - return response.status(500).send(text); | |
| 43 | - } | |
| 44 | - | |
| 45 | - const data = await result.json(); | |
| 46 | - return response.json(data); | |
| 47 | - } catch (error) { | |
| 48 | - console.log(error); | |
| 49 | - return response.sendStatus(500); | |
| 50 | - } | |
| 51 | -}); | |
| 52 | - | |
| 53 | 25 | /** |
| 54 | 26 | * GetExtract the transcript of a YouTube video |
| 55 | - * @copyright https://github.com/Kakulukian/youtube-transcript (MIT License) | |
| 27 | + * @param {string} videoPageBody HTML of the video page | |
| 28 | + * @param {string} lang Language code | |
| 29 | + * @returns {Promise<string>} Transcript text | |
| 56 | 30 | */ |
| 57 | -router.post('/transcript', jsonParser, async (request, response) => { | |
| 31 | +async function extractTranscript(videoPageBody, lang) { | |
| 58 | - try { | |
| 59 | 32 | const he = require('he'); |
| 60 | 33 | const RE_XML_TRANSCRIPT = /<text start="([^"]*)" dur="([^"]*)">([^<]*)<\/text>/g; |
| 61 | - const id = request.body.id; | |
| 62 | - const lang = request.body.lang; | |
| 63 | - const json = request.body.json; | |
| 64 | - | |
| 65 | - if (!id) { | |
| 66 | - console.log('Id is required for /transcript'); | |
| 67 | - return response.sendStatus(400); | |
| 68 | - } | |
| 69 | - | |
| 70 | - const videoPageResponse = await fetch(`https://www.youtube.com/watch?v=${id}`, { | |
| 71 | - headers: { | |
| 72 | - ...(lang && { 'Accept-Language': lang }), | |
| 73 | - 'User-Agent': visitHeaders['User-Agent'], | |
| 74 | - }, | |
| 75 | - }); | |
| 76 | - | |
| 77 | - const videoPageBody = await videoPageResponse.text(); | |
| 78 | 34 | const splittedHTML = videoPageBody.split('"captions":'); |
| 79 | 35 | |
| 80 | 36 | if (splittedHTML.length <= 1) { |
| @@ -129,11 +85,73 @@ router.post('/transcript', jsonParser, async (request, response) => { | ||
| 129 | 85 | })); |
| 130 | 86 | // The text is double-encoded |
| 131 | 87 | const transcriptText = transcript.map((line) => he.decode(he.decode(line.text))).join(' '); |
| 88 | + return transcriptText; | |
| 89 | +} | |
| 90 | + | |
| 91 | +router.post('/serpapi', jsonParser, async (request, response) => { | |
| 92 | + try { | |
| 93 | + const key = readSecret(request.user.directories, SECRET_KEYS.SERPAPI); | |
| 94 | + | |
| 95 | + if (!key) { | |
| 96 | + console.log('No SerpApi key found'); | |
| 97 | + return response.sendStatus(400); | |
| 98 | + } | |
| 99 | + | |
| 100 | + const { query } = request.body; | |
| 101 | + const result = await fetch(`https://serpapi.com/search.json?q=${encodeURIComponent(query)}&api_key=${key}`); | |
| 102 | + | |
| 103 | + console.log('SerpApi query', query); | |
| 132 | 104 | |
| 105 | + if (!result.ok) { | |
| 106 | + const text = await result.text(); | |
| 107 | + console.log('SerpApi request failed', result.statusText, text); | |
| 108 | + return response.status(500).send(text); | |
| 109 | + } | |
| 110 | + | |
| 111 | + const data = await result.json(); | |
| 112 | + return response.json(data); | |
| 113 | + } catch (error) { | |
| 114 | + console.log(error); | |
| 115 | + return response.sendStatus(500); | |
| 116 | + } | |
| 117 | +}); | |
| 118 | + | |
| 119 | +/** | |
| 120 | + * Get the transcript of a YouTube video | |
| 121 | + * @copyright https://github.com/Kakulukian/youtube-transcript (MIT License) | |
| 122 | + */ | |
| 123 | +router.post('/transcript', jsonParser, async (request, response) => { | |
| 124 | + try { | |
| 125 | + const id = request.body.id; | |
| 126 | + const lang = request.body.lang; | |
| 127 | + const json = request.body.json; | |
| 128 | + | |
| 129 | + if (!id) { | |
| 130 | + console.log('Id is required for /transcript'); | |
| 131 | + return response.sendStatus(400); | |
| 132 | + } | |
| 133 | + | |
| 134 | + const videoPageResponse = await fetch(`https://www.youtube.com/watch?v=${id}`, { | |
| 135 | + headers: { | |
| 136 | + ...(lang && { 'Accept-Language': lang }), | |
| 137 | + 'User-Agent': visitHeaders['User-Agent'], | |
| 138 | + }, | |
| 139 | + }); | |
| 140 | + | |
| 141 | + const videoPageBody = await videoPageResponse.text(); | |
| 142 | + | |
| 143 | + try { | |
| 144 | + const transcriptText = await extractTranscript(videoPageBody, lang); | |
| 133 | 145 | return json |
| 134 | 146 | ? response.json({ transcript: transcriptText, html: videoPageBody }) |
| 135 | 147 | : response.send(transcriptText); |
| 136 | 148 | } catch (error) { |
| 149 | + if (json) { | |
| 150 | + return response.json({ html: videoPageBody, transcript: '' }); | |
| 151 | + } | |
| 152 | + throw error; | |
| 153 | + } | |
| 154 | + } catch (error) { | |
| 137 | 155 | console.log(error); |
| 138 | 156 | return response.sendStatus(500); |
| 139 | 157 | } |