@donlovett/n8n-nodes-youtube-transcript 1.0.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/README.md ADDED
@@ -0,0 +1,94 @@
1
+ # n8n-nodes-youtube-transcript
2
+
3
+ This is an n8n community node that fetches transcripts (captions) from YouTube videos.
4
+
5
+ [n8n](https://n8n.io/) is a [fair-code licensed](https://docs.n8n.io/reference/license/) workflow automation platform.
6
+
7
+ ## Features
8
+
9
+ - Fetch transcripts from any YouTube video that has captions
10
+ - Support for multiple languages
11
+ - Output as full text or timed segments
12
+ - Automatic video ID extraction from URLs
13
+ - Graceful error handling
14
+
15
+ ## Installation
16
+
17
+ ### Community Node Installation (Recommended)
18
+
19
+ 1. Go to **Settings** > **Community Nodes** in your n8n instance
20
+ 2. Select **Install**
21
+ 3. Enter `n8n-nodes-youtube-transcript`
22
+ 4. Click **Install**
23
+
24
+ ### Manual Installation
25
+
26
+ ```bash
27
+ cd ~/.n8n/custom
28
+ npm install n8n-nodes-youtube-transcript
29
+ ```
30
+
31
+ Then restart n8n.
32
+
33
+ ## Operations
34
+
35
+ ### Get Transcript
36
+
37
+ Fetches the transcript/captions from a YouTube video.
38
+
39
+ **Parameters:**
40
+ - **Video ID or URL**: The YouTube video ID (e.g., `dQw4w9WgXcQ`) or full URL
41
+ - **Language**: Language code for the transcript (default: `en`)
42
+ - **Output Format**:
43
+ - `Full Text` - Returns transcript as a single text string
44
+ - `Segments` - Returns array of timed segments with start time and duration
45
+
46
+ **Output:**
47
+ ```json
48
+ {
49
+ "videoId": "dQw4w9WgXcQ",
50
+ "language": "en",
51
+ "hasTranscript": true,
52
+ "transcript": "The full transcript text...",
53
+ "wordCount": 1234,
54
+ "segmentCount": 156
55
+ }
56
+ ```
57
+
58
+ ## Supported URL Formats
59
+
60
+ - `https://www.youtube.com/watch?v=VIDEO_ID`
61
+ - `https://youtu.be/VIDEO_ID`
62
+ - `https://youtube.com/embed/VIDEO_ID`
63
+ - Plain video ID: `VIDEO_ID`
64
+
65
+ ## Error Handling
66
+
67
+ If a video doesn't have a transcript available, the node returns:
68
+ ```json
69
+ {
70
+ "videoId": "...",
71
+ "hasTranscript": false,
72
+ "error": "No transcript available for this video",
73
+ "transcript": "",
74
+ "wordCount": 0
75
+ }
76
+ ```
77
+
78
+ Enable "Continue On Fail" in the node settings to process multiple videos even if some fail.
79
+
80
+ ## Limitations
81
+
82
+ - This node uses YouTube's unofficial transcript API
83
+ - Only works with videos that have captions enabled
84
+ - Auto-generated captions may have accuracy issues
85
+ - Rate limiting may apply for high-volume usage
86
+
87
+ ## License
88
+
89
+ MIT
90
+
91
+ ## Resources
92
+
93
+ - [n8n Community Nodes Documentation](https://docs.n8n.io/integrations/community-nodes/)
94
+ - [youtube-transcript npm package](https://www.npmjs.com/package/youtube-transcript)
@@ -0,0 +1,6 @@
1
+ import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class YouTubeTranscript implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
6
+ //# sourceMappingURL=YouTubeTranscript.node.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"YouTubeTranscript.node.d.ts","sourceRoot":"","sources":["../../../nodes/YouTubeTranscript/YouTubeTranscript.node.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,iBAAiB,EACjB,kBAAkB,EAClB,SAAS,EACT,oBAAoB,EAEpB,MAAM,cAAc,CAAC;AA4BtB,qBAAa,iBAAkB,YAAW,SAAS;IAClD,WAAW,EAAE,oBAAoB,CAgF/B;IAEI,OAAO,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC;CAqGvE"}
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.YouTubeTranscript = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ // @ts-ignore - youtube-transcript doesn't have types
6
+ const youtube_transcript_1 = require("youtube-transcript");
7
+ // Helper function to extract video ID from URL
8
+ function extractVideoId(input) {
9
+ // Already a video ID (11 characters, alphanumeric with - and _)
10
+ if (/^[a-zA-Z0-9_-]{11}$/.test(input)) {
11
+ return input;
12
+ }
13
+ // Try to extract from various URL formats
14
+ const patterns = [
15
+ /(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/|youtube\.com\/v\/)([a-zA-Z0-9_-]{11})/,
16
+ /[?&]v=([a-zA-Z0-9_-]{11})/,
17
+ ];
18
+ for (const pattern of patterns) {
19
+ const match = input.match(pattern);
20
+ if (match) {
21
+ return match[1];
22
+ }
23
+ }
24
+ return input; // Return as-is and let the API handle validation
25
+ }
26
+ class YouTubeTranscript {
27
+ constructor() {
28
+ this.description = {
29
+ displayName: 'YouTube Transcript',
30
+ name: 'youTubeTranscript',
31
+ icon: 'file:youtube.svg',
32
+ group: ['transform'],
33
+ version: 1,
34
+ subtitle: '={{$parameter["operation"]}}',
35
+ description: 'Fetch transcripts from YouTube videos',
36
+ defaults: {
37
+ name: 'YouTube Transcript',
38
+ },
39
+ inputs: ['main'],
40
+ outputs: ['main'],
41
+ properties: [
42
+ {
43
+ displayName: 'Operation',
44
+ name: 'operation',
45
+ type: 'options',
46
+ noDataExpression: true,
47
+ options: [
48
+ {
49
+ name: 'Get Transcript',
50
+ value: 'getTranscript',
51
+ description: 'Get the transcript of a YouTube video',
52
+ action: 'Get transcript of a youtube video',
53
+ },
54
+ ],
55
+ default: 'getTranscript',
56
+ },
57
+ {
58
+ displayName: 'Video ID or URL',
59
+ name: 'videoId',
60
+ type: 'string',
61
+ default: '',
62
+ required: true,
63
+ displayOptions: {
64
+ show: {
65
+ operation: ['getTranscript'],
66
+ },
67
+ },
68
+ placeholder: 'dQw4w9WgXcQ or https://youtu.be/dQw4w9WgXcQ',
69
+ description: 'The YouTube video ID or full URL',
70
+ },
71
+ {
72
+ displayName: 'Language',
73
+ name: 'language',
74
+ type: 'string',
75
+ default: 'en',
76
+ displayOptions: {
77
+ show: {
78
+ operation: ['getTranscript'],
79
+ },
80
+ },
81
+ description: 'Language code for the transcript (e.g., en, es, de)',
82
+ },
83
+ {
84
+ displayName: 'Output Format',
85
+ name: 'outputFormat',
86
+ type: 'options',
87
+ options: [
88
+ {
89
+ name: 'Full Text',
90
+ value: 'fullText',
91
+ description: 'Return transcript as a single text string',
92
+ },
93
+ {
94
+ name: 'Segments',
95
+ value: 'segments',
96
+ description: 'Return transcript as an array of timed segments',
97
+ },
98
+ ],
99
+ default: 'fullText',
100
+ displayOptions: {
101
+ show: {
102
+ operation: ['getTranscript'],
103
+ },
104
+ },
105
+ description: 'How to format the transcript output',
106
+ },
107
+ ],
108
+ };
109
+ }
110
+ async execute() {
111
+ const items = this.getInputData();
112
+ const returnData = [];
113
+ for (let i = 0; i < items.length; i++) {
114
+ try {
115
+ const operation = this.getNodeParameter('operation', i);
116
+ if (operation === 'getTranscript') {
117
+ const videoIdInput = this.getNodeParameter('videoId', i);
118
+ const language = this.getNodeParameter('language', i);
119
+ const outputFormat = this.getNodeParameter('outputFormat', i);
120
+ // Extract video ID from URL if needed
121
+ const videoId = extractVideoId(videoIdInput);
122
+ if (!videoId) {
123
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Invalid video ID or URL provided', { itemIndex: i });
124
+ }
125
+ // Fetch transcript
126
+ const transcriptSegments = await youtube_transcript_1.YoutubeTranscript.fetchTranscript(videoId, {
127
+ lang: language,
128
+ });
129
+ if (!transcriptSegments || transcriptSegments.length === 0) {
130
+ // Return empty result instead of error
131
+ returnData.push({
132
+ json: {
133
+ videoId,
134
+ language,
135
+ hasTranscript: false,
136
+ error: 'No transcript available for this video',
137
+ transcript: '',
138
+ wordCount: 0,
139
+ },
140
+ });
141
+ continue;
142
+ }
143
+ if (outputFormat === 'fullText') {
144
+ // Combine all segments into a single text
145
+ const fullText = transcriptSegments
146
+ .map((segment) => segment.text)
147
+ .join(' ')
148
+ .replace(/\s+/g, ' ')
149
+ .trim();
150
+ const wordCount = fullText.split(/\s+/).filter((w) => w).length;
151
+ returnData.push({
152
+ json: {
153
+ videoId,
154
+ language,
155
+ hasTranscript: true,
156
+ transcript: fullText,
157
+ wordCount,
158
+ segmentCount: transcriptSegments.length,
159
+ },
160
+ });
161
+ }
162
+ else {
163
+ // Return segments with timing
164
+ returnData.push({
165
+ json: {
166
+ videoId,
167
+ language,
168
+ hasTranscript: true,
169
+ segments: transcriptSegments,
170
+ segmentCount: transcriptSegments.length,
171
+ },
172
+ });
173
+ }
174
+ }
175
+ }
176
+ catch (error) {
177
+ if (this.continueOnFail()) {
178
+ const videoIdInput = this.getNodeParameter('videoId', i, '');
179
+ returnData.push({
180
+ json: {
181
+ videoId: extractVideoId(videoIdInput),
182
+ hasTranscript: false,
183
+ error: error instanceof Error ? error.message : 'Unknown error',
184
+ transcript: '',
185
+ wordCount: 0,
186
+ },
187
+ });
188
+ continue;
189
+ }
190
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error instanceof Error ? error.message : 'Failed to fetch transcript', { itemIndex: i });
191
+ }
192
+ }
193
+ return [returnData];
194
+ }
195
+ }
196
+ exports.YouTubeTranscript = YouTubeTranscript;
197
+ //# sourceMappingURL=YouTubeTranscript.node.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"YouTubeTranscript.node.js","sourceRoot":"","sources":["../../../nodes/YouTubeTranscript/YouTubeTranscript.node.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="#FF0000">
2
+ <path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>
3
+ </svg>
package/package.json ADDED
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@donlovett/n8n-nodes-youtube-transcript",
3
+ "version": "1.0.0",
4
+ "description": "n8n node to fetch YouTube video transcripts",
5
+ "keywords": [
6
+ "n8n-community-node-package",
7
+ "youtube",
8
+ "transcript",
9
+ "captions"
10
+ ],
11
+ "license": "MIT",
12
+ "author": {
13
+ "name": "don",
14
+ "email": "don@projectbitz.com"
15
+ },
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/projectbitz/n8n-nodes-youtube-transcript.git"
19
+ },
20
+ "main": "dist/index.js",
21
+ "scripts": {
22
+ "build": "tsc && gulp build:icons",
23
+ "dev": "tsc --watch",
24
+ "format": "prettier nodes --write",
25
+ "lint": "eslint nodes",
26
+ "lintfix": "eslint nodes --fix",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "files": [
30
+ "dist"
31
+ ],
32
+ "n8n": {
33
+ "n8nNodesApiVersion": 1,
34
+ "nodes": [
35
+ "dist/nodes/YouTubeTranscript/YouTubeTranscript.node.js"
36
+ ]
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^22.0.0",
40
+ "eslint": "^8.57.0",
41
+ "gulp": "^4.0.2",
42
+ "n8n-workflow": "^1.0.0",
43
+ "prettier": "^3.2.5",
44
+ "typescript": "~5.4.0"
45
+ },
46
+ "dependencies": {
47
+ "youtube-transcript": "^1.2.1"
48
+ },
49
+ "peerDependencies": {
50
+ "n8n-workflow": "*"
51
+ }
52
+ }