diff --git a/albums.php b/albums.php
new file mode 100644
index 0000000..80449b5
--- /dev/null
+++ b/albums.php
@@ -0,0 +1,100 @@
+isDot()) continue;
+ if ($item->isDir()) {
+ $albumPath = $item->getPathname();
+ $info = getAlbumInfo($albumPath);
+
+ $images = hasSubfolders($albumPath) ?
+ getImagesRecursively($albumPath) :
+ getLatestImages($albumPath);
+
+ $albums[] = [
+ 'path' => str_replace('\\', '/', $albumPath),
+ 'title' => $info['title'],
+ 'description' => $info['description'],
+ 'images' => $images,
+ 'hasSubfolders' => hasSubfolders($albumPath),
+ 'hasImages' => hasImages($albumPath)
+ ];
+ }
+}
+
+// Déterminer le chemin parent pour le bouton retour
+$parentPath = dirname($currentPath);
+if (!isSecurePath($parentPath)) {
+ $parentPath = null;
+}
+?>
+
+
+
+
+ Albums - ICO
+
+
+
+
+
+ Retour
+
+ Retour
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/favicon.png b/favicon.png
new file mode 100644
index 0000000..d7a4106
Binary files /dev/null and b/favicon.png differ
diff --git a/fonctions.php b/fonctions.php
new file mode 100644
index 0000000..0d4704c
--- /dev/null
+++ b/fonctions.php
@@ -0,0 +1,204 @@
+ basename($albumPath),
+ 'description' => ''
+ ];
+
+ if (file_exists($infoFile)) {
+ $content = file_get_contents($infoFile);
+ $lines = explode("\n", $content);
+ if (isset($lines[0])) $info['title'] = trim($lines[0]);
+ if (isset($lines[1])) $info['description'] = trim($lines[1]);
+ }
+
+ return $info;
+}
+
+/**
+ * Récupère les dernières images d'un dossier
+ * @param string $albumPath Chemin vers l'album
+ * @param int $limit Nombre d'images à récupérer
+ * @return array Tableau des URLs des images
+ */
+function getLatestImages($albumPath, $limit = 4) {
+ $images = [];
+ $baseUrl = getBaseUrl();
+
+ if (!is_dir($albumPath)) return $images;
+
+ foreach (new DirectoryIterator($albumPath) as $file) {
+ if ($file->isDot()) continue;
+ if ($file->isFile()) {
+ $extension = strtolower($file->getExtension());
+ if (in_array($extension, ALLOWED_EXTENSIONS)) {
+ $relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
+ $images[] = $baseUrl . '/' . ltrim($relativePath, '/');
+ }
+ }
+ }
+
+ usort($images, function($a, $b) {
+ $pathA = realpath('.') . str_replace(getBaseUrl(), '', $a);
+ $pathB = realpath('.') . str_replace(getBaseUrl(), '', $b);
+ return filectime($pathB) - filectime($pathA);
+ });
+
+ return array_slice($images, 0, $limit);
+}
+
+/**
+ * Récupère les images de manière récursive dans tous les sous-dossiers
+ * @param string $albumPath Chemin vers l'album
+ * @param int $limit Nombre d'images à récupérer
+ * @return array Tableau des URLs des images
+ */
+function getImagesRecursively($albumPath, $limit = 4) {
+ $images = [];
+ $baseUrl = getBaseUrl();
+
+ if (!is_dir($albumPath)) return $images;
+
+ $iterator = new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator($albumPath),
+ RecursiveIteratorIterator::SELF_FIRST
+ );
+
+ foreach ($iterator as $file) {
+ if ($file->isFile()) {
+ $extension = strtolower($file->getExtension());
+ if (in_array($extension, ALLOWED_EXTENSIONS)) {
+ $relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
+ $images[] = $baseUrl . '/' . ltrim($relativePath, '/');
+ }
+ }
+ }
+
+ usort($images, function($a, $b) {
+ $pathA = realpath('.') . str_replace(getBaseUrl(), '', $a);
+ $pathB = realpath('.') . str_replace(getBaseUrl(), '', $b);
+ return filectime($pathB) - filectime($pathA);
+ });
+
+ return array_slice($images, 0, $limit);
+}
+
+/**
+ * Vérifie si un dossier contient des sous-dossiers
+ * @param string $path Chemin du dossier
+ * @return bool True si le dossier contient des sous-dossiers
+ */
+function hasSubfolders($path) {
+ if (!is_dir($path)) return false;
+
+ foreach (new DirectoryIterator($path) as $item) {
+ if ($item->isDot()) continue;
+ if ($item->isDir()) return true;
+ }
+ return false;
+}
+
+/**
+ * Vérifie si un dossier contient des images
+ * @param string $path Chemin du dossier
+ * @return bool True si le dossier contient des images
+ */
+function hasImages($path) {
+ if (!is_dir($path)) return false;
+
+ foreach (new DirectoryIterator($path) as $item) {
+ if ($item->isDot()) continue;
+ if ($item->isFile()) {
+ $extension = strtolower($item->getExtension());
+ if (in_array($extension, ALLOWED_EXTENSIONS)) return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * Vérifie si le chemin est sécurisé (dans le dossier liste_albums)
+ * @param string $path Chemin à vérifier
+ * @return bool True si le chemin est sécurisé
+ */
+function isSecurePath($path) {
+ $realPath = realpath($path);
+ $rootPath = realpath('./liste_albums');
+ return $realPath && strpos($realPath, $rootPath) === 0;
+}
+
+/**
+ * Formate la taille d'un fichier en format lisible
+ * @param int $bytes Taille en octets
+ * @return string Taille formatée (ex: "1.2 MB")
+ */
+function formatFileSize($bytes) {
+ $units = ['B', 'KB', 'MB', 'GB', 'TB'];
+ $bytes = max($bytes, 0);
+ $pow = floor(($bytes ? log($bytes) : 0) / log(1024));
+ $pow = min($pow, count($units) - 1);
+ $bytes /= pow(1024, $pow);
+ return round($bytes, 1) . ' ' . $units[$pow];
+}
+
+/**
+ * Récupère les dimensions d'une image de manière sécurisée
+ * @param string $path Chemin vers l'image
+ * @return array|false Tableau contenant width et height ou false en cas d'erreur
+ */
+function getSecureImageSize($path) {
+ try {
+ if (!file_exists($path)) return false;
+ $imageInfo = getimagesize($path);
+ if ($imageInfo === false) return false;
+ return [
+ 'width' => $imageInfo[0],
+ 'height' => $imageInfo[1]
+ ];
+ } catch (Exception $e) {
+ return false;
+ }
+}
+
+/**
+ * Génère un identifiant unique sécurisé
+ * @param int $length Longueur de l'identifiant
+ * @return string Identifiant unique
+ */
+function generateSecureId($length = 32) {
+ return bin2hex(random_bytes($length / 2));
+}
+
+/**
+ * Nettoie et sécurise un nom de fichier
+ * @param string $filename Nom du fichier à nettoyer
+ * @return string Nom de fichier sécurisé
+ */
+function sanitizeFilename($filename) {
+ // Supprime les caractères spéciaux
+ $filename = preg_replace('/[^a-zA-Z0-9._-]/', '-', $filename);
+ // Évite les noms de fichiers commençant par un point
+ $filename = ltrim($filename, '.');
+ // Limite la longueur du nom de fichier
+ return substr($filename, 0, 255);
+}
+?>
\ No newline at end of file
diff --git a/galeries.php b/galeries.php
new file mode 100644
index 0000000..188ba13
--- /dev/null
+++ b/galeries.php
@@ -0,0 +1,110 @@
+isDot()) continue;
+ if ($file->isFile()) {
+ $extension = strtolower($file->getExtension());
+ if (in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
+ $relativePath = str_replace('\\', '/', substr($file->getPathname(), strlen(realpath('./'))));
+ $url = $baseUrl . '/' . ltrim($relativePath, '/');
+ $images[] = $url;
+ }
+ }
+}
+
+usort($images, function($a, $b) {
+ $pathA = realpath('.') . str_replace(getBaseUrl(), '', $a);
+ $pathB = realpath('.') . str_replace(getBaseUrl(), '', $b);
+ return filectime($pathB) - filectime($pathA);
+});
+
+$headerImage = !empty($images) ? $images[0] : null;
+
+$parentPath = dirname($currentPath);
+if (!isSecurePath($parentPath)) {
+ $parentPath = './liste_albums';
+}
+?>
+
+
+
+
+ - ICO
+
+
+
+
+ Retour
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/index.php b/index.php
new file mode 100644
index 0000000..c2c4d82
--- /dev/null
+++ b/index.php
@@ -0,0 +1,79 @@
+isFile()) {
+ $extension = strtolower($file->getExtension());
+ if (in_array($extension, ['jpg', 'jpeg', 'png', 'gif'])) {
+ $images[] = str_replace('\\', '/', $file->getPathname());
+ }
+ }
+ }
+
+ usort($images, function($a, $b) {
+ return filectime($b) - filectime($a);
+ });
+
+ return array_slice($images, 0, $limit);
+}
+
+$latestImages = getLatestImages();
+?>
+
+
+
+
+
+
+ ICO - Galerie d'images
+
+
+
+
+
+ $image): ?>
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/liste_albums/infos.txt b/liste_albums/infos.txt
new file mode 100644
index 0000000..22a4746
--- /dev/null
+++ b/liste_albums/infos.txt
@@ -0,0 +1,2 @@
+Titre
+Description sur la seconde ligne.
\ No newline at end of file
diff --git a/partage.php b/partage.php
new file mode 100644
index 0000000..fd5daff
--- /dev/null
+++ b/partage.php
@@ -0,0 +1,200 @@
+
+
+
+
+
+
+
+ Image - ICO
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Partager l'image - ICO
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..1b85ee9
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,460 @@
+/* Accessibilité */
+@media (prefers-reduced-motion: reduce) {
+ * {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ scroll-behavior: auto !important;
+ }
+}/* Reset et styles de base */
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: 'Segoe UI', system-ui, sans-serif;
+ background-color: #121212;
+ color: #ffffff;
+ min-height: 100vh;
+ overflow-x: hidden;
+ position: relative;
+}
+
+/* Styles de la page d'accueil */
+.carousel {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100vh;
+ z-index: 1;
+}
+
+.carousel-slide {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+ transition: opacity 1s ease-in-out;
+ pointer-events: none;
+}
+
+.carousel-slide.active {
+ opacity: 1;
+ z-index: 2;
+}
+
+.carousel-slide img {
+ width: 100%;
+ height: 100vh;
+ object-fit: cover;
+ filter: brightness(0.6);
+}
+
+/* Overlay contenu */
+.overlay {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+ z-index: 20;
+ width: 90%;
+ max-width: 600px;
+ padding: 2rem;
+ background-color: rgba(0, 0, 0, 0.7);
+ border-radius: 1rem;
+ backdrop-filter: blur(10px);
+}
+
+h1 {
+ font-size: 4rem;
+ margin-bottom: 1rem;
+ font-weight: 700;
+ letter-spacing: 0.2rem;
+}
+
+p {
+ font-size: 1.2rem;
+ margin-bottom: 2rem;
+ line-height: 1.6;
+ color: #e0e0e0;
+}
+
+.cta-button {
+ display: inline-block;
+ padding: 1rem 2rem;
+ background-color: #2196f3;
+ color: white;
+ text-decoration: none;
+ border-radius: 2rem;
+ font-size: 1.1rem;
+ font-weight: 500;
+ transition: all 0.3s ease;
+ border: 2px solid transparent;
+}
+
+.cta-button:hover {
+ background-color: #1976d2;
+ transform: translateY(-2px);
+ box-shadow: 0 4px 12px rgba(33, 150, 243, 0.3);
+}
+
+/* Styles pour la page des albums */
+.albums-page {
+ background-color: #121212;
+ min-height: 100vh;
+ padding: 2rem;
+ overflow-y: auto;
+}
+
+.albums-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
+ gap: 2rem;
+ max-width: 1800px;
+ margin: 4rem auto 2rem auto;
+}
+
+.album-card {
+ background-color: #1e1e1e;
+ border-radius: 1rem;
+ overflow: hidden;
+ text-decoration: none;
+ color: white;
+ opacity: 0;
+ transform: translateY(20px);
+ transition: opacity 0.5s ease, transform 0.5s ease;
+}
+
+.album-card.visible {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.album-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
+}
+
+.album-images {
+ position: relative;
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ grid-template-rows: repeat(2, 1fr);
+ aspect-ratio: 1;
+ gap: 2px;
+ background-color: #2a2a2a;
+}
+
+.album-image {
+ width: 100%;
+ height: 100%;
+ background-size: cover;
+ background-position: center;
+ background-repeat: no-repeat;
+}
+
+.empty-image {
+ width: 100%;
+ height: 100%;
+ background-color: #2a2a2a;
+}
+
+.empty-album {
+ width: 100%;
+ height: 100%;
+ background-color: #2a2a2a;
+ grid-column: 1 / -1;
+ grid-row: 1 / -1;
+}
+
+.album-info {
+ padding: 1rem;
+}
+
+.album-info h2 {
+ font-size: 1.2rem;
+ margin: 0;
+ font-weight: 500;
+}
+
+/* Styles pour la page galerie */
+.gallery-page {
+ background-color: #121212;
+ min-height: 100vh;
+ padding: 0;
+ margin: 0;
+}
+
+.gallery-header {
+ width: 100%;
+ height: 500px;
+ overflow: hidden;
+ position: relative;
+}
+
+.header-image {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.gallery-info {
+ padding: 2rem;
+ max-width: 1800px;
+ margin: 0 auto;
+}
+
+.gallery-grid {
+ padding: 2rem;
+ max-width: 1800px;
+ margin: 0 auto;
+}
+
+.gallery-grid.is-loading .gallery-item {
+ opacity: 0;
+}
+
+.gallery-grid .gallery-item {
+ opacity: 1;
+ transition: opacity 0.3s ease;
+}
+
+.gallery-item {
+ width: calc((100% - (7 * 20px)) / 8);
+ min-width: 200px;
+ margin-bottom: 20px;
+ border-radius: 8px;
+ overflow: hidden;
+ background-color: #1e1e1e;
+ transition: transform 0.3s ease;
+}
+
+.gallery-item:hover {
+ transform: translateY(-5px);
+}
+
+.gallery-item img {
+ width: 100%;
+ height: auto;
+ min-height: 150px;
+ object-fit: cover;
+ display: block;
+}
+
+/* Bouton retour commun */
+.back-button {
+ position: fixed;
+ top: 2rem;
+ left: 2rem;
+ padding: 0.8rem 1.5rem;
+ background-color: rgba(0, 0, 0, 0.7);
+ color: white;
+ text-decoration: none;
+ border-radius: 2rem;
+ backdrop-filter: blur(10px);
+ z-index: 100;
+ transition: all 0.3s ease;
+}
+
+.back-button:hover {
+ background-color: rgba(0, 0, 0, 0.9);
+ transform: translateY(-2px);
+}
+
+/* Media Queries */
+@media (min-width: 1400px) {
+ .albums-grid {
+ grid-template-columns: repeat(5, 1fr);
+ }
+}
+
+@media (max-width: 1600px) {
+ .gallery-item {
+ width: calc((100% - (5 * 20px)) / 6);
+ }
+}
+
+@media (max-width: 1200px) {
+ .albums-grid {
+ grid-template-columns: repeat(4, 1fr);
+ }
+ .gallery-item {
+ width: calc((100% - (3 * 20px)) / 5);
+ }
+}
+
+@media (max-width: 992px) {
+ .albums-grid {
+ grid-template-columns: repeat(3, 1fr);
+ }
+ .gallery-item {
+ width: calc((100% - (3 * 20px)) / 4);
+ }
+}
+
+@media (max-width: 768px) {
+ .albums-grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 1rem;
+ }
+
+ .albums-page {
+ padding: 1rem;
+ }
+
+ .gallery-item {
+ width: calc((100% - (2 * 20px)) / 3);
+ }
+
+ .gallery-header {
+ height: 300px;
+ }
+
+ .gallery-info {
+ padding: 1.5rem;
+ }
+
+ .gallery-info h1 {
+ font-size: 2rem;
+ }
+
+ h1 {
+ font-size: 3rem;
+ }
+
+ p {
+ font-size: 1rem;
+ }
+
+ .cta-button {
+ padding: 0.8rem 1.6rem;
+ font-size: 1rem;
+ }
+}
+
+@media (max-width: 480px) {
+ .albums-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .gallery-item {
+ width: calc((100% - 20px) / 2);
+ }
+
+ .gallery-grid {
+ padding: 0.5rem;
+ }
+
+ .gallery-header {
+ height: 200px;
+ }
+
+ .back-button {
+ top: 1rem;
+ left: 1rem;
+ padding: 0.6rem 1.2rem;
+ font-size: 0.9rem;
+ }
+}
+
+.share-page {
+ background-color: rgba(0, 0, 0, 0.9);
+ min-height: 100vh;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.share-container {
+ max-width: 90vw;
+ max-height: 90vh;
+ position: relative;
+}
+
+.share-image {
+ border-radius: 8px;
+ overflow: hidden;
+ background-color: #1e1e1e;
+ box-shadow: 0 4px 24px rgba(0, 0, 0, 0.5);
+}
+
+.share-image img {
+ max-width: 100%;
+ max-height: 90vh;
+ display: block;
+ object-fit: contain;
+}
+
+.share-actions {
+ position: fixed;
+ bottom: 2rem;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
+ gap: 1rem;
+ background-color: rgba(0, 0, 0, 0.8);
+ padding: 1rem;
+ border-radius: 2rem;
+ backdrop-filter: blur(10px);
+}
+
+/* Ajuster .action-button pour la page de partage */
+.share-actions .action-button {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.5rem;
+ padding: 0.8rem 1.5rem;
+ background-color: #2196f3;
+ color: white;
+ border: none;
+ border-radius: 2rem;
+ cursor: pointer;
+ font-size: 1rem;
+ text-decoration: none;
+ transition: all 0.3s ease;
+}
+
+.share-actions .action-button:hover {
+ background-color: #1976d2;
+ transform: translateY(-2px);
+}
+
+.share-actions .action-button .icon {
+ width: 1.2em;
+ height: 1.2em;
+}
+
+/* Protection par mot de passe */
+.protected-indicator {
+ display: inline-block;
+ margin-left: 0.5rem;
+ font-size: 0.8em;
+ vertical-align: middle;
+}
+
+/* Media queries pour la page de partage */
+@media (max-width: 768px) {
+ .share-actions {
+ flex-direction: column;
+ padding: 0.8rem;
+ }
+
+ .share-actions .action-button {
+ padding: 0.6rem 1.2rem;
+ font-size: 0.9rem;
+ }
+}
+@media (prefers-reduced-motion: reduce) {
+ * {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
+ scroll-behavior: auto !important;
+ }
+}
\ No newline at end of file