Compare commits

...

23 Commits

Author SHA1 Message Date
32630edd85 added details about project 2025-04-02 12:36:38 +02:00
20838adf13 fixed the best title h2 to h3 2025-03-12 11:35:38 +01:00
7c660447eb fixed semantic : structure, img alt, titles 2025-03-11 18:08:03 +01:00
a3519dcba9 fixed the uncovered image bkgrd on bestfilm small screen 2025-03-11 13:59:49 +01:00
b7f66d09a7 fixed size of image in best film small screen 2025-03-11 09:41:25 +01:00
8829df6d2b fixed the re-generation on resize for category selected 2025-03-10 18:17:08 +01:00
c36a0dcfc9 add category 2 block : animation 2025-03-10 17:35:27 +01:00
ac3c1a20bc fixed minor errors shown by W3 validator 2025-03-10 17:20:25 +01:00
3d5eb43884 clean JS, add comments 2025-03-10 12:05:12 +01:00
907d5e7379 remove unused var 2025-03-10 11:43:14 +01:00
207ea011b3 fix the movie detail in modal 2025-03-10 11:42:23 +01:00
211dc5640d add onclick to function on generated buttons : works 2025-03-09 18:32:22 +01:00
14cd793643 Merge branch 'master' of https://mcstn.fr/gitea/Yann/Projet6 2025-03-09 17:45:36 +01:00
74806e2e49 fix block generation 2025-03-09 17:45:04 +01:00
82ab71e865 Supprimer test.js 2025-03-09 16:32:20 +00:00
69b0751867 Supprimer test2.js 2025-03-09 16:32:15 +00:00
08df61d67c Supprimer style.css 2025-03-09 16:31:54 +00:00
441ac4a5a9 Supprimer index2.html 2025-03-09 16:31:44 +00:00
074eb6fb92 cleaning 2025-03-09 17:30:38 +01:00
0ff6a70ba4 see more/see less in js function; not working yet 2025-03-09 17:27:17 +01:00
f3fd9dc2e8 works except show more and show less 2025-03-07 20:18:17 +01:00
619bf05069 modal ok, category blocks ok, before changing best block 2025-03-07 10:30:03 +01:00
1d5dad00e3 before changing the modal for a bootstrap one 2025-03-06 07:44:17 +01:00
4 changed files with 462 additions and 149 deletions

49
README.md Normal file
View File

@@ -0,0 +1,49 @@
# OCR / DA Python - Project6
## JustStreamIt
Build a website using HTML/CSS (bootstrap) and Javascript
Retrieving data from remote API
### Introduction
These instructions allow you to :
- get the program
- install the required environment
- run and use it
### Requirements
1. modules
```
packages : python 3.11, python3.11-venv, git
```
2. API
https://github.com/OpenClassrooms-Student-Center/OCMovies-API-EN-FR
### Installation
1. Create the virtual environment
```
python3.11 -m venv env
source env/bin/activate
```
2. clone this repo
## Execution
Make sure the API is started (refer to its instructions) then open the index.html in your browser
## Use
Browse as any website
## Author
YaL <yann@needsome.coffee>
## License
MIT License
Copyright (c) 2025

View File

@@ -2,163 +2,115 @@
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>JustStreamIt</title>
</head>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
<link href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
<style>
.square {
width: 100%;
padding-top: 100%;
background-color: lightgray;
margin-bottom: 1rem;
}
.bfilm {
width: 100%;
padding-top: 80%;
}
.overlay {
position: absolute;
top: 10rem;
height: 8rem;
width: 90%;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
justify-content: space-between;
margin: 0.1rem;
}
</style>
</head>
<body>
<center>
<header>
<img alt="logo" title="logo" src=None>
<h1 textalign=center> Vidéos à la demande </h1>
</header>
<div class=best_film>
<h2> Meilleur film </h2>
<div class=category>
<h2> film </h2>
<img src=https://picsum.photos/seed/picsum/200/300 alt="film cover" >
<p> Lorem ipsum odor amet, consectetuer adipiscing elit. Tempus non pellentesque bibendum eu sapien litora neque tortor. Volutpat diam himenaeos risus vitae congue hendrerit elit. Porttitor semper leo pretium mattis bibendum consequat. Tincidunt tincidunt cursus eget aenean senectus pretium consectetur cubilia senectus. Rutrum ex fermentum consectetur parturient ornare. Quam amet maximus potenti ac in penatibus hendrerit lacus. Duis neque non at dictum ligula nullam amet orci. Consequat lectus consequat morbi dis suscipit ridiculus ultricies.
</p>
<button>Details</button>
</div>
</div>
<div class=categorie_mystery >
<h2> Mystery </h2>
<div class=category>
<ul>
<div>Film 1
</div>
<div>
Film 2
</div>
<div>
Film 3
</div>
<div>
Film 4
</div>
<div>
Film 5
</div>
<div>
Film 6
</div>
</ul>
<header>
<div class="container text-bg-secondary shadow rounded-4 p-3 mt-4">
<div class="row">
<div class="col-md-2">
<img alt="JustStreamIt Logo" title="logo" src="logo.png" class="img-fluid">
</div>
<div class="col align-self-center d-none d-sm-block ms-5">
<h1 style="color: white; text-align: left"> Vidéos à la demande </h1>
</div>
</div>
</div>
</div>
</header>
<!-- best film -->
<main>
<section>
<div class="container mt-5">
<h2> Meilleur film </h2></div>
<div class="container border border-black border-5 mt-1">
<div class="row" id="bestFilm" >
</div>
</div>
</section>
<div class=category>
<h2> Category2 </h2>
<p>
<ul>
<div>Film 1
</div>
<div>
Film 2
</div>
<div>
Film 3
</div>
<div>
Film 4
</div>
<div>
Film 5
</div>
<div>
Film 6
</div>
</ul>
</p>
</div>
<div class=category>
<h2> Category2 </h2>
<p>
<ul>
<div>Film 1
</div>
<div>
Film 2
</div>
<div>
Film 3
</div>
<div>
Film 4
</div>
<div>
Film 5
</div>
<div>
Film 6
</div>
</ul>
</p>
</div>
<div class=categorie_other>
<h2> Other </h2>
<div class=category>
<ul>
<div>Film 1
</div>
<div>
Film 2
</div>
<div>
Film 3
</div>
<div>
Film 4
</div>
<div>
Film 5
</div>
<div>
Film 6
</div>
</ul>
<!-- best note -->
<section class="py-5">
<div class="container">
<h2>Films les mieux notés</h2>
<div class="row" id="bestRated">
<!-- square block model-->
</div>
</div>
</div>
<div class=category>
<h2> Category </h2>
</section>
<label for="category-select">Category</label>
<!-- mystery -->
<section class="py-5">
<div class="container">
<h2>Mystery</h2>
<div class="row" id="mystery">
<!-- square block model-->
</div>
</div>
</section>
<select name="category" id="category-select">
<option value="Action">Action</option>
<option value="Adult">Adult</option>
<option value="Adventure">Adventure</option>
<option value="Animation">Animation</option>
<option value="Biography">Biography</option>
<option value="Comedy">Comedy</option>
<option value="Crime">Crime</option>
<option value="Documentary">Documentary</option>
<option value="Drama">Drama</option>
<option value="Family">Family</option>
<option value="Fantasy">Fantasy</option>
<option value="Film-Noir">Film-Noir</option>
<option value="History">History</option>
<option value="Horror">Horror</option>
<option value="Music">Music</option>
<option value="Musical">Musical</option>
<option value="Mystery">Mystery</option>
<option value="News">News</option>
<option value="Reality-TV">Reality-TV</option>
<option value="Romance">Romance</option>
<option value="Sci-Fi">Sci-Fi</option>
<option value="Sport">Sport</option>
<option value="Thriller">Thriller</option>
<option value="War">War</option>
<option value="Western">Western</option>
<!-- animation -->
<section class="py-5">
<div class="container">
<h2>Animation</h2>
<div class="row" id="animation">
<!-- square block model-->
</div>
</div>
</section>
</select>
</center>
<!-- other -->
<section class="py-5">
<div class="container">
<div id="menu">
<span><label for="category-select"><h2>Autres :</h2></label></span>
<select name="category" id="category-select">
<option selected id="selected">Category</option>
<!-- getCategory() to fill the options -->
</select>
</div>
<div class="row" id="other">
<!-- square block model-->
</div>
</div>
</section>
</main>
<footer></footer>
<script src="script.js" defer></script>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.12.9/dist/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@4.0.0/dist/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
</body>
</html>

BIN
logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

312
script.js Normal file
View File

@@ -0,0 +1,312 @@
const url_title = "http://localhost:8000/api/v1/titles/";
const url_genre = "http://localhost:8000/api/v1/genres/";
// create the best film block and get a movie from a given ID
async function bestFilm(filmId) {
const response = await fetch(`${url_title}${filmId}`);
const bfilm = await response.json();
let blockToLook = document.getElementById("bestFilm");
let detail = `
<div class="col d-flex justify-content-center my-2">
<img class="img-fluid d-none d-md-block" src="${bfilm.image_url}" alt="${bfilm.title}">
</div>
<div class="col-12 d-flex items- d-block d-md-none">
<div class="bfilm" style="background-image: url(${bfilm.image_url}); background-size: cover; "></div>
</div>
<div class="col-12 col-md-9 my-3">
<div class="col-12 d-flex justify-content-start"><h3>${bfilm.title}</h3></div>
<div class="col-12">${bfilm.description}</div>
<div class="col-12 d-flex justify-content-end">
<button type="button" class="btn btn-danger rounded-4 px-4" data-toggle="modal" data-target="#${bfilm.id}">Détail</button>
</div>
</div>
`;
blockToLook.innerHTML += detail;
getModalDetail(bfilm, bfilm.id, blockToLook);
}
// call the movie URL, retrieve data and create modal block, then insert it in HTML directly
// avoid to manage a promise object in createBlock()
async function getModalDetail(filmData, modalId, blockToLook) {
const response = await fetch(`${filmData.url}`);
const film = await response.json();
let recette = "N/A";
let genre = recette;
if (film.worldwide_gross_income) {
recette = film.worldwide_gross_income;
}
if (film.genre) {
genre = film.genre;
}
let modalContent = `
<div class="modal fade" id="${modalId}" tabindex="-1" aria-labelledby="${modalId}" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<div class="modal-title" id="${modalId}">Détail du film</div>
<button type="button" class="close" data-dismiss="modal" aria-label="Fermer">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<!-- first part -->
<div class="row">
<div class="col-12-auto col-lg-6 ">
<div class="p-2">
<div class="fs-2 py-2 fw-bolder">${film.title}</div>
<div class="fs-4">${film.year} - ${genre}</div>
<div class="fs-4">${film.duration} minutes (${film.countries})</div>
<div class="fs-4">IMDB score: ${film.imdb_score}/10</div>
<div class="fs-4">Recettes au Box-Office : ${recette}</div>
<div class="py-3 fs-4">Realisé par:
<p class="fs-5">${film.directors}</p>
</div>
</div>
</div>
<div class="col-12-auto col-lg-6 d-flex justify-content-center">
<img src="${film.image_url}" class="img-fluid my-3 d-none d-lg-block" alt="${film.title}">
</div>
</div>
<!-- second part -->
<div class="row">
<div class="col-12 order-md-1">
<div class="p-3 border bg-light">${film.long_description}
</div>
<div class="col-12 d-flex justify-content-center">
<img src="${film.image_url}" class=" my-3 d-lg-none" alt="${film.title}">
</div>
<div class="p-3 border order-md-3 bg-light mt-3">
<strong>Avec: </strong>
<p>${film.actors}</p>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-danger rounded-4 px-4 d-none d-lg-block" data-dismiss="modal">Fermer</button>
</div>
</div>
</div>
</div>
`
blockToLook.innerHTML += modalContent
}
// get a list of movies from a first general call based on query string;
// -> returns a promise
async function getMovies3(url) {
const filmList = [];
for (let j=1; j<3; j++) {
const response = await fetch(`${url_title}${url}&format=json&page=${j}`);
const film1 = await response.json();
for (i in film1.results) {
filmList.push(film1.results[i]);
}
}
return filmList;
}
// create HTML blocks from a movie list
// Called by generateMovies (using screen size) and the button "more" to create 6 blocks
// (they've already handled the promise)
function createBlock(filmList, id, count) {
// get the element to change
let blockToLook = document.getElementById(id);
blockToLook.innerHTML = "";
for (let i = 0; i < count; i++) {
film = filmList[i];
let movieBlock = `
<div class="col-12 col-md-6 col-lg-4 pb-5" id="${i}">
<div class="square" style="background-image: url(${film.image_url}); background-size: cover">
</div>
<div class="overlay row">
<div class="col-12">
<h3 class="fs-4 text-white">${film.title}</h3>
</div>
<div class="col d-flex justify-content-end">
<button type="button" class="btn btn-secondary rounded-4 px-4" data-toggle="modal" data-target="#${film.id}"><strong>Détail</strong></button>
</div>
</div>
</div>
`;
// create the block, then call the modal creation from the async function
blockToLook.innerHTML += movieBlock
getModalDetail(film, film.id, blockToLook);
}
}
// get categories and create the options in select menu
async function getCategory() {
listeGenres = [];
for (let i = 1; i<6; i++) {
const response = await fetch(`${url_genre}?page=${i}`);
const genres = await response.json();
for (let j in genres.results) {
listeGenres.push(genres.results[j].name);
};
}
let categorySelect = document.getElementById("category-select");
for (i in listeGenres) {
let option = `
<option value="${listeGenres[i]}">${listeGenres[i]}</option>
`;
categorySelect.innerHTML += option;
}
}
// ==================== buttons
// add button with requested display
function addSeeMore(id, display) {
let blockToChange = document.getElementById(id)
let blockToAdd = `
<div id="seeMore" class="row d-flex justify-content-center">
<button onclick="${id}More()" class="col-4 btn btn-danger rounded-4 ${display}" id="more" type="button" >Voir plus</button>
</div>
`
blockToChange.innerHTML += blockToAdd
}
function addSeeLess(id, display) {
let blockToChange = document.getElementById(id)
let blockToAdd = `
<div id="seeLess" class="row d-flex justify-content-center">
<button onclick="${id}Less()" class="col-4 btn btn-danger rounded-4 ${display}" id="less" type="button" >Voir moins</button>
</div>
`
blockToChange.innerHTML += blockToAdd;
}
//==================== end buttons
// ==================== functions called by buttons
//for each container... could be improved (factorized)
function bestRatedMore() {
bestRated.then((data) => {
createBlock(data, "bestRated", 6);
addSeeMore("bestRated", "d-none");
addSeeLess("bestRated", "d-block");
})
}
function bestRatedLess() {
generateMovies(bestRated, "bestRated")
}
function mysteryMore() {
mystery.then((data) => {
createBlock(data, "mystery", 6);
addSeeMore("mystery", "d-none");
addSeeLess("mystery", "d-block");
})
}
function mysteryLess() {
generateMovies(mystery, "mystery")
}
function animationMore() {
animation.then((data) => {
createBlock(data, "animation", 6);
addSeeMore("animation", "d-none");
addSeeLess("animation", "d-block");
})
}
function animationLess() {
generateMovies(animation, "animation")
}
function otherMore() {
other.then((data) => {
createBlock(data, "other", 6);
addSeeMore("other", "d-none");
addSeeLess("other", "d-block");
})
}
function otherLess() {
generateMovies(other, "other")
}
// ==================== end of functions called by buttons
// responsive management
// generate the movies block depending on screen size
function generateMovies(movieProm, id) {
if (screen.width < 768 ) {
movieProm.then((data) => {
createBlock(data, id, 2);
addSeeMore(id, "d-block");
})
}
if (screen.width === 768 || screen.width > 768 && screen.width < 992) {
movieProm.then((data) => {
createBlock(data, id, 4);
addSeeMore(id, "d-block");
})
}
if (screen.width > 992) {
movieProm.then((data) => {
createBlock(data, id, 6);
addSeeMore(id, "d-none");
})
}
}
// main
// ============ best film ============
// Il Grande Lebowski
// bestFilm("118715")
//bestFilm("133093");
bestFilm("234215");
// Get resources
let bestRated = getMovies3("?sort_by=-imdb_score");
let mystery = getMovies3("?genre=mystery");
let animation = getMovies3("?genre=animation");
let other = getMovies3("?genre=action");
// ============ Best Rated ============
generateMovies(bestRated, "bestRated");
// ============ Mystery ============
generateMovies(mystery, "mystery")
// ============ Animation ============
generateMovies(animation, "animation")
// ============ Other ============
// fill the selection menu
getCategory();
generateMovies(other, "other");
// Make it dynamic, step 8
// listen for any change in menu
let selectedItem = document.getElementById("category-select");
selectedItem.addEventListener("change", ()=> {
let block = getMovies3(`?genre=${selectedItem.value}`);
generateMovies(block, "other");
other = block;
});
// Make it dynamic, step 7
// responsive management
// when the screen changes, regenerate blocks for all
window.addEventListener("resize", ()=> {
generateMovies(bestRated, "bestRated");
generateMovies(mystery, "mystery");
generateMovies(animation, "animation");
generateMovies(other, "other");
})