diff --git a/renders/renders.go b/renders/renders.go index cefb1e8..8ab83dd 100644 --- a/renders/renders.go +++ b/renders/renders.go @@ -980,33 +980,90 @@ func PathMedia(db *gorm.DB) http.HandlerFunc { }) } } - +type mediaDetailView struct { + Title string + Summary string + DurationFmt string + ThumbURL string + StreamURL string +} // MediaDetail affiche la page détail + player func MediaDetail(db *gorm.DB) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { + // 1) Extraire le partID partID, _ := strconv.ParseInt(mux.Vars(r)["partID"], 10, 64) - // on récupère 1 metadata + file pour ce media_part - var item struct { - models.MetadataItem - MediaPartID int64 - File string - } - db.Table("metadata_items"). - Select("metadata_items.*, media_parts.id AS media_part_id, media_parts.file"). - Joins("JOIN media_items ON media_items.metadata_item_id = metadata_items.id"). - Joins("JOIN media_parts ON media_parts.media_item_id = media_items.id"). - Where("media_parts.id = ?", partID). - Scan(&item) + var view mediaDetailView - if item.MediaPartID == 0 { - http.Error(w, "Média introuvable", http.StatusNotFound) - return + if partID > 0 { + // --- CAS BDD --- + var item struct { + models.MetadataItem + MediaPartID int64 + File string + UserThumbURL string + } + db.Table("metadata_items"). + Select("metadata_items.*, media_parts.id AS media_part_id, media_parts.file, metadata_items.user_thumb_url"). + Joins("JOIN media_items ON media_items.metadata_item_id = metadata_items.id"). + Joins("JOIN media_parts ON media_parts.media_item_id = media_items.id"). + Where("media_parts.id = ?", partID). + Scan(&item) + + if item.MediaPartID == 0 { + http.Error(w, "Média introuvable", http.StatusNotFound) + return + } + + // formater la durée + m := item.Duration / 60 + s := item.Duration % 60 + view = mediaDetailView{ + Title: item.Title, + Summary: item.Summary, + DurationFmt: strconv.FormatInt(m, 10) + ":" + fmt.Sprintf("%02d", s), + ThumbURL: item.UserThumbURL, + StreamURL: "/stream/" + strconv.FormatInt(item.MediaPartID, 10), + } + + } else { + // --- CAS FS-ONLY --- + path := r.URL.Query().Get("path") + if path == "" { + http.Error(w, "Média introuvable", http.StatusNotFound) + return + } + + // titre + title := filepath.Base(path) + + // génère un thumbnail si besoin + ext := filepath.Ext(path) + base := strings.TrimSuffix(filepath.Base(path), ext) + thumbDir := filepath.Join("static", "thumbs") + os.MkdirAll(thumbDir, 0755) + thumbPath := filepath.Join(thumbDir, base+".jpg") + if _, err := os.Stat(thumbPath); os.IsNotExist(err) { + // screenshot au 5s + exec.CommandContext(r.Context(), + "ffmpeg", "-ss", "5", "-i", path, "-frames:v", "1", thumbPath, + ).Run() + } + + view = mediaDetailView{ + Title: title, + Summary: "", // pas de résumé en FS-only + DurationFmt: "", // on ne probe pas ici + ThumbURL: "/static/thumbs/" + base + ".jpg", + // on passe le path en query pour le streaming + StreamURL: "/stream/0?path=" + url.QueryEscape(path), + } } - renderTemplate(w, "media_detail", map[string]interface{}{ - "item": item, + // 3) Render partial dans #content + renderPartial(w, "media_detail", map[string]interface{}{ + "item": view, }) } } diff --git a/templates/media_detail.pages.tmpl b/templates/media_detail.pages.tmpl index 24375a9..b515c3c 100644 --- a/templates/media_detail.pages.tmpl +++ b/templates/media_detail.pages.tmpl @@ -1,9 +1,14 @@ -
- {{.item.Title}} -

{{.item.Title}}

- {{with .item.Summary}}

{{.}}

{{end}} -