Files
go2tv/utils/dlnatools.go
2022-09-27 20:57:14 +03:00

130 lines
3.5 KiB
Go

package utils
import (
"errors"
"fmt"
"io"
"strings"
"github.com/h2non/filetype"
)
const (
// dlnaOrgFlagSenderPaced = 1 << 31
// dlnaOrgFlagTimeBasedSeek = 1 << 30
// dlnaOrgFlagByteBasedSeek = 1 << 29
// dlnaOrgFlagPlayContainer = 1 << 28
// dlnaOrgFlagS0Increase = 1 << 27
// dlnaOrgFlagSnIncrease = 1 << 26
// dlnaOrgFlagRtspPause = 1 << 25
dlnaOrgFlagStreamingTransferMode = 1 << 24
// dlnaOrgFlagInteractiveTransfertMode = 1 << 23
dlnaOrgFlagBackgroundTransfertMode = 1 << 22
dlnaOrgFlagConnectionStall = 1 << 21
dlnaOrgFlagDlnaV15 = 1 << 20
)
var (
dlnaprofiles = map[string]string{
"video/x-mkv": "DLNA.ORG_PN=MATROSKA",
"video/x-matroska": "DLNA.ORG_PN=MATROSKA",
"video/x-msvideo": "DLNA.ORG_PN=AVI",
"video/mpeg": "DLNA.ORG_PN=MPEG1",
"video/vnd.dlna.mpeg-tts": "DLNA.ORG_PN=MPEG1",
"video/mp4": "DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5",
"video/quicktime": "DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5",
"video/x-m4v": "DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5",
"video/3gpp": "DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5",
"video/x-flv": "DLNA.ORG_PN=AVC_MP4_MP_SD_AAC_MULT5",
"video/x-ms-wmv": "DLNA.ORG_PN=WMVHIGH_FULL",
"audio/mpeg": "DLNA.ORG_PN=MP3",
"image/jpeg": "JPEG_LRG",
"image/png": "PNG_LRG",
}
ErrInvalidSeekFlag = errors.New("invalid seek flag")
)
func defaultStreamingFlags() string {
return fmt.Sprintf("%.8x%.24x", dlnaOrgFlagStreamingTransferMode|
dlnaOrgFlagBackgroundTransfertMode|
dlnaOrgFlagConnectionStall|
dlnaOrgFlagDlnaV15, 0)
}
// BuildContentFeatures builds the content features string
// for the "contentFeatures.dlna.org" header.
func BuildContentFeatures(mediaType string, seek string, transcode bool) (string, error) {
var cf strings.Builder
if mediaType != "" {
dlnaProf, profExists := dlnaprofiles[mediaType]
if profExists {
cf.WriteString(dlnaProf + ";")
}
}
// "00" neither time seek range nor range supported
// "01" range supported
// "10" time seek range supported
// "11" both time seek range and range supported
switch seek {
case "00":
cf.WriteString("DLNA.ORG_OP=00;")
case "01":
cf.WriteString("DLNA.ORG_OP=01;")
case "10":
cf.WriteString("DLNA.ORG_OP=10;")
case "11":
cf.WriteString("DLNA.ORG_OP=11;")
default:
return "", ErrInvalidSeekFlag
}
switch transcode {
case true:
cf.WriteString("DLNA.ORG_CI=1;")
default:
cf.WriteString("DLNA.ORG_CI=0;")
}
cf.WriteString("DLNA.ORG_FLAGS=")
cf.WriteString(defaultStreamingFlags())
return cf.String(), nil
}
// GetMimeDetailsFromFile returns the media file mime details.
func GetMimeDetailsFromFile(f io.ReadCloser) (string, error) {
defer f.Close()
head := make([]byte, 261)
_, err := f.Read(head)
if err != nil {
return "", fmt.Errorf("getMimeDetailsFromFile error #2: %w", err)
}
kind, err := filetype.Match(head)
if err != nil {
return "", fmt.Errorf("getMimeDetailsFromFile error #3: %w", err)
}
return fmt.Sprintf("%s/%s", kind.MIME.Type, kind.MIME.Subtype), nil
}
// GetMimeDetailsFromStream returns the media URL mime details.
func GetMimeDetailsFromStream(s io.ReadCloser) (string, error) {
defer s.Close()
head := make([]byte, 261)
_, err := s.Read(head)
if err != nil {
return "", fmt.Errorf("getMimeDetailsFromStream error: %w", err)
}
kind, err := filetype.Match(head)
if err != nil {
return "", fmt.Errorf("getMimeDetailsFromStream error #2: %w", err)
}
return fmt.Sprintf("%s/%s", kind.MIME.Type, kind.MIME.Subtype), nil
}