@@ -16,6 +16,50 @@ import clipboard from 'clipboardy';
16
16
17
17
const videos = [ ] ;
18
18
19
+ /**
20
+ * @typedef {object } VideoInfo
21
+ * @property {string } title
22
+ * @property {string } description
23
+ * @property {string } videoNumber
24
+ * @property {string } videoId
25
+ * @property {string } date
26
+ * @property {string[] } languages
27
+ * @property {string } nebulaSlug
28
+ * @property {string[] } topics
29
+ * @property {boolean } canContribute
30
+ * @property {string[] } relatedChallenges
31
+ * @property {{time: number, title: string}[] } timestamps
32
+ * @property {{time: number, title: string}[] } corrections
33
+ * @property {{title: string, description: string, image: string, urls: object}[] } codeExamples
34
+ * @property {{title: string, links: {title: string, url: string, description: string}[]}[] } groupLinks
35
+ */
36
+
37
+ /**
38
+ * A Coding Train Video
39
+ */
40
+ class Video {
41
+ constructor (
42
+ data ,
43
+ parentTracks ,
44
+ urls ,
45
+ filePath ,
46
+ canonicalTrack ,
47
+ slug ,
48
+ canonicalURL ,
49
+ isMultipartChallenge = false
50
+ ) {
51
+ /** @type {VideoInfo } */
52
+ this . data = data ;
53
+ this . parentTracks = parentTracks ;
54
+ this . urls = urls ;
55
+ this . filePath = filePath ;
56
+ this . canonicalTrack = canonicalTrack ;
57
+ this . slug = slug ;
58
+ this . canonicalURL = canonicalURL ;
59
+ this . isMultipartChallenge = isMultipartChallenge ;
60
+ }
61
+ }
62
+
19
63
/**
20
64
* Searches for `index.json` files in a given directory and returns an array of parsed files.
21
65
* @param {string } dir Name of directory to search for files
@@ -82,31 +126,48 @@ function parseTrack(track) {
82
126
function getVideoData ( file ) {
83
127
const videoList = [ ] ;
84
128
const content = fs . readFileSync ( `./${ file } ` , 'utf-8' ) ;
85
- const video = JSON . parse ( content ) ;
129
+ const videoData = JSON . parse ( content ) ;
86
130
87
131
const filePath = file . split ( path . sep ) . slice ( 2 ) ;
88
132
const videoPath = filePath . slice ( 0 , - 1 ) . join ( '/' ) ;
89
133
// console.log('[Parsing File]:', filePath.join('/'));
90
- let url , canonicalTrack ;
134
+ let urls = [ ] ,
135
+ canonicalTrack ,
136
+ canonicalURL ;
137
+ const parentTracks = [ ] ;
91
138
92
139
if ( filePath [ 0 ] === 'challenges' ) {
93
- url = filePath . slice ( 0 , 2 ) . join ( '/' ) ;
140
+ urls . push ( filePath . slice ( 0 , 2 ) . join ( '/' ) ) ;
141
+ canonicalURL = urls [ 0 ] ;
142
+ for ( let track of allTracks ) {
143
+ if ( track . videoList . includes ( videoPath ) ) {
144
+ urls . push ( [ 'tracks' , track . trackName , videoPath ] . join ( '/' ) ) ;
145
+ parentTracks . push ( track . trackName ) ;
146
+ }
147
+ }
94
148
} else {
95
- if ( video . canonicalTrack ) {
96
- canonicalTrack = video . canonicalTrack ;
97
- url = [ 'tracks' , video . canonicalTrack , videoPath ] . join ( '/' ) ;
149
+ if ( videoData . canonicalTrack ) {
150
+ canonicalTrack = videoData . canonicalTrack ;
151
+ canonicalURL = [ 'tracks' , canonicalTrack , videoPath ] . join ( '/' ) ;
152
+ for ( let track of allTracks ) {
153
+ if ( track . videoList . includes ( videoPath ) ) {
154
+ urls . push ( [ 'tracks' , track . trackName , videoPath ] . join ( '/' ) ) ;
155
+ parentTracks . push ( track . trackName ) ;
156
+ }
157
+ }
98
158
} else {
99
159
for ( let track of allTracks ) {
100
160
if ( track . videoList . includes ( videoPath ) ) {
101
161
canonicalTrack = track . trackName ;
102
- url = [ 'tracks' , track . trackName , videoPath ] . join ( '/' ) ;
103
- break ;
162
+ urls . push ( [ 'tracks' , track . trackName , videoPath ] . join ( '/' ) ) ;
163
+ parentTracks . push ( track . trackName ) ;
104
164
}
105
165
}
166
+ canonicalURL = urls [ 0 ] ;
106
167
}
107
168
}
108
169
109
- if ( ! url ) {
170
+ if ( urls . length == 0 ) {
110
171
console . log (
111
172
'⚠️ Warning: Could not find this video: ' +
112
173
videoPath +
@@ -115,50 +176,50 @@ function getVideoData(file) {
115
176
return [ ] ;
116
177
}
117
178
118
- const slug = url . split ( '/' ) . at ( - 1 ) ;
119
-
120
- if ( ! url || url . length == 0 ) {
121
- throw new Error (
122
- 'Something went wrong in parsing this file: ' + filePath . join ( '/' )
123
- ) ;
124
- }
179
+ const slug = urls [ 0 ] . split ( '/' ) . at ( - 1 ) ;
125
180
126
- if ( video . parts && video . parts . length > 0 ) {
181
+ if ( videoData . parts && videoData . parts . length > 0 ) {
127
182
// Multipart Coding Challenge
128
183
// https://github.com/CodingTrain/thecodingtrain.com/issues/420#issuecomment-1218529904
129
184
130
- for ( const part of video . parts ) {
185
+ for ( const part of videoData . parts ) {
131
186
// copy all info from base object
132
- const partInfo = JSON . parse ( JSON . stringify ( video ) ) ;
187
+ const partInfo = JSON . parse ( JSON . stringify ( videoData ) ) ;
133
188
delete partInfo . parts ;
134
189
135
190
// copy videoId, title, timestamps from parts
136
191
partInfo . videoId = part . videoId ;
137
192
partInfo . timestamps = part . timestamps ;
138
- partInfo . challengeTitle = video . title ;
193
+ partInfo . challengeTitle = videoData . title ;
139
194
partInfo . partTitle = part . title ;
140
- partInfo . title = video . title + ' - ' + part . title ;
195
+ partInfo . title = videoData . title + ' - ' + part . title ;
141
196
142
- const videoData = {
143
- pageURL : url ,
144
- data : partInfo ,
145
- filePath : file ,
146
- isMultipartChallenge : true ,
197
+ const video = new Video (
198
+ partInfo ,
199
+ parentTracks ,
200
+ urls ,
201
+ file ,
147
202
canonicalTrack ,
148
- slug : slug
149
- } ;
150
- videoList . push ( videoData ) ;
203
+ slug ,
204
+ canonicalURL ,
205
+ true
206
+ ) ;
207
+
208
+ videoList . push ( video ) ;
151
209
}
152
210
} else {
153
- video . challengeTitle = video . title ;
154
- const videoData = {
155
- pageURL : url ,
156
- data : video ,
157
- filePath : file ,
211
+ videoData . challengeTitle = videoData . title ;
212
+ const video = new Video (
213
+ videoData ,
214
+ parentTracks ,
215
+ urls ,
216
+ file ,
158
217
canonicalTrack ,
159
- slug : slug
160
- } ;
161
- videoList . push ( videoData ) ;
218
+ slug ,
219
+ canonicalURL
220
+ ) ;
221
+
222
+ videoList . push ( video ) ;
162
223
}
163
224
return videoList ;
164
225
}
@@ -219,7 +280,7 @@ function resolveCTLink(url) {
219
280
220
281
let page ;
221
282
try {
222
- page = videos . find ( ( vid ) => vid . pageURL === location ) . data ;
283
+ page = videos . find ( ( vid ) => vid . urls . includes ( location ) ) . data ;
223
284
} catch ( err ) {
224
285
console . warn ( '⚠️ Warning: Could not resolve to YT video:' , url ) ;
225
286
return `https://thecodingtrain.com${ url } ` ;
@@ -273,7 +334,7 @@ function findMaxOccurences(arr) {
273
334
*/
274
335
function writeDescription ( video ) {
275
336
const data = video . data ;
276
- const pageURL = video . pageURL ;
337
+ const pageURL = video . canonicalURL ;
277
338
let description = '' ;
278
339
279
340
// Description
@@ -340,7 +401,7 @@ function writeDescription(video) {
340
401
341
402
// Previous Video / Next Video / All Videos
342
403
343
- if ( video . pageURL . startsWith ( 'challenges/' ) ) {
404
+ if ( video . canonicalURL . startsWith ( 'challenges/' ) ) {
344
405
const i = + video . data . videoNumber ;
345
406
const previousVideo = videos . find ( ( vid ) => vid . data . videoNumber == i - 1 ) ;
346
407
const nextVideo = videos . find ( ( vid ) => vid . data . videoNumber == i + 1 ) ;
@@ -355,7 +416,7 @@ function writeDescription(video) {
355
416
description += `🎥 Next: https://youtu.be/${ nextVideo . data . videoId } ?list=${ challengePL } \n` ;
356
417
description += `🎥 All: https://www.youtube.com/playlist?list=${ challengePL } \n` ;
357
418
} else {
358
- const path = video . pageURL . split ( '/' ) ;
419
+ const path = video . canonicalURL . split ( '/' ) ;
359
420
const videoDir = path . slice ( 2 ) . join ( '/' ) ;
360
421
361
422
const track = allTracks . find ( ( t ) => t . trackName === video . canonicalTrack ) ;
@@ -365,13 +426,13 @@ function writeDescription(video) {
365
426
let id = track . videoList . indexOf ( videoDir ) ;
366
427
367
428
const previousPath = track . videoList [ id - 1 ] ;
368
- const previousVideo = videos . find (
369
- ( vid ) => vid . pageURL == 'tracks/' + track . trackName + '/' + previousPath
429
+ const previousVideo = videos . find ( ( vid ) =>
430
+ vid . urls . includes ( 'tracks/' + track . trackName + '/' + previousPath )
370
431
) ;
371
432
372
433
const nextPath = track . videoList [ id + 1 ] ;
373
- const nextVideo = videos . find (
374
- ( vid ) => vid . pageURL == 'tracks/' + track . trackName + '/' + nextPath
434
+ const nextVideo = videos . find ( ( vid ) =>
435
+ vid . urls . includes ( 'tracks/' + track . trackName + '/' + nextPath )
375
436
) ;
376
437
const plId = track . data . playlistId
377
438
? `?list=${ track . data . playlistId } `
@@ -413,12 +474,12 @@ function writeDescription(video) {
413
474
if ( data . relatedChallenges && data . relatedChallenges . length > 0 ) {
414
475
description += `\nRelated Coding Challenges:\n` ;
415
476
for ( const challenge of data . relatedChallenges ) {
416
- const challengeData = videos . find (
417
- ( vid ) => vid . pageURL === `challenges/${ challenge } `
477
+ const challengeData = videos . find ( ( vid ) =>
478
+ vid . urls . includes ( `challenges/${ challenge } ` )
418
479
) ;
419
480
if ( challengeData ) {
420
481
const { videoNumber, challengeTitle } = challengeData . data ;
421
- const url = challengeData . pageURL ;
482
+ const url = challengeData . canonicalURL ;
422
483
description +=
423
484
`🚂 ${ videoNumber } ${ challengeTitle } : ${ resolveCTLink ( url ) } ` + '\n' ;
424
485
} else {
@@ -526,9 +587,7 @@ const allTracks = [...mainTracks, ...sideTracks];
526
587
const url = new URL ( video ) ;
527
588
if ( url . hostname == 'thecodingtrain.com' ) {
528
589
const pathName = url . pathname ;
529
- specifiedVideos = videos . filter (
530
- ( data ) => '/' + data . pageURL === pathName
531
- ) ;
590
+ specifiedVideos = videos . filter ( ( data ) => data . urls . includes ( pathName ) ) ;
532
591
} else {
533
592
const video = resolveYTLink ( url ) ;
534
593
if ( video ) {
0 commit comments