youtube-transcript-nodejs 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +337 -0
- package/dist/cjs/api/InnerTubeClient.js +236 -0
- package/dist/cjs/api/InnerTubeClient.js.map +1 -0
- package/dist/cjs/api/YouTubeTranscriptApi.js +160 -0
- package/dist/cjs/api/YouTubeTranscriptApi.js.map +1 -0
- package/dist/cjs/api/fetchStrategies.js +51 -0
- package/dist/cjs/api/fetchStrategies.js.map +1 -0
- package/dist/cjs/config/constants.js +34 -0
- package/dist/cjs/config/constants.js.map +1 -0
- package/dist/cjs/config/innerTubeContext.js +82 -0
- package/dist/cjs/config/innerTubeContext.js.map +1 -0
- package/dist/cjs/errors/BotDetected.js +20 -0
- package/dist/cjs/errors/BotDetected.js.map +1 -0
- package/dist/cjs/errors/FailedToCreateConsentCookie.js +17 -0
- package/dist/cjs/errors/FailedToCreateConsentCookie.js.map +1 -0
- package/dist/cjs/errors/InvalidVideoId.js +16 -0
- package/dist/cjs/errors/InvalidVideoId.js.map +1 -0
- package/dist/cjs/errors/IpBlocked.js +17 -0
- package/dist/cjs/errors/IpBlocked.js.map +1 -0
- package/dist/cjs/errors/NoTranscriptFound.js +23 -0
- package/dist/cjs/errors/NoTranscriptFound.js.map +1 -0
- package/dist/cjs/errors/NotTranslatable.js +15 -0
- package/dist/cjs/errors/NotTranslatable.js.map +1 -0
- package/dist/cjs/errors/PoTokenRequired.js +18 -0
- package/dist/cjs/errors/PoTokenRequired.js.map +1 -0
- package/dist/cjs/errors/TooManyRequests.js +19 -0
- package/dist/cjs/errors/TooManyRequests.js.map +1 -0
- package/dist/cjs/errors/TranscriptsDisabled.js +15 -0
- package/dist/cjs/errors/TranscriptsDisabled.js.map +1 -0
- package/dist/cjs/errors/TranslationLanguageNotAvailable.js +16 -0
- package/dist/cjs/errors/TranslationLanguageNotAvailable.js.map +1 -0
- package/dist/cjs/errors/VideoUnavailable.js +15 -0
- package/dist/cjs/errors/VideoUnavailable.js.map +1 -0
- package/dist/cjs/errors/VideoUnplayable.js +22 -0
- package/dist/cjs/errors/VideoUnplayable.js.map +1 -0
- package/dist/cjs/errors/YouTubeDataUnparsable.js +15 -0
- package/dist/cjs/errors/YouTubeDataUnparsable.js.map +1 -0
- package/dist/cjs/errors/YouTubeRequestFailed.js +18 -0
- package/dist/cjs/errors/YouTubeRequestFailed.js.map +1 -0
- package/dist/cjs/errors/YouTubeTranscriptError.js +19 -0
- package/dist/cjs/errors/YouTubeTranscriptError.js.map +1 -0
- package/dist/cjs/errors/index.js +34 -0
- package/dist/cjs/errors/index.js.map +1 -0
- package/dist/cjs/formatters/Formatter.js +3 -0
- package/dist/cjs/formatters/Formatter.js.map +1 -0
- package/dist/cjs/formatters/JSONFormatter.js +24 -0
- package/dist/cjs/formatters/JSONFormatter.js.map +1 -0
- package/dist/cjs/formatters/PrettyPrintFormatter.js +26 -0
- package/dist/cjs/formatters/PrettyPrintFormatter.js.map +1 -0
- package/dist/cjs/formatters/SRTFormatter.js +39 -0
- package/dist/cjs/formatters/SRTFormatter.js.map +1 -0
- package/dist/cjs/formatters/TextFormatter.js +20 -0
- package/dist/cjs/formatters/TextFormatter.js.map +1 -0
- package/dist/cjs/formatters/WebVTTFormatter.js +39 -0
- package/dist/cjs/formatters/WebVTTFormatter.js.map +1 -0
- package/dist/cjs/formatters/base.js +61 -0
- package/dist/cjs/formatters/base.js.map +1 -0
- package/dist/cjs/formatters/index.js +19 -0
- package/dist/cjs/formatters/index.js.map +1 -0
- package/dist/cjs/http/cookieJar.js +91 -0
- package/dist/cjs/http/cookieJar.js.map +1 -0
- package/dist/cjs/http/httpClient.js +228 -0
- package/dist/cjs/http/httpClient.js.map +1 -0
- package/dist/cjs/index.js +50 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/models/FetchedTranscript.js +80 -0
- package/dist/cjs/models/FetchedTranscript.js.map +1 -0
- package/dist/cjs/models/FetchedTranscriptSnippet.js +29 -0
- package/dist/cjs/models/FetchedTranscriptSnippet.js.map +1 -0
- package/dist/cjs/models/FormattedTranscript.js +3 -0
- package/dist/cjs/models/FormattedTranscript.js.map +1 -0
- package/dist/cjs/models/Transcript.js +75 -0
- package/dist/cjs/models/Transcript.js.map +1 -0
- package/dist/cjs/models/TranscriptList.js +84 -0
- package/dist/cjs/models/TranscriptList.js.map +1 -0
- package/dist/cjs/models/TranscriptMetadata.js +3 -0
- package/dist/cjs/models/TranscriptMetadata.js.map +1 -0
- package/dist/cjs/models/TranslationLanguage.js +3 -0
- package/dist/cjs/models/TranslationLanguage.js.map +1 -0
- package/dist/cjs/models/index.js +13 -0
- package/dist/cjs/models/index.js.map +1 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/parsers/captionTrackParser.js +96 -0
- package/dist/cjs/parsers/captionTrackParser.js.map +1 -0
- package/dist/cjs/parsers/videoIdParser.js +65 -0
- package/dist/cjs/parsers/videoIdParser.js.map +1 -0
- package/dist/cjs/parsers/watchHtmlParser.js +78 -0
- package/dist/cjs/parsers/watchHtmlParser.js.map +1 -0
- package/dist/cjs/parsers/xmlTranscriptParser.js +84 -0
- package/dist/cjs/parsers/xmlTranscriptParser.js.map +1 -0
- package/dist/cjs/potoken/AutoPoTokenProvider.js +203 -0
- package/dist/cjs/potoken/AutoPoTokenProvider.js.map +1 -0
- package/dist/cjs/potoken/HttpPoTokenProvider.js +105 -0
- package/dist/cjs/potoken/HttpPoTokenProvider.js.map +1 -0
- package/dist/cjs/potoken/NoopPoTokenProvider.js +18 -0
- package/dist/cjs/potoken/NoopPoTokenProvider.js.map +1 -0
- package/dist/cjs/potoken/baseScriptCache.js +41 -0
- package/dist/cjs/potoken/baseScriptCache.js.map +1 -0
- package/dist/cjs/potoken/index.js +14 -0
- package/dist/cjs/potoken/index.js.map +1 -0
- package/dist/cjs/potoken/types.js +10 -0
- package/dist/cjs/potoken/types.js.map +1 -0
- package/dist/cjs/util/htmlSanitizer.js +77 -0
- package/dist/cjs/util/htmlSanitizer.js.map +1 -0
- package/dist/cjs/util/proxyUrl.js +42 -0
- package/dist/cjs/util/proxyUrl.js.map +1 -0
- package/dist/cjs/util/retry.js +65 -0
- package/dist/cjs/util/retry.js.map +1 -0
- package/dist/esm/api/InnerTubeClient.d.ts +61 -0
- package/dist/esm/api/InnerTubeClient.d.ts.map +1 -0
- package/dist/esm/api/InnerTubeClient.js +198 -0
- package/dist/esm/api/InnerTubeClient.js.map +1 -0
- package/dist/esm/api/YouTubeTranscriptApi.d.ts +88 -0
- package/dist/esm/api/YouTubeTranscriptApi.d.ts.map +1 -0
- package/dist/esm/api/YouTubeTranscriptApi.js +157 -0
- package/dist/esm/api/YouTubeTranscriptApi.js.map +1 -0
- package/dist/esm/api/fetchStrategies.d.ts +20 -0
- package/dist/esm/api/fetchStrategies.d.ts.map +1 -0
- package/dist/esm/api/fetchStrategies.js +48 -0
- package/dist/esm/api/fetchStrategies.js.map +1 -0
- package/dist/esm/config/constants.d.ts +30 -0
- package/dist/esm/config/constants.d.ts.map +1 -0
- package/dist/esm/config/constants.js +31 -0
- package/dist/esm/config/constants.js.map +1 -0
- package/dist/esm/config/innerTubeContext.d.ts +47 -0
- package/dist/esm/config/innerTubeContext.d.ts.map +1 -0
- package/dist/esm/config/innerTubeContext.js +77 -0
- package/dist/esm/config/innerTubeContext.js.map +1 -0
- package/dist/esm/errors/BotDetected.d.ts +13 -0
- package/dist/esm/errors/BotDetected.d.ts.map +1 -0
- package/dist/esm/errors/BotDetected.js +16 -0
- package/dist/esm/errors/BotDetected.js.map +1 -0
- package/dist/esm/errors/FailedToCreateConsentCookie.d.ts +10 -0
- package/dist/esm/errors/FailedToCreateConsentCookie.d.ts.map +1 -0
- package/dist/esm/errors/FailedToCreateConsentCookie.js +13 -0
- package/dist/esm/errors/FailedToCreateConsentCookie.js.map +1 -0
- package/dist/esm/errors/InvalidVideoId.d.ts +9 -0
- package/dist/esm/errors/InvalidVideoId.d.ts.map +1 -0
- package/dist/esm/errors/InvalidVideoId.js +12 -0
- package/dist/esm/errors/InvalidVideoId.js.map +1 -0
- package/dist/esm/errors/IpBlocked.d.ts +10 -0
- package/dist/esm/errors/IpBlocked.d.ts.map +1 -0
- package/dist/esm/errors/IpBlocked.js +13 -0
- package/dist/esm/errors/IpBlocked.js.map +1 -0
- package/dist/esm/errors/NoTranscriptFound.d.ts +19 -0
- package/dist/esm/errors/NoTranscriptFound.d.ts.map +1 -0
- package/dist/esm/errors/NoTranscriptFound.js +19 -0
- package/dist/esm/errors/NoTranscriptFound.js.map +1 -0
- package/dist/esm/errors/NotTranslatable.d.ts +9 -0
- package/dist/esm/errors/NotTranslatable.d.ts.map +1 -0
- package/dist/esm/errors/NotTranslatable.js +11 -0
- package/dist/esm/errors/NotTranslatable.js.map +1 -0
- package/dist/esm/errors/PoTokenRequired.d.ts +10 -0
- package/dist/esm/errors/PoTokenRequired.d.ts.map +1 -0
- package/dist/esm/errors/PoTokenRequired.js +14 -0
- package/dist/esm/errors/PoTokenRequired.js.map +1 -0
- package/dist/esm/errors/TooManyRequests.d.ts +12 -0
- package/dist/esm/errors/TooManyRequests.d.ts.map +1 -0
- package/dist/esm/errors/TooManyRequests.js +15 -0
- package/dist/esm/errors/TooManyRequests.js.map +1 -0
- package/dist/esm/errors/TranscriptsDisabled.d.ts +9 -0
- package/dist/esm/errors/TranscriptsDisabled.d.ts.map +1 -0
- package/dist/esm/errors/TranscriptsDisabled.js +11 -0
- package/dist/esm/errors/TranscriptsDisabled.js.map +1 -0
- package/dist/esm/errors/TranslationLanguageNotAvailable.d.ts +9 -0
- package/dist/esm/errors/TranslationLanguageNotAvailable.d.ts.map +1 -0
- package/dist/esm/errors/TranslationLanguageNotAvailable.js +12 -0
- package/dist/esm/errors/TranslationLanguageNotAvailable.js.map +1 -0
- package/dist/esm/errors/VideoUnavailable.d.ts +9 -0
- package/dist/esm/errors/VideoUnavailable.d.ts.map +1 -0
- package/dist/esm/errors/VideoUnavailable.js +11 -0
- package/dist/esm/errors/VideoUnavailable.js.map +1 -0
- package/dist/esm/errors/VideoUnplayable.d.ts +12 -0
- package/dist/esm/errors/VideoUnplayable.d.ts.map +1 -0
- package/dist/esm/errors/VideoUnplayable.js +18 -0
- package/dist/esm/errors/VideoUnplayable.js.map +1 -0
- package/dist/esm/errors/YouTubeDataUnparsable.d.ts +9 -0
- package/dist/esm/errors/YouTubeDataUnparsable.d.ts.map +1 -0
- package/dist/esm/errors/YouTubeDataUnparsable.js +11 -0
- package/dist/esm/errors/YouTubeDataUnparsable.js.map +1 -0
- package/dist/esm/errors/YouTubeRequestFailed.d.ts +10 -0
- package/dist/esm/errors/YouTubeRequestFailed.d.ts.map +1 -0
- package/dist/esm/errors/YouTubeRequestFailed.js +14 -0
- package/dist/esm/errors/YouTubeRequestFailed.js.map +1 -0
- package/dist/esm/errors/YouTubeTranscriptError.d.ts +10 -0
- package/dist/esm/errors/YouTubeTranscriptError.d.ts.map +1 -0
- package/dist/esm/errors/YouTubeTranscriptError.js +15 -0
- package/dist/esm/errors/YouTubeTranscriptError.js.map +1 -0
- package/dist/esm/errors/index.d.ts +17 -0
- package/dist/esm/errors/index.d.ts.map +1 -0
- package/dist/esm/errors/index.js +16 -0
- package/dist/esm/errors/index.js.map +1 -0
- package/dist/esm/formatters/Formatter.d.ts +33 -0
- package/dist/esm/formatters/Formatter.d.ts.map +1 -0
- package/dist/esm/formatters/Formatter.js +2 -0
- package/dist/esm/formatters/Formatter.js.map +1 -0
- package/dist/esm/formatters/JSONFormatter.d.ts +10 -0
- package/dist/esm/formatters/JSONFormatter.d.ts.map +1 -0
- package/dist/esm/formatters/JSONFormatter.js +20 -0
- package/dist/esm/formatters/JSONFormatter.js.map +1 -0
- package/dist/esm/formatters/PrettyPrintFormatter.d.ts +15 -0
- package/dist/esm/formatters/PrettyPrintFormatter.d.ts.map +1 -0
- package/dist/esm/formatters/PrettyPrintFormatter.js +22 -0
- package/dist/esm/formatters/PrettyPrintFormatter.js.map +1 -0
- package/dist/esm/formatters/SRTFormatter.d.ts +22 -0
- package/dist/esm/formatters/SRTFormatter.d.ts.map +1 -0
- package/dist/esm/formatters/SRTFormatter.js +35 -0
- package/dist/esm/formatters/SRTFormatter.js.map +1 -0
- package/dist/esm/formatters/TextFormatter.d.ts +10 -0
- package/dist/esm/formatters/TextFormatter.d.ts.map +1 -0
- package/dist/esm/formatters/TextFormatter.js +16 -0
- package/dist/esm/formatters/TextFormatter.js.map +1 -0
- package/dist/esm/formatters/WebVTTFormatter.d.ts +22 -0
- package/dist/esm/formatters/WebVTTFormatter.d.ts.map +1 -0
- package/dist/esm/formatters/WebVTTFormatter.js +35 -0
- package/dist/esm/formatters/WebVTTFormatter.js.map +1 -0
- package/dist/esm/formatters/base.d.ts +25 -0
- package/dist/esm/formatters/base.d.ts.map +1 -0
- package/dist/esm/formatters/base.js +55 -0
- package/dist/esm/formatters/base.js.map +1 -0
- package/dist/esm/formatters/index.d.ts +8 -0
- package/dist/esm/formatters/index.d.ts.map +1 -0
- package/dist/esm/formatters/index.js +7 -0
- package/dist/esm/formatters/index.js.map +1 -0
- package/dist/esm/http/cookieJar.d.ts +39 -0
- package/dist/esm/http/cookieJar.d.ts.map +1 -0
- package/dist/esm/http/cookieJar.js +87 -0
- package/dist/esm/http/cookieJar.js.map +1 -0
- package/dist/esm/http/httpClient.d.ts +88 -0
- package/dist/esm/http/httpClient.d.ts.map +1 -0
- package/dist/esm/http/httpClient.js +191 -0
- package/dist/esm/http/httpClient.js.map +1 -0
- package/dist/esm/index.d.ts +19 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +14 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/models/FetchedTranscript.d.ts +49 -0
- package/dist/esm/models/FetchedTranscript.d.ts.map +1 -0
- package/dist/esm/models/FetchedTranscript.js +76 -0
- package/dist/esm/models/FetchedTranscript.js.map +1 -0
- package/dist/esm/models/FetchedTranscriptSnippet.d.ts +15 -0
- package/dist/esm/models/FetchedTranscriptSnippet.d.ts.map +1 -0
- package/dist/esm/models/FetchedTranscriptSnippet.js +25 -0
- package/dist/esm/models/FetchedTranscriptSnippet.js.map +1 -0
- package/dist/esm/models/FormattedTranscript.d.ts +9 -0
- package/dist/esm/models/FormattedTranscript.d.ts.map +1 -0
- package/dist/esm/models/FormattedTranscript.js +2 -0
- package/dist/esm/models/FormattedTranscript.js.map +1 -0
- package/dist/esm/models/Transcript.d.ts +36 -0
- package/dist/esm/models/Transcript.d.ts.map +1 -0
- package/dist/esm/models/Transcript.js +71 -0
- package/dist/esm/models/Transcript.js.map +1 -0
- package/dist/esm/models/TranscriptList.d.ts +48 -0
- package/dist/esm/models/TranscriptList.d.ts.map +1 -0
- package/dist/esm/models/TranscriptList.js +79 -0
- package/dist/esm/models/TranscriptList.js.map +1 -0
- package/dist/esm/models/TranscriptMetadata.d.ts +10 -0
- package/dist/esm/models/TranscriptMetadata.d.ts.map +1 -0
- package/dist/esm/models/TranscriptMetadata.js +2 -0
- package/dist/esm/models/TranscriptMetadata.js.map +1 -0
- package/dist/esm/models/TranslationLanguage.d.ts +8 -0
- package/dist/esm/models/TranslationLanguage.d.ts.map +1 -0
- package/dist/esm/models/TranslationLanguage.js +2 -0
- package/dist/esm/models/TranslationLanguage.js.map +1 -0
- package/dist/esm/models/index.d.ts +8 -0
- package/dist/esm/models/index.d.ts.map +1 -0
- package/dist/esm/models/index.js +5 -0
- package/dist/esm/models/index.js.map +1 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/parsers/captionTrackParser.d.ts +29 -0
- package/dist/esm/parsers/captionTrackParser.d.ts.map +1 -0
- package/dist/esm/parsers/captionTrackParser.js +91 -0
- package/dist/esm/parsers/captionTrackParser.js.map +1 -0
- package/dist/esm/parsers/videoIdParser.d.ts +11 -0
- package/dist/esm/parsers/videoIdParser.d.ts.map +1 -0
- package/dist/esm/parsers/videoIdParser.js +61 -0
- package/dist/esm/parsers/videoIdParser.js.map +1 -0
- package/dist/esm/parsers/watchHtmlParser.d.ts +33 -0
- package/dist/esm/parsers/watchHtmlParser.d.ts.map +1 -0
- package/dist/esm/parsers/watchHtmlParser.js +70 -0
- package/dist/esm/parsers/watchHtmlParser.js.map +1 -0
- package/dist/esm/parsers/xmlTranscriptParser.d.ts +17 -0
- package/dist/esm/parsers/xmlTranscriptParser.d.ts.map +1 -0
- package/dist/esm/parsers/xmlTranscriptParser.js +81 -0
- package/dist/esm/parsers/xmlTranscriptParser.js.map +1 -0
- package/dist/esm/potoken/AutoPoTokenProvider.d.ts +28 -0
- package/dist/esm/potoken/AutoPoTokenProvider.d.ts.map +1 -0
- package/dist/esm/potoken/AutoPoTokenProvider.js +166 -0
- package/dist/esm/potoken/AutoPoTokenProvider.js.map +1 -0
- package/dist/esm/potoken/HttpPoTokenProvider.d.ts +41 -0
- package/dist/esm/potoken/HttpPoTokenProvider.d.ts.map +1 -0
- package/dist/esm/potoken/HttpPoTokenProvider.js +101 -0
- package/dist/esm/potoken/HttpPoTokenProvider.js.map +1 -0
- package/dist/esm/potoken/NoopPoTokenProvider.d.ts +12 -0
- package/dist/esm/potoken/NoopPoTokenProvider.d.ts.map +1 -0
- package/dist/esm/potoken/NoopPoTokenProvider.js +14 -0
- package/dist/esm/potoken/NoopPoTokenProvider.js.map +1 -0
- package/dist/esm/potoken/baseScriptCache.d.ts +28 -0
- package/dist/esm/potoken/baseScriptCache.d.ts.map +1 -0
- package/dist/esm/potoken/baseScriptCache.js +37 -0
- package/dist/esm/potoken/baseScriptCache.js.map +1 -0
- package/dist/esm/potoken/index.d.ts +10 -0
- package/dist/esm/potoken/index.d.ts.map +1 -0
- package/dist/esm/potoken/index.js +6 -0
- package/dist/esm/potoken/index.js.map +1 -0
- package/dist/esm/potoken/types.d.ts +40 -0
- package/dist/esm/potoken/types.d.ts.map +1 -0
- package/dist/esm/potoken/types.js +7 -0
- package/dist/esm/potoken/types.js.map +1 -0
- package/dist/esm/util/htmlSanitizer.d.ts +21 -0
- package/dist/esm/util/htmlSanitizer.d.ts.map +1 -0
- package/dist/esm/util/htmlSanitizer.js +69 -0
- package/dist/esm/util/htmlSanitizer.js.map +1 -0
- package/dist/esm/util/proxyUrl.d.ts +22 -0
- package/dist/esm/util/proxyUrl.d.ts.map +1 -0
- package/dist/esm/util/proxyUrl.js +39 -0
- package/dist/esm/util/proxyUrl.js.map +1 -0
- package/dist/esm/util/retry.d.ts +37 -0
- package/dist/esm/util/retry.d.ts.map +1 -0
- package/dist/esm/util/retry.js +60 -0
- package/dist/esm/util/retry.js.map +1 -0
- package/package.json +111 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 youtube-transcript-plus contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
# youtube-transcript-nodejs
|
|
2
|
+
|
|
3
|
+
A complete Node.js + TypeScript port of [jdepoix/youtube-transcript-api](https://github.com/jdepoix/youtube-transcript-api), with optional self-contained PoToken generation.
|
|
4
|
+
|
|
5
|
+
[]() []() []() []()
|
|
6
|
+
|
|
7
|
+
## Why this port?
|
|
8
|
+
|
|
9
|
+
The Python library is the de-facto standard for YouTube transcript fetching. It works by issuing requests via the **ANDROID InnerTube client**, which historically is not subject to YouTube's PoToken bot-detection. Node.js has lacked a 1:1 equivalent:
|
|
10
|
+
|
|
11
|
+
| Capability | jdepoix Python | `youtube-transcript` | `youtube-transcript-nodejs` (this) |
|
|
12
|
+
|---|---|---|---|
|
|
13
|
+
| `fetch` transcript | ✅ | ✅ | ✅ |
|
|
14
|
+
| `list` all tracks | ✅ | ❌ | ✅ |
|
|
15
|
+
| `translate` to another language | ✅ | ❌ | ✅ |
|
|
16
|
+
| `translationLanguages` enumeration | ✅ | ❌ | ✅ |
|
|
17
|
+
| 5 formatters (SRT/VTT/JSON/Text/Pretty) | ✅ | ❌ | ✅ |
|
|
18
|
+
| Full error hierarchy | ✅ | ⚠️ | ✅ |
|
|
19
|
+
| Proxy / custom cookies / headers | ✅ | ❌ | ✅ |
|
|
20
|
+
| Self-contained PoToken | ❌ | ❌ | ✅ (opt-in) |
|
|
21
|
+
| EU CONSENT cookie handling | ❌ | ❌ | ✅ |
|
|
22
|
+
| TypeScript-first, ESM + CJS dual | ❌ | ⚠️ | ✅ |
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm install youtube-transcript-nodejs
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Requires **Node.js ≥ 20** (for native `fetch` and stable ESM).
|
|
31
|
+
|
|
32
|
+
The core API has **no runtime dependencies** beyond `he` and `fast-xml-parser`. Optional peer dependencies (`bgutils-js`, `undici`) are only loaded when used:
|
|
33
|
+
|
|
34
|
+
- `bgutils-js` — only required for `AutoPoTokenProvider`
|
|
35
|
+
- `undici` — only required when you pass `proxyConfig`
|
|
36
|
+
|
|
37
|
+
## Quick Start
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
import { YouTubeTranscriptApi } from 'youtube-transcript-nodejs';
|
|
41
|
+
|
|
42
|
+
const api = new YouTubeTranscriptApi();
|
|
43
|
+
const transcript = await api.fetch('dQw4w9WgXcQ');
|
|
44
|
+
console.log(transcript.toText().content.slice(0, 200));
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Test Results
|
|
48
|
+
|
|
49
|
+
End-to-end verified against the live YouTube API (test video: `dQw4w9WgXcQ`).
|
|
50
|
+
|
|
51
|
+
### Unit tests — 182 / 182 passing
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
✓ test/unit/parsers/captionTrackParser.test.ts (12 tests)
|
|
55
|
+
✓ test/unit/config/config.test.ts (15 tests)
|
|
56
|
+
✓ test/unit/util/util.test.ts (19 tests)
|
|
57
|
+
✓ test/unit/formatters/formatters.test.ts (22 tests)
|
|
58
|
+
✓ test/unit/http/httpClient.test.ts ( 8 tests)
|
|
59
|
+
✓ test/unit/errors/errors.test.ts (21 tests)
|
|
60
|
+
✓ test/unit/api/innerTubeClient.test.ts (18 tests)
|
|
61
|
+
✓ test/unit/potoken/potoken.test.ts (11 tests)
|
|
62
|
+
✓ test/unit/parsers/watchHtmlParser.test.ts (11 tests)
|
|
63
|
+
✓ test/unit/parsers/videoIdParser.test.ts (12 tests)
|
|
64
|
+
✓ test/unit/api/youTubeTranscriptApi.test.ts (11 tests)
|
|
65
|
+
✓ test/unit/http/cookieJar.test.ts ( 7 tests)
|
|
66
|
+
✓ test/unit/parsers/xmlTranscriptParser.test.ts ( 7 tests)
|
|
67
|
+
✓ test/unit/potoken/autoPoTokenProvider.test.ts ( 6 tests)
|
|
68
|
+
✓ test/unit/smoke.test.ts ( 1 test)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Live integration tests — 4 / 4 passing
|
|
72
|
+
|
|
73
|
+
Run with `RUN_LIVE=1 YT_TEST_VIDEO_ID=dQw4w9WgXcQ npm run test:live`.
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
✓ live: fetch > fetches the transcript for a public video (6.4 s)
|
|
77
|
+
✓ live: list > returns at least one transcript track (5.9 s)
|
|
78
|
+
✓ live: translate > translates to a target language (11.7 s)
|
|
79
|
+
✓ live: translationLanguages > returns many translation targets (6.6 s)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Build & lint
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
✓ npm run lint — 0 errors, 0 warnings (ESLint strict)
|
|
86
|
+
✓ npm run typecheck — tsc --noEmit (strict mode)
|
|
87
|
+
✓ npm run build — ESM (dist/esm) + CJS (dist/cjs) dual publish
|
|
88
|
+
✓ npm pack --dry-run — 76.4 KB tarball
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Real output (sanitized)
|
|
92
|
+
|
|
93
|
+
```text
|
|
94
|
+
$ npx tsx examples/basic.ts
|
|
95
|
+
Video: dQw4w9WgXcQ
|
|
96
|
+
Language: English (en)
|
|
97
|
+
Generated: false
|
|
98
|
+
Snippets: 61
|
|
99
|
+
---
|
|
100
|
+
[♪♪♪]
|
|
101
|
+
♪ We're no strangers to love ♪
|
|
102
|
+
♪ You know the rules
|
|
103
|
+
and so do I ♪
|
|
104
|
+
♪ A full commitment's
|
|
105
|
+
what I'm thinking of ♪
|
|
106
|
+
♪ You wouldn't get this
|
|
107
|
+
from any other guy ♪
|
|
108
|
+
|
|
109
|
+
$ npx tsx examples/list.ts
|
|
110
|
+
Video: dQw4w9WgXcQ
|
|
111
|
+
Manually created: en, de-DE, ja, pt-BR, es-419
|
|
112
|
+
Auto-generated: en
|
|
113
|
+
Translation targets: 18
|
|
114
|
+
Selected: English (en, manual)
|
|
115
|
+
First cue: [1.36s] [♪♪♪]
|
|
116
|
+
|
|
117
|
+
$ head -10 dQw4w9WgXcQ.es.srt # after `tsx examples/translate.ts`
|
|
118
|
+
1
|
|
119
|
+
00:00:01,360 --> 00:00:03,040
|
|
120
|
+
[♪♪♪]
|
|
121
|
+
|
|
122
|
+
2
|
|
123
|
+
00:00:18,640 --> 00:00:21,880
|
|
124
|
+
♪ No somos extraños al amor ♪
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## API Reference
|
|
128
|
+
|
|
129
|
+
### `new YouTubeTranscriptApi(config?)`
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
interface YouTubeTranscriptApiConfig {
|
|
133
|
+
/** HTTP proxy URL. Triggers undici dynamic import. */
|
|
134
|
+
proxyConfig?: { url: string };
|
|
135
|
+
/** Pre-set cookies (e.g. CONSENT for EU users). */
|
|
136
|
+
cookies?: Array<{ name: string; value: string; domain?: string; path?: string }>;
|
|
137
|
+
/** Default headers added to every request. */
|
|
138
|
+
headers?: Record<string, string>;
|
|
139
|
+
/** Replace the default NoopPoTokenProvider. */
|
|
140
|
+
poTokenProvider?: PoTokenProvider;
|
|
141
|
+
/** When true, ANDROID failures automatically retry via WEB + PoToken. */
|
|
142
|
+
poTokenFallback?: boolean;
|
|
143
|
+
/** Custom User-Agent override. */
|
|
144
|
+
userAgent?: string;
|
|
145
|
+
/** Custom fetch implementation (testing). */
|
|
146
|
+
fetchImpl?: typeof fetch;
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Methods
|
|
151
|
+
|
|
152
|
+
#### `api.fetch(videoId, opts?): Promise<FetchedTranscript>`
|
|
153
|
+
|
|
154
|
+
```ts
|
|
155
|
+
const t = await api.fetch('dQw4w9WgXcQ', {
|
|
156
|
+
languages: ['en', 'en-US', 'en-GB'], // ordered preference
|
|
157
|
+
preserveFormatting: false, // keep <i>/<b> tags
|
|
158
|
+
});
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
#### `api.list(videoId): Promise<TranscriptList>`
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
const list = await api.list('dQw4w9WgXcQ');
|
|
165
|
+
console.log('manual:', list.manuallyCreatedTranscripts.map(t => t.languageCode));
|
|
166
|
+
console.log('auto:', list.generatedTranscripts.map(t => t.languageCode));
|
|
167
|
+
|
|
168
|
+
const en = list.findTranscript(['en', 'en-US']);
|
|
169
|
+
const data = await en.fetch();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
#### `api.translate(videoId, targetLang, opts?): Promise<FetchedTranscript>`
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
const zh = await api.translate('dQw4w9WgXcQ', 'zh-Hans');
|
|
176
|
+
console.log(zh.toSRT().content);
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
#### `api.translationLanguages(videoId, sourceLang): Promise<TranslationLanguage[]>`
|
|
180
|
+
|
|
181
|
+
```ts
|
|
182
|
+
const langs = await api.translationLanguages('dQw4w9WgXcQ', 'en');
|
|
183
|
+
// [{ languageCode: 'es', languageName: 'Spanish' }, ...]
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
#### `api.setPoTokenProvider(provider): void`
|
|
187
|
+
|
|
188
|
+
Swap the provider at runtime.
|
|
189
|
+
|
|
190
|
+
## Formatters
|
|
191
|
+
|
|
192
|
+
`FetchedTranscript` exposes five convenience methods plus a generic `format()`:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
t.toRaw(); // JSON array of { text, start, duration }
|
|
196
|
+
t.toText(); // Plain text, one snippet per line
|
|
197
|
+
t.toSRT(); // SRT subtitle format
|
|
198
|
+
t.toVTT(); // WebVTT subtitle format
|
|
199
|
+
t.toPretty(); // [HH:MM:SS.mmm] text
|
|
200
|
+
|
|
201
|
+
// Or bring your own:
|
|
202
|
+
import type { Formatter } from 'youtube-transcript-nodejs';
|
|
203
|
+
class MyCsvFormatter implements Formatter { /* ... */ }
|
|
204
|
+
t.format(new MyCsvFormatter());
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Errors
|
|
208
|
+
|
|
209
|
+
The package throws specific error subclasses that all extend `YouTubeTranscriptError`:
|
|
210
|
+
|
|
211
|
+
| Class | Trigger |
|
|
212
|
+
|---|---|
|
|
213
|
+
| `InvalidVideoId` | Provided id cannot be parsed (not 11 chars, not a URL). |
|
|
214
|
+
| `FailedToCreateConsentCookie` | EU consent gate can't be auto-handled. |
|
|
215
|
+
| `VideoUnavailable` | InnerTube `playabilityStatus.status === ERROR` + "This video is unavailable". |
|
|
216
|
+
| `VideoUnplayable` | Other unplayable status, with `reason` and `subreason` populated. |
|
|
217
|
+
| `TooManyRequests` | Explicit rate-limit (not normally thrown; 429 maps to `IpBlocked`). |
|
|
218
|
+
| `IpBlocked` | HTTP 429 or captcha gate; status code attached. |
|
|
219
|
+
| `BotDetected` | InnerTube reports BotGuard block (bent-quote reason). |
|
|
220
|
+
| `PoTokenRequired` | Caption track's `baseUrl` contains `&exp=xpe`. |
|
|
221
|
+
| `TranscriptsDisabled` | InnerTube returns no caption tracks. |
|
|
222
|
+
| `NotTranslatable` | `.translate()` on a non-translatable track. |
|
|
223
|
+
| `TranslationLanguageNotAvailable` | Target language not in `translationLanguages`. |
|
|
224
|
+
| `NoTranscriptFound` | None of the requested language codes match. |
|
|
225
|
+
| `YouTubeRequestFailed` | Generic HTTP failure. |
|
|
226
|
+
| `YouTubeDataUnparsable` | Watch HTML / InnerTube JSON couldn't be parsed. |
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
import { NoTranscriptFound, BotDetected } from 'youtube-transcript-nodejs';
|
|
230
|
+
|
|
231
|
+
try {
|
|
232
|
+
await api.fetch(videoId);
|
|
233
|
+
} catch (err) {
|
|
234
|
+
if (err instanceof BotDetected) {
|
|
235
|
+
// Provide a PoToken provider or proxy
|
|
236
|
+
} else if (err instanceof NoTranscriptFound) {
|
|
237
|
+
console.log('Try different languages:', err.requestedLanguageCodes);
|
|
238
|
+
} else {
|
|
239
|
+
throw err;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Proxy, Cookies, Headers
|
|
245
|
+
|
|
246
|
+
```ts
|
|
247
|
+
const api = new YouTubeTranscriptApi({
|
|
248
|
+
proxyConfig: { url: 'http://user:pass@proxy.example.com:8080' },
|
|
249
|
+
// or: 'socks5://localhost:1080'
|
|
250
|
+
cookies: [
|
|
251
|
+
{ name: 'CONSENT', value: 'YES+cb', domain: '.youtube.com' },
|
|
252
|
+
],
|
|
253
|
+
headers: { 'X-Forwarded-For': '1.2.3.4' },
|
|
254
|
+
userAgent: 'MyCrawler/1.0',
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## PoToken Support
|
|
259
|
+
|
|
260
|
+
YouTube may rate-limit or block ANDROID InnerTube on some networks. To enable automatic retry via the WEB client + PoToken:
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { YouTubeTranscriptApi, AutoPoTokenProvider } from 'youtube-transcript-nodejs';
|
|
264
|
+
|
|
265
|
+
// 1. Self-contained (requires `npm install bgutils-js`)
|
|
266
|
+
const api = new YouTubeTranscriptApi({
|
|
267
|
+
poTokenProvider: new AutoPoTokenProvider(),
|
|
268
|
+
poTokenFallback: true,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// 2. External HTTP provider (e.g. bgutil-ytdlp-pot-provider sidecar)
|
|
272
|
+
import { HttpPoTokenProvider } from 'youtube-transcript-nodejs';
|
|
273
|
+
const api = new YouTubeTranscriptApi({
|
|
274
|
+
poTokenProvider: new HttpPoTokenProvider({
|
|
275
|
+
baseUrl: 'http://localhost:4416',
|
|
276
|
+
}),
|
|
277
|
+
poTokenFallback: true,
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
// 3. Custom — implement PoTokenProvider yourself
|
|
281
|
+
const myProvider: PoTokenProvider = {
|
|
282
|
+
async generateForVideo(videoId) {
|
|
283
|
+
return {
|
|
284
|
+
poToken: '...',
|
|
285
|
+
visitorData: '...',
|
|
286
|
+
expiresAt: new Date(Date.now() + 12 * 3600 * 1000),
|
|
287
|
+
};
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
`AutoPoTokenProvider` lazy-loads `bgutils-js` at runtime. If the package is not installed, `generateForVideo()` throws `BotDetected` so the caller can surface a clear error.
|
|
293
|
+
|
|
294
|
+
## Differences from the Python Library
|
|
295
|
+
|
|
296
|
+
| | Python | Node (this package) |
|
|
297
|
+
|---|---|---|
|
|
298
|
+
| HTTP client | `requests` | Native `fetch` (Node 18+) |
|
|
299
|
+
| InnerTube key extraction | regex `"INNERTUBE_API_KEY":\s*"([^"]+)"` | Same regex + 2 fallbacks |
|
|
300
|
+
| Default User-Agent | `python-requests/X.Y.Z` | `Mozilla/5.0 ... Chrome/124` (PoToken-friendly) |
|
|
301
|
+
| XML parser | `defusedxml.ElementTree` | `fast-xml-parser` for root + regex for `<text>` (preserves inline HTML) |
|
|
302
|
+
| Formatters | Module-level functions | Classes implementing `Formatter` interface |
|
|
303
|
+
| Cookies | `requests.Session` jar | Custom minimal `CookieJar` |
|
|
304
|
+
| Retry on 429 | `urllib3.Retry` | Exponential backoff (configurable) |
|
|
305
|
+
| PoToken | Passive detection only | Pluggable provider + self-contained default |
|
|
306
|
+
|
|
307
|
+
## Limitations
|
|
308
|
+
|
|
309
|
+
- Not every video has subtitles; `TranscriptsDisabled` is expected behavior.
|
|
310
|
+
- YouTube may change its protocol at any time; pin a version.
|
|
311
|
+
- AutoPoTokenProvider depends on `bgutils-js` staying current with YouTube's botguard obfuscation.
|
|
312
|
+
- Live (streaming) videos are not supported.
|
|
313
|
+
- Region-locked videos require a proxy / cookie in the right region.
|
|
314
|
+
- ASR (auto-generated) captions can be inaccurate.
|
|
315
|
+
|
|
316
|
+
## Contributing
|
|
317
|
+
|
|
318
|
+
```bash
|
|
319
|
+
git clone <repo>
|
|
320
|
+
cd yttapi
|
|
321
|
+
npm install
|
|
322
|
+
npm run lint # ESLint
|
|
323
|
+
npm run typecheck # tsc --noEmit
|
|
324
|
+
npm test # unit tests (always runs, 182 tests)
|
|
325
|
+
npm run test:live # live integration tests (RUN_LIVE=1, 4 tests)
|
|
326
|
+
npm run build # ESM + CJS dual publish
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Live tests default to skipped. To exercise them:
|
|
330
|
+
|
|
331
|
+
```bash
|
|
332
|
+
RUN_LIVE=1 YT_TEST_VIDEO_ID=dQw4w9WgXcQ npm run test:live
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## License
|
|
336
|
+
|
|
337
|
+
MIT
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.InnerTubeClient = void 0;
|
|
37
|
+
exports.mapHttpStatusToError = mapHttpStatusToError;
|
|
38
|
+
const constants_js_1 = require("../config/constants.js");
|
|
39
|
+
const innerTubeContext_js_1 = require("../config/innerTubeContext.js");
|
|
40
|
+
const index_js_1 = require("../errors/index.js");
|
|
41
|
+
const watchHtmlParser_js_1 = require("../parsers/watchHtmlParser.js");
|
|
42
|
+
const captionTrackParser_js_1 = require("../parsers/captionTrackParser.js");
|
|
43
|
+
/**
|
|
44
|
+
* Reason sent by InnerTube when BotGuard blocks the request.
|
|
45
|
+
*
|
|
46
|
+
* NOTE: the apostrophe is a U+2019 "RIGHT SINGLE QUOTATION MARK", not
|
|
47
|
+
* ASCII. This is the exact string returned by YouTube.
|
|
48
|
+
*/
|
|
49
|
+
const BOT_DETECT_REASON = 'Sign in to confirm you’re not a bot';
|
|
50
|
+
const AGE_RESTRICTED_REASON = 'This video may be inappropriate for some users.';
|
|
51
|
+
const VIDEO_UNAVAILABLE_REASON = 'This video is unavailable';
|
|
52
|
+
/**
|
|
53
|
+
* Thin InnerTube client implementing the ANDROID-first strategy.
|
|
54
|
+
*
|
|
55
|
+
* Mirrors the Python library's `_transcripts.TranscriptListFetcher._fetch_innertube_data`
|
|
56
|
+
* but with explicit client selection.
|
|
57
|
+
*/
|
|
58
|
+
class InnerTubeClient {
|
|
59
|
+
http;
|
|
60
|
+
constructor(opts) {
|
|
61
|
+
this.http = opts.http;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Fetch the watch HTML for a video id, returning the raw HTML.
|
|
65
|
+
*
|
|
66
|
+
* Performs EU-consent gate handling: if the response is the consent
|
|
67
|
+
* page, parses out the `v` form value and retries once with the
|
|
68
|
+
* CONSENT cookie set.
|
|
69
|
+
*/
|
|
70
|
+
async fetchWatchHtml(videoId) {
|
|
71
|
+
const url = constants_js_1.WATCH_URL.replace('{video_id}', videoId);
|
|
72
|
+
let html = await (await this.http.get(url)).text();
|
|
73
|
+
if ((0, watchHtmlParser_js_1.isConsentGate)(html)) {
|
|
74
|
+
const v = (0, watchHtmlParser_js_1.extractConsentFormValue)(html);
|
|
75
|
+
if (!v) {
|
|
76
|
+
throw new index_js_1.FailedToCreateConsentCookie(videoId);
|
|
77
|
+
}
|
|
78
|
+
// Set CONSENT cookie and re-fetch.
|
|
79
|
+
const cookieValue = (0, watchHtmlParser_js_1.buildConsentCookieValue)(v);
|
|
80
|
+
// We poke the underlying cookie jar via an extra request that
|
|
81
|
+
// triggers ingestion; HttpClient stores cookies from Set-Cookie
|
|
82
|
+
// headers. Here we simulate by setting a request header that
|
|
83
|
+
// YouTube recognises.
|
|
84
|
+
html = await (await this.http.get(url, {
|
|
85
|
+
headers: { Cookie: `CONSENT=${cookieValue}` },
|
|
86
|
+
})).text();
|
|
87
|
+
if ((0, watchHtmlParser_js_1.isConsentGate)(html)) {
|
|
88
|
+
throw new index_js_1.FailedToCreateConsentCookie(videoId);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return html;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Extract INNERTUBE_API_KEY from watch HTML.
|
|
95
|
+
*/
|
|
96
|
+
extractApiKey(html, videoId) {
|
|
97
|
+
return (0, watchHtmlParser_js_1.extractInnertubeApiKey)(html, videoId);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Issue an InnerTube player request with the given client context.
|
|
101
|
+
*/
|
|
102
|
+
async fetchPlayer(videoId, apiKey, ctx) {
|
|
103
|
+
const url = constants_js_1.INNERTUBE_API_URL.replace('{api_key}', apiKey);
|
|
104
|
+
const body = {
|
|
105
|
+
context: {
|
|
106
|
+
client: {
|
|
107
|
+
clientName: ctx.client.clientName,
|
|
108
|
+
clientVersion: ctx.client.clientVersion,
|
|
109
|
+
hl: ctx.client.hl,
|
|
110
|
+
gl: ctx.client.gl,
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
videoId,
|
|
114
|
+
};
|
|
115
|
+
if (ctx.client.androidSdkVersion !== undefined) {
|
|
116
|
+
body['context']['client'] = {
|
|
117
|
+
...body['context']['client'],
|
|
118
|
+
androidSdkVersion: ctx.client.androidSdkVersion,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
const headers = {
|
|
122
|
+
'X-YouTube-Client-Name': (0, innerTubeContext_js_1.clientNameHeader)(ctx),
|
|
123
|
+
'X-YouTube-Client-Version': (0, innerTubeContext_js_1.clientVersionHeader)(ctx),
|
|
124
|
+
};
|
|
125
|
+
if (ctx.client.userAgent) {
|
|
126
|
+
headers['User-Agent'] = ctx.client.userAgent;
|
|
127
|
+
}
|
|
128
|
+
const response = await this.http.postJson(url, body, { headers });
|
|
129
|
+
return response.json();
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Validate the player response and extract caption tracks.
|
|
133
|
+
*/
|
|
134
|
+
extractCaptionTracks(playerJson, videoId) {
|
|
135
|
+
if (typeof playerJson !== 'object' || playerJson === null) {
|
|
136
|
+
throw new index_js_1.YouTubeDataUnparsable(videoId, 'InnerTube response is not an object');
|
|
137
|
+
}
|
|
138
|
+
const data = playerJson;
|
|
139
|
+
this.assertPlayability(data['playabilityStatus'], videoId);
|
|
140
|
+
const captions = data['captions'];
|
|
141
|
+
if (typeof captions !== 'object' || captions === null) {
|
|
142
|
+
throw new index_js_1.TranscriptsDisabled(videoId);
|
|
143
|
+
}
|
|
144
|
+
const tracklistJson = captions['playerCaptionsTracklistRenderer'];
|
|
145
|
+
const parsed = (0, captionTrackParser_js_1.parseCaptionTracklist)(tracklistJson);
|
|
146
|
+
if (!parsed) {
|
|
147
|
+
throw new index_js_1.TranscriptsDisabled(videoId);
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
captionTracks: parsed.captionTracks,
|
|
151
|
+
translationLanguages: parsed.translationLanguages ?? [],
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Fetch the timedtext XML for a single caption track.
|
|
156
|
+
*/
|
|
157
|
+
async fetchTranscriptXml(baseUrl, videoId) {
|
|
158
|
+
if (baseUrl.includes('&exp=xpe')) {
|
|
159
|
+
const { PoTokenRequired } = await Promise.resolve().then(() => __importStar(require('../errors/index.js')));
|
|
160
|
+
throw new PoTokenRequired(videoId);
|
|
161
|
+
}
|
|
162
|
+
const response = await this.http.get(baseUrl);
|
|
163
|
+
return response.text();
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Append `tlang=...` to a baseUrl for translation.
|
|
167
|
+
*/
|
|
168
|
+
buildTranslateUrl(baseUrl, targetLang) {
|
|
169
|
+
const sep = baseUrl.includes('?') ? '&' : '?';
|
|
170
|
+
return `${baseUrl}${sep}tlang=${encodeURIComponent(targetLang)}`;
|
|
171
|
+
}
|
|
172
|
+
assertPlayability(playabilityStatus, videoId) {
|
|
173
|
+
if (typeof playabilityStatus !== 'object' || playabilityStatus === null)
|
|
174
|
+
return;
|
|
175
|
+
const ps = playabilityStatus;
|
|
176
|
+
const status = ps['status'];
|
|
177
|
+
if (status === 'OK' || status === undefined)
|
|
178
|
+
return;
|
|
179
|
+
const reason = typeof ps['reason'] === 'string' ? ps['reason'] : undefined;
|
|
180
|
+
if (status === 'LOGIN_REQUIRED') {
|
|
181
|
+
if (reason === BOT_DETECT_REASON) {
|
|
182
|
+
throw new index_js_1.BotDetected(videoId);
|
|
183
|
+
}
|
|
184
|
+
if (reason === AGE_RESTRICTED_REASON) {
|
|
185
|
+
// We don't model AgeRestricted as a class; use VideoUnplayable.
|
|
186
|
+
throw new index_js_1.VideoUnplayable(videoId, reason, []);
|
|
187
|
+
}
|
|
188
|
+
throw new index_js_1.VideoUnplayable(videoId, reason, extractSubreasons(ps));
|
|
189
|
+
}
|
|
190
|
+
if (status === 'ERROR') {
|
|
191
|
+
if (reason === VIDEO_UNAVAILABLE_REASON) {
|
|
192
|
+
if (videoId.startsWith('http://') || videoId.startsWith('https://')) {
|
|
193
|
+
// URL passed in — looks like an invalid id case.
|
|
194
|
+
throw new index_js_1.YouTubeDataUnparsable(videoId, 'video id looks like a URL');
|
|
195
|
+
}
|
|
196
|
+
throw new index_js_1.VideoUnavailable(videoId);
|
|
197
|
+
}
|
|
198
|
+
throw new index_js_1.VideoUnplayable(videoId, reason, extractSubreasons(ps));
|
|
199
|
+
}
|
|
200
|
+
throw new index_js_1.VideoUnplayable(videoId, reason, extractSubreasons(ps));
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
exports.InnerTubeClient = InnerTubeClient;
|
|
204
|
+
function extractSubreasons(playabilityStatus) {
|
|
205
|
+
const errorScreen = playabilityStatus['errorScreen'];
|
|
206
|
+
if (typeof errorScreen !== 'object' || errorScreen === null)
|
|
207
|
+
return [];
|
|
208
|
+
const playerErr = errorScreen['playerErrorMessageRenderer'];
|
|
209
|
+
if (typeof playerErr !== 'object' || playerErr === null)
|
|
210
|
+
return [];
|
|
211
|
+
const subreason = playerErr['subreason'];
|
|
212
|
+
if (typeof subreason !== 'object' || subreason === null)
|
|
213
|
+
return [];
|
|
214
|
+
const runs = subreason['runs'];
|
|
215
|
+
if (!Array.isArray(runs))
|
|
216
|
+
return [];
|
|
217
|
+
const texts = [];
|
|
218
|
+
for (const run of runs) {
|
|
219
|
+
if (typeof run === 'object' && run !== null) {
|
|
220
|
+
const t = run['text'];
|
|
221
|
+
if (typeof t === 'string')
|
|
222
|
+
texts.push(t);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return texts;
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Map a generic HTTP failure status (after retries) to a specific error.
|
|
229
|
+
*/
|
|
230
|
+
function mapHttpStatusToError(videoId, status, statusText, url) {
|
|
231
|
+
if (status === 429) {
|
|
232
|
+
return new index_js_1.IpBlocked(videoId, status);
|
|
233
|
+
}
|
|
234
|
+
return new index_js_1.YouTubeRequestFailed(videoId, status, statusText, url);
|
|
235
|
+
}
|
|
236
|
+
//# sourceMappingURL=InnerTubeClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InnerTubeClient.js","sourceRoot":"","sources":["../../../src/api/InnerTubeClient.ts"],"names":[],"mappings":"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"}
|