This commit is contained in:
cangui 2025-06-21 19:04:12 +02:00
parent 7f332aa88b
commit b1866a2cc5
3 changed files with 81 additions and 12 deletions

View File

@ -228,6 +228,7 @@ func RoutesProtected(r *mux.Router, bd *gorm.DB) {
r.HandleFunc("/api/paths/{id:[0-9]+}/media", renders.PathMedia(bd)).Methods("GET") 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("/stream/{partID:[0-9]+}", renders.Stream(bd)).Methods("GET")
r.HandleFunc("/media/{partID:[0-9]+}", renders.MediaDetail(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")
//API Scan folder //API Scan folder

View File

@ -985,7 +985,7 @@ type mediaDetailView struct {
Summary string Summary string
DurationFmt string DurationFmt string
ThumbURL string ThumbURL string
StreamURL string HLSURL string // ajouté
} }
// MediaDetail affiche la page détail + player // MediaDetail affiche la page détail + player
@ -1024,7 +1024,7 @@ func MediaDetail(db *gorm.DB) http.HandlerFunc {
Summary: item.Summary, Summary: item.Summary,
DurationFmt: strconv.FormatInt(m, 10) + ":" + fmt.Sprintf("%02d", s), DurationFmt: strconv.FormatInt(m, 10) + ":" + fmt.Sprintf("%02d", s),
ThumbURL: item.UserThumbURL, ThumbURL: item.UserThumbURL,
StreamURL: "/stream/" + strconv.FormatInt(item.MediaPartID, 10), HLSURL: fmt.Sprintf("/hls/%d/index.m3u8", partID),
} }
} else { } else {
@ -1098,6 +1098,49 @@ func Stream(db *gorm.DB) http.HandlerFunc {
} }
} }
// renders/media.go (ajoutez cette fonction)
func HLSStream(db *gorm.DB) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// 1) identifier le média
partID, _ := strconv.ParseInt(mux.Vars(r)["partID"], 10, 64)
var part models.MediaPart
if err := db.First(&part, partID).Error; err != nil {
http.Error(w, "Média introuvable", http.StatusNotFound)
return
}
// 2) dossier temporaire / cache
tmpDir := filepath.Join(os.TempDir(), fmt.Sprintf("hls_%d", partID))
os.MkdirAll(tmpDir, 0755)
playlist := filepath.Join(tmpDir, "index.m3u8")
// 3) si besoin, (re)générer lHLS via ffmpeg
if _, err := os.Stat(playlist); os.IsNotExist(err) {
// ffmpeg -i input -c:v copy -c:a copy -f hls -hls_time 4 -hls_list_size 0 tmp/index.m3u8
cmd := exec.CommandContext(r.Context(),
"ffmpeg",
"-i", part.File,
"-c:v", "copy", "-c:a", "copy",
"-f", "hls",
"-hls_time", "4",
"-hls_list_size", "0",
"-hls_segment_filename", filepath.Join(tmpDir, "seg%d.ts"),
playlist,
)
if out, err := cmd.CombinedOutput(); err != nil {
log.Println("ffmpeg HLS error:", err, string(out))
http.Error(w, "Erreur de transcodage", 500)
return
}
}
// 4) servir le dossier HLS en statique
http.StripPrefix(
fmt.Sprintf("/hls/%d/", partID),
http.FileServer(http.Dir(tmpDir)),
).ServeHTTP(w, r)
}
}

View File

@ -1,14 +1,39 @@
<div class="detail p-4"> <div class="detail p-4">
<h1 class="text-2xl font-bold mb-4">{{.item.Title}}</h1> <h1 class="text-2xl font-bold mb-4">{{.item.Title}}</h1>
{{with .item.Summary}} {{with .item.Summary}}<p class="mb-4">{{.}}</p>{{end}}
<p class="mb-4">{{.}}</p> {{with .item.DurationFmt}}<small class="text-gray-500">{{.}}</small>{{end}}
{{end}}
{{with .item.DurationFmt}}
<small class="text-gray-500">{{.}}</small>
{{end}}
<img src="{{.item.ThumbURL}}" alt="{{.item.Title}}" class="cover mb-4 rounded shadow" /> <img src="{{.item.ThumbURL}}" alt="{{.item.Title}}" class="cover mb-4 rounded shadow" />
<video controls autoplay width="100%" class="rounded shadow">
<source src="{{.item.StreamURL}}" type="video/mp4"> <!-- 1) Le bouton Play -->
Votre navigateur ne supporte pas la vidéo. <button
</video> id="play-btn"
class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>▶ Play</button>
<!-- 2) Container vide pour le player -->
<div id="player-container" class="mt-4"></div>
<script src="https://unpkg.com/hls.js@1.4.0"></script>
<script>
document.getElementById('play-btn').addEventListener('click', function(){
const url = "{{.item.HLSURL}}";
const container = document.getElementById('player-container');
// injecte la balise video
container.innerHTML = '<video id="video-player" controls autoplay width="100%" class="rounded shadow"></video>';
const video = document.getElementById('video-player');
// si hls.js est supporté, on lutilise
if (Hls.isSupported()) {
const hls = new Hls();
hls.loadSource(url);
hls.attachMedia(video);
} else {
// sinon on pose directement la source (Chrome & Safari gèrent nativement HLS)
video.src = url;
}
// cache le bouton
this.style.display = 'none';
});
</script>
</div> </div>