eleventy-plugin-podcaster 2.0.0-alpha.2 → 2.0.0-alpha.4

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/.env ADDED
@@ -0,0 +1,5 @@
1
+ BUCKET_SECRET_KEY="GiBsTUbU7VwKdSZGZKvYb47i87lWjkUXRH1Edd+I2KQ"
2
+ BUCKET_ACCESS_KEY="DO801WKX4LJP3Y9DWE2H"
3
+ BUCKET_ENDPOINT="https://sfo3.digitaloceanspaces.com"
4
+ BUCKET_REGION="us-east-1"
5
+ BUCKET_NAME="startlingbarbarabain"
@@ -1,17 +1,27 @@
1
+ import path from 'node:path'
2
+
1
3
  import podcastFeed from './src/podcastFeed.js'
2
4
  import podcastData from './src/podcastData.js'
3
5
  import episodeData from './src/episodeData.js'
4
- import calculateFilenameSizeAndDuration from './src/calculateFilenameSizeAndDuration.js'
6
+ import calculateEpisodeSizeAndDuration from './src/calculateEpisodeSizeAndDuration.js'
7
+ import calculateEpisodeFilename from './src/calculateEpisodeFilename.js'
8
+
5
9
  import readableFilters from './src/readableFilters.js'
6
10
  import excerpts from './src/excerpts.js'
7
11
  import drafts from './src/drafts.js'
8
12
  import pageTitle from './src/pageTitle.js'
9
13
 
10
14
  export default function (eleventyConfig, options = {}) {
15
+ const episodePostsDirectory = options.episodePostsDirectory ?? 'episode-posts'
16
+ options.episodePostsDirectory = path.join(eleventyConfig.directories.input, episodePostsDirectory)
17
+ const episodeFilesDirectory = options.episodeFilesDirectory ?? 'episode-files'
18
+ options.episodeFilesDirectory = path.join(eleventyConfig.directories.input, episodeFilesDirectory)
19
+
11
20
  eleventyConfig.addPlugin(podcastFeed, options)
12
21
  eleventyConfig.addPlugin(podcastData, options)
13
22
  eleventyConfig.addPlugin(episodeData, options)
14
- eleventyConfig.addPlugin(calculateFilenameSizeAndDuration, options)
23
+ eleventyConfig.addPlugin(calculateEpisodeSizeAndDuration, options)
24
+ eleventyConfig.addPlugin(calculateEpisodeFilename, options)
15
25
 
16
26
  // Filters
17
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eleventy-plugin-podcaster",
3
- "version": "2.0.0-alpha.2",
3
+ "version": "2.0.0-alpha.4",
4
4
  "description": "An Eleventy plugin that allows you to create a podcast and its accompanying website",
5
5
  "main": "eleventy.config.js",
6
6
  "exports": {
@@ -30,8 +30,8 @@
30
30
  "dependencies": {
31
31
  "@11ty/eleventy": "^3.0.0",
32
32
  "@11ty/eleventy-plugin-rss": "^2.0.1",
33
+ "@aws-sdk/client-s3": "^3.862.0",
33
34
  "@tsmx/human-readable": "^2.0.3",
34
- "chalk": "^5.3.0",
35
35
  "dom-serializer": "^2.0.0",
36
36
  "htmlparser2": "^9.1.0",
37
37
  "luxon": "^3.4.4",
@@ -40,7 +40,9 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "ava": "^6.1.3",
43
+ "dotenv": "^17.2.1",
43
44
  "fast-xml-parser": "^4.4.0",
45
+ "mock-aws-s3-v3": "^6.0.5",
44
46
  "neostandard": "^0.11.4"
45
47
  }
46
48
  }
@@ -0,0 +1,41 @@
1
+ import isEpisodePost from './isEpisodePost.js'
2
+
3
+ function findMatchingFilename (episodeData, thisEpisode) {
4
+ const filenameSeasonAndEpisodePattern =
5
+ /^.*?\b[sS](?<seasonNumber>\d+)\s*[eE](?<episodeNumber>\d+)\b.*\.(mp3|m4a)$/
6
+ const filenameEpisodePattern = /^.*?\b(?<episodeNumber>\d+)\b.*\.(mp3|m4a)$/
7
+ const { seasonNumber, episodeNumber } = thisEpisode
8
+
9
+ for (const file of Object.keys(episodeData)) {
10
+ if (seasonNumber && episodeNumber) {
11
+ const seasonAndEpisodeMatch = file.match(filenameSeasonAndEpisodePattern)
12
+ if (seasonAndEpisodeMatch) {
13
+ const matchedSeasonNumber = parseInt(seasonAndEpisodeMatch.groups.seasonNumber)
14
+ const matchedEpisodeNumber = parseInt(seasonAndEpisodeMatch.groups.episodeNumber)
15
+ if (matchedSeasonNumber === seasonNumber &&
16
+ matchedEpisodeNumber === episodeNumber) {
17
+ return file
18
+ }
19
+ }
20
+ } else if (episodeNumber) {
21
+ const episodeMatch = file.match(filenameEpisodePattern)
22
+ if (episodeMatch) {
23
+ const matchedEpisodeNumber = parseInt(episodeMatch.groups.episodeNumber)
24
+ if (matchedEpisodeNumber === episodeNumber) {
25
+ return file
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+
32
+ export default function (eleventyConfig, options) {
33
+ eleventyConfig.addGlobalData('eleventyComputed.episode.filename', () => {
34
+ return data => {
35
+ if (data.episode.filename) return data.episode.filename
36
+ if (!isEpisodePost(data, options)) return
37
+
38
+ return findMatchingFilename(data.episodeData, data.episode)
39
+ }
40
+ })
41
+ }
@@ -0,0 +1,207 @@
1
+ import { Duration, DateTime } from 'luxon'
2
+ import path from 'node:path'
3
+ import { existsSync } from 'node:fs'
4
+ import { readdir, stat, writeFile } from 'node:fs/promises'
5
+ import { Writable } from 'node:stream'
6
+ import { S3Client, ListObjectsCommand, GetObjectCommand, PutObjectCommand } from '@aws-sdk/client-s3'
7
+ import { parseFile as parseFileMetadata, parseBuffer as parseBufferMetadata } from 'music-metadata'
8
+ import hr from '@tsmx/human-readable'
9
+ import isEpisodePost from './isEpisodePost.js'
10
+
11
+ const isAudioFile = episodeFilename => episodeFilename.endsWith('.mp3') ||
12
+ episodeFilename.endsWith('.m4a')
13
+
14
+ const convertSecondsToReadableDuration = seconds =>
15
+ Duration.fromMillis(seconds * 1000)
16
+ .shiftTo('days', 'hours', 'minutes', 'seconds')
17
+ .toHuman()
18
+
19
+ const convertReadableDurationToSeconds = duration => {
20
+ const durationPattern = /^(?:(?<hours>\d+):)?(?<minutes>\d{1,2}):(?<seconds>\d{2}(?:\.\d+)?)$/
21
+
22
+ let match
23
+ if (duration?.match) {
24
+ match = duration.match(durationPattern)
25
+ }
26
+
27
+ if (match) {
28
+ const hours = isNaN(parseInt(match.groups.hours))
29
+ ? 0
30
+ : parseInt(match.groups.hours)
31
+ const minutes = parseInt(match.groups.minutes)
32
+ const seconds = parseFloat(match.groups.seconds)
33
+ return hours * 3600 + minutes * 60 + seconds
34
+ }
35
+ }
36
+
37
+ async function readEpisodeDataLocally (episodeFilesDirectory) {
38
+ const episodes = await readdir(episodeFilesDirectory)
39
+ const episodeData = {}
40
+ for (const episode of episodes) {
41
+ if (!isAudioFile(episode)) continue
42
+
43
+ const episodePath = path.join(episodeFilesDirectory, episode)
44
+ const episodeSize = (await stat(episodePath)).size
45
+ const episodeMetadata = await parseFileMetadata(episodePath, { duration: true })
46
+ const episodeDuration = episodeMetadata.format.duration
47
+ episodeData[episode] = {
48
+ size: episodeSize,
49
+ duration: episodeDuration
50
+ }
51
+ }
52
+ return episodeData
53
+ }
54
+
55
+ function calculatePodcastData (episodeData) {
56
+ const episodeDataValues = Object.values(episodeData)
57
+ const numberOfEpisodes = episodeDataValues.length
58
+ const totalSize = episodeDataValues.map(x => x.size).reduce((x, y) => x + y)
59
+ const totalDuration = episodeDataValues.map(x => x.duration).reduce((x, y) => x + y)
60
+ return { numberOfEpisodes, totalSize, totalDuration }
61
+ }
62
+
63
+ function reportPodcastData (podcastData) {
64
+ const { numberOfEpisodes, totalSize, totalDuration } = podcastData
65
+ console.log(`\u001b[33m${numberOfEpisodes} episodes; ${hr.fromBytes(totalSize)}; ${convertSecondsToReadableDuration(totalDuration)}.\u001b[0m`)
66
+ }
67
+
68
+ async function writePodcastDataLocally (episodeData, podcastData, directories) {
69
+ const dataDir = path.join(process.cwd(), directories.data)
70
+ await writeFile(path.join(dataDir, 'episodeData.json'), JSON.stringify(episodeData, null, 2))
71
+ await writeFile(path.join(dataDir, 'podcastData.json'), JSON.stringify(podcastData, null, 2))
72
+ }
73
+
74
+ function getS3Client (options) {
75
+ if (options.s3ClientObject) return options.s3ClientObject
76
+
77
+ if (options.s3Client) {
78
+ return new S3Client({
79
+ forcePathStyle: true,
80
+ endpoint: options.s3Client.endpoint,
81
+ region: options.s3Client.region,
82
+ credentials: {
83
+ accessKeyId: options.s3Client.accessKey,
84
+ secretAccessKey: options.s3Client.secretKey
85
+ }
86
+ })
87
+ }
88
+ }
89
+
90
+ async function getObjectFromS3Bucket (s3Client, s3Bucket, key) {
91
+ const getObjectResponse = await s3Client.send(new GetObjectCommand({ Bucket: s3Bucket, Key: key }))
92
+
93
+ const chunks = []
94
+ if (typeof getObjectResponse.Body.pipe === 'function') {
95
+ // this is to cope with the behaviour of the mock, which doesn't return an iterator full of chunks
96
+ const writable = new Writable({
97
+ write (chunk, encoding, callback) {
98
+ chunks.push(chunk)
99
+ callback()
100
+ },
101
+ final (callback) {
102
+ callback()
103
+ }
104
+ })
105
+ getObjectResponse.Body.pipe(writable)
106
+ await new Promise((resolve, reject) => {
107
+ writable.on('finish', resolve)
108
+ writable.on('error', reject)
109
+ })
110
+ } else {
111
+ for await (const chunk of getObjectResponse.Body) {
112
+ chunks.push(chunk)
113
+ }
114
+ }
115
+ const buffer = Buffer.concat(chunks)
116
+ return { buffer, lastModified: getObjectResponse.LastModified }
117
+ }
118
+
119
+ async function getStoredEpisodeDataFromS3Bucket (s3Client, s3BucketName) {
120
+ try {
121
+ const { buffer, lastModified } = await getObjectFromS3Bucket(s3Client, s3BucketName, 'episodeData.json')
122
+ return { episodeData: JSON.parse(buffer.toString()), lastModified }
123
+ } catch (err) {
124
+ return { episodeData: {}, lastModified: null }
125
+ }
126
+ }
127
+
128
+ async function updateEpisodeDataFromS3Bucket (s3Client, s3Bucket) {
129
+ const storedEpisodeData = await getStoredEpisodeDataFromS3Bucket(s3Client, s3Bucket)
130
+ const storedEpisodeDataLastModifiedDate = (storedEpisodeData.lastModified)
131
+ ? DateTime.fromISO(storedEpisodeData.lastModified)
132
+ : null
133
+ const list = await s3Client.send(new ListObjectsCommand({ Bucket: s3Bucket }))
134
+ const result = { ...storedEpisodeData.episodeData }
135
+ for (const item of list.Contents ?? []) {
136
+ if (!isAudioFile(item.Key)) continue
137
+
138
+ const { Key: filename, Size: size, LastModified: lastModified } = item
139
+
140
+ if (!(filename in result) ||
141
+ !('size' in result[filename]) ||
142
+ !('duration' in result[filename]) ||
143
+ !storedEpisodeDataLastModifiedDate ||
144
+ storedEpisodeDataLastModifiedDate > DateTime.fromISO(lastModified)) {
145
+ const { buffer } = await getObjectFromS3Bucket(s3Client, s3Bucket, filename)
146
+ const metadata = await parseBufferMetadata(buffer, null, { duration: true })
147
+ const duration = metadata.format.duration
148
+ result[filename] = { size, duration }
149
+ }
150
+ }
151
+ return result
152
+ }
153
+
154
+ async function writeEpisodeDataToS3Bucket (s3Client, s3Bucket, episodeData) {
155
+ await s3Client.send(new PutObjectCommand({
156
+ Bucket: s3Bucket,
157
+ Key: 'episodeData.json',
158
+ Body: JSON.stringify(episodeData, null, 2),
159
+ ContentType: 'application/json'
160
+ }))
161
+ }
162
+
163
+ export default function (eleventyConfig, options = {}) {
164
+ let firstRun = true
165
+ eleventyConfig.on('eleventy.before', async ({ directories }) => {
166
+ if (!firstRun || process.env.SKIP_EPISODE_CALCULATIONS === 'true') return
167
+ firstRun = false
168
+
169
+ const episodeFilesDirectory = options.episodeFilesDirectory
170
+ let episodeData
171
+ if (existsSync(episodeFilesDirectory)) {
172
+ episodeData = await readEpisodeDataLocally(episodeFilesDirectory)
173
+ } else if (options.s3ClientObject || options.s3Client) {
174
+ const s3Client = getS3Client(options)
175
+ const s3Bucket = options.s3Client.bucket
176
+ episodeData = await updateEpisodeDataFromS3Bucket(s3Client, s3Bucket)
177
+ await writeEpisodeDataToS3Bucket(s3Client, s3Bucket, episodeData)
178
+ } else {
179
+ return
180
+ }
181
+ const podcastData = calculatePodcastData(episodeData)
182
+ await writePodcastDataLocally(episodeData, podcastData, directories)
183
+ if (!eleventyConfig.quietMode) reportPodcastData(podcastData)
184
+ })
185
+
186
+ eleventyConfig.addGlobalData('eleventyComputed.episode.size', () => {
187
+ return data => {
188
+ if (data.episode.size) return data.episode.size
189
+ if (isEpisodePost(data, options) && data.episodeData) {
190
+ return data.episodeData[data.episode.filename]?.size
191
+ }
192
+ }
193
+ })
194
+
195
+ eleventyConfig.addGlobalData('eleventyComputed.episode.duration', () => {
196
+ return data => {
197
+ if (data.episode.duration) {
198
+ const convertedReadableDuration = convertReadableDurationToSeconds(data.episode.duration)
199
+ return convertedReadableDuration ?? data.episode.duration
200
+ }
201
+
202
+ if (isEpisodePost(data, options) && data.episodeData) {
203
+ return data.episodeData[data.episode.filename]?.duration
204
+ }
205
+ }
206
+ })
207
+ }
package/src/drafts.js CHANGED
@@ -13,9 +13,9 @@ export default (eleventyConfig, options = {}) => {
13
13
  }
14
14
  if (!hasLoggedAboutDrafts) {
15
15
  if (shouldIncludeDrafts) {
16
- console.log('Including drafts.')
16
+ if (!eleventyConfig.quietMode) console.log('Including drafts.')
17
17
  } else {
18
- console.log('Excluding drafts.')
18
+ if (!eleventyConfig.quietMode) console.log('Excluding drafts.')
19
19
  }
20
20
  hasLoggedAboutDrafts = true
21
21
  }
@@ -1,3 +1,5 @@
1
+ import isEpisodePost from './isEpisodePost.js'
2
+
1
3
  export default function (eleventyConfig, options = {}) {
2
4
  const postFilenameSeasonAndEpisodePattern =
3
5
  /^[sS](?<seasonNumber>\d+)[eE](?<episodeNumber>\d+)/i
@@ -7,7 +9,7 @@ export default function (eleventyConfig, options = {}) {
7
9
  return data => {
8
10
  if (data.episode?.seasonNumber) return data.episode.seasonNumber
9
11
 
10
- if (!data.page.inputPath.includes('/episodePosts/')) return
12
+ if (!isEpisodePost(data, options)) return
11
13
 
12
14
  const seasonAndEpisodeMatch = data.page.fileSlug.match(postFilenameSeasonAndEpisodePattern)
13
15
  if (seasonAndEpisodeMatch) {
@@ -20,7 +22,7 @@ export default function (eleventyConfig, options = {}) {
20
22
  return data => {
21
23
  if (data.episode?.episodeNumber) return data.episode.episodeNumber
22
24
 
23
- if (!data.page.inputPath.includes('/episodePosts/')) return
25
+ if (!isEpisodePost(data, options)) return
24
26
 
25
27
  const seasonAndEpisodeMatch = data.page.fileSlug.match(postFilenameSeasonAndEpisodePattern)
26
28
  if (seasonAndEpisodeMatch) {
@@ -49,7 +51,7 @@ export default function (eleventyConfig, options = {}) {
49
51
 
50
52
  eleventyConfig.addGlobalData('eleventyComputed.episode.url', () => {
51
53
  return data => {
52
- if (!data.page.inputPath.includes('/episodePosts/')) return
54
+ if (!isEpisodePost(data, options)) return
53
55
 
54
56
  const episodeUrlBase = data.podcast.episodeUrlBase
55
57
  const filename = data.episode.filename
package/src/excerpts.js CHANGED
@@ -1,11 +1,12 @@
1
1
  import * as htmlparser2 from 'htmlparser2'
2
2
  import render from 'dom-serializer'
3
3
  import markdownIt from 'markdown-it'
4
+ import isEpisodePost from './isEpisodePost.js'
4
5
 
5
6
  export default function (eleventyConfig, options = {}) {
6
7
  eleventyConfig.addGlobalData('eleventyComputed.excerpt', () => {
7
8
  return (data) => {
8
- if (!data.page.inputPath.includes('/episodePosts/')) return
9
+ if (!isEpisodePost(data, options)) return
9
10
 
10
11
  const md = markdownIt({
11
12
  html: true,
@@ -0,0 +1,10 @@
1
+ import path from 'node:path'
2
+
3
+ export default function isEpisodePost (data, options) {
4
+ if (data.page?.inputPath) {
5
+ const importPath = path.normalize(data.page.inputPath)
6
+ return importPath.startsWith(options.episodePostsDirectory)
7
+ } else {
8
+ return false
9
+ }
10
+ }
@@ -1,6 +1,7 @@
1
1
  import rssPlugin from '@11ty/eleventy-plugin-rss'
2
2
  import { readFileSync } from 'node:fs'
3
3
  import path from 'node:path'
4
+ import isEpisodePost from './isEpisodePost.js'
4
5
 
5
6
  export default function (eleventyConfig, options = {}) {
6
7
  if (!('addTemplate' in eleventyConfig)) {
@@ -12,7 +13,7 @@ export default function (eleventyConfig, options = {}) {
12
13
  eleventyConfig.addTemplate('feed.njk', readFileSync(podcastFeedPath), {
13
14
  eleventyExcludeFromCollections: true,
14
15
  eleventyImport: {
15
- collections: ['podcastEpisode']
16
+ collections: ['episodePost']
16
17
  }
17
18
  })
18
19
 
@@ -22,7 +23,7 @@ export default function (eleventyConfig, options = {}) {
22
23
  }
23
24
  })
24
25
 
25
- eleventyConfig.addCollection('podcastEpisode', (collectionApi) => {
26
- return collectionApi.getFilteredByGlob('**/episodePosts/*')
26
+ eleventyConfig.addCollection('episodePost', (collectionApi) => {
27
+ return collectionApi.getAll().filter(item => isEpisodePost(item.data, options))
27
28
  })
28
29
  }
@@ -17,7 +17,7 @@ eleventyAllowMissingExtension: true
17
17
  <description>{{ podcast.description }}</description>
18
18
  <language>{{ podcast.language }}</language>
19
19
  <copyright>{{ podcast.copyrightNotice }}</copyright>
20
- <pubDate>{{ collections.podcastEpisode | getNewestCollectionItemDate | dateToRfc3339 }}</pubDate>
20
+ <pubDate>{{ collections.episodePost | getNewestCollectionItemDate | dateToRfc3339 }}</pubDate>
21
21
  <lastBuildDate>{{ podcast.feedLastBuildDate }}</lastBuildDate>
22
22
  <itunes:image href="{{ podcast.imagePath | htmlBaseUrl(siteUrl) }}"></itunes:image>
23
23
  {%- if podcast.subcategory %}
@@ -47,7 +47,7 @@ eleventyAllowMissingExtension: true
47
47
  </itunes:owner>
48
48
  {%- endif %}
49
49
 
50
- {% for post in collections.podcastEpisode | reverse %}
50
+ {% for post in collections.episodePost | reverse %}
51
51
  <item>
52
52
  <title>{{ post.data.episode.title or post.data.title }}</title>
53
53
  {% if post.data.episode.itunesTitle %}
@@ -1,106 +0,0 @@
1
- import { Duration } from 'luxon'
2
- import path from 'node:path'
3
- import { existsSync } from 'node:fs'
4
- import { readdir, stat, writeFile } from 'node:fs/promises'
5
- import { parseFile } from 'music-metadata'
6
- import hr from '@tsmx/human-readable'
7
- import chalk from 'chalk'
8
-
9
- const convertSecondsToReadableDuration = seconds =>
10
- Duration.fromMillis(seconds * 1000)
11
- .shiftTo('days', 'hours', 'minutes', 'seconds')
12
- .toHuman()
13
-
14
- export default function (eleventyConfig) {
15
- let firstRun = true
16
- eleventyConfig.on('eleventy.before', async ({ directories }) => {
17
- // don't keep recalculating episode data in serve mode
18
- if (!firstRun || process.env.SKIP_EPISODE_CALCULATIONS === 'true') return
19
- firstRun = false
20
- const episodesDir = path.join(directories.input, 'episodeFiles')
21
- if (!existsSync(episodesDir)) return
22
-
23
- const episodes = await readdir(episodesDir)
24
- const episodeData = {}
25
- let numberOfEpisodes = 0
26
- let totalSize = 0
27
- let totalDuration = 0
28
-
29
- for (const episode of episodes) {
30
- if (!episode.endsWith('.mp3')) continue
31
-
32
- numberOfEpisodes++
33
- const episodePath = path.join(episodesDir, episode)
34
- const episodeSize = (await stat(episodePath)).size
35
- totalSize += episodeSize
36
- const episodeMetadata = await parseFile(episodePath, { duration: true })
37
- const episodeDuration = episodeMetadata.format.duration
38
- totalDuration += episodeDuration
39
- episodeData[episode] = {
40
- size: episodeSize,
41
- duration: Math.round(episodeDuration * 1000) / 1000
42
- }
43
- totalDuration = Math.round(totalDuration * 1000) / 1000
44
- }
45
- const podcastData = { numberOfEpisodes, totalSize, totalDuration }
46
-
47
- const dataDir = path.join(process.cwd(), directories.data)
48
- await writeFile(path.join(dataDir, 'episodeData.json'), JSON.stringify(episodeData, null, 2))
49
- await writeFile(path.join(dataDir, 'podcastData.json'), JSON.stringify(podcastData, null, 2))
50
-
51
- console.log(chalk.yellow(`${numberOfEpisodes} episodes; ${hr.fromBytes(totalSize)}; ${convertSecondsToReadableDuration(totalDuration)}.`))
52
- })
53
-
54
- const filenameSeasonAndEpisodePattern =
55
- /^.*?\b[sS](?<seasonNumber>\d+)\s*[eE](?<episodeNumber>\d+)\b.*\.mp3$/
56
- const filenameEpisodePattern = /^.*?\b(?<episodeNumber>\d+)\b.*\.mp3$/
57
-
58
- eleventyConfig.addGlobalData('eleventyComputed.episode.filename', () => {
59
- return data => {
60
- if (data.episode.filename) return data.episode.filename
61
-
62
- if (!data.page.inputPath.includes('/episodePosts/')) return
63
-
64
- for (const file of Object.keys(data.episodeData)) {
65
- if (data.episode.seasonNumber && data.episode.episodeNumber) {
66
- const seasonAndEpisodeMatch = file.match(filenameSeasonAndEpisodePattern)
67
- if (seasonAndEpisodeMatch) {
68
- const matchedSeasonNumber = parseInt(seasonAndEpisodeMatch.groups.seasonNumber)
69
- const matchedEpisodeNumber = parseInt(seasonAndEpisodeMatch.groups.episodeNumber)
70
- if (matchedSeasonNumber === data.episode.seasonNumber &&
71
- matchedEpisodeNumber === data.episode.episodeNumber) {
72
- return file
73
- }
74
- }
75
- } else if (data.episode.episodeNumber) {
76
- const episodeMatch = file.match(filenameEpisodePattern)
77
- if (episodeMatch) {
78
- const matchedEpisodeNumber = parseInt(episodeMatch.groups.episodeNumber)
79
- if (matchedEpisodeNumber === data.episode.episodeNumber) {
80
- return file
81
- }
82
- }
83
- }
84
- }
85
- }
86
- })
87
-
88
- eleventyConfig.addGlobalData('eleventyComputed.episode.size', () => {
89
- return data => {
90
- if (data.episode.size) return data.episode.size
91
- if (data.page.inputPath.includes('/episodePosts/') && data.episodeData) {
92
- return data.episodeData[data.episode.filename]?.size
93
- }
94
- }
95
- })
96
-
97
- eleventyConfig.addGlobalData('eleventyComputed.episode.duration', () => {
98
- return data => {
99
- if (data.episode.duration) return data.episode.duration
100
-
101
- if (data.page.inputPath.includes('/episodePosts/') && data.episodeData) {
102
- return data.episodeData[data.episode.filename]?.duration
103
- }
104
- }
105
- })
106
- }