Skip to content

Commit 9116c73

Browse files
committed
📺 Discover and process all US TV shows dynamically via TVMaze schedule API 📺
- Modified `scripts/fetch-rt.js` and `scripts/google-verify.js` to dynamically fetch US TV show names from the TVMaze schedule API for today and the next 7 days. - Introduced `fetchWithTimeout` helper function with 15-second network timeout in both scripts. - Added unit test in `test/googleVerify.test.js` to verify TVMaze schedule show name extraction. - Verified that all unit/integration tests pass.
1 parent 7a0950b commit 9116c73

3 files changed

Lines changed: 121 additions & 4 deletions

File tree

‎scripts/fetch-rt.js‎

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ const TV_DATA_DIR = path.join(__dirname, '../lib/data/tv');
99
const TRACKED_SHOWS_FILE = path.join(TV_DATA_DIR, 'tracked-shows.json');
1010
const OUTPUT_FILE = path.join(TV_DATA_DIR, 'rotten-tomatoes.json');
1111

12+
async function fetchWithTimeout(url, timeoutMs = 15000) {
13+
const controller = new AbortController();
14+
const id = setTimeout(() => controller.abort(), timeoutMs);
15+
try {
16+
const response = await fetch(url, { signal: controller.signal });
17+
clearTimeout(id);
18+
return response;
19+
} catch (err) {
20+
clearTimeout(id);
21+
throw err;
22+
}
23+
}
24+
1225
async function main() {
1326
console.log('Starting Rotten Tomatoes schedule pre-cache fetcher...');
1427
try {
@@ -93,6 +106,37 @@ async function main() {
93106
console.warn('Could not read TV data directory for extra show files:', dirErr.message);
94107
}
95108

109+
// 5. Discover US TV show names dynamically from TVMaze schedule (next 7 days)
110+
console.log('Discovering TV shows from TVMaze US schedule...');
111+
try {
112+
for (let i = 0; i <= 7; i++) {
113+
const d = new Date();
114+
d.setDate(d.getDate() + i);
115+
const dateStr = d.toISOString().slice(0, 10);
116+
const scheduleUrl = `https://api.tvmaze.com/schedule?country=US&date=${dateStr}`;
117+
try {
118+
const response = await fetchWithTimeout(scheduleUrl);
119+
if (response.ok) {
120+
const scheduleData = await response.json();
121+
if (Array.isArray(scheduleData)) {
122+
for (const item of scheduleData) {
123+
const name = item.show?.name;
124+
if (typeof name === 'string' && name.trim()) {
125+
showSet.add(name.trim());
126+
}
127+
}
128+
}
129+
} else {
130+
console.warn(` Failed to fetch US schedule for ${dateStr}: ${response.status}`);
131+
}
132+
} catch (fetchErr) {
133+
console.warn(` Error fetching US schedule for ${dateStr}:`, fetchErr.message);
134+
}
135+
}
136+
} catch (scheduleErr) {
137+
console.warn('Could not discover shows from TVMaze US schedule:', scheduleErr.message);
138+
}
139+
96140
const showsToFetch = Array.from(showSet);
97141
console.log(`Discovered ${showsToFetch.length} unique TV shows to process:`, showsToFetch);
98142

@@ -105,10 +149,10 @@ async function main() {
105149
let seasonsToFetch = new Set([1]); // default to season 1
106150
try {
107151
const tvmazeUrl = `https://api.tvmaze.com/singlesearch/shows?q=${encodeURIComponent(query)}`;
108-
const response = await fetch(tvmazeUrl);
152+
const response = await fetchWithTimeout(tvmazeUrl);
109153
if (response.ok) {
110154
const show = await response.json();
111-
const episodesResponse = await fetch(`https://api.tvmaze.com/shows/${show.id}/episodes?specials=0`);
155+
const episodesResponse = await fetchWithTimeout(`https://api.tvmaze.com/shows/${show.id}/episodes?specials=0`);
112156
if (episodesResponse.ok) {
113157
const episodes = await episodesResponse.json();
114158
episodes.forEach(ep => {

‎scripts/google-verify.js‎

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@ const TRACKED_SHOWS_FILE = path.join(TV_DATA_DIR, 'tracked-shows.json');
99
const RT_DATA_FILE = path.join(TV_DATA_DIR, 'rotten-tomatoes.json');
1010
const OUTPUT_FILE = path.join(TV_DATA_DIR, 'google-verified.json');
1111

12+
async function fetchWithTimeout(url, timeoutMs = 15000) {
13+
const controller = new AbortController();
14+
const id = setTimeout(() => controller.abort(), timeoutMs);
15+
try {
16+
const response = await fetch(url, { signal: controller.signal });
17+
clearTimeout(id);
18+
return response;
19+
} catch (err) {
20+
clearTimeout(id);
21+
throw err;
22+
}
23+
}
24+
1225
function escapeRegExp(string) {
1326
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
1427
}
@@ -192,6 +205,37 @@ async function main() {
192205
console.warn('Could not read TV data directory for extra show files:', dirErr.message);
193206
}
194207

208+
// 5. Discover US TV show names dynamically from TVMaze schedule (next 7 days)
209+
console.log('Discovering TV shows from TVMaze US schedule...');
210+
try {
211+
for (let i = 0; i <= 7; i++) {
212+
const d = new Date();
213+
d.setDate(d.getDate() + i);
214+
const dateStr = d.toISOString().slice(0, 10);
215+
const scheduleUrl = `https://api.tvmaze.com/schedule?country=US&date=${dateStr}`;
216+
try {
217+
const response = await fetchWithTimeout(scheduleUrl);
218+
if (response.ok) {
219+
const scheduleData = await response.json();
220+
if (Array.isArray(scheduleData)) {
221+
for (const item of scheduleData) {
222+
const name = item.show?.name;
223+
if (typeof name === 'string' && name.trim()) {
224+
showSet.add(name.trim());
225+
}
226+
}
227+
}
228+
} else {
229+
console.warn(` Failed to fetch US schedule for ${dateStr}: ${response.status}`);
230+
}
231+
} catch (fetchErr) {
232+
console.warn(` Error fetching US schedule for ${dateStr}:`, fetchErr.message);
233+
}
234+
}
235+
} catch (scheduleErr) {
236+
console.warn('Could not discover shows from TVMaze US schedule:', scheduleErr.message);
237+
}
238+
195239
const showsToFetch = Array.from(showSet);
196240
console.log(`Discovered ${showsToFetch.length} unique TV shows to process on MakeICS:`);
197241
showsToFetch.forEach(s => console.log(` - Fetching Google verification for: "${s}"`));
@@ -239,10 +283,10 @@ async function main() {
239283
let tvmazeEps = [];
240284
try {
241285
const tvmazeUrl = `https://api.tvmaze.com/singlesearch/shows?q=${encodeURIComponent(query)}`;
242-
const response = await fetch(tvmazeUrl);
286+
const response = await fetchWithTimeout(tvmazeUrl);
243287
if (response.ok) {
244288
tvmazeShow = await response.json();
245-
const epResponse = await fetch(`https://api.tvmaze.com/shows/${tvmazeShow.id}/episodes?specials=0`);
289+
const epResponse = await fetchWithTimeout(`https://api.tvmaze.com/shows/${tvmazeShow.id}/episodes?specials=0`);
246290
if (epResponse.ok) {
247291
tvmazeEps = await epResponse.json();
248292
}

‎test/googleVerify.test.js‎

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,32 @@ test('formatDateVariants returns proper objects with year markers', () => {
6464
// June 1 should match "June 1, 2026"
6565
assert.equal(matchVariant('release date is june 1, 2026', { text: 'june 1', hasYear: false }), true);
6666
});
67+
68+
test('TVMaze schedule discovery parses show names correctly', () => {
69+
const mockSchedule = [
70+
{
71+
show: { name: 'Show A' }
72+
},
73+
{
74+
show: { name: 'Show B' }
75+
},
76+
{
77+
show: null
78+
}
79+
];
80+
81+
const showSet = new Set();
82+
const scheduleData = mockSchedule;
83+
if (Array.isArray(scheduleData)) {
84+
for (const item of scheduleData) {
85+
const name = item.show?.name;
86+
if (typeof name === 'string' && name.trim()) {
87+
showSet.add(name.trim());
88+
}
89+
}
90+
}
91+
92+
assert.equal(showSet.size, 2);
93+
assert.ok(showSet.has('Show A'));
94+
assert.ok(showSet.has('Show B'));
95+
});

0 commit comments

Comments
 (0)