83 lines
2.6 KiB
Go
83 lines
2.6 KiB
Go
package torrentstream
|
|
|
|
import (
|
|
"net/http"
|
|
"seanime/internal/util/torrentutil"
|
|
"strconv"
|
|
"time"
|
|
|
|
"github.com/anacrolix/torrent"
|
|
)
|
|
|
|
var _ = http.Handler(&handler{})
|
|
|
|
type (
|
|
// handler serves the torrent stream
|
|
handler struct {
|
|
repository *Repository
|
|
}
|
|
)
|
|
|
|
func newHandler(repository *Repository) *handler {
|
|
return &handler{
|
|
repository: repository,
|
|
}
|
|
}
|
|
|
|
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
h.repository.logger.Trace().Str("range", r.Header.Get("Range")).Msg("torrentstream: Stream endpoint hit")
|
|
|
|
if h.repository.client.currentFile.IsAbsent() || h.repository.client.currentTorrent.IsAbsent() {
|
|
h.repository.logger.Error().Msg("torrentstream: No torrent to stream")
|
|
http.Error(w, "No torrent to stream", http.StatusNotFound)
|
|
return
|
|
}
|
|
|
|
if r.Method == http.MethodHead {
|
|
r.Response.Header.Set("Content-Type", "video/mp4")
|
|
r.Response.Header.Set("Content-Length", strconv.Itoa(int(h.repository.client.currentFile.MustGet().Length())))
|
|
r.Response.Header.Set("Content-Disposition", "inline; filename="+h.repository.client.currentFile.MustGet().DisplayPath())
|
|
r.Response.Header.Set("Accept-Ranges", "bytes")
|
|
r.Response.Header.Set("Cache-Control", "no-cache")
|
|
r.Response.Header.Set("Pragma", "no-cache")
|
|
r.Response.Header.Set("Expires", "0")
|
|
r.Response.Header.Set("X-Content-Type-Options", "nosniff")
|
|
|
|
// No content, just headers
|
|
w.WriteHeader(http.StatusOK)
|
|
return
|
|
}
|
|
|
|
file := h.repository.client.currentFile.MustGet()
|
|
h.repository.logger.Trace().Str("file", file.DisplayPath()).Msg("torrentstream: New reader")
|
|
tr := file.NewReader()
|
|
defer func(tr torrent.Reader) {
|
|
h.repository.logger.Trace().Msg("torrentstream: Closing reader")
|
|
_ = tr.Close()
|
|
}(tr)
|
|
|
|
tr.SetResponsive()
|
|
// Read ahead 5MB for better streaming performance
|
|
// DEVNOTE: Not sure if dynamic prioritization overwrites this but whatever
|
|
tr.SetReadahead(5 * 1024 * 1024)
|
|
|
|
// If this is a range request for a later part of the file, prioritize those pieces
|
|
rangeHeader := r.Header.Get("Range")
|
|
if rangeHeader != "" && h.repository.client.currentTorrent.IsPresent() {
|
|
t := h.repository.client.currentTorrent.MustGet()
|
|
// Attempt to prioritize the pieces requested in the range
|
|
torrentutil.PrioritizeRangeRequestPieces(rangeHeader, t, file, h.repository.logger)
|
|
}
|
|
|
|
h.repository.logger.Trace().Str("file", file.DisplayPath()).Msg("torrentstream: Serving file content")
|
|
w.Header().Set("Content-Type", "video/mp4")
|
|
http.ServeContent(
|
|
w,
|
|
r,
|
|
file.DisplayPath(),
|
|
time.Now(),
|
|
tr,
|
|
)
|
|
h.repository.logger.Trace().Msg("torrentstream: File content served")
|
|
}
|