mirror of
https://github.com/truewhile/MeBox.git
synced 2026-09-29 19:36:36 +08:00
139 lines
3.7 KiB
Go
139 lines
3.7 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"path/filepath"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/ShukeBta/MediaStationGo/internal/model"
|
|
)
|
|
|
|
func (s *STRMService) strmPlaybackURL(ctx context.Context, media model.Media, baseURL, playbackToken string) string {
|
|
if media.ID == "" {
|
|
return ""
|
|
}
|
|
query := url.Values{}
|
|
token := strings.TrimSpace(playbackToken)
|
|
if token == "" {
|
|
token = s.defaultSTRMPlaybackToken(ctx)
|
|
}
|
|
if token != "" {
|
|
query.Set("token", token)
|
|
}
|
|
return buildAbsoluteSTRMAPIURL(firstNonEmpty(baseURL, PublicServerURL(ctx, s.repo, s.cfg)), "/api/stream/"+url.PathEscape(media.ID), query)
|
|
}
|
|
|
|
func (s *STRMService) defaultSTRMPlaybackToken(ctx context.Context) string {
|
|
if s == nil || s.repo == nil || s.repo.User == nil || s.cfg == nil || strings.TrimSpace(s.cfg.Secrets.JWTSecret) == "" {
|
|
return ""
|
|
}
|
|
admin, err := s.repo.User.FirstAdmin(ctx)
|
|
if err != nil || admin == nil {
|
|
if err != nil && s.log != nil {
|
|
s.log.Warn("generate strm playback token failed", zap.Error(err))
|
|
}
|
|
return ""
|
|
}
|
|
token, err := signSTRMPlaybackToken(admin, s.cfg.Secrets.JWTSecret)
|
|
if err != nil {
|
|
if s.log != nil {
|
|
s.log.Warn("sign strm playback token failed", zap.Error(err))
|
|
}
|
|
return ""
|
|
}
|
|
return token
|
|
}
|
|
|
|
func signSTRMPlaybackToken(u *model.User, secret string) (string, error) {
|
|
if u == nil || strings.TrimSpace(u.ID) == "" || strings.TrimSpace(secret) == "" {
|
|
return "", ErrSTRMURLInvalid
|
|
}
|
|
claims := Claims{
|
|
UserID: u.ID,
|
|
Role: u.Role,
|
|
Tier: u.Tier,
|
|
RegisteredClaims: jwt.RegisteredClaims{
|
|
IssuedAt: jwt.NewNumericDate(time.Now()),
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(EmbyTokenDuration)),
|
|
Issuer: "mediastationgo",
|
|
Subject: u.ID,
|
|
},
|
|
}
|
|
t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
|
return t.SignedString([]byte(secret))
|
|
}
|
|
|
|
func (s *STRMService) strmRelativePath(lib model.Library, media model.Media) string {
|
|
title := strings.TrimSpace(media.Title)
|
|
if title == "" {
|
|
title = strings.TrimSuffix(filepath.Base(media.Path), filepath.Ext(media.Path))
|
|
}
|
|
if title == "" {
|
|
return ""
|
|
}
|
|
seriesLike := isSeriesLibraryType(lib.Type) || media.SeasonNum > 0 || media.EpisodeNum > 0
|
|
if seriesLike {
|
|
show := inferSeriesNameFromPath(media.Path)
|
|
if show == "" {
|
|
show = title
|
|
}
|
|
season := media.SeasonNum
|
|
if season <= 0 {
|
|
season = 1
|
|
}
|
|
name := title
|
|
if media.EpisodeNum > 0 {
|
|
name = fmt.Sprintf("%s - S%02dE%02d", show, season, media.EpisodeNum)
|
|
}
|
|
return filepath.Join(sanitizeFilename(show), fmt.Sprintf("Season %02d", season), sanitizeFilename(name)+".strm")
|
|
}
|
|
folder := title
|
|
if media.Year > 0 && !strings.Contains(folder, strconv.Itoa(media.Year)) {
|
|
folder = fmt.Sprintf("%s (%d)", folder, media.Year)
|
|
}
|
|
safe := sanitizeFilename(folder)
|
|
return filepath.Join(safe, safe+".strm")
|
|
}
|
|
|
|
func absolutizeSTRMURL(raw, baseURL string) string {
|
|
raw = strings.TrimSpace(raw)
|
|
if raw == "" || strings.HasPrefix(raw, "//") {
|
|
return raw
|
|
}
|
|
u, err := url.Parse(raw)
|
|
if err == nil && u.IsAbs() {
|
|
return raw
|
|
}
|
|
return buildAbsoluteSTRMAPIURL(baseURL, raw, nil)
|
|
}
|
|
|
|
func buildAbsoluteSTRMAPIURL(baseURL, apiPath string, query url.Values) string {
|
|
apiPath = "/" + strings.TrimLeft(strings.TrimSpace(apiPath), "/")
|
|
if query != nil && len(query) > 0 {
|
|
apiPath += "?" + query.Encode()
|
|
}
|
|
baseURL = strings.TrimRight(strings.TrimSpace(baseURL), "/")
|
|
if baseURL == "" {
|
|
return apiPath
|
|
}
|
|
base, err := url.Parse(baseURL)
|
|
if err != nil || base.Scheme == "" || base.Host == "" {
|
|
return apiPath
|
|
}
|
|
target, err := url.Parse(apiPath)
|
|
if err != nil {
|
|
return apiPath
|
|
}
|
|
base.Path = strings.TrimRight(base.Path, "/") + "/" + strings.TrimLeft(target.Path, "/")
|
|
base.RawQuery = target.RawQuery
|
|
base.Fragment = ""
|
|
return base.String()
|
|
}
|