diff --git a/package.json b/package.json
index 82177c4..f185980 100644
--- a/package.json
+++ b/package.json
@@ -4,7 +4,7 @@
"version": "0.0.0",
"type": "module",
"scripts": {
- "dev": "vite",
+ "dev": "vite --host",
"build": "tsc -b && vite build",
"lint": "eslint .",
"preview": "vite preview",
@@ -12,17 +12,21 @@
},
"dependencies": {
"react": "^19.1.1",
- "react-dom": "^19.1.1"
+ "react-dom": "^19.1.1",
+ "react-router-dom": "^7.9.1"
},
"devDependencies": {
"@eslint/js": "^9.33.0",
"@types/react": "^19.1.10",
"@types/react-dom": "^19.1.7",
"@vitejs/plugin-react-swc": "^4.0.0",
+ "autoprefixer": "^10.4.21",
"eslint": "^9.33.0",
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.3.0",
+ "postcss": "^8.5.6",
+ "tailwindcss": "3.4.17",
"typescript": "~5.8.3",
"typescript-eslint": "^8.39.1",
"vite": "^7.1.2"
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..2e7af2b
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+}
diff --git a/src/App.tsx b/src/App.tsx
index 3d7ded3..6fa3df6 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,35 +1,15 @@
-import { useState } from 'react'
-import reactLogo from './assets/react.svg'
-import viteLogo from '/vite.svg'
-import './App.css'
+import { Routes, Route } from "react-router-dom";
+import Home from "./pages/Home";
+import Layout from "./components/layout/Layout";
function App() {
- const [count, setCount] = useState(0)
-
return (
- <>
-
- Vite + React
-
-
-
- Edit src/App.tsx and save to test HMR
-
-
-
- Click on the Vite and React logos to learn more
-
- >
- )
+
+ }>
+ } />
+
+
+ );
}
-export default App
+export default App;
\ No newline at end of file
diff --git a/src/components/layout/Layout.tsx b/src/components/layout/Layout.tsx
new file mode 100644
index 0000000..4011487
--- /dev/null
+++ b/src/components/layout/Layout.tsx
@@ -0,0 +1,11 @@
+import { Outlet } from "react-router-dom";
+
+export default function Layout() {
+ return (
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/features/youtube/YoutubeForm.tsx b/src/features/youtube/YoutubeForm.tsx
new file mode 100644
index 0000000..3d2a23b
--- /dev/null
+++ b/src/features/youtube/YoutubeForm.tsx
@@ -0,0 +1,144 @@
+import { useState } from "react";
+import { sendYoutubeUrl } from "../../services/apiClient";
+
+interface VideoJob {
+ id: string;
+ url: string;
+ format: string;
+ status: "pending" | "processing" | "done" | "error";
+}
+
+export default function YoutubeForm() {
+ const [url, setUrl] = useState("");
+ const [format, setFormat] = useState("mp3"); // format par défaut
+ const [loading, setLoading] = useState(false);
+ const [message, setMessage] = useState(null);
+ const [jobs, setJobs] = useState([]);
+
+ const handleSubmit = async (e: React.FormEvent) => {
+ e.preventDefault();
+ setLoading(true);
+ setMessage(null);
+
+ try {
+ const res = await sendYoutubeUrl(url, format);
+ const newJob: VideoJob = {
+ id: res.jobId || Date.now().toString(),
+ url,
+ format,
+ status: "processing",
+ };
+ setJobs((prev) => [newJob, ...prev]);
+ setMessage(res.message || "Vidéo envoyée avec succès !");
+ setUrl("");
+ } catch (err: any) {
+ setMessage(err.message || "Erreur lors de l’envoi.");
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const clearHistory = () => {
+ setJobs([]);
+ setMessage("Historique vidé ✅");
+ };
+
+ return (
+
+
+ Youtube To Jellyfin
+
+
+ Collez un lien YouTube ci-dessous et choisissez le format. La vidéo sera
+ téléchargée, convertie et envoyée automatiquement sur votre serveur.
+
+
+
+
+ {message && (
+
{message}
+ )}
+
+ {jobs.length > 0 && (
+
+
+
Vidéos envoyées
+
+
+
+
+ {jobs.map((job) => (
+ -
+
+ {job.url} ({job.format})
+
+
+ {job.status}
+
+
+ ))}
+
+
+ )}
+
+ );
+}
\ No newline at end of file
diff --git a/src/index.css b/src/index.css
deleted file mode 100644
index 08a3ac9..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,68 +0,0 @@
-:root {
- font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
-
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
-
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-a {
- font-weight: 500;
- color: #646cff;
- text-decoration: inherit;
-}
-a:hover {
- color: #535bf2;
-}
-
-body {
- margin: 0;
- display: flex;
- place-items: center;
- min-width: 320px;
- min-height: 100vh;
-}
-
-h1 {
- font-size: 3.2em;
- line-height: 1.1;
-}
-
-button {
- border-radius: 8px;
- border: 1px solid transparent;
- padding: 0.6em 1.2em;
- font-size: 1em;
- font-weight: 500;
- font-family: inherit;
- background-color: #1a1a1a;
- cursor: pointer;
- transition: border-color 0.25s;
-}
-button:hover {
- border-color: #646cff;
-}
-button:focus,
-button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
-}
-
-@media (prefers-color-scheme: light) {
- :root {
- color: #213547;
- background-color: #ffffff;
- }
- a:hover {
- color: #747bff;
- }
- button {
- background-color: #f9f9f9;
- }
-}
diff --git a/src/main.tsx b/src/main.tsx
index bef5202..c3669e1 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -1,10 +1,14 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
-import './index.css'
+
+import { BrowserRouter } from 'react-router-dom'
import App from './App.tsx'
+import "./styles/index.css";
createRoot(document.getElementById('root')!).render(
-
- ,
+
+
+
+
)
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
index e69de29..f4ab315 100644
--- a/src/pages/Home.tsx
+++ b/src/pages/Home.tsx
@@ -0,0 +1,9 @@
+import YoutubeForm from "../features/youtube/YoutubeForm";
+
+export default function Home() {
+ return (
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/pages/NotFound.tsx b/src/pages/NotFound.tsx
deleted file mode 100644
index e69de29..0000000
diff --git a/src/services/apiClient.ts b/src/services/apiClient.ts
new file mode 100644
index 0000000..049d863
--- /dev/null
+++ b/src/services/apiClient.ts
@@ -0,0 +1,25 @@
+export interface ApiResponse {
+ success: boolean;
+ message: string;
+ jobId?: string;
+}
+
+const API_URL = import.meta.env.VITE_API_URL;
+const API_KEY = import.meta.env.VITE_API_KEY;
+
+export async function sendYoutubeUrl(url: string, format: string): Promise {
+ const res = await fetch(`${API_URL}/api/download`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "Authorization": `Bearer ${API_KEY}`,
+ },
+ body: JSON.stringify({ url, format }),
+ });
+
+ if (!res.ok) {
+ throw new Error(`Erreur serveur : ${res.status}`);
+ }
+
+ return res.json();
+}
\ No newline at end of file
diff --git a/src/services/jellyfinApi.ts b/src/services/jellyfinApi.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/src/styles/index.css b/src/styles/index.css
new file mode 100644
index 0000000..9c5f721
--- /dev/null
+++ b/src/styles/index.css
@@ -0,0 +1,25 @@
+/* eslint-disable @tailwindcss/no-custom-at-rules */
+html, body, #root {
+ height: 100%;
+ margin: 0;
+ padding: 0;
+}
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(10px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.animate-fadeIn {
+ animation: fadeIn 0.4s ease-out forwards;
+}
+
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
\ No newline at end of file
diff --git a/src/types/api.ts b/src/types/api.ts
new file mode 100644
index 0000000..40d4136
--- /dev/null
+++ b/src/types/api.ts
@@ -0,0 +1,5 @@
+export interface ApiResponse {
+ success: boolean;
+ message: string;
+ jobId?: string; // si tu veux suivre le téléchargement
+}
\ No newline at end of file
diff --git a/src/types/global.d.ts b/src/types/global.d.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/tailwind.config.js b/tailwind.config.js
new file mode 100644
index 0000000..0f2d896
--- /dev/null
+++ b/tailwind.config.js
@@ -0,0 +1,28 @@
+/** @type {import('tailwindcss').Config} */
+export default {
+ content: [
+ "./index.html",
+ "./src/**/*.{js,ts,jsx,tsx}",
+ ],
+ theme: {
+ extend: {
+ container: {
+ center: true,
+ padding: "1rem",
+ },
+ colors: {
+ background: "#121212",
+ surface: "#1E1E1E",
+ surfaceLight: "#2A2A2A",
+ primary: "#BB86FC",
+ secondary: "#03DAC6",
+ text: {
+ DEFAULT: "#E0E0E0",
+ muted: "#A0A0A0",
+ },
+ },
+
+ },
+ },
+ plugins: [],
+}
\ No newline at end of file