From 7d9a970cacbba790b31500de05a5d8ecc0b626e6 Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Wed, 29 Jul 2026 15:36:37 +0100 Subject: [PATCH 01/31] Update title with name and github username. index.html --- index.html | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/index.html b/index.html index 9a400d1..75dbcd5 100644 --- a/index.html +++ b/index.html @@ -3,14 +3,13 @@ - TV Show Project | My Name (My GitHub username) + TV Show Project | Liridona Shehu (shehu-dona) -
-
+
From 4352b5880651164029d1278bb956d7df8276719e Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Sat, 1 Aug 2026 14:08:29 +0100 Subject: [PATCH 02/31] formatEpisodeCode,makePageForEpisodes --- script.js | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/script.js b/script.js index 87a7de8..6287271 100644 --- a/script.js +++ b/script.js @@ -4,9 +4,37 @@ function setup() { makePageForEpisodes(allEpisodes); } +function formatEpisodeCode(episode) { + const season = episode.season.toString().padStart(2, "0"); + const number = episode.number.toString().padStart(2, "0"); + const formattedEpisodeCode = `S${season}E${number}`; + return formattedEpisodeCode; +} + function makePageForEpisodes(episodeList) { const rootElem = document.getElementById("root"); - rootElem.textContent = `Got ${episodeList.length} episode(s)`; + const attribution = document.createElement("p"); + attribution.innerHTML = `The data has (originally) come from TVMaze.com`; + + episodeList.forEach((episode) => { + const episodeDiv = document.createElement("div"); + const title = document.createElement("h3"); + const image = document.createElement("img"); + const summaryParagraph = document.createElement("p"); + + title.textContent = `${episode.name} - ${formatEpisodeCode(episode)}`; + + image.src = episode.image.medium; + image.alt = "Image Cover"; + + summaryParagraph.innerHTML = episode.summary; + + rootElem.appendChild(episodeDiv); + episodeDiv.appendChild(title); + episodeDiv.appendChild(image); + episodeDiv.appendChild(summaryParagraph); + }); + rootElem.appendChild(attribution); } window.onload = setup; From c42efd045c80010e6b1c68e96954446d534420e3 Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Sat, 1 Aug 2026 23:18:42 +0100 Subject: [PATCH 03/31] changed the contrast to pass accessibility --- index.html | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 75dbcd5..933f2ec 100644 --- a/index.html +++ b/index.html @@ -9,8 +9,15 @@ -
- +
+ +
From e2958b0a1ed27f6bc70b5d2fa77fd306853aab54 Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Sat, 1 Aug 2026 23:20:13 +0100 Subject: [PATCH 04/31] used
-
+
+

diff --git a/script.js b/script.js index dafd9e4..f348c98 100644 --- a/script.js +++ b/script.js @@ -2,6 +2,7 @@ const filmGrid = document.getElementById('film-grid'); const filterDisplay = document.querySelector('.filter-display'); +const filmSelect = document.getElementById('film-select'); const searchInput = document.getElementById('film-search'); const allEpisodes = getAllEpisodes(); @@ -9,10 +10,12 @@ const allEpisodes = getAllEpisodes(); const state = { query: '', films: allEpisodes, + selectedFilm: {}, }; function setup() { renderFilms(); + populateFilmSelect(); } // formats episode and season numbers to show 2 digits @@ -72,9 +75,50 @@ const renderFilms = () => { rootElem.append(...filmCards); }; +// populate each option for film select +const populateOption = (film) => { + const option = document.createElement('option'); + const { id, season, number, name } = film; + const seasonEpisodeDetails = `${formatEpisodeCode('S', season)}${formatEpisodeCode('E', number)}`; + option.value = String(id); + option.textContent = `${seasonEpisodeDetails} - ${name}`; + return option; +}; +// populate film select +const populateFilmSelect = () => { + const populateOptions = allEpisodes.map(populateOption); + filmSelect.append(...populateOptions); +}; +// display single film when select option is chosen +const displaySelectedFilm = () => { + const singleFilmGrid = document.querySelector('.show-single-film'); + // get film card + const chosenFilm = createFilmCard(state.selectedFilm); + // clear single film grid before adding a film + singleFilmGrid.innerHTML = ''; + singleFilmGrid.append(chosenFilm); + // show it on single-film - hide film grid and search area + // on exit button click + // clear single film and hide single film + // show search area and film grid +}; + +// EVENT LISTENERS +//event listener for search input searchInput.addEventListener('input', (e) => { state.query = e.target.value.toLowerCase(); renderFilms(); }); +//event listener for select +filmSelect.addEventListener('change', (e) => { + if (!e.target.value) return; + state.selectedFilm = allEpisodes.filter( + (film) => film.id === Number(e.target.value.trim()) + )[0]; + // reset select + e.target.value = ''; + displaySelectedFilm(); +}); + window.onload = setup; From baa9d224e97cfda6e0970887b5c2a635b6541913 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Wed, 5 Aug 2026 17:22:35 +0100 Subject: [PATCH 17/31] add functionality to show / hide single film grid --- index.html | 2 +- script.js | 29 ++++++++++++++++++++++------- style.css | 26 ++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/index.html b/index.html index 1440e35..367b8d1 100644 --- a/index.html +++ b/index.html @@ -28,7 +28,7 @@

-
+ diff --git a/script.js b/script.js index f348c98..71e83fe 100644 --- a/script.js +++ b/script.js @@ -1,9 +1,12 @@ //You can edit ALL of the code here const filmGrid = document.getElementById('film-grid'); +const singleFilmContainer = document.querySelector('.single-film-grid'); const filterDisplay = document.querySelector('.filter-display'); +const searchArea = document.querySelector('.search-area'); const filmSelect = document.getElementById('film-select'); const searchInput = document.getElementById('film-search'); +const exitButton = document.querySelector('.exit'); const allEpisodes = getAllEpisodes(); // track state of changes - eg input searches @@ -91,16 +94,19 @@ const populateFilmSelect = () => { }; // display single film when select option is chosen const displaySelectedFilm = () => { - const singleFilmGrid = document.querySelector('.show-single-film'); + const singleFilmContent = document.querySelector('.show-single-film'); // get film card const chosenFilm = createFilmCard(state.selectedFilm); // clear single film grid before adding a film - singleFilmGrid.innerHTML = ''; - singleFilmGrid.append(chosenFilm); - // show it on single-film - hide film grid and search area - // on exit button click - // clear single film and hide single film - // show search area and film grid + singleFilmContent.innerHTML = ''; + // reset state.selectedFilm to empty object + state.selectedFilm = {}; + singleFilmContent.append(chosenFilm); + // show the single selected film + singleFilmContainer.classList.remove('hidden'); + // hide search area and film grid + searchArea.classList.add('hidden'); + filmGrid.classList.add('hidden'); }; // EVENT LISTENERS @@ -121,4 +127,13 @@ filmSelect.addEventListener('change', (e) => { displaySelectedFilm(); }); +// event listener to exit single film grid +exitButton.addEventListener('click', (e) => { + // hide the single film grid + singleFilmContainer.classList.add('hidden'); + // show search area and film grid + searchArea.classList.remove('hidden'); + filmGrid.classList.remove('hidden'); +}); + window.onload = setup; diff --git a/style.css b/style.css index 15ba4dd..963ab98 100644 --- a/style.css +++ b/style.css @@ -81,6 +81,32 @@ main { max-width: 1200px; } +/* single film css */ + +.single-film-grid { + margin: 2rem 0; + display: flex; + flex-direction: column; + align-items: center; +} +.show-single-film { + width: 500px; +} + +button.exit { + padding: 0.7rem 0.5rem; + border: none; + border-radius: 5px; + margin: 2rem 0; + background-color: black; + color: white; + font-weight: 700; +} +/* css to hide divs as required */ +.hidden { + display: none; +} + @media (min-width: 700px) { .film-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); From 33c84d7be4398ce3333c904d08187115094f1c43 Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Thu, 6 Aug 2026 16:07:58 +0100 Subject: [PATCH 18/31] Removing script.js, adding loading

--- index.html | 84 +++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/index.html b/index.html index 367b8d1..46f92f5 100644 --- a/index.html +++ b/index.html @@ -1,47 +1,47 @@ + + + + TV Show Project | Liridona Shehu (shehu-dona) + + + - - - - TV Show Project | Liridona Shehu (shehu-dona) - - - + +

+ + + + +

+
+ +
+
+ +
+

+ Film data source: + tvmaze.com +

+

- -
- - - - -

-
- -
-
- -
-

- Film data source: - tvmaze.com -

- - + + - - - - - \ No newline at end of file + + + + From 1922292360a2c178cf869d355c48ca676d297519 Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Thu, 6 Aug 2026 16:11:59 +0100 Subject: [PATCH 19/31] Switch to TVMaze API, remove static data, add loading/error messages --- script.js | 112 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 37 deletions(-) diff --git a/script.js b/script.js index 71e83fe..162999a 100644 --- a/script.js +++ b/script.js @@ -1,29 +1,61 @@ //You can edit ALL of the code here -const filmGrid = document.getElementById('film-grid'); -const singleFilmContainer = document.querySelector('.single-film-grid'); -const filterDisplay = document.querySelector('.filter-display'); -const searchArea = document.querySelector('.search-area'); -const filmSelect = document.getElementById('film-select'); -const searchInput = document.getElementById('film-search'); -const exitButton = document.querySelector('.exit'); -const allEpisodes = getAllEpisodes(); +async function fetchEpisodes() { + try { + const response = await fetch("https://api.tvmaze.com/shows/82/episodes"); + if (!response.ok) { + throw new Error("Failed to load episodes"); + } + const data = await response.json(); + return data; + } catch (error) { + throw error; + } +} +let allEpisodes = []; +const filmGrid = document.getElementById("film-grid"); +const singleFilmContainer = document.querySelector(".single-film-grid"); +const filterDisplay = document.querySelector(".filter-display"); +const searchArea = document.querySelector(".search-area"); +const filmSelect = document.getElementById("film-select"); +const searchInput = document.getElementById("film-search"); +const exitButton = document.querySelector(".exit"); // track state of changes - eg input searches const state = { - query: '', + query: "", films: allEpisodes, selectedFilm: {}, }; -function setup() { - renderFilms(); - populateFilmSelect(); +async function setup() { + showLoadingMessage(); + try { + allEpisodes = await fetchEpisodes(); + state.films = allEpisodes; + hideLoadingMessage(); + renderFilms(); + populateFilmSelect(); + } catch (error) { + showErrorMessage(); + } +} +function showLoadingMessage() { + document.getElementById("loading").innerText = "Loading episodes..."; +} + +function hideLoadingMessage() { + document.getElementById("loading").innerText = ""; +} + +function showErrorMessage(message) { + document.getElementById("loading").innerText = + "Could not load episodes. Please try again later."; } // formats episode and season numbers to show 2 digits const formatEpisodeCode = (prefix, value) => - `${prefix}${String(value).padStart(2, '0')}`; + `${prefix}${String(value).padStart(2, "0")}`; const createFilmCard = (film) => { const { @@ -34,25 +66,28 @@ const createFilmCard = (film) => { summary, } = film; const filmCard = document - .getElementById('film-card-template') + .getElementById("film-card-template") .content.cloneNode(true); - const title = filmCard.querySelector('h3'); - title.innerText = `${name} - ${formatEpisodeCode('S', season)}${formatEpisodeCode('E', number)}`; + const title = filmCard.querySelector("h3"); + title.innerText = `${name} - ${formatEpisodeCode( + "S", + season + )}${formatEpisodeCode("E", number)}`; - const filmImage = filmCard.querySelector('img'); + const filmImage = filmCard.querySelector("img"); filmImage.src = medium; - filmImage.alt = 'image from film'; + filmImage.alt = "image from film"; - const filmSummary = filmCard.querySelector('p'); + const filmSummary = filmCard.querySelector("p"); filmSummary.innerHTML = summary; return filmCard; }; const renderFilms = () => { - const rootElem = document.getElementById('film-grid'); + const rootElem = document.getElementById("film-grid"); // clear film grid before repopulating it - rootElem.innerHTML = ''; + rootElem.innerHTML = ""; // input query searches const { query, films } = state; @@ -66,9 +101,9 @@ const renderFilms = () => { let episodeList; // check if film list is filtered or not - if (state.query === '') { + if (state.query === "") { episodeList = films; - filterDisplay.innerText = ''; + filterDisplay.innerText = ""; } else { episodeList = filmSearch; filterDisplay.innerText = `Displaying ${filmSearch.length}/${films.length}`; @@ -80,9 +115,12 @@ const renderFilms = () => { // populate each option for film select const populateOption = (film) => { - const option = document.createElement('option'); + const option = document.createElement("option"); const { id, season, number, name } = film; - const seasonEpisodeDetails = `${formatEpisodeCode('S', season)}${formatEpisodeCode('E', number)}`; + const seasonEpisodeDetails = `${formatEpisodeCode( + "S", + season + )}${formatEpisodeCode("E", number)}`; option.value = String(id); option.textContent = `${seasonEpisodeDetails} - ${name}`; return option; @@ -94,46 +132,46 @@ const populateFilmSelect = () => { }; // display single film when select option is chosen const displaySelectedFilm = () => { - const singleFilmContent = document.querySelector('.show-single-film'); + const singleFilmContent = document.querySelector(".show-single-film"); // get film card const chosenFilm = createFilmCard(state.selectedFilm); // clear single film grid before adding a film - singleFilmContent.innerHTML = ''; + singleFilmContent.innerHTML = ""; // reset state.selectedFilm to empty object state.selectedFilm = {}; singleFilmContent.append(chosenFilm); // show the single selected film - singleFilmContainer.classList.remove('hidden'); + singleFilmContainer.classList.remove("hidden"); // hide search area and film grid - searchArea.classList.add('hidden'); - filmGrid.classList.add('hidden'); + searchArea.classList.add("hidden"); + filmGrid.classList.add("hidden"); }; // EVENT LISTENERS //event listener for search input -searchInput.addEventListener('input', (e) => { +searchInput.addEventListener("input", (e) => { state.query = e.target.value.toLowerCase(); renderFilms(); }); //event listener for select -filmSelect.addEventListener('change', (e) => { +filmSelect.addEventListener("change", (e) => { if (!e.target.value) return; state.selectedFilm = allEpisodes.filter( (film) => film.id === Number(e.target.value.trim()) )[0]; // reset select - e.target.value = ''; + e.target.value = ""; displaySelectedFilm(); }); // event listener to exit single film grid -exitButton.addEventListener('click', (e) => { +exitButton.addEventListener("click", (e) => { // hide the single film grid - singleFilmContainer.classList.add('hidden'); + singleFilmContainer.classList.add("hidden"); // show search area and film grid - searchArea.classList.remove('hidden'); - filmGrid.classList.remove('hidden'); + searchArea.classList.remove("hidden"); + filmGrid.classList.remove("hidden"); }); window.onload = setup; From 5828c2841d470a30fba199f0159e61a192c661a8 Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Thu, 6 Aug 2026 16:12:31 +0100 Subject: [PATCH 20/31] Styled loading paragraph --- style.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/style.css b/style.css index 963ab98..58c10d9 100644 --- a/style.css +++ b/style.css @@ -28,6 +28,11 @@ body { color: #222; } +#loading { + color: red; + font-size: 1.5rem; + padding-left: 25%; +} .source { text-align: center; } From 76dcb393ddcf17a6bfe0898c442c6a9a5d2b5527 Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Sat, 8 Aug 2026 09:49:02 +0100 Subject: [PATCH 21/31] Deleted episodes.js --- 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 415687e72825ffc5855e25b31d07fe978232a690 Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Sat, 8 Aug 2026 09:49:49 +0100 Subject: [PATCH 22/31] Deleted the --- index.html | 4 ---- 1 file changed, 4 deletions(-) diff --git a/index.html b/index.html index 46f92f5..da45aba 100644 --- a/index.html +++ b/index.html @@ -37,10 +37,6 @@

tvmaze.com

- - - - From 026a2b9aa4f5fc383c96acd6cb6be26de43efb1f Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Mon, 10 Aug 2026 18:18:42 +0100 Subject: [PATCH 23/31] London | 26-ITP-May | Martin Mwaka | Sprint 3 | Project-TV-Show Level 400 (#6) * udpate index, script and css files * update index.html --- script.js | 19 +++++++++---------- style.css | 17 ++++++++++++++++- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/script.js b/script.js index 162999a..0eb12a9 100644 --- a/script.js +++ b/script.js @@ -1,4 +1,4 @@ -//You can edit ALL of the code here +// You can edit ALL of the code here async function fetchEpisodes() { try { @@ -21,7 +21,6 @@ const filmSelect = document.getElementById("film-select"); const searchInput = document.getElementById("film-search"); const exitButton = document.querySelector(".exit"); -// track state of changes - eg input searches const state = { query: "", films: allEpisodes, @@ -82,23 +81,23 @@ const createFilmCard = (film) => { filmSummary.innerHTML = summary; return filmCard; -}; +} const renderFilms = () => { const rootElem = document.getElementById("film-grid"); // clear film grid before repopulating it rootElem.innerHTML = ""; - // input query searches const { query, films } = state; - const filmSearch = films.filter((film) => { - return ( - film.name.toLowerCase().includes(query) || - film.summary.toLowerCase().includes(query) - ); + const normalisedQuery = query.trim().toLowerCase(); + + const filteredFilms = films.filter((film) => { + const name = film.name?.toLowerCase() || ''; + const summary = film.summary?.toLowerCase() || ''; + return name.includes(normalisedQuery) || summary.includes(normalisedQuery); }); - let episodeList; + const episodeList = normalisedQuery === '' ? films : filteredFilms; // check if film list is filtered or not if (state.query === "") { diff --git a/style.css b/style.css index 58c10d9..a3da92a 100644 --- a/style.css +++ b/style.css @@ -95,7 +95,8 @@ main { align-items: center; } .show-single-film { - width: 500px; + max-width: 500px; + width: 90%; } button.exit { @@ -107,6 +108,20 @@ button.exit { color: white; font-weight: 700; } + +.app-message { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: #222; + color: white; + padding: 0.8rem 1rem; + border-radius: 6px; + z-index: 1000; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); +} + /* css to hide divs as required */ .hidden { display: none; From dce3bdfdc782a61a9d2a9b211733640f1d07dbb1 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Tue, 11 Aug 2026 03:57:52 +0100 Subject: [PATCH 24/31] update index.html --- index.html | 117 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 85 insertions(+), 32 deletions(-) diff --git a/index.html b/index.html index da45aba..1b928d5 100644 --- a/index.html +++ b/index.html @@ -1,5 +1,11 @@ + + + + + + @@ -8,36 +14,83 @@ - -
- - - - -

-
- -
-
- -
-

- Film data source: - tvmaze.com -

-

- - - + +
+ + + + + + +

+
+ +
+
+ +
+

+ Film data source: + tvmaze.com +

+ + + + + + + + +TV Show Project | Liridona Shehu (shehu-dona) + + + + + +
+ + + + +

+
+ +
+
+ +
+

+ Film data source: + tvmaze.com +

+

+ + + + + \ No newline at end of file From 43ae722f7e6252d0b7ca8cd0b2196dce966dd2c9 Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Tue, 11 Aug 2026 03:58:56 +0100 Subject: [PATCH 25/31] update script.js --- index.html | 1 + script.js | 301 ++++++++++++++++++++++++++++++++--------------------- 2 files changed, 183 insertions(+), 119 deletions(-) diff --git a/index.html b/index.html index 1b928d5..3d23fa9 100644 --- a/index.html +++ b/index.html @@ -54,6 +54,7 @@

+ TV Show Project | Liridona Shehu (shehu-dona) diff --git a/script.js b/script.js index 0eb12a9..eca7497 100644 --- a/script.js +++ b/script.js @@ -1,92 +1,167 @@ // You can edit ALL of the code here -async function fetchEpisodes() { - try { - const response = await fetch("https://api.tvmaze.com/shows/82/episodes"); - if (!response.ok) { - throw new Error("Failed to load episodes"); - } - const data = await response.json(); - return data; - } catch (error) { - throw error; - } -} -let allEpisodes = []; -const filmGrid = document.getElementById("film-grid"); -const singleFilmContainer = document.querySelector(".single-film-grid"); -const filterDisplay = document.querySelector(".filter-display"); -const searchArea = document.querySelector(".search-area"); -const filmSelect = document.getElementById("film-select"); -const searchInput = document.getElementById("film-search"); -const exitButton = document.querySelector(".exit"); +const filmGrid = document.getElementById('film-grid'); +const singleFilmContainer = document.querySelector('.single-film-grid'); +const filterDisplay = document.querySelector('.filter-display'); +const searchArea = document.querySelector('.search-area'); +const filmSelect = document.getElementById('film-select'); +const showSelect = document.getElementById('show-select'); +const searchInput = document.getElementById('film-search'); +const exitButton = document.querySelector('.exit'); +const API_SHOW_URL = 'https://api.tvmaze.com/shows'; + +let showsCache = null; +let showsPromise = null; +const filmsCache = new Map(); +const filmsPromises = new Map(); const state = { - query: "", - films: allEpisodes, + query: '', + films: [], + shows: [], + episodeId: 124, selectedFilm: {}, }; +function showMessage(message, duration = 3000) { + const existingMessage = document.querySelector('.app-message'); + if (existingMessage) existingMessage.remove(); + + const messageBox = document.createElement('div'); + messageBox.className = 'app-message'; + messageBox.textContent = message; + document.body.appendChild(messageBox); + + setTimeout(() => { + messageBox.remove(); + }, duration); +} + +function clearMessage() { + document.querySelector('.app-message')?.remove(); +} + +async function fetchJson(url) { + const response = await fetch(url); + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + return response.json(); +} + +async function fetchShows() { + if (showsCache) return showsCache; + + if (!showsPromise) { + showsPromise = fetchJson(API_SHOW_URL).then((data) => { + showsCache = data; + return showsCache; + }); + } + + return showsPromise; +} + +async function fetchFilms(showId = state.episodeId) { + if (filmsCache.has(showId)) { + return filmsCache.get(showId); + } + + if (!filmsPromises.has(showId)) { + const promise = fetchJson( + `https://api.tvmaze.com/shows/${showId}/episodes` + ).then((data) => { + filmsCache.set(showId, data); + return data; + }); + + filmsPromises.set(showId, promise); + } + + return filmsPromises.get(showId); +} + async function setup() { - showLoadingMessage(); + showMessage('Loading shows...', 1000); + try { - allEpisodes = await fetchEpisodes(); - state.films = allEpisodes; - hideLoadingMessage(); + const fetchedShows = await fetchShows(); + state.shows = fetchedShows; + renderFilms(); + populateShowSelect(); + clearMessage(); + showMessage('Shows loaded', 1500); + } catch (error) { + console.error('Failed to load shows:', error); + showMessage('Sorry, we could not load the shows right now.'); + } +} + +async function getFilms(showId = state.episodeId) { + showMessage('Loading films...', 1000); + try { + const fetchedFilms = await fetchFilms(showId); + state.films = fetchedFilms; renderFilms(); populateFilmSelect(); + clearMessage(); + showMessage('Films loaded', 1500); } catch (error) { - showErrorMessage(); + console.error('Failed to load films:', error); + showMessage('Sorry, we could not load the films right now.'); } } -function showLoadingMessage() { - document.getElementById("loading").innerText = "Loading episodes..."; + +function populateShowOption(show) { + const option = document.createElement('option'); + const { id, name } = show; + option.value = String(id); + option.textContent = name; + return option; } -function hideLoadingMessage() { - document.getElementById("loading").innerText = ""; +function populateShowSelect() { + const sortedShows = state.shows + .map(({ id, name }) => ({ id, name })) + .sort((a, b) => + a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }) + ); + + showSelect.innerHTML = ''; + showSelect.append(...sortedShows.map(populateShowOption)); + + // set select to id of first show + if (sortedShows.length > 0) { + const firstShowId = sortedShows[0].id; + state.episodeId = firstShowId; + showSelect.value = state.episodeId; + getFilms(state.episodeId); + } } -function showErrorMessage(message) { - document.getElementById("loading").innerText = - "Could not load episodes. Please try again later."; +function formatEpisodeCode(prefix, value) { + return `${prefix}${String(value).padStart(2, '0')}`; } -// formats episode and season numbers to show 2 digits -const formatEpisodeCode = (prefix, value) => - `${prefix}${String(value).padStart(2, "0")}`; - -const createFilmCard = (film) => { - const { - name, - season, - number, - image: { medium }, - summary, - } = film; +function createFilmCard(film) { const filmCard = document - .getElementById("film-card-template") + .getElementById('film-card-template') .content.cloneNode(true); - const title = filmCard.querySelector("h3"); - title.innerText = `${name} - ${formatEpisodeCode( - "S", - season - )}${formatEpisodeCode("E", number)}`; - - const filmImage = filmCard.querySelector("img"); - filmImage.src = medium; - filmImage.alt = "image from film"; + const title = filmCard.querySelector('h3'); + const filmImage = filmCard.querySelector('img'); + const filmSummary = filmCard.querySelector('p'); - const filmSummary = filmCard.querySelector("p"); - filmSummary.innerHTML = summary; + title.innerText = `${film.name} - ${formatEpisodeCode('S', film.season)}${formatEpisodeCode('E', film.number)}`; + filmImage.src = film.image?.medium || ''; + filmImage.alt = film.name || 'image from film'; + filmSummary.innerHTML = film.summary || ''; return filmCard; } -const renderFilms = () => { - const rootElem = document.getElementById("film-grid"); - // clear film grid before repopulating it - rootElem.innerHTML = ""; +function renderFilms() { + const rootElem = filmGrid; + rootElem.innerHTML = ''; const { query, films } = state; const normalisedQuery = query.trim().toLowerCase(); @@ -99,78 +174,66 @@ const renderFilms = () => { const episodeList = normalisedQuery === '' ? films : filteredFilms; - // check if film list is filtered or not - if (state.query === "") { - episodeList = films; - filterDisplay.innerText = ""; - } else { - episodeList = filmSearch; - filterDisplay.innerText = `Displaying ${filmSearch.length}/${films.length}`; - } - // create film cards and append to film-grid - const filmCards = episodeList.map(createFilmCard); - rootElem.append(...filmCards); -}; + filterDisplay.innerText = `Displaying ${episodeList.length}/${films.length}`; -// populate each option for film select -const populateOption = (film) => { - const option = document.createElement("option"); + rootElem.append(...episodeList.map(createFilmCard)); +} + +function populateOption(film) { + const option = document.createElement('option'); const { id, season, number, name } = film; - const seasonEpisodeDetails = `${formatEpisodeCode( - "S", - season - )}${formatEpisodeCode("E", number)}`; + const seasonEpisodeDetails = `${formatEpisodeCode('S', season)}${formatEpisodeCode('E', number)}`; option.value = String(id); option.textContent = `${seasonEpisodeDetails} - ${name}`; return option; -}; -// populate film select -const populateFilmSelect = () => { - const populateOptions = allEpisodes.map(populateOption); - filmSelect.append(...populateOptions); -}; -// display single film when select option is chosen -const displaySelectedFilm = () => { - const singleFilmContent = document.querySelector(".show-single-film"); - // get film card +} + +function populateFilmSelect() { + filmSelect.innerHTML = ''; + filmSelect.append(...state.films.map(populateOption)); +} + +function displaySelectedFilm() { + const singleFilmContent = document.querySelector('.show-single-film'); const chosenFilm = createFilmCard(state.selectedFilm); - // clear single film grid before adding a film - singleFilmContent.innerHTML = ""; - // reset state.selectedFilm to empty object + + singleFilmContent.innerHTML = ''; state.selectedFilm = {}; singleFilmContent.append(chosenFilm); - // show the single selected film - singleFilmContainer.classList.remove("hidden"); - // hide search area and film grid - searchArea.classList.add("hidden"); - filmGrid.classList.add("hidden"); -}; -// EVENT LISTENERS -//event listener for search input -searchInput.addEventListener("input", (e) => { - state.query = e.target.value.toLowerCase(); + singleFilmContainer.classList.remove('hidden'); + searchArea.classList.add('hidden'); + filmGrid.classList.add('hidden'); +} + +// EVENT HANDLERS +searchInput.addEventListener('input', (event) => { + state.query = event.target.value; renderFilms(); }); -//event listener for select -filmSelect.addEventListener("change", (e) => { - if (!e.target.value) return; - state.selectedFilm = allEpisodes.filter( - (film) => film.id === Number(e.target.value.trim()) - )[0]; - // reset select - e.target.value = ""; +filmSelect.addEventListener('change', (event) => { + const selectedValue = event.target.value.trim(); + if (!selectedValue) return; + + state.selectedFilm = + state.films.find((film) => film.id === Number(selectedValue)) || {}; + event.target.value = ''; displaySelectedFilm(); }); -// event listener to exit single film grid -exitButton.addEventListener("click", (e) => { - // hide the single film grid - singleFilmContainer.classList.add("hidden"); - // show search area and film grid - searchArea.classList.remove("hidden"); - filmGrid.classList.remove("hidden"); +showSelect.addEventListener('change', async (event) => { + const selectedValue = event.target.value.trim(); + if (!selectedValue) return; + + state.episodeId = Number(selectedValue); + await getFilms(state.episodeId); +}); + +exitButton.addEventListener('click', () => { + singleFilmContainer.classList.add('hidden'); + searchArea.classList.remove('hidden'); + filmGrid.classList.remove('hidden'); }); window.onload = setup; From 79c89a617a3348aa90ccf1443a5ec91a957a383c Mon Sep 17 00:00:00 2001 From: Martin Mwaka Date: Tue, 11 Aug 2026 04:00:03 +0100 Subject: [PATCH 26/31] update style.css --- style.css | 5 ----- 1 file changed, 5 deletions(-) diff --git a/style.css b/style.css index a3da92a..3b03ec8 100644 --- a/style.css +++ b/style.css @@ -28,11 +28,6 @@ body { color: #222; } -#loading { - color: red; - font-size: 1.5rem; - padding-left: 25%; -} .source { text-align: center; } From d0a8c23d2869ca03f1079338ca694c24c2d1de0e Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Tue, 11 Aug 2026 14:19:25 +0100 Subject: [PATCH 27/31] change variable naming from film --> episode --- script.js | 203 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 105 insertions(+), 98 deletions(-) diff --git a/script.js b/script.js index eca7497..b165582 100644 --- a/script.js +++ b/script.js @@ -1,34 +1,34 @@ // You can edit ALL of the code here -const filmGrid = document.getElementById('film-grid'); -const singleFilmContainer = document.querySelector('.single-film-grid'); -const filterDisplay = document.querySelector('.filter-display'); -const searchArea = document.querySelector('.search-area'); -const filmSelect = document.getElementById('film-select'); -const showSelect = document.getElementById('show-select'); -const searchInput = document.getElementById('film-search'); -const exitButton = document.querySelector('.exit'); -const API_SHOW_URL = 'https://api.tvmaze.com/shows'; +const episodeGrid = document.getElementById("film-grid"); +const singleEpisodeContainer = document.querySelector(".single-film-grid"); +const filterDisplay = document.querySelector(".filter-display"); +const searchArea = document.querySelector(".search-area"); +const episodeSelect = document.getElementById("film-select"); +const showSelect = document.getElementById("show-select"); +const searchInput = document.getElementById("film-search"); +const exitButton = document.querySelector(".exit"); +const API_SHOW_URL = "https://api.tvmaze.com/shows"; let showsCache = null; let showsPromise = null; -const filmsCache = new Map(); -const filmsPromises = new Map(); +const episodesCache = new Map(); +const episodesPromises = new Map(); const state = { - query: '', - films: [], + query: "", + episodes: [], shows: [], - episodeId: 124, - selectedFilm: {}, + selectedShowId: 124, + selectedEpisode: {}, }; function showMessage(message, duration = 3000) { - const existingMessage = document.querySelector('.app-message'); + const existingMessage = document.querySelector(".app-message"); if (existingMessage) existingMessage.remove(); - const messageBox = document.createElement('div'); - messageBox.className = 'app-message'; + const messageBox = document.createElement("div"); + messageBox.className = "app-message"; messageBox.textContent = message; document.body.appendChild(messageBox); @@ -38,7 +38,7 @@ function showMessage(message, duration = 3000) { } function clearMessage() { - document.querySelector('.app-message')?.remove(); + document.querySelector(".app-message")?.remove(); } async function fetchJson(url) { @@ -62,58 +62,58 @@ async function fetchShows() { return showsPromise; } -async function fetchFilms(showId = state.episodeId) { - if (filmsCache.has(showId)) { - return filmsCache.get(showId); +async function fetchEpisodes(showId = state.selectedShowId) { + if (episodesCache.has(showId)) { + return episodesCache.get(showId); } - if (!filmsPromises.has(showId)) { + if (!episodesPromises.has(showId)) { const promise = fetchJson( `https://api.tvmaze.com/shows/${showId}/episodes` ).then((data) => { - filmsCache.set(showId, data); + episodesCache.set(showId, data); return data; }); - filmsPromises.set(showId, promise); + episodesPromises.set(showId, promise); } - return filmsPromises.get(showId); + return episodesPromises.get(showId); } async function setup() { - showMessage('Loading shows...', 1000); + showMessage("Loading shows...", 1000); try { const fetchedShows = await fetchShows(); state.shows = fetchedShows; - renderFilms(); + renderEpisodes(); populateShowSelect(); clearMessage(); - showMessage('Shows loaded', 1500); + showMessage("Shows loaded", 1500); } catch (error) { - console.error('Failed to load shows:', error); - showMessage('Sorry, we could not load the shows right now.'); + console.error("Failed to load shows:", error); + showMessage("Sorry, we could not load the shows right now."); } } -async function getFilms(showId = state.episodeId) { - showMessage('Loading films...', 1000); +async function loadEpisodes(showId = state.selectedShowId) { + showMessage("Loading episodes...", 1000); try { - const fetchedFilms = await fetchFilms(showId); - state.films = fetchedFilms; - renderFilms(); - populateFilmSelect(); + const fetchedEpisodes = await fetchEpisodes(showId); + state.episodes = fetchedEpisodes; + renderEpisodes(); + populateEpisodeSelect(); clearMessage(); - showMessage('Films loaded', 1500); + showMessage("Episodes loaded", 1500); } catch (error) { - console.error('Failed to load films:', error); - showMessage('Sorry, we could not load the films right now.'); + console.error("Failed to load episodes:", error); + showMessage("Sorry, we could not load the episodes right now."); } } function populateShowOption(show) { - const option = document.createElement('option'); + const option = document.createElement("option"); const { id, name } = show; option.value = String(id); option.textContent = name; @@ -124,116 +124,123 @@ function populateShowSelect() { const sortedShows = state.shows .map(({ id, name }) => ({ id, name })) .sort((a, b) => - a.name.localeCompare(b.name, undefined, { sensitivity: 'base' }) + a.name.localeCompare(b.name, undefined, { sensitivity: "base" }) ); showSelect.innerHTML = ''; showSelect.append(...sortedShows.map(populateShowOption)); - // set select to id of first show if (sortedShows.length > 0) { const firstShowId = sortedShows[0].id; - state.episodeId = firstShowId; - showSelect.value = state.episodeId; - getFilms(state.episodeId); + state.selectedShowId = firstShowId; + showSelect.value = state.selectedShowId; + loadEpisodes(state.selectedShowId); } } function formatEpisodeCode(prefix, value) { - return `${prefix}${String(value).padStart(2, '0')}`; + return `${prefix}${String(value).padStart(2, "0")}`; } -function createFilmCard(film) { - const filmCard = document - .getElementById('film-card-template') +function createEpisodeCard(episode) { + const episodeCard = document + .getElementById("film-card-template") .content.cloneNode(true); - const title = filmCard.querySelector('h3'); - const filmImage = filmCard.querySelector('img'); - const filmSummary = filmCard.querySelector('p'); - title.innerText = `${film.name} - ${formatEpisodeCode('S', film.season)}${formatEpisodeCode('E', film.number)}`; - filmImage.src = film.image?.medium || ''; - filmImage.alt = film.name || 'image from film'; - filmSummary.innerHTML = film.summary || ''; + const title = episodeCard.querySelector("h3"); + const episodeImage = episodeCard.querySelector("img"); + const episodeSummary = episodeCard.querySelector("p"); - return filmCard; + title.innerText = `${episode.name} - ${formatEpisodeCode( + "S", + episode.season + )}${formatEpisodeCode("E", episode.number)}`; + episodeImage.src = episode.image?.medium || ""; + episodeImage.alt = episode.name || "episode image"; + episodeSummary.innerHTML = episode.summary || ""; + + return episodeCard; } -function renderFilms() { - const rootElem = filmGrid; - rootElem.innerHTML = ''; +function renderEpisodes() { + const rootElem = episodeGrid; + rootElem.innerHTML = ""; - const { query, films } = state; + const { query, episodes } = state; const normalisedQuery = query.trim().toLowerCase(); - const filteredFilms = films.filter((film) => { - const name = film.name?.toLowerCase() || ''; - const summary = film.summary?.toLowerCase() || ''; + const filteredEpisodes = episodes.filter((episode) => { + const name = episode.name?.toLowerCase() || ""; + const summary = episode.summary?.toLowerCase() || ""; return name.includes(normalisedQuery) || summary.includes(normalisedQuery); }); - const episodeList = normalisedQuery === '' ? films : filteredFilms; + const episodeList = normalisedQuery === "" ? episodes : filteredEpisodes; - filterDisplay.innerText = `Displaying ${episodeList.length}/${films.length}`; + filterDisplay.innerText = `Displaying ${episodeList.length}/${episodes.length}`; - rootElem.append(...episodeList.map(createFilmCard)); + rootElem.append(...episodeList.map(createEpisodeCard)); } -function populateOption(film) { - const option = document.createElement('option'); - const { id, season, number, name } = film; - const seasonEpisodeDetails = `${formatEpisodeCode('S', season)}${formatEpisodeCode('E', number)}`; +function populateEpisodeOption(episode) { + const option = document.createElement("option"); + const { id, season, number, name } = episode; + const seasonEpisodeDetails = `${formatEpisodeCode( + "S", + season + )}${formatEpisodeCode("E", number)}`; option.value = String(id); option.textContent = `${seasonEpisodeDetails} - ${name}`; return option; } -function populateFilmSelect() { - filmSelect.innerHTML = ''; - filmSelect.append(...state.films.map(populateOption)); +function populateEpisodeSelect() { + episodeSelect.innerHTML = ''; + episodeSelect.append(...state.episodes.map(populateEpisodeOption)); } -function displaySelectedFilm() { - const singleFilmContent = document.querySelector('.show-single-film'); - const chosenFilm = createFilmCard(state.selectedFilm); +function displaySelectedEpisode() { + const singleEpisodeContent = document.querySelector(".show-single-film"); + const chosenEpisode = createEpisodeCard(state.selectedEpisode); - singleFilmContent.innerHTML = ''; - state.selectedFilm = {}; - singleFilmContent.append(chosenFilm); + singleEpisodeContent.innerHTML = ""; + state.selectedEpisode = {}; + singleEpisodeContent.append(chosenEpisode); - singleFilmContainer.classList.remove('hidden'); - searchArea.classList.add('hidden'); - filmGrid.classList.add('hidden'); + singleEpisodeContainer.classList.remove("hidden"); + searchArea.classList.add("hidden"); + episodeGrid.classList.add("hidden"); } // EVENT HANDLERS -searchInput.addEventListener('input', (event) => { +searchInput.addEventListener("input", (event) => { state.query = event.target.value; - renderFilms(); + renderEpisodes(); }); -filmSelect.addEventListener('change', (event) => { +episodeSelect.addEventListener("change", (event) => { const selectedValue = event.target.value.trim(); if (!selectedValue) return; - state.selectedFilm = - state.films.find((film) => film.id === Number(selectedValue)) || {}; - event.target.value = ''; - displaySelectedFilm(); + state.selectedEpisode = + state.episodes.find((episode) => episode.id === Number(selectedValue)) || + {}; + event.target.value = ""; + displaySelectedEpisode(); }); -showSelect.addEventListener('change', async (event) => { +showSelect.addEventListener("change", async (event) => { const selectedValue = event.target.value.trim(); if (!selectedValue) return; - state.episodeId = Number(selectedValue); - await getFilms(state.episodeId); + state.selectedShowId = Number(selectedValue); + await loadEpisodes(state.selectedShowId); }); -exitButton.addEventListener('click', () => { - singleFilmContainer.classList.add('hidden'); - searchArea.classList.remove('hidden'); - filmGrid.classList.remove('hidden'); +exitButton.addEventListener("click", () => { + singleEpisodeContainer.classList.add("hidden"); + searchArea.classList.remove("hidden"); + episodeGrid.classList.remove("hidden"); }); window.onload = setup; From e217f192076b5762948ac831c514db44a05055ba Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Fri, 14 Aug 2026 14:07:28 +0100 Subject: [PATCH 28/31] Updated html for level 500 --- index.html | 132 ++++++++++++++++++++--------------------------------- 1 file changed, 49 insertions(+), 83 deletions(-) diff --git a/index.html b/index.html index 3d23fa9..04ff092 100644 --- a/index.html +++ b/index.html @@ -1,11 +1,5 @@ - - - - - - @@ -14,84 +8,56 @@ - -
- - - - - - -

-
- -
-
- -
-

- Film data source: - tvmaze.com -

- - + + + - - - + +
+ - - - -TV Show Project | Liridona Shehu (shehu-dona) - - - - - -
- - - - -

-
- -
-
- -
-

- Film data source: - tvmaze.com -

-

- - - - \ No newline at end of file + +
+ + +
+ + + + +

+
+ + + +
+
+ + +
+ +

+ Film data source: + tvmaze.com +

+ + + + From 7c9357fd438a168d17dafd1f3ce547ad04067bac Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Fri, 14 Aug 2026 14:08:13 +0100 Subject: [PATCH 29/31] Updated script.js for level 500 --- script.js | 86 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 6 deletions(-) diff --git a/script.js b/script.js index b165582..3eec7a8 100644 --- a/script.js +++ b/script.js @@ -1,5 +1,5 @@ // You can edit ALL of the code here - +const showsControls = document.querySelector(".shows-controls"); const episodeGrid = document.getElementById("film-grid"); const singleEpisodeContainer = document.querySelector(".single-film-grid"); const filterDisplay = document.querySelector(".filter-display"); @@ -61,6 +61,52 @@ async function fetchShows() { return showsPromise; } +function createShowCard(show) { + const card = document.createElement("section"); + card.className = "show-card"; + card.innerHTML = ` +

${show.name}

+  ${show.name} +

${show.summary || ""}

+

Genres: ${show.genres.join(", ")}

+

Status: ${show.status}

+

Rating: ${show.rating?.average || "N/A"}

+

Runtime: ${show.runtime} minutes

+ `; + card.addEventListener("click", () => { + document.querySelector(".shows-listing").classList.add("hidden"); + showsControls.classList.add("hidden"); + + searchArea.classList.remove("hidden"); + episodeGrid.classList.remove("hidden"); + singleEpisodeContainer.classList.add("hidden"); + + document.querySelector(".back-to-shows").classList.remove("hidden"); + + state.selectedShowId = show.id; + loadEpisodes(show.id); + }); + return card; +} + +function renderShowsListing() { + const showsContainer = document.querySelector(".shows-listing"); + showsContainer.innerHTML = ""; + state.shows.forEach((show) => { + const card = createShowCard(show); + showsContainer.append(card); + }); +} + +function renderFilteredShows(filteredShows) { + const showsContainer = document.querySelector(".shows-listing"); + showsContainer.innerHTML = ""; + + filteredShows.forEach((show) => { + const card = createShowCard(show); + showsContainer.append(card); + }); +} async function fetchEpisodes(showId = state.selectedShowId) { if (episodesCache.has(showId)) { @@ -87,7 +133,15 @@ async function setup() { try { const fetchedShows = await fetchShows(); state.shows = fetchedShows; - renderEpisodes(); + showsControls.classList.remove("hidden"); + document.getElementById("show-select-label").classList.add("hidden"); + showSelect.classList.add("hidden"); + + renderShowsListing(); + searchArea.classList.add("hidden"); + episodeGrid.classList.add("hidden"); + singleEpisodeContainer.classList.add("hidden"); + populateShowSelect(); clearMessage(); showMessage("Shows loaded", 1500); @@ -134,7 +188,6 @@ function populateShowSelect() { const firstShowId = sortedShows[0].id; state.selectedShowId = firstShowId; showSelect.value = state.selectedShowId; - loadEpisodes(state.selectedShowId); } } @@ -228,6 +281,19 @@ episodeSelect.addEventListener("change", (event) => { event.target.value = ""; displaySelectedEpisode(); }); +const showSearchInput = document.getElementById("show-search"); + +showSearchInput.addEventListener("input", (event) => { + const query = event.target.value.toLowerCase().trim(); + + const filteredShows = state.shows.filter((show) => { + const name = show.name.toLowerCase(); + const summary = show.summary?.toLowerCase() || ""; + return name.includes(query) || summary.includes(query); + }); + + renderFilteredShows(filteredShows); +}); showSelect.addEventListener("change", async (event) => { const selectedValue = event.target.value.trim(); @@ -237,10 +303,18 @@ showSelect.addEventListener("change", async (event) => { await loadEpisodes(state.selectedShowId); }); -exitButton.addEventListener("click", () => { +document.querySelector(".back-to-shows").addEventListener("click", () => { + // show shows page + document.querySelector(".shows-listing").classList.remove("hidden"); + showsControls.classList.remove("hidden"); + + // hide episodes page + searchArea.classList.add("hidden"); + episodeGrid.classList.add("hidden"); singleEpisodeContainer.classList.add("hidden"); - searchArea.classList.remove("hidden"); - episodeGrid.classList.remove("hidden"); + + // hide back button + document.querySelector(".back-to-shows").classList.add("hidden"); }); window.onload = setup; From 5eea9cfb7120897eee6b15159a0d59f1d88b0acd Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Fri, 14 Aug 2026 14:08:50 +0100 Subject: [PATCH 30/31] added styling for shows --- style.css | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/style.css b/style.css index 3b03ec8..a511233 100644 --- a/style.css +++ b/style.css @@ -44,6 +44,29 @@ body { grid-template-columns: 1fr; gap: 1rem; } +#show-search { + width: 400px; + height: 1.5rem; + margin: 1rem auto; + display: block; + border-radius: 4px; + padding: 0.25rem 0.5rem; +} +.shows-listing { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); + gap: 1.5rem; + padding: 1rem; +} + +.show-card img { + width: 100%; + max-width: 250px; + height: auto; + display: block; + margin: 20px auto 20px auto; + border-radius: 8px; +} .card { width: 100%; From bca813ab7bfa496ae108fe745f8df4000917cbdf Mon Sep 17 00:00:00 2001 From: dona-shehu Date: Tue, 18 Aug 2026 21:39:19 +0100 Subject: [PATCH 31/31] fixed show select dropdown --- script.js | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/script.js b/script.js index 3eec7a8..bc56829 100644 --- a/script.js +++ b/script.js @@ -74,17 +74,7 @@ function createShowCard(show) {

Runtime: ${show.runtime} minutes

`; card.addEventListener("click", () => { - document.querySelector(".shows-listing").classList.add("hidden"); - showsControls.classList.add("hidden"); - - searchArea.classList.remove("hidden"); - episodeGrid.classList.remove("hidden"); - singleEpisodeContainer.classList.add("hidden"); - - document.querySelector(".back-to-shows").classList.remove("hidden"); - - state.selectedShowId = show.id; - loadEpisodes(show.id); + showEpisodesView(show.id); }); return card; } @@ -98,6 +88,19 @@ function renderShowsListing() { }); } +function showEpisodesView(showId) { + document.querySelector(".shows-listing").classList.add("hidden"); + showsControls.classList.add("hidden"); + + searchArea.classList.remove("hidden"); + episodeGrid.classList.remove("hidden"); + singleEpisodeContainer.classList.add("hidden"); + + document.querySelector(".back-to-shows").classList.remove("hidden"); + + state.selectedShowId = showId; + loadEpisodes(showId); +} function renderFilteredShows(filteredShows) { const showsContainer = document.querySelector(".shows-listing"); showsContainer.innerHTML = ""; @@ -134,8 +137,6 @@ async function setup() { const fetchedShows = await fetchShows(); state.shows = fetchedShows; showsControls.classList.remove("hidden"); - document.getElementById("show-select-label").classList.add("hidden"); - showSelect.classList.add("hidden"); renderShowsListing(); searchArea.classList.add("hidden"); @@ -281,6 +282,11 @@ episodeSelect.addEventListener("change", (event) => { event.target.value = ""; displaySelectedEpisode(); }); +exitButton.addEventListener("click", () => { + singleEpisodeContainer.classList.add("hidden"); + searchArea.classList.remove("hidden"); + episodeGrid.classList.remove("hidden"); +}); const showSearchInput = document.getElementById("show-search"); showSearchInput.addEventListener("input", (event) => { @@ -289,7 +295,10 @@ showSearchInput.addEventListener("input", (event) => { const filteredShows = state.shows.filter((show) => { const name = show.name.toLowerCase(); const summary = show.summary?.toLowerCase() || ""; - return name.includes(query) || summary.includes(query); + const genres = show.genres.join(" ").toLowerCase(); + return ( + name.includes(query) || summary.includes(query) || genres.includes(query) + ); }); renderFilteredShows(filteredShows); @@ -299,8 +308,7 @@ showSelect.addEventListener("change", async (event) => { const selectedValue = event.target.value.trim(); if (!selectedValue) return; - state.selectedShowId = Number(selectedValue); - await loadEpisodes(state.selectedShowId); + showEpisodesView(Number(selectedValue)); }); document.querySelector(".back-to-shows").addEventListener("click", () => {