From 1edf20f66f02724e65ab8dabb4d73753b8753651 Mon Sep 17 00:00:00 2001 From: Tobias Date: Fri, 31 Jul 2026 14:00:20 +0100 Subject: [PATCH 01/44] updated the index.html file with my name and Github name --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 9a400d1..0b3e15d 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - TV Show Project | My Name (My GitHub username) + TV Show Project | Tobias Amaechina (Tobias-Amaechina) From f7c7e01b3fc74de8a7a3a7dab7fadc1e578f84a7 Mon Sep 17 00:00:00 2001 From: Tobias Date: Fri, 31 Jul 2026 14:12:24 +0100 Subject: [PATCH 02/44] Updated the Index.html file with my personal name and gihub name --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 0b3e15d..64f590a 100644 --- a/index.html +++ b/index.html @@ -3,7 +3,7 @@ - TV Show Project | Tobias Amaechina (Tobias-Amaechina) + TV Show Project | Tobias Amaechina (Tobias-Amaechina) From a5b9fe279a76cbd069d8b3a39865be49f09c6510 Mon Sep 17 00:00:00 2001 From: Tobias Amaechina <128807480+Tobias-Amaechina@users.noreply.github.com> Date: Sat, 1 Aug 2026 15:32:52 +0100 Subject: [PATCH 03/44] Fix header tag for TV Show Project --- index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/index.html b/index.html index 64f590a..5ba4a02 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,7 @@ +
From e1766998e1b6166a0853eb71cf48188c4a2f2893 Mon Sep 17 00:00:00 2001 From: Tobias Amaechina <128807480+Tobias-Amaechina@users.noreply.github.com> Date: Sat, 1 Aug 2026 15:34:55 +0100 Subject: [PATCH 04/44] Update index.html --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 5ba4a02..3eb66c4 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,7 @@ - +

TV Show Project | Tobias Amaechina (Tobias-Amaechina)>

From 160e88797ef2b270956aa13f0effdb1a46de06e3 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 12:21:43 +0100 Subject: [PATCH 05/44] printed the array of the episodes on the screen --- .vscode/settings.json | 3 +++ index.html | 1 - script.js | 8 +++++++- 3 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..6b665aa --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.port": 5501 +} diff --git a/index.html b/index.html index 3eb66c4..64f590a 100644 --- a/index.html +++ b/index.html @@ -9,7 +9,6 @@ -

TV Show Project | Tobias Amaechina (Tobias-Amaechina)>

diff --git a/script.js b/script.js index 87a7de8..f293765 100644 --- a/script.js +++ b/script.js @@ -1,4 +1,8 @@ //You can edit ALL of the code here +const allEpisodes = getAllEpisodes(); + +console.log(allEpisodes); + function setup() { const allEpisodes = getAllEpisodes(); makePageForEpisodes(allEpisodes); @@ -7,6 +11,8 @@ function setup() { function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); rootElem.textContent = `Got ${episodeList.length} episode(s)`; -} + } + + window.onload = setup; From 519d4d53043ffb625ed874f1d2ba8795073a5b10 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 12:33:37 +0100 Subject: [PATCH 06/44] Created h2 element and displayed the first episode name --- script.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/script.js b/script.js index f293765..dfb2119 100644 --- a/script.js +++ b/script.js @@ -10,7 +10,11 @@ function setup() { function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); - rootElem.textContent = `Got ${episodeList.length} episode(s)`; + const title = document.createElement("h2"); + + title.textContent = episodeList[0].name; + + rootElem.appendChild(title); } From 09f71e9d2f7bb292fbb41ab00f3e9b25081ed1d3 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 12:42:35 +0100 Subject: [PATCH 07/44] Created a FoEch loop to loop through each episode creating H2 elemt --- script.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/script.js b/script.js index dfb2119..2dc5415 100644 --- a/script.js +++ b/script.js @@ -10,11 +10,13 @@ function setup() { function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); - const title = document.createElement("h2"); + episodeList.forEach(function (episode) { + const title = document.createElement("h2"); - title.textContent = episodeList[0].name; + title.textContent = episode.name; - rootElem.appendChild(title); + rootElem.appendChild(title); + }); } From 5e5d431fc38ffc24227215a681d8c4820b74f31e Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 12:59:52 +0100 Subject: [PATCH 08/44] create an article for each episode --- script.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script.js b/script.js index 2dc5415..4890547 100644 --- a/script.js +++ b/script.js @@ -11,11 +11,13 @@ function setup() { function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); episodeList.forEach(function (episode) { + const card = document.createElement("article"); const title = document.createElement("h2"); title.textContent = episode.name; - rootElem.appendChild(title); + card.appendChild(title); + rootElem.appendChild(card); }); } From 589e7454468790efcd60cd2ddf619fb19af115ca Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:20:10 +0100 Subject: [PATCH 09/44] used padstart method to format both the episode season and number to two digit format --- script.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/script.js b/script.js index 4890547..fe71ccb 100644 --- a/script.js +++ b/script.js @@ -11,11 +11,18 @@ function setup() { function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); episodeList.forEach(function (episode) { + const season = String(episode.season).padStart(2, "0"); + const number = String(episode.number).padStart(2, "0"); + const episodeCode = `S${season}E${number}`; + const card = document.createElement("article"); const title = document.createElement("h2"); + const code = document.createElement("p"); title.textContent = episode.name; - + code.textContent = episodeCode; + + card.appendChild(code); card.appendChild(title); rootElem.appendChild(card); }); From 9834a918155a5946b473b3e91f93e458be9063ed Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:23:28 +0100 Subject: [PATCH 10/44] create and image element img --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index fe71ccb..7249535 100644 --- a/script.js +++ b/script.js @@ -18,6 +18,7 @@ function makePageForEpisodes(episodeList) { const card = document.createElement("article"); const title = document.createElement("h2"); const code = document.createElement("p"); + const image = document.createElement("img"); title.textContent = episode.name; code.textContent = episodeCode; From c3457c1d09e7e3895af583d091a08b70187a8b9f Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:25:04 +0100 Subject: [PATCH 11/44] set the image source using the medium size --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index 7249535..557c922 100644 --- a/script.js +++ b/script.js @@ -19,6 +19,7 @@ function makePageForEpisodes(episodeList) { const title = document.createElement("h2"); const code = document.createElement("p"); const image = document.createElement("img"); + image.src = episode.image.medium; title.textContent = episode.name; code.textContent = episodeCode; From 3c45c1ade2f88aed2578a0474051009cb946c3ea Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:26:24 +0100 Subject: [PATCH 12/44] add the image alt test for accessibility --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index 557c922..b660a20 100644 --- a/script.js +++ b/script.js @@ -20,6 +20,7 @@ function makePageForEpisodes(episodeList) { const code = document.createElement("p"); const image = document.createElement("img"); image.src = episode.image.medium; + image.alt = episode.name; title.textContent = episode.name; code.textContent = episodeCode; From 0d45f59a9cf19d94c546bb5f236a4c8393b4bedd Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:27:42 +0100 Subject: [PATCH 13/44] add the image to the card --- script.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script.js b/script.js index b660a20..911d510 100644 --- a/script.js +++ b/script.js @@ -24,8 +24,9 @@ function makePageForEpisodes(episodeList) { title.textContent = episode.name; code.textContent = episodeCode; - - card.appendChild(code); + + card.appendChild(image); + card.appendChild(code); card.appendChild(title); rootElem.appendChild(card); }); From 8c52da39bdd5d8fb695cc62d76f4ac0a39f46fc2 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:49:31 +0100 Subject: [PATCH 14/44] created a div element for summary and display with innerHtml --- script.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/script.js b/script.js index 911d510..13ae454 100644 --- a/script.js +++ b/script.js @@ -21,10 +21,14 @@ function makePageForEpisodes(episodeList) { const image = document.createElement("img"); image.src = episode.image.medium; image.alt = episode.name; + const summary = document.createElement("div"); + summary.innerHTML = episode.summary; title.textContent = episode.name; code.textContent = episodeCode; + + card.appendChild(summary); card.appendChild(image); card.appendChild(code); card.appendChild(title); From 110ab44f0753d1e6cd748ca6fbf43e2431b93155 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:51:32 +0100 Subject: [PATCH 15/44] created a link element --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index 13ae454..1239c81 100644 --- a/script.js +++ b/script.js @@ -26,6 +26,7 @@ function makePageForEpisodes(episodeList) { title.textContent = episode.name; code.textContent = episodeCode; + const link = document.createElement("a"); card.appendChild(summary); From e5b2dfbf4dea3fb3c1cc29a553cf8ffa43d8bd73 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:52:22 +0100 Subject: [PATCH 16/44] set the link path --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index 1239c81..4d1ae28 100644 --- a/script.js +++ b/script.js @@ -27,6 +27,7 @@ function makePageForEpisodes(episodeList) { title.textContent = episode.name; code.textContent = episodeCode; const link = document.createElement("a"); + link.href = episode.url; card.appendChild(summary); From 0f32882fcc7bf04342701ca9af02160268b277ee Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:53:35 +0100 Subject: [PATCH 17/44] add some text to explain the link --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index 4d1ae28..6d553a7 100644 --- a/script.js +++ b/script.js @@ -28,6 +28,7 @@ function makePageForEpisodes(episodeList) { code.textContent = episodeCode; const link = document.createElement("a"); link.href = episode.url; + link.textContent = "View on TVMaze"; card.appendChild(summary); From 3770b73061d2562940672233b0b359701473478e Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:54:46 +0100 Subject: [PATCH 18/44] set the target to _blank so it opens in new tab --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index 6d553a7..fce7503 100644 --- a/script.js +++ b/script.js @@ -29,6 +29,7 @@ function makePageForEpisodes(episodeList) { const link = document.createElement("a"); link.href = episode.url; link.textContent = "View on TVMaze"; + link.target = "_blank"; card.appendChild(summary); From 7241f7ef3fb9e2d670c53bd54310c57012ed9808 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:56:52 +0100 Subject: [PATCH 19/44] added nooperner noreferrer for more security --- script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/script.js b/script.js index fce7503..8186fc0 100644 --- a/script.js +++ b/script.js @@ -30,6 +30,7 @@ function makePageForEpisodes(episodeList) { link.href = episode.url; link.textContent = "View on TVMaze"; link.target = "_blank"; + link.rel = "noopener noreferrer"; card.appendChild(summary); From c94004649bb13f4ad9c7b5331a9694f2a2dd3b14 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 13:57:47 +0100 Subject: [PATCH 20/44] added link to the card --- script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script.js b/script.js index 8186fc0..dbd9dbf 100644 --- a/script.js +++ b/script.js @@ -32,7 +32,7 @@ function makePageForEpisodes(episodeList) { link.target = "_blank"; link.rel = "noopener noreferrer"; - + card.appendChild(link); card.appendChild(summary); card.appendChild(image); card.appendChild(code); From 73141342c268f7c60ae0dcd085ee09b5aa3290b9 Mon Sep 17 00:00:00 2001 From: Tobias Date: Mon, 3 Aug 2026 14:22:29 +0100 Subject: [PATCH 21/44] Used one element to create elemet that holds name and code, --- script.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/script.js b/script.js index dbd9dbf..97fbf5d 100644 --- a/script.js +++ b/script.js @@ -17,27 +17,29 @@ function makePageForEpisodes(episodeList) { const card = document.createElement("article"); const title = document.createElement("h2"); - const code = document.createElement("p"); const image = document.createElement("img"); image.src = episode.image.medium; image.alt = episode.name; const summary = document.createElement("div"); summary.innerHTML = episode.summary; - title.textContent = episode.name; - code.textContent = episodeCode; + title.textContent = `${episode.name}-${episodeCode}`; + const link = document.createElement("a"); link.href = episode.url; link.textContent = "View on TVMaze"; link.target = "_blank"; link.rel = "noopener noreferrer"; - card.appendChild(link); - card.appendChild(summary); - card.appendChild(image); - card.appendChild(code); + + + + card.appendChild(title); - rootElem.appendChild(card); + card.appendChild(image); + card.appendChild(summary); + card.appendChild(link); + rootElem.appendChild(card); }); } From fe332f4aa3186fbf6bbe4614e99d570f412f7d14 Mon Sep 17 00:00:00 2001 From: Alina Sofragiu Date: Sat, 8 Aug 2026 10:46:21 +0100 Subject: [PATCH 22/44] Refactor episode setup and filtering functionality in script.js --- script.js | 134 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 103 insertions(+), 31 deletions(-) diff --git a/script.js b/script.js index 97fbf5d..513d8cc 100644 --- a/script.js +++ b/script.js @@ -1,48 +1,120 @@ //You can edit ALL of the code here -const allEpisodes = getAllEpisodes(); - -console.log(allEpisodes); function setup() { const allEpisodes = getAllEpisodes(); + const rootElem = document.getElementById("root"); + + // Create the search and filter controls + const controls = document.createElement("div"); + + const searchInput = document.createElement("input"); + searchInput.type = "text"; + searchInput.placeholder = "Search episodes..."; + + const episodeSelect = document.createElement("select"); + + const defaultOption = document.createElement("option"); + defaultOption.value = ""; + defaultOption.textContent = "All Episodes"; + episodeSelect.appendChild(defaultOption); + + allEpisodes.forEach(function (episode) { + const season = String(episode.season).padStart(2, "0"); + const number = String(episode.number).padStart(2, "0"); + + const option = document.createElement("option"); + option.value = episode.id; + option.textContent = `S${season}E${number} - ${episode.name}`; + + episodeSelect.appendChild(option); + }); + + const results = document.createElement("p"); + results.textContent = `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; + + controls.appendChild(searchInput); + controls.appendChild(episodeSelect); + controls.appendChild(results); + + document.body.insertBefore(controls, rootElem); + makePageForEpisodes(allEpisodes); + + // Filter episodes while typing + searchInput.addEventListener("input", function () { + const searchTerm = searchInput.value.toLowerCase(); + + const filteredEpisodes = allEpisodes.filter(function (episode) { + return ( + episode.name.toLowerCase().includes(searchTerm) || + episode.summary.toLowerCase().includes(searchTerm) + ); + }); + + makePageForEpisodes(filteredEpisodes); + + results.textContent = `Displaying ${filteredEpisodes.length}/${allEpisodes.length} episodes`; + + episodeSelect.value = ""; + }); + + // Show the selected episode + episodeSelect.addEventListener("change", function () { + if (episodeSelect.value === "") { + makePageForEpisodes(allEpisodes); + + results.textContent = `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; + + return; + } + + const selectedEpisode = allEpisodes.filter(function (episode) { + return episode.id == episodeSelect.value; + }); + + makePageForEpisodes(selectedEpisode); + + results.textContent = `Displaying ${selectedEpisode.length}/${allEpisodes.length} episodes`; + + searchInput.value = ""; + }); } function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); - episodeList.forEach(function (episode) { + + // Clear previous episodes before displaying new ones + rootElem.innerHTML = ""; + + episodeList.forEach(function (episode) { const season = String(episode.season).padStart(2, "0"); const number = String(episode.number).padStart(2, "0"); const episodeCode = `S${season}E${number}`; - + const card = document.createElement("article"); - const title = document.createElement("h2"); - const image = document.createElement("img"); - image.src = episode.image.medium; - image.alt = episode.name; - const summary = document.createElement("div"); - summary.innerHTML = episode.summary; - - title.textContent = `${episode.name}-${episodeCode}`; - - const link = document.createElement("a"); - link.href = episode.url; - link.textContent = "View on TVMaze"; - link.target = "_blank"; - link.rel = "noopener noreferrer"; - - - - - - card.appendChild(title); - card.appendChild(image); - card.appendChild(summary); - card.appendChild(link); - rootElem.appendChild(card); - }); - } + const title = document.createElement("h2"); + const image = document.createElement("img"); + image.src = episode.image.medium; + image.alt = episode.name; + const summary = document.createElement("div"); + summary.innerHTML = episode.summary; + title.textContent = `${episode.name}-${episodeCode}`; + + const link = document.createElement("a"); + link.href = episode.url; + link.textContent = "View on TVMaze"; + link.target = "_blank"; + link.rel = "noopener noreferrer"; + + card.appendChild(title); + card.appendChild(image); + card.appendChild(summary); + card.appendChild(link); + + rootElem.appendChild(card); + }); +} window.onload = setup; From 16b7687ef4efdadcd9adc6a6f2f80c65eb5b792d Mon Sep 17 00:00:00 2001 From: Tobias Date: Sun, 9 Aug 2026 18:56:21 +0100 Subject: [PATCH 23/44] Deleted Episodes.js file --- episodes.js | 1855 --------------------------------------------------- 1 file changed, 1855 deletions(-) delete mode 100644 episodes.js diff --git a/episodes.js b/episodes.js deleted file mode 100644 index 5ef6e9e..0000000 --- a/episodes.js +++ /dev/null @@ -1,1855 +0,0 @@ -//DO NOT EDIT THIS FILE - -//This content is from https://www.tvmaze.com/ -//specifically: https://api.tvmaze.com/shows/82/episodes - -function getOneEpisode() { - return { - id: 4952, - url: - "http://www.tvmaze.com/episodes/4952/game-of-thrones-1x01-winter-is-coming", - name: "Winter is Coming", - season: 1, - number: 1, - airdate: "2011-04-17", - airtime: "21:00", - airstamp: "2011-04-18T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2668.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2668.jpg", - }, - summary: - "

Lord Eddard Stark, ruler of the North, is summoned to court by his old friend, King Robert Baratheon, to serve as the King's Hand. Eddard reluctantly agrees after learning of a possible threat to the King's life. Eddard's bastard son Jon Snow must make a painful decision about his own future, while in the distant east Viserys Targaryen plots to reclaim his father's throne, usurped by Robert, by selling his sister in marriage.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4952", - }, - }, - }; -} - -function getAllEpisodes() { - return [ - { - id: 4952, - url: - "http://www.tvmaze.com/episodes/4952/game-of-thrones-1x01-winter-is-coming", - name: "Winter is Coming", - season: 1, - number: 1, - airdate: "2011-04-17", - airtime: "21:00", - airstamp: "2011-04-18T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2668.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2668.jpg", - }, - summary: - "

Lord Eddard Stark, ruler of the North, is summoned to court by his old friend, King Robert Baratheon, to serve as the King's Hand. Eddard reluctantly agrees after learning of a possible threat to the King's life. Eddard's bastard son Jon Snow must make a painful decision about his own future, while in the distant east Viserys Targaryen plots to reclaim his father's throne, usurped by Robert, by selling his sister in marriage.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4952", - }, - }, - }, - { - id: 4953, - url: - "http://www.tvmaze.com/episodes/4953/game-of-thrones-1x02-the-kingsroad", - name: "The Kingsroad", - season: 1, - number: 2, - airdate: "2011-04-24", - airtime: "21:00", - airstamp: "2011-04-25T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2669.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2669.jpg", - }, - summary: - "

An incident on the Kingsroad threatens Eddard and Robert's friendship. Jon and Tyrion travel to the Wall, where they discover that the reality of the Night's Watch may not match the heroic image of it.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4953", - }, - }, - }, - { - id: 4954, - url: "http://www.tvmaze.com/episodes/4954/game-of-thrones-1x03-lord-snow", - name: "Lord Snow", - season: 1, - number: 3, - airdate: "2011-05-01", - airtime: "21:00", - airstamp: "2011-05-02T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2671.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2671.jpg", - }, - summary: - "

Jon Snow attempts to find his place amongst the Night's Watch. Eddard and his daughters arrive at King's Landing.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4954", - }, - }, - }, - { - id: 4955, - url: - "http://www.tvmaze.com/episodes/4955/game-of-thrones-1x04-cripples-bastards-and-broken-things", - name: "Cripples, Bastards, and Broken Things", - season: 1, - number: 4, - airdate: "2011-05-08", - airtime: "21:00", - airstamp: "2011-05-09T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2673.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2673.jpg", - }, - summary: - "

Tyrion stops at Winterfell on his way home and gets a frosty reception from Robb Stark. Eddard's investigation into the death of his predecessor gets underway.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4955", - }, - }, - }, - { - id: 4956, - url: - "http://www.tvmaze.com/episodes/4956/game-of-thrones-1x05-the-wolf-and-the-lion", - name: "The Wolf and the Lion", - season: 1, - number: 5, - airdate: "2011-05-15", - airtime: "21:00", - airstamp: "2011-05-16T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2674.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2674.jpg", - }, - summary: - "

Catelyn's actions on the road have repercussions for Eddard. Tyrion enjoys the dubious hospitality of the Eyrie.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4956", - }, - }, - }, - { - id: 4957, - url: - "http://www.tvmaze.com/episodes/4957/game-of-thrones-1x06-a-golden-crown", - name: "A Golden Crown", - season: 1, - number: 6, - airdate: "2011-05-22", - airtime: "21:00", - airstamp: "2011-05-23T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2676.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2676.jpg", - }, - summary: - "

Viserys is increasingly frustrated by the lack of progress towards gaining his crown.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4957", - }, - }, - }, - { - id: 4958, - url: - "http://www.tvmaze.com/episodes/4958/game-of-thrones-1x07-you-win-or-you-die", - name: "You Win or You Die", - season: 1, - number: 7, - airdate: "2011-05-29", - airtime: "21:00", - airstamp: "2011-05-30T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2677.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2677.jpg", - }, - summary: - "

Eddard's investigations in King's Landing reach a climax and a dark secret is revealed.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4958", - }, - }, - }, - { - id: 4959, - url: - "http://www.tvmaze.com/episodes/4959/game-of-thrones-1x08-the-pointy-end", - name: "The Pointy End", - season: 1, - number: 8, - airdate: "2011-06-05", - airtime: "21:00", - airstamp: "2011-06-06T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2678.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2678.jpg", - }, - summary: - "

Tyrion joins his father's army with unexpected allies. Events in King's Landing take a turn for the worse as Arya's lessons are put to the test.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4959", - }, - }, - }, - { - id: 4960, - url: "http://www.tvmaze.com/episodes/4960/game-of-thrones-1x09-baelor", - name: "Baelor", - season: 1, - number: 9, - airdate: "2011-06-12", - airtime: "21:00", - airstamp: "2011-06-13T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2679.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2679.jpg", - }, - summary: - "

Catelyn must negotiate with the irascible Lord Walder Frey.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4960", - }, - }, - }, - { - id: 4961, - url: - "http://www.tvmaze.com/episodes/4961/game-of-thrones-1x10-fire-and-blood", - name: "Fire and Blood", - season: 1, - number: 10, - airdate: "2011-06-19", - airtime: "21:00", - airstamp: "2011-06-20T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2681.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2681.jpg", - }, - summary: - "

Daenerys must realize her destiny. Jaime finds himself in an unfamiliar predicament.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4961", - }, - }, - }, - { - id: 4962, - url: - "http://www.tvmaze.com/episodes/4962/game-of-thrones-2x01-the-north-remembers", - name: "The North Remembers", - season: 2, - number: 1, - airdate: "2012-04-01", - airtime: "21:00", - airstamp: "2012-04-02T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3174.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3174.jpg", - }, - summary: - "

War grips the continent of Westeros. As Tyrion Lannister tries to take his strong-willed nephew in hand in King's Landing, Stannis Baratheon launches his own campaign to take the Iron Throne with the help of a mysterious priestess. In the east, Daenerys must lead her retinue through a desolate wasteland whilst beyond the Wall the Night's Watch seeks the aid of a wildling.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4962", - }, - }, - }, - { - id: 4963, - url: - "http://www.tvmaze.com/episodes/4963/game-of-thrones-2x02-the-night-lands", - name: "The Night Lands", - season: 2, - number: 2, - airdate: "2012-04-08", - airtime: "21:00", - airstamp: "2012-04-09T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3175.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3175.jpg", - }, - summary: - "

Stannis uses Ser Davos to seek out new allies for his war with the Lannisters. On the road north, Arya confides in Gendry. Robb Stark sends Theon Greyjoy to win an alliance with his father and the fierce warriors of the Iron Islands. Cersei and Tyrion clash on how to rule in King's Landing.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4963", - }, - }, - }, - { - id: 4964, - url: - "http://www.tvmaze.com/episodes/4964/game-of-thrones-2x03-what-is-dead-may-never-die", - name: "What is Dead May Never Die", - season: 2, - number: 3, - airdate: "2012-04-15", - airtime: "21:00", - airstamp: "2012-04-16T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3176.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3176.jpg", - }, - summary: - "

Catelyn Stark treats with King Renly in the hope of winning an alliance. Tyrion undertakes a complex plan in King's Landing to expose an enemy. At Winterfell, Bran's dreams continue to trouble him.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4964", - }, - }, - }, - { - id: 4965, - url: - "http://www.tvmaze.com/episodes/4965/game-of-thrones-2x04-garden-of-bones", - name: "Garden of Bones", - season: 2, - number: 4, - airdate: "2012-04-22", - airtime: "21:00", - airstamp: "2012-04-23T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3177.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3177.jpg", - }, - summary: - "

Tyrion attempts to restrain Joffrey's cruelty. Catelyn attempts to broker a peace between Stannis and Renly. Daenerys and her followers arrive at the great city of Qarth and hope to find refuge there. Arya and Gendry arrive at Harrenhal, a great castle now under Lannister occupation.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4965", - }, - }, - }, - { - id: 4966, - url: - "http://www.tvmaze.com/episodes/4966/game-of-thrones-2x05-the-ghost-of-harrenhal", - name: "The Ghost of Harrenhal", - season: 2, - number: 5, - airdate: "2012-04-29", - airtime: "21:00", - airstamp: "2012-04-30T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3178.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3178.jpg", - }, - summary: - "

Confusion rages in the Stormlands in the wake of a devastating reversal. Catelyn must flee with a new ally, whilst Littlefinger sees an opportunity in the chaos. Theon seeks to prove himself to his father in battle. Arya receives a promise from the enigmatic Jaqen H'ghar. The Night's Watch arrives at the Fist of the First Men. Daenerys Targaryen receives a marriage proposal.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4966", - }, - }, - }, - { - id: 4967, - url: - "http://www.tvmaze.com/episodes/4967/game-of-thrones-2x06-the-old-gods-and-the-new", - name: "The Old Gods and the New", - season: 2, - number: 6, - airdate: "2012-05-06", - airtime: "21:00", - airstamp: "2012-05-07T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3180.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3180.jpg", - }, - summary: - "

Arya has a surprise visitor; Dany vows to take what is hers; Joffrey meets his subjects; Qhorin gives Jon a chance to prove himself.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4967", - }, - }, - }, - { - id: 4968, - url: - "http://www.tvmaze.com/episodes/4968/game-of-thrones-2x07-a-man-without-honor", - name: "A Man Without Honor", - season: 2, - number: 7, - airdate: "2012-05-13", - airtime: "21:00", - airstamp: "2012-05-14T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3192.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3192.jpg", - }, - summary: - "

Jaime meets a relative; Theon hunts; Dany receives an invitation.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4968", - }, - }, - }, - { - id: 4969, - url: - "http://www.tvmaze.com/episodes/4969/game-of-thrones-2x08-the-prince-of-winterfell", - name: "The Prince of Winterfell", - season: 2, - number: 8, - airdate: "2012-05-20", - airtime: "21:00", - airstamp: "2012-05-21T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3194.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3194.jpg", - }, - summary: - "

Theon holds the fort; Arya calls in her debt with Jaqen; Robb is betrayed; Stannis and Davos approach their destination.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4969", - }, - }, - }, - { - id: 4970, - url: - "http://www.tvmaze.com/episodes/4970/game-of-thrones-2x09-blackwater", - name: "Blackwater", - season: 2, - number: 9, - airdate: "2012-05-27", - airtime: "21:00", - airstamp: "2012-05-28T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3196.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3196.jpg", - }, - summary: - "

A massive battle rages for control of King's Landing and the Iron Throne.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4970", - }, - }, - }, - { - id: 4971, - url: - "http://www.tvmaze.com/episodes/4971/game-of-thrones-2x10-valar-morghulis", - name: "Valar Morghulis", - season: 2, - number: 10, - airdate: "2012-06-03", - airtime: "21:00", - airstamp: "2012-06-04T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/3197.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/3197.jpg", - }, - summary: - "

Tyrion awakens to a changed situation. King Joffrey doles out rewards to his subjects. As Theon stirs his men to action, Luwin offers some final advice. Brienne silences Jaime; Arya receives a gift from Jaqen; Dany goes to a strange place; Jon proves himself to Qhorin.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4971", - }, - }, - }, - { - id: 4972, - url: - "http://www.tvmaze.com/episodes/4972/game-of-thrones-3x01-valar-dohaeris", - name: "Valar Dohaeris", - season: 3, - number: 1, - airdate: "2013-03-31", - airtime: "21:00", - airstamp: "2013-04-01T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2628.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2628.jpg", - }, - summary: - "

Jon is brought before Mance Rayder, the King Beyond the Wall, while the Night's Watch survivors retreat south. In King's Landing, Tyrion asks for his reward. Littlefinger offers Sansa a way out. Cersei hosts a dinner for the royal family. Daenerys sails into Slaver's Bay.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4972", - }, - }, - }, - { - id: 4973, - url: - "http://www.tvmaze.com/episodes/4973/game-of-thrones-3x02-dark-wings-dark-words", - name: "Dark Wings, Dark Words", - season: 3, - number: 2, - airdate: "2013-04-07", - airtime: "21:00", - airstamp: "2013-04-08T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2618.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2618.jpg", - }, - summary: - "

Sansa says too much. Shae asks Tyrion for a favor. Jaime finds a way to pass the time. Arya runs into the Brotherhood Without Banners.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4973", - }, - }, - }, - { - id: 4974, - url: - "http://www.tvmaze.com/episodes/4974/game-of-thrones-3x03-walk-of-punishment", - name: "Walk of Punishment", - season: 3, - number: 3, - airdate: "2013-04-14", - airtime: "21:00", - airstamp: "2013-04-15T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2616.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2616.jpg", - }, - summary: - "

Tyrion shoulders new responsibilities. Jon is taken to the Fist of the First Men. Daenerys meets with the slavers. Jaime strikes a deal with his captors.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4974", - }, - }, - }, - { - id: 4975, - url: - "http://www.tvmaze.com/episodes/4975/game-of-thrones-3x04-and-now-his-watch-is-ended", - name: "And Now His Watch is Ended", - season: 3, - number: 4, - airdate: "2013-04-21", - airtime: "21:00", - airstamp: "2013-04-22T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2615.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2615.jpg", - }, - summary: - "

The Night's Watch takes stock. Varys meets his better. Arya is taken to the commander of the Brotherhood. Daenerys exchanges a chain for a Whip.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4975", - }, - }, - }, - { - id: 4976, - url: - "http://www.tvmaze.com/episodes/4976/game-of-thrones-3x05-kissed-by-fire", - name: "Kissed by Fire", - season: 3, - number: 5, - airdate: "2013-04-28", - airtime: "21:00", - airstamp: "2013-04-29T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2614.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2614.jpg", - }, - summary: - "

The Hound is judged by the gods; Jaime is judged by men. Jon proves himself; Robb is betrayed. Tyrion learns the cost of weddings.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4976", - }, - }, - }, - { - id: 4977, - url: "http://www.tvmaze.com/episodes/4977/game-of-thrones-3x06-the-climb", - name: "The Climb", - season: 3, - number: 6, - airdate: "2013-05-05", - airtime: "21:00", - airstamp: "2013-05-06T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2612.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2612.jpg", - }, - summary: - "

Tywin plans strategic unions for the Lannisters. Melisandre visits the Riverlands. Robb weighs a compromise to repair his alliance with House Frey. Roose Bolton decides what to do with Jaime Lannister. Jon, Ygritte and the Wildlings face a daunting climb.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4977", - }, - }, - }, - { - id: 4978, - url: - "http://www.tvmaze.com/episodes/4978/game-of-thrones-3x07-the-bear-and-the-maiden-fair", - name: "The Bear and the Maiden Fair", - season: 3, - number: 7, - airdate: "2013-05-12", - airtime: "21:00", - airstamp: "2013-05-13T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2611.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2611.jpg", - }, - summary: - "

Daenerys exchanges gifts with a slave lord outside Yunkai. As Sansa frets about her prospects, Shae chafes at Tyrion's new situation. Tywin counsels the king, and Melisandre reveals a secret to Gendry. Brienne faces a formidable foe in Harrenhal.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4978", - }, - }, - }, - { - id: 4979, - url: - "http://www.tvmaze.com/episodes/4979/game-of-thrones-3x08-second-sons", - name: "Second Sons", - season: 3, - number: 8, - airdate: "2013-05-19", - airtime: "21:00", - airstamp: "2013-05-20T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2599.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2599.jpg", - }, - summary: - "

King's Landing hosts a wedding, and Tyrion and Sansa spend the night together. Daenerys meets the Titan's Bastard. Davos demands proof from Melisandre. Sam and Gilly meet an older Gentleman.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4979", - }, - }, - }, - { - id: 4980, - url: - "http://www.tvmaze.com/episodes/4980/game-of-thrones-3x09-the-rains-of-castamere", - name: "The Rains of Castamere", - season: 3, - number: 9, - airdate: "2013-06-02", - airtime: "21:00", - airstamp: "2013-06-03T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2598.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2598.jpg", - }, - summary: - "

Robb presents himself to Walder Frey, and Edmure meets his bride. Jon faces his harshest test yet. Bran discovers a new gift. Daario and Jorah debate how to take Yunkai. House Frey joins with House Tully.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4980", - }, - }, - }, - { - id: 4981, - url: "http://www.tvmaze.com/episodes/4981/game-of-thrones-3x10-mhysa", - name: "Mhysa", - season: 3, - number: 10, - airdate: "2013-06-09", - airtime: "21:00", - airstamp: "2013-06-10T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2597.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2597.jpg", - }, - summary: - "

Joffrey challenges Tywin. Bran tells a ghost story. In Dragonstone, mercy comes from strange quarters. Daenerys waits to see if she is a conqueror or a liberator.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4981", - }, - }, - }, - { - id: 4982, - url: - "http://www.tvmaze.com/episodes/4982/game-of-thrones-4x01-two-swords", - name: "Two Swords", - season: 4, - number: 1, - airdate: "2014-04-06", - airtime: "21:00", - airstamp: "2014-04-07T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2583.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2583.jpg", - }, - summary: - "

Tyrion welcomes a guest to King's Landing. At Castle Black, Jon Snow finds himself unwelcome. Dany is pointed to Meereen, the mother of all slave cities. Arya runs into an old friend.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4982", - }, - }, - }, - { - id: 4983, - url: - "http://www.tvmaze.com/episodes/4983/game-of-thrones-4x02-the-lion-and-the-rose", - name: "The Lion and the Rose", - season: 4, - number: 2, - airdate: "2014-04-13", - airtime: "21:00", - airstamp: "2014-04-14T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2584.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2584.jpg", - }, - summary: - "

Tyrion lends Jaime a hand. Joffrey and Margaery host a breakfast. At Dragonstone, Stannis loses patience with Davos. Ramsay finds a purpose for his pet. North of the Wall, Bran sees where they must go.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4983", - }, - }, - }, - { - id: 4984, - url: - "http://www.tvmaze.com/episodes/4984/game-of-thrones-4x03-breaker-of-chains", - name: "Breaker of Chains", - season: 4, - number: 3, - airdate: "2014-04-20", - airtime: "21:00", - airstamp: "2014-04-21T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2585.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2585.jpg", - }, - summary: - "

Tyrion ponders his options. Tywin extends an olive branch. Sam realizes Castle Black isn't safe, and Jon proposes a bold plan. The Hound teaches Arya the way things are. Dany chooses her Champion.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4984", - }, - }, - }, - { - id: 4985, - url: - "http://www.tvmaze.com/episodes/4985/game-of-thrones-4x04-oathkeeper", - name: "Oathkeeper", - season: 4, - number: 4, - airdate: "2014-04-27", - airtime: "21:00", - airstamp: "2014-04-28T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2586.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2586.jpg", - }, - summary: - "

Dany balances justice and mercy. Jaime tasks Brienne with his honor. Jon secures volunteers while Bran, Jojen, Meera and Hodor stumble on shelter.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4985", - }, - }, - }, - { - id: 4986, - url: - "http://www.tvmaze.com/episodes/4986/game-of-thrones-4x05-first-of-his-name", - name: "First of His Name", - season: 4, - number: 5, - airdate: "2014-05-04", - airtime: "21:00", - airstamp: "2014-05-05T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2587.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2587.jpg", - }, - summary: - "

Cersei and Tywin plot the Crown's next move. Dany discusses future plans. Jon embarks on a new mission.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4986", - }, - }, - }, - { - id: 4987, - url: - "http://www.tvmaze.com/episodes/4987/game-of-thrones-4x06-the-laws-of-gods-and-men", - name: "The Laws of Gods and Men", - season: 4, - number: 6, - airdate: "2014-05-11", - airtime: "21:00", - airstamp: "2014-05-12T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2588.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2588.jpg", - }, - summary: - "

Stannis and Davos set sail with a new strategy. Dany meets with supplicants. Tyrion faces down his father in the throne room.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4987", - }, - }, - }, - { - id: 4988, - url: - "http://www.tvmaze.com/episodes/4988/game-of-thrones-4x07-mockingbird", - name: "Mockingbird", - season: 4, - number: 7, - airdate: "2014-05-18", - airtime: "21:00", - airstamp: "2014-05-19T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2589.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2589.jpg", - }, - summary: - "

Tyrion enlists an unlikely ally. Daario entreats Dany to allow him to do what he does best. Jon's warnings about the Wall's vulnerability fall on deaf ears. Brienne follows a new lead on the road with Pod.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4988", - }, - }, - }, - { - id: 4989, - url: - "http://www.tvmaze.com/episodes/4989/game-of-thrones-4x08-the-mountain-and-the-viper", - name: "The Mountain and the Viper", - season: 4, - number: 8, - airdate: "2014-06-01", - airtime: "21:00", - airstamp: "2014-06-02T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2591.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2591.jpg", - }, - summary: - "

Mole's Town receives unexpected visitors. Littlefinger's motives are questioned. Ramsay attempts to prove himself to his father. Tyrion's fate is decided.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4989", - }, - }, - }, - { - id: 4990, - url: - "http://www.tvmaze.com/episodes/4990/game-of-thrones-4x09-the-watchers-on-the-wall", - name: "The Watchers on the Wall", - season: 4, - number: 9, - airdate: "2014-06-08", - airtime: "21:00", - airstamp: "2014-06-09T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2593.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2593.jpg", - }, - summary: - "

Jon Snow and the rest of the Night's Watch face the biggest challenge to the Wall yet.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4990", - }, - }, - }, - { - id: 4991, - url: - "http://www.tvmaze.com/episodes/4991/game-of-thrones-4x10-the-children", - name: "The Children", - season: 4, - number: 10, - airdate: "2014-06-15", - airtime: "21:00", - airstamp: "2014-06-16T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/1/2594.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/1/2594.jpg", - }, - summary: - "

An unexpected arrival north of the Wall changes circumstances. Dany is forced to face harsh realities. Bran learns more of his destiny. Tyrion sees the truth of his situation.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/4991", - }, - }, - }, - { - id: 116522, - url: - "http://www.tvmaze.com/episodes/116522/game-of-thrones-5x01-the-wars-to-come", - name: "The Wars to Come", - season: 5, - number: 1, - airdate: "2015-04-12", - airtime: "21:00", - airstamp: "2015-04-13T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/25988.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/25988.jpg", - }, - summary: - "

Varys discusses a conspiracy with Tyrion; Daenerys' rule faces a new threat; Jon finds himself between two kings; and Cersei and Jaime try to move on from Tywin's demise.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/116522", - }, - }, - }, - { - id: 144328, - url: - "http://www.tvmaze.com/episodes/144328/game-of-thrones-5x02-the-house-of-black-and-white", - name: "The House of Black and White", - season: 5, - number: 2, - airdate: "2015-04-19", - airtime: "21:00", - airstamp: "2015-04-20T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/25989.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/25989.jpg", - }, - summary: - "

Arya arrives in Braavos; Brienne and Podrick find danger while traveling; Cersei worries about Myrcella in Dorne when Ellaria Sand seeks revenge for Oberyn's death; Jon is tempted by Stannis.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/144328", - }, - }, - }, - { - id: 144329, - url: - "http://www.tvmaze.com/episodes/144329/game-of-thrones-5x03-high-sparrow", - name: "High Sparrow", - season: 5, - number: 3, - airdate: "2015-04-26", - airtime: "21:00", - airstamp: "2015-04-27T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/25990.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/25990.jpg", - }, - summary: - "

Cersei meets the High Sparrow after learning of a clergyman's embarrassing tale. Meanwhile, Davos talks to Jon about the future of Winterfell, where Ramsay Snow has just learned the identity of his future bride; Arya grows impatient doing menial tasks in the House of Black and White; and Tyrion searches for more comfortable surroundings on a long trip with Varys.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/144329", - }, - }, - }, - { - id: 144330, - url: - "http://www.tvmaze.com/episodes/144330/game-of-thrones-5x04-sons-of-the-harpy", - name: "Sons of the Harpy", - season: 5, - number: 4, - airdate: "2015-05-03", - airtime: "21:00", - airstamp: "2015-05-04T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/26444.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/26444.jpg", - }, - summary: - "

Margaery seeks prudent counsel. Jaime Struggles in foreign lands. Dany answers the Harpy's call.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/144330", - }, - }, - }, - { - id: 151777, - url: - "http://www.tvmaze.com/episodes/151777/game-of-thrones-5x05-kill-the-boy", - name: "Kill the Boy", - season: 5, - number: 5, - airdate: "2015-05-10", - airtime: "21:00", - airstamp: "2015-05-11T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/26819.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/26819.jpg", - }, - summary: - "

Dany makes a difficult decision in Meereen. Jon recruits the help of an unexpected ally. Brienne searches for Sansa. Theon remains under Ramsay's control.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/151777", - }, - }, - }, - { - id: 152766, - url: - "http://www.tvmaze.com/episodes/152766/game-of-thrones-5x06-unbowed-unbent-unbroken", - name: "Unbowed, Unbent, Unbroken", - season: 5, - number: 6, - airdate: "2015-05-17", - airtime: "21:00", - airstamp: "2015-05-18T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/10/27259.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/10/27259.jpg", - }, - summary: - "

Arya trains. Jorah and Tyrion run into slavers. Trystane and Myrcella make plans. Jaime and Bronn reach their destination. The Sand Snakes attack.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/152766", - }, - }, - }, - { - id: 153327, - url: - "http://www.tvmaze.com/episodes/153327/game-of-thrones-5x07-the-gift", - name: "The Gift", - season: 5, - number: 7, - airdate: "2015-05-24", - airtime: "21:00", - airstamp: "2015-05-25T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/11/27535.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/11/27535.jpg", - }, - summary: - "

Jon prepares for conflict. Sansa tries to talk to Theon. Brienne waits for a sign. Stannis remains stubborn. Jaime attempts to reconnect with family.



", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/153327", - }, - }, - }, - { - id: 155299, - url: - "http://www.tvmaze.com/episodes/155299/game-of-thrones-5x08-hardhome", - name: "Hardhome", - season: 5, - number: 8, - airdate: "2015-05-31", - airtime: "21:00", - airstamp: "2015-06-01T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/11/28151.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/11/28151.jpg", - }, - summary: - "

Arya makes progress in her training. Sansa confronts an old friend. Cersei struggles. Jon travels.



", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/155299", - }, - }, - }, - { - id: 160040, - url: - "http://www.tvmaze.com/episodes/160040/game-of-thrones-5x09-the-dance-of-dragons", - name: "The Dance of Dragons", - season: 5, - number: 9, - airdate: "2015-06-07", - airtime: "21:00", - airstamp: "2015-06-08T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/11/29160.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/11/29160.jpg", - }, - summary: - "

Stannis confronts a troubling decision. Jon returns to The Wall. Mace visits the Iron Bank. Arya encounters someone from her past. Dany reluctantly oversees a traditional celebration of athleticism.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/160040", - }, - }, - }, - { - id: 162186, - url: - "http://www.tvmaze.com/episodes/162186/game-of-thrones-5x10-mothers-mercy", - name: "Mother's Mercy", - season: 5, - number: 10, - airdate: "2015-06-14", - airtime: "21:00", - airstamp: "2015-06-15T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/12/30012.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/12/30012.jpg", - }, - summary: - "

Cersei seeks forgiveness; Jon faces a new challenge; Arya plots to cross a name off her list; Tyrion sees a familiar face; and Daenerys finds herself surrounded by strangers.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/162186", - }, - }, - }, - { - id: 560813, - url: - "http://www.tvmaze.com/episodes/560813/game-of-thrones-6x01-the-red-woman", - name: "The Red Woman", - season: 6, - number: 1, - airdate: "2016-04-24", - airtime: "21:00", - airstamp: "2016-04-25T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/56/142371.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/56/142371.jpg", - }, - summary: - "

Jon Snow is dead. Daenerys meets a strong man. Cersei sees her daughter again.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/560813", - }, - }, - }, - { - id: 664672, - url: "http://www.tvmaze.com/episodes/664672/game-of-thrones-6x02-home", - name: "Home", - season: 6, - number: 2, - airdate: "2016-05-01", - airtime: "21:00", - airstamp: "2016-05-02T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/56/142372.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/56/142372.jpg", - }, - summary: - "

Bran trains with the Three-Eyed Raven. In King's Landing, Jaime advises Tommen. Tyrion demands good news, but has to make his own. At Castle Black, the Night's Watch stands behind Thorne. Ramsay Bolton proposes a plan, and Balon Greyjoy entertains other proposals.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664672", - }, - }, - }, - { - id: 664673, - url: - "http://www.tvmaze.com/episodes/664673/game-of-thrones-6x03-oathbreaker", - name: "Oathbreaker", - season: 6, - number: 3, - airdate: "2016-05-08", - airtime: "21:00", - airstamp: "2016-05-09T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/56/142370.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/56/142370.jpg", - }, - summary: - "

Daenerys meets her future. Bran meets the past. Tommen confronts the High Sparrow. Arya trains to be No One. Varys finds an answer. Ramsay gets a gift.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664673", - }, - }, - }, - { - id: 664674, - url: - "http://www.tvmaze.com/episodes/664674/game-of-thrones-6x04-book-of-the-stranger", - name: "Book of the Stranger", - season: 6, - number: 4, - airdate: "2016-05-15", - airtime: "21:00", - airstamp: "2016-05-16T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/56/142273.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/56/142273.jpg", - }, - summary: - "

Tyrion strikes a deal. Jorah and Daario undertake a difficult task. Jaime and Cersei try to improve their situation.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664674", - }, - }, - }, - { - id: 664675, - url: - "http://www.tvmaze.com/episodes/664675/game-of-thrones-6x05-the-door", - name: "The Door", - season: 6, - number: 5, - airdate: "2016-05-22", - airtime: "21:00", - airstamp: "2016-05-23T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/60/150273.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/60/150273.jpg", - }, - summary: - "

Tyrion seeks a strange ally. Bran learns a great deal. Brienne goes on a mission. Arya is given a chance to prove herself.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664675", - }, - }, - }, - { - id: 664676, - url: - "http://www.tvmaze.com/episodes/664676/game-of-thrones-6x06-blood-of-my-blood", - name: "Blood of My Blood", - season: 6, - number: 6, - airdate: "2016-05-29", - airtime: "21:00", - airstamp: "2016-05-30T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/60/150274.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/60/150274.jpg", - }, - summary: - "

An old foe comes back into the picture. Gilly meets Sam's family. Arya faces a difficult choice. Jaime faces off against the High Sparrow.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/664676", - }, - }, - }, - { - id: 717449, - url: - "http://www.tvmaze.com/episodes/717449/game-of-thrones-6x07-the-broken-man", - name: "The Broken Man", - season: 6, - number: 7, - airdate: "2016-06-05", - airtime: "21:00", - airstamp: "2016-06-06T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/60/150275.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/60/150275.jpg", - }, - summary: - "

The High Sparrow eyes another target. Jaime confronts a hero. Arya makes a plan. The North is reminded.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/717449", - }, - }, - }, - { - id: 729573, - url: "http://www.tvmaze.com/episodes/729573/game-of-thrones-6x08-no-one", - name: "No One", - season: 6, - number: 8, - airdate: "2016-06-12", - airtime: "21:00", - airstamp: "2016-06-13T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/61/153044.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/61/153044.jpg", - }, - summary: - "

Jaime encounters a hero; the High Sparrow fixates on another prey; Arya hatches a new plan; Yara and Theon plot their next move; Olenna and Cersei discuss their families' futures.

While Jaime weighs his options, Cersei answers a request. Tyrion's plans bear fruit. Arya faces a new test.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/729573", - }, - }, - }, - { - id: 729574, - url: - "http://www.tvmaze.com/episodes/729574/game-of-thrones-6x09-battle-of-the-bastards", - name: "Battle of the Bastards", - season: 6, - number: 9, - airdate: "2016-06-19", - airtime: "21:00", - airstamp: "2016-06-20T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/62/155042.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/62/155042.jpg", - }, - summary: - "

Ramsay surprises his audience. Jon retaliates. Dany is true to her word.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/729574", - }, - }, - }, - { - id: 729575, - url: - "http://www.tvmaze.com/episodes/729575/game-of-thrones-6x10-the-winds-of-winter", - name: "The Winds of Winter", - season: 6, - number: 10, - airdate: "2016-06-26", - airtime: "21:00", - airstamp: "2016-06-27T01:00:00+00:00", - runtime: 69, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/63/157920.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/63/157920.jpg", - }, - summary: - "

Alliances are made, the High Sparrow is holding trials at King's Landing, Daenerys is sailing for the Seven Kingdoms and a new King of the North is crowned.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/729575", - }, - }, - }, - { - id: 937256, - url: - "http://www.tvmaze.com/episodes/937256/game-of-thrones-7x01-dragonstone", - name: "Dragonstone", - season: 7, - number: 1, - airdate: "2017-07-16", - airtime: "21:00", - airstamp: "2017-07-17T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/120/302038.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/120/302038.jpg", - }, - summary: - "

Jon organizes the defense of the North. Cersei tries to even the odds. Daenerys comes home.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/937256", - }, - }, - }, - { - id: 1221410, - url: - "http://www.tvmaze.com/episodes/1221410/game-of-thrones-7x02-stormborn", - name: "Stormborn", - season: 7, - number: 2, - airdate: "2017-07-23", - airtime: "21:00", - airstamp: "2017-07-24T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/120/302047.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/120/302047.jpg", - }, - summary: - "

Daenerys receives an unexpected visitor. Jon faces a revolt. Tyrion plans the conquest of Westeros.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221410", - }, - }, - }, - { - id: 1221411, - url: - "http://www.tvmaze.com/episodes/1221411/game-of-thrones-7x03-the-queens-justice", - name: "The Queen's Justice", - season: 7, - number: 3, - airdate: "2017-07-30", - airtime: "21:00", - airstamp: "2017-07-31T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/122/306938.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/122/306938.jpg", - }, - summary: - "

Daenerys holds court. Cersei returns a gift. Jaime learns from his mistakes.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221411", - }, - }, - }, - { - id: 1221412, - url: - "http://www.tvmaze.com/episodes/1221412/game-of-thrones-7x04-the-spoils-of-war", - name: "The Spoils of War", - season: 7, - number: 4, - airdate: "2017-08-06", - airtime: "21:00", - airstamp: "2017-08-07T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/123/307677.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/123/307677.jpg", - }, - summary: - "

Arya gets to the final destination. Daenerys takes it upon herself to strike back.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221412", - }, - }, - }, - { - id: 1221413, - url: - "http://www.tvmaze.com/episodes/1221413/game-of-thrones-7x05-eastwatch", - name: "Eastwatch", - season: 7, - number: 5, - airdate: "2017-08-13", - airtime: "21:00", - airstamp: "2017-08-14T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/124/310839.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/124/310839.jpg", - }, - summary: - "

Daenerys demands loyalty from the surviving Lannister soldiers; Jon heeds Bran's warning about White Walkers on the move; Cersei vows to vanquish anyone or anything that stands in her way.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221413", - }, - }, - }, - { - id: 1221414, - url: - "http://www.tvmaze.com/episodes/1221414/game-of-thrones-7x06-beyond-the-wall", - name: "Beyond the Wall", - season: 7, - number: 6, - airdate: "2017-08-20", - airtime: "21:00", - airstamp: "2017-08-21T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/125/312651.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/125/312651.jpg", - }, - summary: - "

Jon's mission continues north of the wall, but the odds against his ragged band of misfits may be greater than he imagined.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221414", - }, - }, - }, - { - id: 1221415, - url: - "http://www.tvmaze.com/episodes/1221415/game-of-thrones-7x07-the-dragon-and-the-wolf", - name: "The Dragon and the Wolf", - season: 7, - number: 7, - airdate: "2017-08-27", - airtime: "21:00", - airstamp: "2017-08-28T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/125/314502.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/125/314502.jpg", - }, - summary: - "

Cersei sits on the Iron Throne; Daenerys sails across the Narrow Sea; Jon Snow is King in the North, and winter is finally here.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1221415", - }, - }, - }, - { - id: 1590943, - url: - "http://www.tvmaze.com/episodes/1590943/game-of-thrones-8x01-winterfell", - name: "Winterfell", - season: 8, - number: 1, - airdate: "2019-04-14", - airtime: "21:00", - airstamp: "2019-04-15T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/191/479477.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/191/479477.jpg", - }, - summary: - "

Arriving at Winterfell, Jon and Daenerys struggle to unite a divided North. Jon gets some big news.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1590943", - }, - }, - }, - { - id: 1623964, - url: - "http://www.tvmaze.com/episodes/1623964/game-of-thrones-8x02-a-knight-of-the-seven-kingdoms", - name: "A Knight of the Seven Kingdoms", - season: 8, - number: 2, - airdate: "2019-04-21", - airtime: "21:00", - airstamp: "2019-04-22T01:00:00+00:00", - runtime: 60, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/192/482451.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/192/482451.jpg", - }, - summary: - "

Jaime faces judgement and Winterfell prepares for the battle to come.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623964", - }, - }, - }, - { - id: 1623965, - url: - "http://www.tvmaze.com/episodes/1623965/game-of-thrones-8x03-the-long-night", - name: "The Long Night", - season: 8, - number: 3, - airdate: "2019-04-28", - airtime: "21:00", - airstamp: "2019-04-29T01:00:00+00:00", - runtime: 90, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/192/482465.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/192/482465.jpg", - }, - summary: "

Winterfell fights the Army of the Dead.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623965", - }, - }, - }, - { - id: 1623966, - url: - "http://www.tvmaze.com/episodes/1623966/game-of-thrones-8x04-the-last-of-the-starks", - name: "The Last of the Starks", - season: 8, - number: 4, - airdate: "2019-05-05", - airtime: "21:00", - airstamp: "2019-05-06T01:00:00+00:00", - runtime: 78, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/195/487839.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/195/487839.jpg", - }, - summary: - "

The survivors plan their next steps; Cersei makes a power move.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623966", - }, - }, - }, - { - id: 1623967, - url: - "http://www.tvmaze.com/episodes/1623967/game-of-thrones-8x05-the-bells", - name: "The Bells", - season: 8, - number: 5, - airdate: "2019-05-12", - airtime: "21:00", - airstamp: "2019-05-13T01:00:00+00:00", - runtime: 79, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/196/491994.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/196/491994.jpg", - }, - summary: - "

Varys betrays his queen, and Daenerys brings her forces to King's Landing.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623967", - }, - }, - }, - { - id: 1623968, - url: - "http://www.tvmaze.com/episodes/1623968/game-of-thrones-8x06-the-iron-throne", - name: "The Iron Throne", - season: 8, - number: 6, - airdate: "2019-05-19", - airtime: "21:00", - airstamp: "2019-05-20T01:00:00+00:00", - runtime: 80, - image: { - medium: - "http://static.tvmaze.com/uploads/images/medium_landscape/198/495648.jpg", - original: - "http://static.tvmaze.com/uploads/images/original_untouched/198/495648.jpg", - }, - summary: - "

In the aftermath of the devastating attack on King's Landing, Daenerys must face the survivors.

", - _links: { - self: { - href: "http://api.tvmaze.com/episodes/1623968", - }, - }, - }, - ]; -} From d41c40bfce4920a4a342b2e431cbb11d2f353fe3 Mon Sep 17 00:00:00 2001 From: Tobias Date: Sun, 9 Aug 2026 18:58:19 +0100 Subject: [PATCH 24/44] Deleted the script tag in index.html file --- index.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/index.html b/index.html index 64f590a..5e18a7c 100644 --- a/index.html +++ b/index.html @@ -11,10 +11,6 @@
- - - - From 9a2b13b48b3ed80bb5d0802f2bd80de45fb00de7 Mon Sep 17 00:00:00 2001 From: Tobias Date: Sun, 9 Aug 2026 19:38:40 +0100 Subject: [PATCH 25/44] Show loading message in the root element in the indext.htm file --- script.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/script.js b/script.js index 513d8cc..d2c573d 100644 --- a/script.js +++ b/script.js @@ -1,8 +1,9 @@ //You can edit ALL of the code here function setup() { - const allEpisodes = getAllEpisodes(); const rootElem = document.getElementById("root"); + // Show a loading message as we are now loading data async. + rooElem.innerHTML = "

loading episodes, please wait ..

"; // Create the search and filter controls const controls = document.createElement("div"); From e9cc750a1f2c10c630bd56e66dcec6b7343c0f8a Mon Sep 17 00:00:00 2001 From: Tobias Date: Sun, 9 Aug 2026 20:13:11 +0100 Subject: [PATCH 26/44] implements Fetch async and error message --- script.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/script.js b/script.js index d2c573d..f010841 100644 --- a/script.js +++ b/script.js @@ -4,7 +4,18 @@ function setup() { const rootElem = document.getElementById("root"); // Show a loading message as we are now loading data async. rooElem.innerHTML = "

loading episodes, please wait ..

"; - + //implement fetch request to TVMaze API (Executed exactly once) + fetch("https://api.tvmaze.com/shows/82/episodes") + .then(function (response) { + if (!response.ok){ + throw new Error(`Server responded with status:${response.status}`); + } + return response.json(); + }) + .then(function (allEpisodes) { + // clear the loading message before rendering the application layout + rootElem.innerHTML =""; + // Create the search and filter controls const controls = document.createElement("div"); @@ -79,8 +90,18 @@ function setup() { searchInput.value = ""; }); +}) +.catch(function (error) { + // implement error that user will see + rootElem.innerHTML = ` +
+

Oops! Something went wrong.

+

We couldn't load the episodes right now. Please try refreshing the page.

+

Error details: ${error.message}

+
+ `; + }); } - function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); From 44b6d0fad260478045ac5739303f12d263a99db9 Mon Sep 17 00:00:00 2001 From: Tobias Date: Sun, 9 Aug 2026 20:31:11 +0100 Subject: [PATCH 27/44] fixed a bug , by correcting the name from rooElem to rootElem @ line 6 --- script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script.js b/script.js index f010841..36107fd 100644 --- a/script.js +++ b/script.js @@ -3,7 +3,7 @@ function setup() { const rootElem = document.getElementById("root"); // Show a loading message as we are now loading data async. - rooElem.innerHTML = "

loading episodes, please wait ..

"; + rootElem.innerHTML = "

loading episodes, please wait ..

"; //implement fetch request to TVMaze API (Executed exactly once) fetch("https://api.tvmaze.com/shows/82/episodes") .then(function (response) { From d7c4d3612559cecffa5bc02516a6994227cdc36c Mon Sep 17 00:00:00 2001 From: Alina Sofragiu Date: Wed, 12 Aug 2026 20:35:26 +0100 Subject: [PATCH 28/44] Refactor setup and fetchEpisodes functions to improve error handling and loading messages; enhance episode display with search functionality and better control management. --- script.js | 204 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 176 insertions(+), 28 deletions(-) diff --git a/script.js b/script.js index 36107fd..8584d7e 100644 --- a/script.js +++ b/script.js @@ -1,33 +1,155 @@ -//You can edit ALL of the code here +// You can edit ALL of the code here + +// LEVEL 400: Store the TV shows URL +const showsUrl = "https://api.tvmaze.com/shows"; + +// LEVEL 400: Cache episode requests so the same URL is never fetched twice +const episodeCache = {}; function setup() { const rootElem = document.getElementById("root"); + // Show a loading message as we are now loading data async. - rootElem.innerHTML = "

loading episodes, please wait ..

"; - //implement fetch request to TVMaze API (Executed exactly once) - fetch("https://api.tvmaze.com/shows/82/episodes") - .then(function (response) { - if (!response.ok){ - throw new Error(`Server responded with status:${response.status}`); + rootElem.innerHTML = + "

Loading episodes, please wait...

"; + + // LEVEL 400: Fetch all TV shows from TVMaze + fetch(showsUrl) + .then(function (response) { + if (!response.ok) { + throw new Error(`Server responded with status: ${response.status}`); + } + + return response.json(); + }) + .then(function (shows) { + // LEVEL 400: Sort TV shows alphabetically, ignoring capital letters + shows.sort(function (a, b) { + return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); + }); + + // LEVEL 400: Create a TV show selector + const showSelector = document.createElement("select"); + showSelector.id = "showSelector"; + + const showLabel = document.createElement("label"); + showLabel.htmlFor = "showSelector"; + showLabel.textContent = "Choose a TV show:"; + + // LEVEL 400: Add each TV show to the selector + shows.forEach(function (show) { + const option = document.createElement("option"); + + option.value = show.id; + option.textContent = show.name; + + showSelector.appendChild(option); + }); + + // LEVEL 400: Add the show selector to the page + const showControls = document.createElement("div"); + showControls.id = "show-controls"; + + showControls.appendChild(showLabel); + showControls.appendChild(showSelector); + + document.body.insertBefore(showControls, rootElem); + + // LEVEL 400: Select the first show automatically + if (shows.length > 0) { + showSelector.value = shows[0].id; + fetchEpisodes(shows[0].id); + } + + // LEVEL 400: Fetch episodes when the user selects another show + showSelector.addEventListener("change", function () { + const showId = showSelector.value; + + fetchEpisodes(showId); + }); + }) + .catch(function (error) { + // Show an error if the TV shows cannot be loaded + showError(error); + }); +} + +// LEVEL 400: Fetch episodes for the selected TV show +function fetchEpisodes(showId) { + const rootElem = document.getElementById("root"); + const episodesUrl = `https://api.tvmaze.com/shows/${showId}/episodes`; + + // LEVEL 400: Use the cached request if this URL has already been fetched + if (Object.prototype.hasOwnProperty.call(episodeCache, episodesUrl)) { + episodeCache[episodesUrl] + .then(function (episodes) { + displayEpisodes(episodes); + }) + .catch(function (error) { + showError(error); + }); + + return; + } + + // LEVEL 400: Remove the previous show's controls + removeEpisodeControls(); + + // Show a loading message while episodes are loading + rootElem.innerHTML = + "

Loading episodes, please wait...

"; + + // LEVEL 400: Store the fetch promise immediately so this URL is only fetched once + episodeCache[episodesUrl] = fetch(episodesUrl).then(function (response) { + if (!response.ok) { + throw new Error(`Server responded with status: ${response.status}`); } + return response.json(); - }) - .then(function (allEpisodes) { - // clear the loading message before rendering the application layout - rootElem.innerHTML =""; - + }); + + episodeCache[episodesUrl] + .then(function (episodes) { + displayEpisodes(episodes); + }) + .catch(function (error) { + showError(error); + }); +} + +// LEVEL 400: Display the search and episode selector for the selected show +function displayEpisodes(allEpisodes) { + const rootElem = document.getElementById("root"); + + removeEpisodeControls(); + + // Clear the loading message before rendering the application layout + rootElem.innerHTML = ""; + // Create the search and filter controls const controls = document.createElement("div"); + controls.id = "episode-controls"; + + const searchLabel = document.createElement("label"); + searchLabel.htmlFor = "searchInput"; + searchLabel.textContent = "Search episodes:"; const searchInput = document.createElement("input"); searchInput.type = "text"; + searchInput.id = "searchInput"; searchInput.placeholder = "Search episodes..."; + const episodeLabel = document.createElement("label"); + episodeLabel.htmlFor = "episodeSelector"; + episodeLabel.textContent = "Choose an episode:"; + const episodeSelect = document.createElement("select"); + episodeSelect.id = "episodeSelector"; const defaultOption = document.createElement("option"); defaultOption.value = ""; defaultOption.textContent = "All Episodes"; + episodeSelect.appendChild(defaultOption); allEpisodes.forEach(function (episode) { @@ -44,7 +166,9 @@ function setup() { const results = document.createElement("p"); results.textContent = `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; + controls.appendChild(searchLabel); controls.appendChild(searchInput); + controls.appendChild(episodeLabel); controls.appendChild(episodeSelect); controls.appendChild(results); @@ -59,7 +183,7 @@ function setup() { const filteredEpisodes = allEpisodes.filter(function (episode) { return ( episode.name.toLowerCase().includes(searchTerm) || - episode.summary.toLowerCase().includes(searchTerm) + (episode.summary || "").toLowerCase().includes(searchTerm) ); }); @@ -81,7 +205,7 @@ function setup() { } const selectedEpisode = allEpisodes.filter(function (episode) { - return episode.id == episodeSelect.value; + return episode.id === Number(episodeSelect.value); }); makePageForEpisodes(selectedEpisode); @@ -90,18 +214,37 @@ function setup() { searchInput.value = ""; }); -}) -.catch(function (error) { - // implement error that user will see - rootElem.innerHTML = ` -
-

Oops! Something went wrong.

-

We couldn't load the episodes right now. Please try refreshing the page.

-

Error details: ${error.message}

-
- `; - }); } + +// LEVEL 400: Remove the previous episode controls when changing shows +function removeEpisodeControls() { + const oldControls = document.getElementById("episode-controls"); + + if (oldControls) { + oldControls.remove(); + } +} + +// Show an error message that the user can see +function showError(error) { + const rootElem = document.getElementById("root"); + + rootElem.innerHTML = ` +
+

Oops! Something went wrong.

+ +

+ We couldn't load the episodes right now. + Please try refreshing the page. +

+ +

+ Error details: ${error.message} +

+
+ `; +} + function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); @@ -116,13 +259,18 @@ function makePageForEpisodes(episodeList) { const card = document.createElement("article"); const title = document.createElement("h2"); const image = document.createElement("img"); - image.src = episode.image.medium; + + // Some episodes may not have an image + if (episode.image) { + image.src = episode.image.medium; + } + image.alt = episode.name; const summary = document.createElement("div"); - summary.innerHTML = episode.summary; + summary.innerHTML = episode.summary || ""; - title.textContent = `${episode.name}-${episodeCode}`; + title.textContent = `${episode.name} - ${episodeCode}`; const link = document.createElement("a"); link.href = episode.url; From 48eabf111be1a7d64b5970dbb50f164bdd184f0c Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 10:03:17 +0100 Subject: [PATCH 29/44] create a variable to hold all Show --- script.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script.js b/script.js index 8584d7e..c206536 100644 --- a/script.js +++ b/script.js @@ -6,6 +6,9 @@ const showsUrl = "https://api.tvmaze.com/shows"; // LEVEL 400: Cache episode requests so the same URL is never fetched twice const episodeCache = {}; +// LEVEL 500: Store all TV shows after the first fetch +let allShows = []; + function setup() { const rootElem = document.getElementById("root"); From 3b218aff01954e2c677d62557f60086b78e78fbf Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 10:10:57 +0100 Subject: [PATCH 30/44] Update the function Setup --- script.js | 35 ++--------------------------------- 1 file changed, 2 insertions(+), 33 deletions(-) diff --git a/script.js b/script.js index c206536..900df95 100644 --- a/script.js +++ b/script.js @@ -31,39 +31,8 @@ function setup() { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); }); - // LEVEL 400: Create a TV show selector - const showSelector = document.createElement("select"); - showSelector.id = "showSelector"; - - const showLabel = document.createElement("label"); - showLabel.htmlFor = "showSelector"; - showLabel.textContent = "Choose a TV show:"; - - // LEVEL 400: Add each TV show to the selector - shows.forEach(function (show) { - const option = document.createElement("option"); - - option.value = show.id; - option.textContent = show.name; - - showSelector.appendChild(option); - }); - - // LEVEL 400: Add the show selector to the page - const showControls = document.createElement("div"); - showControls.id = "show-controls"; - - showControls.appendChild(showLabel); - showControls.appendChild(showSelector); - - document.body.insertBefore(showControls, rootElem); - - // LEVEL 400: Select the first show automatically - if (shows.length > 0) { - showSelector.value = shows[0].id; - fetchEpisodes(shows[0].id); - } - + + // LEVEL 400: Fetch episodes when the user selects another show showSelector.addEventListener("change", function () { const showId = showSelector.value; From 2dffc13fa65eab9dff426e0d7b879b1f2f8cf1cb Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 10:20:20 +0100 Subject: [PATCH 31/44] Implement the function makePageForShow to creat the actual function --- script.js | 153 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 140 insertions(+), 13 deletions(-) diff --git a/script.js b/script.js index 900df95..da60821 100644 --- a/script.js +++ b/script.js @@ -12,11 +12,10 @@ let allShows = []; function setup() { const rootElem = document.getElementById("root"); - // Show a loading message as we are now loading data async. rootElem.innerHTML = - "

Loading episodes, please wait...

"; + "

Loading TV shows, please wait...

"; - // LEVEL 400: Fetch all TV shows from TVMaze + // LEVEL 500: Fetch all TV shows fetch(showsUrl) .then(function (response) { if (!response.ok) { @@ -26,26 +25,154 @@ function setup() { return response.json(); }) .then(function (shows) { + // LEVEL 500: Store all shows for searching and navigation + allShows = shows; + // LEVEL 400: Sort TV shows alphabetically, ignoring capital letters - shows.sort(function (a, b) { + allShows.sort(function (a, b) { return a.name.toLowerCase().localeCompare(b.name.toLowerCase()); }); - - - // LEVEL 400: Fetch episodes when the user selects another show - showSelector.addEventListener("change", function () { - const showId = showSelector.value; - - fetchEpisodes(showId); - }); + // LEVEL 500: Display the shows listing + displayShows(allShows); }) .catch(function (error) { - // Show an error if the TV shows cannot be loaded showError(error); }); } +function displayShows(shows) { + const rootElem = document.getElementById("root"); + + rootElem.innerHTML = ""; + + // Create navigation + const navigation = document.createElement("nav"); + + const showsLink = document.createElement("a"); + showsLink.href = "#"; + showsLink.textContent = "Shows"; + + navigation.appendChild(showsLink); + + // Create heading + const heading = document.createElement("h1"); + heading.textContent = "All TV Shows"; + + // Create search input + const searchLabel = document.createElement("label"); + searchLabel.htmlFor = "showSearch"; + searchLabel.textContent = "Search shows:"; + + const searchInput = document.createElement("input"); + searchInput.type = "text"; + searchInput.id = "showSearch"; + searchInput.placeholder = "Search by name, genre or summary..."; + + // Create results count + const results = document.createElement("p"); + results.textContent = `Displaying ${shows.length}/${allShows.length} shows`; + + // Create show container + const showContainer = document.createElement("div"); + showContainer.id = "shows-list"; + + rootElem.appendChild(navigation); + rootElem.appendChild(heading); + rootElem.appendChild(searchLabel); + rootElem.appendChild(searchInput); + rootElem.appendChild(results); + rootElem.appendChild(showContainer); + + makePageForShows(shows); + + // LEVEL 500: Search shows while typing + searchInput.addEventListener("input", function () { + const searchTerm = searchInput.value.toLowerCase(); + + const filteredShows = allShows.filter(function (show) { + const showGenres = show.genres.join(" ").toLowerCase(); + const showSummary = (show.summary || "").toLowerCase(); + const showName = show.name.toLowerCase(); + + return ( + showName.includes(searchTerm) || + showGenres.includes(searchTerm) || + showSummary.includes(searchTerm) + ); + }); + + results.textContent = + `Displaying ${filteredShows.length}/${allShows.length} shows`; + + makePageForShows(filteredShows); + }); + + // LEVEL 500: Allow user to return to the shows listing + showsLink.addEventListener("click", function (event) { + event.preventDefault(); + + displayShows(allShows); + }); +} + function makePageForShows(showList) { + const showContainer = document.getElementById("shows-list"); + + showContainer.innerHTML = ""; + + showList.forEach(function (show) { + const card = document.createElement("article"); + + const title = document.createElement("h2"); + + // LEVEL 500: Make the show name clickable + const titleLink = document.createElement("a"); + titleLink.href = "#"; + titleLink.textContent = show.name; + + title.appendChild(titleLink); + + const image = document.createElement("img"); + + if (show.image) { + image.src = show.image.medium; + } + image.alt = show.name; + + const summary = document.createElement("div"); + summary.innerHTML = show.summary || ""; + + const genres = document.createElement("p"); + genres.textContent = `Genres: ${show.genres.join(", ")}`; + + const status = document.createElement("p"); + status.textContent = `Status: ${show.status}`; + + const rating = document.createElement("p"); + rating.textContent = `Rating: ${show.rating.average || "N/A"}`; + + const runtime = document.createElement("p"); + runtime.textContent = `Runtime: ${show.runtime || "N/A"} minutes`; + + card.appendChild(title); + card.appendChild(image); + card.appendChild(summary); + card.appendChild(genres); + card.appendChild(status); + card.appendChild(rating); + card.appendChild(runtime); + + showContainer.appendChild(card); + + // LEVEL 500: Fetch episodes when the show name is clicked + titleLink.addEventListener("click", function (event) { + event.preventDefault(); + + displayEpisodePage(show); + }); + }); +} + // LEVEL 400: Fetch episodes for the selected TV show function fetchEpisodes(showId) { const rootElem = document.getElementById("root"); From c70b739965e4525a692863eb6139ffba346469b1 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 11:11:53 +0100 Subject: [PATCH 32/44] Implemented a function to do transition from show to episodes --- script.js | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/script.js b/script.js index da60821..7cf0986 100644 --- a/script.js +++ b/script.js @@ -173,6 +173,34 @@ function displayShows(shows) { }); } +function displayEpisodePage(show) { + const rootElem = document.getElementById("root"); + + rootElem.innerHTML = ""; + + const backLink = document.createElement("a"); + backLink.href = "#"; + backLink.textContent = "← Back to all shows"; + + const heading = document.createElement("h1"); + heading.textContent = `Episodes: ${show.name}`; + + rootElem.appendChild(backLink); + rootElem.appendChild(heading); + + // LEVEL 500: Return to shows listing + backLink.addEventListener("click", function (event) { + event.preventDefault(); + + displayShows(allShows); + }); + + // LEVEL 400: Fetch episodes for selected show + fetchEpisodes(show.id); +} + + + // LEVEL 400: Fetch episodes for the selected TV show function fetchEpisodes(showId) { const rootElem = document.getElementById("root"); From 57d32f5fe1f78ab53c5f98b96b18bc9a2149a7d3 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 11:16:49 +0100 Subject: [PATCH 33/44] Updated the loading message in fetchEpisodes funcion --- script.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/script.js b/script.js index 7cf0986..05a7f3f 100644 --- a/script.js +++ b/script.js @@ -218,13 +218,12 @@ function fetchEpisodes(showId) { return; } + // Show loading message + const loadingMessage = document.createElement("p"); + loadingMessage.className = "Loading-message"; + loadingMessage.textContent = "Loading episodes, please wait..."; - // LEVEL 400: Remove the previous show's controls - removeEpisodeControls(); - - // Show a loading message while episodes are loading - rootElem.innerHTML = - "

Loading episodes, please wait...

"; + rootElem.appendChild(loadingMessage); // LEVEL 400: Store the fetch promise immediately so this URL is only fetched once episodeCache[episodesUrl] = fetch(episodesUrl).then(function (response) { From 4993089bed86d0060f540747a68ac1892aa6dccf Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 11:57:39 +0100 Subject: [PATCH 34/44] Creat epusode heading and deleted dispalyEpisodesPage function --- script.js | 81 ++++++++++++++++++++++++------------------------------- 1 file changed, 35 insertions(+), 46 deletions(-) diff --git a/script.js b/script.js index 05a7f3f..2f2ec8c 100644 --- a/script.js +++ b/script.js @@ -168,38 +168,11 @@ function displayShows(shows) { titleLink.addEventListener("click", function (event) { event.preventDefault(); - displayEpisodePage(show); + displayEpisodePage(show.id); }); }); } -function displayEpisodePage(show) { - const rootElem = document.getElementById("root"); - - rootElem.innerHTML = ""; - - const backLink = document.createElement("a"); - backLink.href = "#"; - backLink.textContent = "← Back to all shows"; - - const heading = document.createElement("h1"); - heading.textContent = `Episodes: ${show.name}`; - - rootElem.appendChild(backLink); - rootElem.appendChild(heading); - - // LEVEL 500: Return to shows listing - backLink.addEventListener("click", function (event) { - event.preventDefault(); - - displayShows(allShows); - }); - - // LEVEL 400: Fetch episodes for selected show - fetchEpisodes(show.id); -} - - // LEVEL 400: Fetch episodes for the selected TV show function fetchEpisodes(showId) { @@ -247,11 +220,20 @@ function fetchEpisodes(showId) { function displayEpisodes(allEpisodes) { const rootElem = document.getElementById("root"); - removeEpisodeControls(); - - // Clear the loading message before rendering the application layout rootElem.innerHTML = ""; + // LEVEL 500: Navigation back to shows + const backLink = document.createElement("a"); + backLink.href = "#"; + backLink.textContent = " Back to all shows"; + + rootElem.appendChild(backLink); + + const heading = document.createElement("h1"); + heading.textContent = "Episodes"; + + rootElem.appendChild(heading); + // Create the search and filter controls const controls = document.createElement("div"); controls.id = "episode-controls"; @@ -283,14 +265,18 @@ function displayEpisodes(allEpisodes) { const number = String(episode.number).padStart(2, "0"); const option = document.createElement("option"); + option.value = episode.id; - option.textContent = `S${season}E${number} - ${episode.name}`; + option.textContent = + `S${season}E${number} - ${episode.name}`; episodeSelect.appendChild(option); }); const results = document.createElement("p"); - results.textContent = `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; + + results.textContent = + `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; controls.appendChild(searchLabel); controls.appendChild(searchInput); @@ -298,11 +284,11 @@ function displayEpisodes(allEpisodes) { controls.appendChild(episodeSelect); controls.appendChild(results); - document.body.insertBefore(controls, rootElem); + rootElem.appendChild(controls); makePageForEpisodes(allEpisodes); - // Filter episodes while typing + // LEVEL 400: Search episodes while typing searchInput.addEventListener("input", function () { const searchTerm = searchInput.value.toLowerCase(); @@ -315,17 +301,19 @@ function displayEpisodes(allEpisodes) { makePageForEpisodes(filteredEpisodes); - results.textContent = `Displaying ${filteredEpisodes.length}/${allEpisodes.length} episodes`; + results.textContent = + `Displaying ${filteredEpisodes.length}/${allEpisodes.length} episodes`; episodeSelect.value = ""; }); - // Show the selected episode + // LEVEL 400: Show selected episode episodeSelect.addEventListener("change", function () { if (episodeSelect.value === "") { makePageForEpisodes(allEpisodes); - results.textContent = `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; + results.textContent = + `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; return; } @@ -336,21 +324,22 @@ function displayEpisodes(allEpisodes) { makePageForEpisodes(selectedEpisode); - results.textContent = `Displaying ${selectedEpisode.length}/${allEpisodes.length} episodes`; + results.textContent = + `Displaying ${selectedEpisode.length}/${allEpisodes.length} episodes`; searchInput.value = ""; }); -} -// LEVEL 400: Remove the previous episode controls when changing shows -function removeEpisodeControls() { - const oldControls = document.getElementById("episode-controls"); + // LEVEL 500: Return to shows + backLink.addEventListener("click", function (event) { + event.preventDefault(); - if (oldControls) { - oldControls.remove(); - } + displayShows(allShows); + }); } + + // Show an error message that the user can see function showError(error) { const rootElem = document.getElementById("root"); From 8c59119c089c09629df9b81d1eb703b99fbc7fba Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 12:06:21 +0100 Subject: [PATCH 35/44] deleted episode selector --- script.js | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/script.js b/script.js index 2f2ec8c..f8e1478 100644 --- a/script.js +++ b/script.js @@ -307,28 +307,7 @@ function displayEpisodes(allEpisodes) { episodeSelect.value = ""; }); - // LEVEL 400: Show selected episode - episodeSelect.addEventListener("change", function () { - if (episodeSelect.value === "") { - makePageForEpisodes(allEpisodes); - - results.textContent = - `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; - - return; - } - - const selectedEpisode = allEpisodes.filter(function (episode) { - return episode.id === Number(episodeSelect.value); - }); - - makePageForEpisodes(selectedEpisode); - - results.textContent = - `Displaying ${selectedEpisode.length}/${allEpisodes.length} episodes`; - - searchInput.value = ""; - }); + // LEVEL 500: Return to shows backLink.addEventListener("click", function (event) { From ad1276228d32b50d9cdd67a45280a607ba8e29d2 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 12:30:03 +0100 Subject: [PATCH 36/44] Changes displayEpisodePage to fetchEpisodes --- script.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script.js b/script.js index f8e1478..c4e64fe 100644 --- a/script.js +++ b/script.js @@ -149,7 +149,7 @@ function displayShows(shows) { status.textContent = `Status: ${show.status}`; const rating = document.createElement("p"); - rating.textContent = `Rating: ${show.rating.average || "N/A"}`; + rating.textContent = `Rating: ${(show.rating && show.rating.average) || "N/A"}`; const runtime = document.createElement("p"); runtime.textContent = `Runtime: ${show.runtime || "N/A"} minutes`; @@ -168,7 +168,7 @@ function displayShows(shows) { titleLink.addEventListener("click", function (event) { event.preventDefault(); - displayEpisodePage(show.id); + fetchEpisodes(show.id); }); }); } From 1cd9df60390e3081ab99fc592019cd23afe5f3f5 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 12:39:28 +0100 Subject: [PATCH 37/44] added the episodeSelect eventlistener --- script.js | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/script.js b/script.js index c4e64fe..2afee60 100644 --- a/script.js +++ b/script.js @@ -101,12 +101,32 @@ function displayShows(shows) { ); }); - results.textContent = - `Displaying ${filteredShows.length}/${allShows.length} shows`; + results.textContent = `Displaying ${filteredShows.length}/${allShows.length} shows`; makePageForShows(filteredShows); }); + // LEVEL 400: Show selected episode + episodeSelect.addEventListener("change", function () { + if (episodeSelect.value === "") { + makePageForEpisodes(allEpisodes); + + results.textContent = `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; + + return; + } + + const selectedEpisode = allEpisodes.filter(function (episode) { + return episode.id === Number(episodeSelect.value); + }); + + makePageForEpisodes(selectedEpisode); + + results.textContent = `Displaying ${selectedEpisode.length}/${allEpisodes.length} episodes`; + + searchInput.value = ""; + }); + // LEVEL 500: Allow user to return to the shows listing showsLink.addEventListener("click", function (event) { event.preventDefault(); From 05f0d4627b2968792ea7b87ba6b3b88f72fd01d9 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 12:50:28 +0100 Subject: [PATCH 38/44] created container for episodes --- script.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/script.js b/script.js index 2afee60..fdfd11d 100644 --- a/script.js +++ b/script.js @@ -306,8 +306,12 @@ function displayEpisodes(allEpisodes) { rootElem.appendChild(controls); - makePageForEpisodes(allEpisodes); + const episodeContainer = document.createElement("div"); + episodeContainer.id = "episodes-list"; + + rootElem.appendChild(episodeContainer); + makePageForEpisodes(allEpisodes); // LEVEL 400: Search episodes while typing searchInput.addEventListener("input", function () { const searchTerm = searchInput.value.toLowerCase(); From c61e954da81304f63ffc9f294eeea0f69a43391f Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 12:58:19 +0100 Subject: [PATCH 39/44] update the function makePageForepisode so it wraps the episodes in a container --- script.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/script.js b/script.js index fdfd11d..c99eb0e 100644 --- a/script.js +++ b/script.js @@ -362,12 +362,11 @@ function showError(error) { `; } - function makePageForEpisodes(episodeList) { - const rootElem = document.getElementById("root"); + const episodeContainer = document.getElementById("episodes-list"); // Clear previous episodes before displaying new ones - rootElem.innerHTML = ""; + episodeContainer.innerHTML = ""; episodeList.forEach(function (episode) { const season = String(episode.season).padStart(2, "0"); @@ -378,7 +377,7 @@ function makePageForEpisodes(episodeList) { const title = document.createElement("h2"); const image = document.createElement("img"); - // Some episodes may not have an image + // Some episodes may not have an image if (episode.image) { image.src = episode.image.medium; } @@ -401,8 +400,10 @@ function makePageForEpisodes(episodeList) { card.appendChild(summary); card.appendChild(link); - rootElem.appendChild(card); + episodeContainer.appendChild(card); }); } + + window.onload = setup; From 00fe355631fba2e58e4e342df1ef98ad9fb8c73e Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 13:33:25 +0100 Subject: [PATCH 40/44] Hide the showing list by wiping it --- script.js | 56 ++++++++++++++++++++++++++----------------------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/script.js b/script.js index c99eb0e..1529907 100644 --- a/script.js +++ b/script.js @@ -106,27 +106,7 @@ function displayShows(shows) { makePageForShows(filteredShows); }); - // LEVEL 400: Show selected episode - episodeSelect.addEventListener("change", function () { - if (episodeSelect.value === "") { - makePageForEpisodes(allEpisodes); - - results.textContent = `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; - - return; - } - - const selectedEpisode = allEpisodes.filter(function (episode) { - return episode.id === Number(episodeSelect.value); - }); - - makePageForEpisodes(selectedEpisode); - - results.textContent = `Displaying ${selectedEpisode.length}/${allEpisodes.length} episodes`; - - searchInput.value = ""; - }); - + // LEVEL 500: Allow user to return to the shows listing showsLink.addEventListener("click", function (event) { event.preventDefault(); @@ -214,8 +194,8 @@ function fetchEpisodes(showId) { // Show loading message const loadingMessage = document.createElement("p"); loadingMessage.className = "Loading-message"; - loadingMessage.textContent = "Loading episodes, please wait..."; - + loadingMessage.textContent = "Loading episodes, please wait..."; + rootElem.innerHTML = ""; rootElem.appendChild(loadingMessage); // LEVEL 400: Store the fetch promise immediately so this URL is only fetched once @@ -287,16 +267,14 @@ function displayEpisodes(allEpisodes) { const option = document.createElement("option"); option.value = episode.id; - option.textContent = - `S${season}E${number} - ${episode.name}`; + option.textContent = `S${season}E${number} - ${episode.name}`; episodeSelect.appendChild(option); }); const results = document.createElement("p"); - results.textContent = - `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; + results.textContent = `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; controls.appendChild(searchLabel); controls.appendChild(searchInput); @@ -325,13 +303,31 @@ function displayEpisodes(allEpisodes) { makePageForEpisodes(filteredEpisodes); - results.textContent = - `Displaying ${filteredEpisodes.length}/${allEpisodes.length} episodes`; + results.textContent = `Displaying ${filteredEpisodes.length}/${allEpisodes.length} episodes`; episodeSelect.value = ""; }); - + // LEVEL 400: Show selected episode + episodeSelect.addEventListener("change", function () { + if (episodeSelect.value === "") { + makePageForEpisodes(allEpisodes); + + results.textContent = `Displaying ${allEpisodes.length}/${allEpisodes.length} episodes`; + + return; + } + + const selectedEpisode = allEpisodes.filter(function (episode) { + return episode.id === Number(episodeSelect.value); + }); + + makePageForEpisodes(selectedEpisode); + + results.textContent = `Displaying ${selectedEpisode.length}/${allEpisodes.length} episodes`; + + searchInput.value = ""; + }); // LEVEL 500: Return to shows backLink.addEventListener("click", function (event) { From 16972ad2430de26264ee4546f819450a7be421be Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 13:52:19 +0100 Subject: [PATCH 41/44] updated the fallbacks for runtime genre and showgenre --- script.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/script.js b/script.js index 1529907..94f1dd0 100644 --- a/script.js +++ b/script.js @@ -90,7 +90,9 @@ function displayShows(shows) { const searchTerm = searchInput.value.toLowerCase(); const filteredShows = allShows.filter(function (show) { - const showGenres = show.genres.join(" ").toLowerCase(); + const showGenres = (show.genres || []).join(" ").toLowerCase(); + + const showSummary = (show.summary || "").toLowerCase(); const showName = show.name.toLowerCase(); @@ -143,7 +145,7 @@ function displayShows(shows) { summary.innerHTML = show.summary || ""; const genres = document.createElement("p"); - genres.textContent = `Genres: ${show.genres.join(", ")}`; + genres.textContent = `Genres: ${(show.genres || []).join(", ")}`; const status = document.createElement("p"); status.textContent = `Status: ${show.status}`; @@ -152,7 +154,8 @@ function displayShows(shows) { rating.textContent = `Rating: ${(show.rating && show.rating.average) || "N/A"}`; const runtime = document.createElement("p"); - runtime.textContent = `Runtime: ${show.runtime || "N/A"} minutes`; + const runtimeValue = show.runtime ?? show.averageRuntime ?? "N/A"; + runtime.textContent = `Runtime: ${runtimeValue} minutes`; card.appendChild(title); card.appendChild(image); From 4e96064e3905d29db3676d83ca5c3afc4e41a92e Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 14:03:43 +0100 Subject: [PATCH 42/44] updated show rating with optional chaing to avoid falsy-trapp risk --- script.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script.js b/script.js index 94f1dd0..5d78bc2 100644 --- a/script.js +++ b/script.js @@ -151,8 +151,9 @@ function displayShows(shows) { status.textContent = `Status: ${show.status}`; const rating = document.createElement("p"); - rating.textContent = `Rating: ${(show.rating && show.rating.average) || "N/A"}`; - + const ratingValue = show.rating?.average ?? "N/A"; + rating.textContent = "Rating: " + ratingValue; + const runtime = document.createElement("p"); const runtimeValue = show.runtime ?? show.averageRuntime ?? "N/A"; runtime.textContent = `Runtime: ${runtimeValue} minutes`; From 6f0939a0d3dd47a5398efc620d49bc7b89b60483 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 14:21:30 +0100 Subject: [PATCH 43/44] updated the Show error function --- script.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/script.js b/script.js index 5d78bc2..6decdab 100644 --- a/script.js +++ b/script.js @@ -37,7 +37,7 @@ function setup() { displayShows(allShows); }) .catch(function (error) { - showError(error); + showError(error,"shows"); }); } function displayShows(shows) { @@ -153,7 +153,7 @@ function displayShows(shows) { const rating = document.createElement("p"); const ratingValue = show.rating?.average ?? "N/A"; rating.textContent = "Rating: " + ratingValue; - + const runtime = document.createElement("p"); const runtimeValue = show.runtime ?? show.averageRuntime ?? "N/A"; runtime.textContent = `Runtime: ${runtimeValue} minutes`; @@ -216,7 +216,7 @@ function fetchEpisodes(showId) { displayEpisodes(episodes); }) .catch(function (error) { - showError(error); + showError(error,"episodes"); }); } @@ -344,18 +344,16 @@ function displayEpisodes(allEpisodes) { // Show an error message that the user can see -function showError(error) { +function showError(error, itemType = "content") { const rootElem = document.getElementById("root"); rootElem.innerHTML = `

Oops! Something went wrong.

-

- We couldn't load the episodes right now. + We couldn't load the ${itemType} right now. Please try refreshing the page.

-

Error details: ${error.message}

From cb3c2840d453a6c54d300e3a663caf58e89b4e10 Mon Sep 17 00:00:00 2001 From: Tobias Date: Thu, 13 Aug 2026 14:41:35 +0100 Subject: [PATCH 44/44] Updated the Style sheet and the root element in index .html from div to main --- index.html | 4 +- style.css | 264 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 265 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 5e18a7c..cbf3630 100644 --- a/index.html +++ b/index.html @@ -9,8 +9,8 @@ -
-
+
+
diff --git a/style.css b/style.css index 77cb8d4..235439c 100644 --- a/style.css +++ b/style.css @@ -1,3 +1,265 @@ +/* ============================ + CSS Variables (Design Tokens) + ============================ */ +:root { + /* Colors */ + --color-bg: #0f1115; + --color-surface: #1a1d24; + --color-surface-hover: #22262f; + --color-border: #2c303a; + + --color-text: #e8e9ec; + --color-text-muted: #9aa0ab; + --color-text-faint: #8b93a0; + + --color-accent: #6c8cff; + --color-accent-hover: #8aa4ff; + + --color-error-bg: #2a1414; + --color-error-border: #7a2c2c; + --color-error-text: #ff9b9b; + + /* Typography */ + --font-base: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + --font-size-base: 1rem; + --font-size-sm: 0.875rem; + --font-size-lg: 1.25rem; + --font-size-xl: 1.75rem; + --line-height-base: 1.5; + + /* Spacing */ + --space-xs: 0.25rem; + --space-sm: 0.5rem; + --space-md: 1rem; + --space-lg: 1.5rem; + --space-xl: 2.5rem; + + /* Layout */ + --max-width: 1100px; + --radius-sm: 6px; + --radius-md: 10px; + --card-min-width: 260px; + + /* Motion */ + --transition-fast: 150ms ease; +} + +/* ============================ + Base + ============================ */ +* { + box-sizing: border-box; +} + +body { + margin: 0; + padding: var(--space-lg); + background-color: var(--color-bg); + color: var(--color-text); + font-family: var(--font-base); + font-size: var(--font-size-base); + line-height: var(--line-height-base); +} + #root { - color: red; + max-width: var(--max-width); + margin: 0 auto; +} + +h1 { + font-size: var(--font-size-xl); + margin: var(--space-md) 0; +} + +h2 { + font-size: var(--font-size-lg); + margin: 0 0 var(--space-sm) 0; +} + +a { + color: var(--color-accent); + text-decoration: none; +} + +a:hover { + color: var(--color-accent-hover); + text-decoration: underline; +} + +/* ============================ + Loading / status message + ============================ */ +.Loading-message { + text-align: center; + color: var(--color-text-muted); + padding: var(--space-xl) 0; + font-size: var(--font-size-lg); +} + +/* ============================ + Navigation (shows link) + ============================ */ +nav { + margin-bottom: var(--space-md); +} + +nav a { + display: inline-block; + padding: var(--space-xs) var(--space-sm); + border-radius: var(--radius-sm); + font-weight: 600; +} + +nav a:hover { + background-color: var(--color-surface-hover); + text-decoration: none; +} + +/* ============================ + Search controls (shows + episodes) + ============================ */ +label { + display: inline-block; + margin-right: var(--space-sm); + color: var(--color-text-muted); + font-size: var(--font-size-sm); +} + +input[type="text"], +select { + background-color: var(--color-surface); + color: var(--color-text); + border: 1px solid var(--color-border); + border-radius: var(--radius-sm); + padding: var(--space-sm) var(--space-md); + font-size: var(--font-size-base); + margin-bottom: var(--space-sm); +} + +input[type="text"] { + width: 100%; + max-width: 400px; +} + +input[type="text"]:focus, +select:focus { + outline: none; + border-color: var(--color-accent); +} + +#episode-controls { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: var(--space-md); + margin-bottom: var(--space-lg); + padding-bottom: var(--space-md); + border-bottom: 1px solid var(--color-border); +} + +/* results count text ("Displaying X/Y ...") */ +#root > p, +#episode-controls p { + color: var(--color-text-muted); + font-size: var(--font-size-sm); +} + +/* ============================ + Shows listing grid + ============================ */ +#shows-list, +#episodes-list { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(var(--card-min-width), 1fr)); + gap: var(--space-lg); + margin-top: var(--space-md); +} + +/* ============================ + Cards (shared by shows + episodes) + ============================ */ +#shows-list article, +#episodes-list article { + background-color: var(--color-surface); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + padding: var(--space-md); + display: flex; + flex-direction: column; + transition: border-color var(--transition-fast), transform var(--transition-fast); +} + +#shows-list article:hover, +#episodes-list article:hover { + border-color: var(--color-accent); + transform: translateY(-2px); +} + +#shows-list article img, +#episodes-list article img { + width: 100%; + height: auto; + border-radius: var(--radius-sm); + margin-bottom: var(--space-sm); + background-color: var(--color-border); +} + +#shows-list article h2 a, +#episodes-list article h2 { + font-size: var(--font-size-lg); +} + +/* the clickable show name button/link inside h2 */ +#shows-list article h2 a { + font-weight: 700; +} + +.summary { + color: var(--color-text-muted); + font-size: var(--font-size-sm); + margin-bottom: var(--space-sm); +} + +.summary p { + margin: 0 0 var(--space-xs) 0; +} + +#shows-list article p { + margin: var(--space-xs) 0; + font-size: var(--font-size-sm); + color: var(--color-text-faint); +} + +/* episode "View on TVMaze" link, pinned to the bottom of the card */ +#episodes-list article a { + margin-top: auto; + padding-top: var(--space-sm); + font-size: var(--font-size-sm); + font-weight: 600; +} + +/* ============================ + Error state + ============================ */ +.error-container { + background-color: var(--color-error-bg); + border: 1px solid var(--color-error-border); + border-radius: var(--radius-md); + padding: var(--space-lg); + margin-top: var(--space-lg); +} + +.error-container h3 { + color: var(--color-error-text); + margin-top: 0; +} + +.error-container p { + color: var(--color-text-muted); } + +.error-details { + font-family: monospace; + font-size: var(--font-size-sm); + color: var(--color-error-text); +} \ No newline at end of file