up
This commit is contained in:
parent
12e9c11536
commit
6aebad2db7
@ -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
|
||||
|
||||
}
|
||||
|
||||
@ -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 d’un 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 {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user