64 lines
1.6 KiB
Go
64 lines
1.6 KiB
Go
package playback
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
)
|
|
|
|
// MimeFromExtension returns a MIME type based on the file extension.
|
|
// Falls back to "application/octet-stream" for unknown extensions.
|
|
func MimeFromExtension(name string) string {
|
|
ext := strings.ToLower(filepath.Ext(name))
|
|
switch ext {
|
|
case ".mp4", ".m4v":
|
|
return "video/mp4"
|
|
case ".mkv":
|
|
return "video/x-matroska"
|
|
case ".webm":
|
|
return "video/webm"
|
|
case ".avi":
|
|
return "video/x-msvideo"
|
|
case ".mov":
|
|
return "video/quicktime"
|
|
case ".ts":
|
|
return "video/mp2t"
|
|
case ".flv":
|
|
return "video/x-flv"
|
|
case ".wmv":
|
|
return "video/x-ms-wmv"
|
|
default:
|
|
return "application/octet-stream"
|
|
}
|
|
}
|
|
|
|
// ServeDirectPlay serves a media file with HTTP byte-range support.
|
|
// Uses http.ServeContent for proper range handling, which supports
|
|
// Range requests, conditional requests (If-Modified-Since, If-None-Match),
|
|
// and Content-Type detection.
|
|
func ServeDirectPlay(w http.ResponseWriter, r *http.Request, filePath string) error {
|
|
f, err := os.Open(filePath)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
http.Error(w, "file not found", http.StatusNotFound)
|
|
return err
|
|
}
|
|
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
return err
|
|
}
|
|
defer f.Close()
|
|
|
|
stat, err := f.Stat()
|
|
if err != nil {
|
|
http.Error(w, "internal server error", http.StatusInternalServerError)
|
|
return err
|
|
}
|
|
|
|
// Set Content-Type explicitly so ServeContent does not sniff.
|
|
w.Header().Set("Content-Type", MimeFromExtension(filePath))
|
|
|
|
http.ServeContent(w, r, stat.Name(), stat.ModTime(), f)
|
|
return nil
|
|
}
|