Initial commit
This commit is contained in:
parent
17ec8a0045
commit
aad4686d48
15 changed files with 439 additions and 52 deletions
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="GradleMigrationSettings" migrationVersion="1" />
|
||||
<component name="GradleSettings">
|
||||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ tasks.withType<JavaCompile> {
|
|||
|
||||
application {
|
||||
mainModule.set("dev.mecdu.javafx")
|
||||
mainClass.set("dev.mecdu.javafx.HelloApplication")
|
||||
mainClass.set("dev.mecdu.javafx.HomeApplication")
|
||||
}
|
||||
|
||||
javafx {
|
||||
|
|
@ -40,6 +40,10 @@ dependencies {
|
|||
implementation("org.kordamp.bootstrapfx:bootstrapfx-core:0.4.0")
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
|
||||
implementation("com.squareup.okhttp3:okhttp:+")
|
||||
compileOnly("org.projectlombok:lombok:+")
|
||||
annotationProcessor("org.projectlombok:lombok:1.+")
|
||||
implementation("com.google.code.gson:gson:+")
|
||||
}
|
||||
|
||||
tasks.withType<Test> {
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
package dev.mecdu.javafx;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class HelloApplication extends Application {
|
||||
@Override
|
||||
public void start(Stage stage) throws IOException {
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
|
||||
Scene scene = new Scene(fxmlLoader.load(), 320, 240);
|
||||
stage.setTitle("Hello!");
|
||||
stage.setScene(scene);
|
||||
stage.show();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
package dev.mecdu.javafx;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.control.Label;
|
||||
|
||||
public class HelloController {
|
||||
@FXML
|
||||
private Label welcomeText;
|
||||
|
||||
@FXML
|
||||
protected void onHelloButtonClick() {
|
||||
welcomeText.setText("Welcome to JavaFX Application!");
|
||||
}
|
||||
}
|
||||
30
src/main/java/dev/mecdu/javafx/HomeApplication.java
Normal file
30
src/main/java/dev/mecdu/javafx/HomeApplication.java
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package dev.mecdu.javafx;
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.fxml.FXMLLoader;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public class HomeApplication extends Application {
|
||||
@Override
|
||||
public void start(Stage stage) throws IOException {
|
||||
FXMLLoader fxmlLoader = new FXMLLoader(HomeApplication.class.getResource("home.fxml"));
|
||||
Parent root = fxmlLoader.load();
|
||||
|
||||
// Récupère le contrôleur si tu veux lui passer des infos
|
||||
// HomeController controller = loader.getController();
|
||||
|
||||
Scene scene = new Scene(root, 1000, 700);
|
||||
stage.setTitle("Streamify 🎬");
|
||||
|
||||
stage.getIcons().add(new Image(Objects.requireNonNull(HomeApplication.class.getResourceAsStream("/dev/mecdu/javafx/logo.png"))));
|
||||
|
||||
stage.setScene(scene);
|
||||
stage.show();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,6 @@ import javafx.application.Application;
|
|||
|
||||
public class Launcher {
|
||||
public static void main(String[] args) {
|
||||
Application.launch(HelloApplication.class, args);
|
||||
Application.launch(HomeApplication.class, args);
|
||||
}
|
||||
}
|
||||
51
src/main/java/dev/mecdu/javafx/api/StreamifyApiClient.java
Normal file
51
src/main/java/dev/mecdu/javafx/api/StreamifyApiClient.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package dev.mecdu.javafx.api;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import dev.mecdu.javafx.model.Program;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpRequest;
|
||||
import java.net.http.HttpResponse;
|
||||
import java.util.List;
|
||||
|
||||
public class StreamifyApiClient {
|
||||
|
||||
private static final String BASE_URL = "http://api-stream.apps.lehub.tf/api"; // à adapter selon ton API
|
||||
private final HttpClient httpClient;
|
||||
private final Gson gson;
|
||||
|
||||
public StreamifyApiClient() {
|
||||
this.httpClient = HttpClient.newHttpClient();
|
||||
this.gson = new Gson();
|
||||
}
|
||||
|
||||
public List<Program> getPrograms() throws IOException, InterruptedException {
|
||||
HttpRequest request = HttpRequest.newBuilder()
|
||||
.uri(URI.create(BASE_URL + "/v1/program"))
|
||||
.GET()
|
||||
.build();
|
||||
|
||||
HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString());
|
||||
|
||||
if (response.statusCode() != 200) {
|
||||
throw new IOException("Erreur API Streamify : " + response.statusCode());
|
||||
}
|
||||
|
||||
// parser l'objet JSON
|
||||
JsonObject obj = gson.fromJson(response.body(), JsonObject.class);
|
||||
|
||||
// récupérer le tableau programs
|
||||
JsonElement programsElement = obj.get("programs");
|
||||
|
||||
Type listType = new TypeToken<List<Program>>() {}.getType();
|
||||
List<Program> programs = gson.fromJson(programsElement, listType);
|
||||
|
||||
return programs;
|
||||
}
|
||||
}
|
||||
76
src/main/java/dev/mecdu/javafx/model/Program.java
Normal file
76
src/main/java/dev/mecdu/javafx/model/Program.java
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package dev.mecdu.javafx.model;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data
|
||||
public class Program {
|
||||
private int id;
|
||||
|
||||
@SerializedName("tmdb_id")
|
||||
private int tmdbId;
|
||||
|
||||
private UUID uuid;
|
||||
|
||||
@SerializedName("programs_id")
|
||||
private int programId;
|
||||
|
||||
@SerializedName("fr_title")
|
||||
private String titleFR;
|
||||
|
||||
@SerializedName("original_title")
|
||||
private String title;
|
||||
|
||||
private String types; // movies - tv
|
||||
|
||||
@SerializedName("released_at")
|
||||
private int releasedAt;
|
||||
|
||||
private String genders;
|
||||
|
||||
private String origins;
|
||||
|
||||
private String directors;
|
||||
|
||||
private String actors;
|
||||
|
||||
private String lengths; // 01:44:00
|
||||
|
||||
private String resum;
|
||||
|
||||
@SerializedName("trailer_link")
|
||||
private String trailerLink;
|
||||
|
||||
@SerializedName("portrait_img_link")
|
||||
private String portraitLink;
|
||||
|
||||
@SerializedName("landscape_img_link")
|
||||
private String landscapeLink;
|
||||
|
||||
private String format; // webm - mp4
|
||||
|
||||
private int active;
|
||||
|
||||
@SerializedName("created_at")
|
||||
private String createdAt;
|
||||
|
||||
@SerializedName("updated_at")
|
||||
private String updatedAt;
|
||||
|
||||
public enum Format {
|
||||
mp4,
|
||||
webm
|
||||
}
|
||||
|
||||
public enum Types {
|
||||
movies,
|
||||
tv
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return title; // utile si tu ne mets pas de cellFactory
|
||||
}
|
||||
}
|
||||
8
src/main/java/dev/mecdu/javafx/model/Programs.java
Normal file
8
src/main/java/dev/mecdu/javafx/model/Programs.java
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package dev.mecdu.javafx.model;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class Programs {
|
||||
private Program[] programs;
|
||||
}
|
||||
159
src/main/java/dev/mecdu/javafx/ui/HomeController.java
Normal file
159
src/main/java/dev/mecdu/javafx/ui/HomeController.java
Normal file
|
|
@ -0,0 +1,159 @@
|
|||
package dev.mecdu.javafx.ui;
|
||||
|
||||
import dev.mecdu.javafx.api.StreamifyApiClient;
|
||||
import dev.mecdu.javafx.model.Program;
|
||||
import javafx.application.Platform;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.control.*;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
public class HomeController implements Initializable {
|
||||
|
||||
public VBox proSection;
|
||||
public VBox tvSection;
|
||||
public VBox moviesSection;
|
||||
@FXML
|
||||
private ListView<Program> movieList;
|
||||
|
||||
@FXML
|
||||
private ScrollPane proCarousel;
|
||||
|
||||
@FXML
|
||||
private HBox proContainer;
|
||||
|
||||
@FXML
|
||||
private ScrollPane tvCarousel;
|
||||
|
||||
@FXML
|
||||
private HBox tvContainer;
|
||||
|
||||
@FXML
|
||||
private HBox moviesContainer;
|
||||
|
||||
@FXML
|
||||
private ScrollPane moviesCarousel;
|
||||
|
||||
private final StreamifyApiClient apiClient = new StreamifyApiClient();
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
|
||||
loadTVShows();
|
||||
}
|
||||
|
||||
private void loadTVShows() {
|
||||
new Thread(() -> {
|
||||
try {
|
||||
List<Program> movies = apiClient.getPrograms();
|
||||
|
||||
// filtrer seulement les programs aléatoire et limiter à 10
|
||||
List<Program> randomShows = movies.stream()
|
||||
.filter(p -> "movie".equalsIgnoreCase(p.getTypes()))
|
||||
.filter(p -> p.getActive() != 0)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// mélanger la liste aléatoirement
|
||||
Collections.shuffle(randomShows);
|
||||
|
||||
randomShows = randomShows.stream()
|
||||
.limit(10)
|
||||
.toList();
|
||||
|
||||
// filtrer seulement les prochainements et limiter à 10
|
||||
List<Program> proShows = movies.stream()
|
||||
.filter(p -> p.getActive() == 0)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Collections.reverse(proShows);
|
||||
|
||||
proShows = proShows.stream()
|
||||
.limit(10)
|
||||
.toList();
|
||||
|
||||
// filtrer seulement les séries TV et limiter à 10
|
||||
List<Program> tvShows = movies.stream()
|
||||
.filter(p -> "tv".equalsIgnoreCase(p.getTypes()))
|
||||
.filter(p -> p.getActive() != 0)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Collections.reverse(tvShows);
|
||||
|
||||
tvShows = tvShows.stream()
|
||||
.limit(10)
|
||||
.toList();
|
||||
|
||||
// filtrer seulement les films MOVIES et limiter à 10
|
||||
List<Program> moviesShows = movies.stream()
|
||||
.filter(p -> "movie".equalsIgnoreCase(p.getTypes()))
|
||||
.filter(p -> p.getActive() != 0)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Collections.reverse(moviesShows);
|
||||
|
||||
moviesShows = moviesShows.stream()
|
||||
.limit(10)
|
||||
.toList();
|
||||
|
||||
List<Program> finalRandomShows = randomShows;
|
||||
List<Program> finalProShows = proShows;
|
||||
List<Program> finalTvShows = tvShows;
|
||||
List<Program> finalMoviesShows = moviesShows;
|
||||
|
||||
Platform.runLater(() -> {
|
||||
|
||||
if (finalProShows.isEmpty()) {
|
||||
proSection.setVisible(false);
|
||||
proSection.setManaged(false);
|
||||
} else {
|
||||
proSection.setVisible(true);
|
||||
proSection.setManaged(true);
|
||||
shows(finalProShows, proContainer);
|
||||
}
|
||||
|
||||
shows(finalTvShows, tvContainer);
|
||||
|
||||
shows(finalMoviesShows, moviesContainer);
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void shows(List<Program> finalShows, HBox container) {
|
||||
container.getChildren().clear();
|
||||
for (Program p : finalShows) {
|
||||
VBox card = new VBox();
|
||||
card.setSpacing(5);
|
||||
card.setPrefWidth(150);
|
||||
card.setStyle("-fx-alignment: center;");
|
||||
|
||||
// image
|
||||
ImageView imageView = new ImageView(new Image(p.getPortraitLink(), 150, 200, true, true));
|
||||
// titre
|
||||
Label title = new Label(p.getTitle());
|
||||
title.setMaxHeight(20);
|
||||
title.setWrapText(true);
|
||||
title.setTextOverrun(OverrunStyle.ELLIPSIS);
|
||||
title.setStyle("-fx-font-size: 14px; -fx-font-weight: bold; -fx-text-alignment: center;");
|
||||
|
||||
card.getChildren().addAll(imageView, title);
|
||||
card.getStyleClass().add("card");
|
||||
container.getChildren().add(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -4,7 +4,16 @@ module dev.mecdu.javafx {
|
|||
|
||||
requires org.controlsfx.controls;
|
||||
requires org.kordamp.bootstrapfx.core;
|
||||
requires static lombok;
|
||||
requires com.google.gson;
|
||||
requires java.net.http;
|
||||
requires java.desktop;
|
||||
requires javafx.graphics;
|
||||
|
||||
opens dev.mecdu.javafx to javafx.fxml;
|
||||
exports dev.mecdu.javafx;
|
||||
exports dev.mecdu.javafx.ui;
|
||||
opens dev.mecdu.javafx.ui to javafx.fxml;
|
||||
|
||||
opens dev.mecdu.javafx.model to com.google.gson;
|
||||
}
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.layout.VBox?>
|
||||
|
||||
<?import javafx.scene.control.Button?>
|
||||
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="dev.mecdu.javafx.HelloController">
|
||||
<padding>
|
||||
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
|
||||
</padding>
|
||||
|
||||
<Label fx:id="welcomeText"/>
|
||||
<Button text="Hello!" onAction="#onHelloButtonClick"/>
|
||||
</VBox>
|
||||
14
src/main/resources/dev/mecdu/javafx/home.css
Normal file
14
src/main/resources/dev/mecdu/javafx/home.css
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
.label {
|
||||
-fx-max-height: 40px; /* environ 2 lignes */
|
||||
-fx-text-overrun: ellipsis;
|
||||
}
|
||||
|
||||
.card {
|
||||
-fx-alignment: center;
|
||||
-fx-cursor: hand;
|
||||
}
|
||||
.card:hover {
|
||||
-fx-scale-x: 1.05;
|
||||
-fx-scale-y: 1.05;
|
||||
-fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.6), 10, 0, 0, 0);
|
||||
}
|
||||
84
src/main/resources/dev/mecdu/javafx/home.fxml
Normal file
84
src/main/resources/dev/mecdu/javafx/home.fxml
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
|
||||
<BorderPane xmlns="http://javafx.com/javafx"
|
||||
xmlns:fx="http://javafx.com/fxml"
|
||||
fx:controller="dev.mecdu.javafx.ui.HomeController"
|
||||
style="-fx-background-color: #141414;"
|
||||
prefWidth="1280" prefHeight="900">
|
||||
|
||||
<!-- HEADER FIXE -->
|
||||
<top>
|
||||
<BorderPane style="-fx-background-color: #000000; -fx-padding: 15 30 15 30;">
|
||||
<left>
|
||||
<Label text="STREAMIFY"
|
||||
style="-fx-text-fill: #E50914; -fx-font-size: 28px; -fx-font-weight: bold;"/>
|
||||
</left>
|
||||
<center>
|
||||
<HBox spacing="25" alignment="CENTER">
|
||||
<Label text="Accueil" style="-fx-text-fill: white; -fx-font-size: 16px;"/>
|
||||
<Label text="Séries" style="-fx-text-fill: white; -fx-font-size: 16px;"/>
|
||||
<Label text="Films" style="-fx-text-fill: white; -fx-font-size: 16px;"/>
|
||||
<Label text="Nouveautés" style="-fx-text-fill: white; -fx-font-size: 16px;"/>
|
||||
<Label text="Ma liste" style="-fx-text-fill: white; -fx-font-size: 16px;"/>
|
||||
</HBox>
|
||||
</center>
|
||||
</BorderPane>
|
||||
</top>
|
||||
|
||||
<!-- CONTENU SCROLLABLE -->
|
||||
<center>
|
||||
<ScrollPane fitToWidth="true" hbarPolicy="NEVER" vbarPolicy="AS_NEEDED"
|
||||
style="-fx-background: #ffffff; -fx-border-color: transparent;">
|
||||
<VBox spacing="40" alignment="TOP_CENTER">
|
||||
<padding>
|
||||
<Insets top="20" right="40" bottom="20" left="40"/>
|
||||
</padding>
|
||||
|
||||
<!-- Carrousel Prochainement -->
|
||||
<VBox spacing="10" fx:id="proSection">
|
||||
<Label text="🔜 Prochainement"
|
||||
style="-fx-text-fill: black; -fx-font-size: 22px; -fx-font-weight: bold;"/>
|
||||
<ScrollPane fx:id="proCarousel" hbarPolicy="ALWAYS" vbarPolicy="NEVER"
|
||||
prefHeight="270" fitToHeight="false" fitToWidth="true"
|
||||
style="-fx-background-color: transparent;">
|
||||
<HBox fx:id="proContainer" spacing="10" style="-fx-padding: 10;"/>
|
||||
</ScrollPane>
|
||||
</VBox>
|
||||
|
||||
<!-- Carrousel Séries -->
|
||||
<VBox spacing="10" fx:id="tvSection">
|
||||
<Label text="📺 Séries récentes"
|
||||
style="-fx-text-fill: black; -fx-font-size: 22px; -fx-font-weight: bold;"/>
|
||||
<ScrollPane fx:id="tvCarousel" hbarPolicy="ALWAYS" vbarPolicy="NEVER"
|
||||
prefHeight="270" fitToHeight="false" fitToWidth="true"
|
||||
style="-fx-background-color: transparent;">
|
||||
<HBox fx:id="tvContainer" spacing="10" style="-fx-padding: 10;"/>
|
||||
</ScrollPane>
|
||||
</VBox>
|
||||
|
||||
<!-- Carrousel Films -->
|
||||
<VBox spacing="10" fx:id="moviesSection">
|
||||
<Label text="🎥 Derniers films"
|
||||
style="-fx-text-fill: black; -fx-font-size: 22px; -fx-font-weight: bold;"/>
|
||||
<ScrollPane fx:id="moviesCarousel" hbarPolicy="ALWAYS" vbarPolicy="NEVER"
|
||||
prefHeight="270" fitToHeight="false" fitToWidth="true"
|
||||
style="-fx-background-color: transparent;">
|
||||
<HBox fx:id="moviesContainer" spacing="10" style="-fx-padding: 10;"/>
|
||||
</ScrollPane>
|
||||
</VBox>
|
||||
|
||||
</VBox>
|
||||
</ScrollPane>
|
||||
</center>
|
||||
|
||||
<!-- FOOTER -->
|
||||
<bottom>
|
||||
<HBox alignment="CENTER" style="-fx-background-color: #000000; -fx-padding: 15;">
|
||||
<Label text="© 2025 Streamify — Inspiré par Netflix"
|
||||
style="-fx-text-fill: #888; -fx-font-size: 12px;"/>
|
||||
</HBox>
|
||||
</bottom>
|
||||
</BorderPane>
|
||||
BIN
src/main/resources/dev/mecdu/javafx/logo.png
Normal file
BIN
src/main/resources/dev/mecdu/javafx/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 60 KiB |
Loading…
Add table
Reference in a new issue