This commit is contained in:
cangui 2025-06-21 19:55:53 +02:00
parent 12e9c11536
commit 6aebad2db7
2 changed files with 41 additions and 35 deletions

View File

@ -229,7 +229,7 @@ r.HandleFunc("/api/paths/{id:[0-9]+}/media", renders.PathMedia(bd)).Methods("GET
r.HandleFunc("/stream/{partID:[0-9]+}", renders.Stream(bd)).Methods("GET")
r.HandleFunc("/media/{partID:[0-9]+}", renders.MediaDetail(bd)).Methods("GET")
r.HandleFunc("/hls/{partID:[0-9]+}/{file}", renders.HLSStream(bd)).Methods("GET")
r.HandleFunc("/hls/{partID:[0-9]+}/", renders.HLSStream(bd)).Methods("GET")
//API Scan folder
}

View File

@ -988,10 +988,11 @@ type mediaDetailView struct {
HLSURL string // ajouté
}
// MediaDetail affiche la page détail + player
// MediaDetail renvoie la partial HTML du détail dun média
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)
var view mediaDetailView
@ -1016,15 +1017,16 @@ func MediaDetail(db *gorm.DB) http.HandlerFunc {
return
}
// formater la durée
// formatage 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),
DurationFmt: fmt.Sprintf("%d:%02d", m, s),
ThumbURL: item.UserThumbURL,
HLSURL: fmt.Sprintf("/hls/%d/index.m3u8", partID),
HLSURL: fmt.Sprintf("/hls/%d/index.m3u8", item.MediaPartID),
}
} else {
@ -1035,39 +1037,37 @@ func MediaDetail(db *gorm.DB) http.HandlerFunc {
return
}
// titre
// base name et thumbnail
title := filepath.Base(path)
// génère un thumbnail si besoin
ext := filepath.Ext(path)
base := strings.TrimSuffix(filepath.Base(path), ext)
base := strings.TrimSuffix(title, 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(),
// capture au 5s
exec.CommandContext(context.Background(),
"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
Summary: "",
DurationFmt: "",
ThumbURL: "/static/thumbs/" + base + ".jpg",
// on passe le path en query pour le streaming
HLSURL: "/hls/0/index.m3u8?path=" + url.QueryEscape(path),
// **ici** on passe le path en query pour que HLSStream sache où chercher
HLSURL: fmt.Sprintf("/hls/0/index.m3u8?path=%s", url.QueryEscape(path)),
}
}
// 3) Render partial dans #content
renderPartial(w, "media_detail", map[string]interface{}{
"item": view,
})
}
}
// Stream : transcode à la volée en MP4 progressif et pipe directement dans la réponse
func Stream(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
@ -1099,13 +1099,18 @@ func Stream(db *gorm.DB) http.HandlerFunc {
}
// renders/media.go (ajoutez cette fonction)
// rend le HLS pour BDD (partID>0) et FS-only (partID==0)
func HLSStream(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
partID, _ := strconv.ParseInt(vars["partID"], 10, 64)
// 1) identifier le partID et le dossier temporaire
partID, _ := strconv.ParseInt(mux.Vars(r)["partID"], 10, 64)
tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("hls_%d", partID))
playlist := filepath.Join(tmpDir, "index.m3u8")
// Déterminer le fichier source
// 2) déterminer s'il faut (re)générer
needGen := false
var filePath string
if partID > 0 {
// cas BDD
var part models.MediaPart
@ -1114,22 +1119,22 @@ func HLSStream(db *gorm.DB) http.HandlerFunc {
return
}
filePath = part.File
needGen = true
} else {
// cas FS-only
// cas FS-only : on génère seulement si playlist manquante
if _, err := os.Stat(playlist); os.IsNotExist(err) {
filePath = r.URL.Query().Get("path")
if filePath == "" {
http.Error(w, "Média introuvable", http.StatusNotFound)
return
}
needGen = true
}
}
// Préparer le dossier de cache HLS
tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("hls_%d", partID))
// 3) (Re)générer le HLS si besoin
if needGen {
os.MkdirAll(tmpDir, 0755)
playlist := filepath.Join(tmpDir, "index.m3u8")
// (Re)générer si nécessaire
if _, err := os.Stat(playlist); os.IsNotExist(err) {
cmd := exec.CommandContext(r.Context(),
"ffmpeg",
"-i", filePath,
@ -1147,7 +1152,7 @@ func HLSStream(db *gorm.DB) http.HandlerFunc {
}
}
// Servir le dossier HLS
// 4) servir **tout** tmpDir sous /hls/{partID}/…
prefix := fmt.Sprintf("/hls/%d/", partID)
http.StripPrefix(prefix,
http.FileServer(http.Dir(tmpDir)),
@ -1159,6 +1164,7 @@ func HLSStream(db *gorm.DB) http.HandlerFunc {
func renderPartial(w http.ResponseWriter, templ string, data map[string]interface{}) {
// Exécute directement le define `<templ>.pages.tmpl`
if err := templates.ExecuteTemplate(w, templ+".pages.tmpl", data); err != nil {