Files
seanime-docker/seanime-2.9.10/internal/mediastream/directplay.go
2025-09-20 14:08:38 +01:00

90 lines
2.8 KiB
Go

package mediastream
import (
"errors"
"fmt"
"net/http"
"net/url"
"os"
"path/filepath"
"seanime/internal/events"
"seanime/internal/util"
"github.com/labstack/echo/v4"
)
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Direct
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
func (r *Repository) ServeEchoFile(c echo.Context, rawFilePath string, clientId string, libraryPaths []string) error {
// Unescape the file path, ignore errors
filePath, _ := url.PathUnescape(rawFilePath)
// If the file path is base64 encoded, decode it
if util.IsBase64(rawFilePath) {
var err error
filePath, err = util.Base64DecodeStr(rawFilePath)
if err != nil {
// this shouldn't happen, but just in case IsBase64 is wrong
filePath, _ = url.PathUnescape(rawFilePath)
}
}
// Make sure the file is in the library directories
inLibrary := false
for _, libraryPath := range libraryPaths {
if util.IsFileUnderDir(filePath, libraryPath) {
inLibrary = true
break
}
}
if !inLibrary {
return c.NoContent(http.StatusNotFound)
}
r.logger.Trace().Str("filepath", filePath).Str("payload", rawFilePath).Msg("mediastream: Served file")
// Content disposition
filename := filepath.Base(filePath)
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", filename))
return c.File(filePath)
}
func (r *Repository) ServeEchoDirectPlay(c echo.Context, clientId string) error {
if !r.IsInitialized() {
r.wsEventManager.SendEvent(events.MediastreamShutdownStream, "Module not initialized")
return errors.New("module not initialized")
}
// Get current media
mediaContainer, found := r.playbackManager.currentMediaContainer.Get()
if !found {
r.wsEventManager.SendEvent(events.MediastreamShutdownStream, "no file has been loaded")
return errors.New("no file has been loaded")
}
if c.Request().Method == http.MethodHead {
r.logger.Trace().Msg("mediastream: Received HEAD request for direct play")
// Get the file size
fileInfo, err := os.Stat(mediaContainer.Filepath)
if err != nil {
r.logger.Error().Msg("mediastream: Failed to get file info")
return c.NoContent(http.StatusInternalServerError)
}
// Set the content length
c.Response().Header().Set("Content-Length", fmt.Sprintf("%d", fileInfo.Size()))
c.Response().Header().Set("Content-Type", "video/mp4")
c.Response().Header().Set("Accept-Ranges", "bytes")
filename := filepath.Base(mediaContainer.Filepath)
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("inline; filename=\"%s\"", filename))
return c.NoContent(http.StatusOK)
}
return c.File(mediaContainer.Filepath)
}