261 lines
7.7 KiB
Go
261 lines
7.7 KiB
Go
package overlays
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/Silo-Server/silo-server/internal/models"
|
|
)
|
|
|
|
func TestNormalizeVideoCodec(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
file *models.MediaFile
|
|
want string
|
|
}{
|
|
{"empty", &models.MediaFile{}, ""},
|
|
{"file codec hevc", &models.MediaFile{CodecVideo: "hevc"}, "H.265"},
|
|
{"file codec h264", &models.MediaFile{CodecVideo: "h264"}, "H.264"},
|
|
{"file codec av1", &models.MediaFile{CodecVideo: "av1"}, "AV1"},
|
|
{"file codec vp9", &models.MediaFile{CodecVideo: "VP9"}, "VP9"},
|
|
{"track overrides file", &models.MediaFile{
|
|
CodecVideo: "h264",
|
|
VideoTracks: []models.VideoTrack{{Codec: "hevc"}},
|
|
}, "H.265"},
|
|
{"empty track codec falls back to file codec", &models.MediaFile{
|
|
CodecVideo: "h265",
|
|
VideoTracks: []models.VideoTrack{{Codec: ""}},
|
|
}, "H.265"},
|
|
{"unknown codec passes through uppercased", &models.MediaFile{CodecVideo: "foobar"}, "FOOBAR"},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := normalizeVideoCodec(tc.file); got != tc.want {
|
|
t.Errorf("normalizeVideoCodec = %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNormalizeAudioChannels(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
file *models.MediaFile
|
|
want string
|
|
}{
|
|
{"empty", &models.MediaFile{}, ""},
|
|
{"file channels 2", &models.MediaFile{AudioChannels: 2}, "Stereo"},
|
|
{"file channels 6", &models.MediaFile{AudioChannels: 6}, "5.1"},
|
|
{"file channels 8", &models.MediaFile{AudioChannels: 8}, "7.1"},
|
|
{"file channels 1", &models.MediaFile{AudioChannels: 1}, "Mono"},
|
|
{"unusual channel count", &models.MediaFile{AudioChannels: 10}, "10ch"},
|
|
{"default track wins over higher", &models.MediaFile{
|
|
AudioTracks: []models.AudioTrack{
|
|
{Channels: 8},
|
|
{Channels: 2, Default: true},
|
|
},
|
|
}, "Stereo"},
|
|
{"highest non-default when no default", &models.MediaFile{
|
|
AudioTracks: []models.AudioTrack{
|
|
{Channels: 2},
|
|
{Channels: 8},
|
|
},
|
|
}, "7.1"},
|
|
{"track beats file fallback", &models.MediaFile{
|
|
AudioChannels: 2,
|
|
AudioTracks: []models.AudioTrack{{Channels: 6, Default: true}},
|
|
}, "5.1"},
|
|
{"default track with 0 channels does not shadow earlier non-default", &models.MediaFile{
|
|
AudioTracks: []models.AudioTrack{
|
|
{Channels: 8},
|
|
{Channels: 0, Default: true},
|
|
},
|
|
}, "7.1"},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := normalizeAudioChannels(tc.file); got != tc.want {
|
|
t.Errorf("normalizeAudioChannels = %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNormalizeContainer(t *testing.T) {
|
|
cases := []struct {
|
|
in, want string
|
|
}{
|
|
{"", ""},
|
|
{"mkv", "MKV"},
|
|
{"MP4", "MP4"},
|
|
{" mov ", "MOV"},
|
|
}
|
|
for _, tc := range cases {
|
|
if got := normalizeContainer(tc.in); got != tc.want {
|
|
t.Errorf("normalizeContainer(%q) = %q, want %q", tc.in, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNormalizeAspectRatio(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
file *models.MediaFile
|
|
want string
|
|
}{
|
|
{"empty", &models.MediaFile{}, ""},
|
|
{"16:9 string", &models.MediaFile{
|
|
VideoTracks: []models.VideoTrack{{AspectRatio: "16:9"}},
|
|
}, "16:9"},
|
|
{"239:100 normalizes to 2.39:1", &models.MediaFile{
|
|
VideoTracks: []models.VideoTrack{{AspectRatio: "239:100"}},
|
|
}, "2.39:1"},
|
|
{"2.40:1 snaps to 2.39:1", &models.MediaFile{
|
|
VideoTracks: []models.VideoTrack{{AspectRatio: "2.40:1"}},
|
|
}, "2.39:1"},
|
|
{"derives from 1920x1080", &models.MediaFile{
|
|
VideoTracks: []models.VideoTrack{{Width: 1920, Height: 1080}},
|
|
}, "16:9"},
|
|
{"derives from 4096x1716 (cinemascope)", &models.MediaFile{
|
|
VideoTracks: []models.VideoTrack{{Width: 4096, Height: 1716}},
|
|
}, "2.39:1"},
|
|
{"derives from 720x480", &models.MediaFile{
|
|
VideoTracks: []models.VideoTrack{{Width: 720, Height: 480}},
|
|
}, "1.50:1"},
|
|
{"malformed ratio falls back to dimensions", &models.MediaFile{
|
|
VideoTracks: []models.VideoTrack{{AspectRatio: "garbage", Width: 1920, Height: 1080}},
|
|
}, "16:9"},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := normalizeAspectRatio(tc.file); got != tc.want {
|
|
t.Errorf("normalizeAspectRatio = %q, want %q", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDetectMultiAudio(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
file *models.MediaFile
|
|
want bool
|
|
}{
|
|
{"empty", &models.MediaFile{}, false},
|
|
{"single track", &models.MediaFile{
|
|
AudioTracks: []models.AudioTrack{{Language: "eng"}},
|
|
}, false},
|
|
{"two same language", &models.MediaFile{
|
|
AudioTracks: []models.AudioTrack{{Language: "eng"}, {Language: "eng"}},
|
|
}, false},
|
|
{"two distinct", &models.MediaFile{
|
|
AudioTracks: []models.AudioTrack{{Language: "eng"}, {Language: "jpn"}},
|
|
}, true},
|
|
{"undefined ignored", &models.MediaFile{
|
|
AudioTracks: []models.AudioTrack{{Language: "und"}, {Language: ""}, {Language: "eng"}},
|
|
}, false},
|
|
{"three distinct returns true at 2", &models.MediaFile{
|
|
AudioTracks: []models.AudioTrack{{Language: "eng"}, {Language: "spa"}, {Language: "fre"}},
|
|
}, true},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := detectMultiAudio(tc.file); got != tc.want {
|
|
t.Errorf("detectMultiAudio = %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestDetectMultiSub(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
file *models.MediaFile
|
|
want bool
|
|
}{
|
|
{"empty", &models.MediaFile{}, false},
|
|
{"one embedded", &models.MediaFile{
|
|
SubtitleTracks: []models.SubtitleTrack{{Language: "eng"}},
|
|
}, true},
|
|
{"one external", &models.MediaFile{
|
|
ExternalSubtitles: []models.ExternalSubtitle{{Language: "eng"}},
|
|
}, true},
|
|
{"both present", &models.MediaFile{
|
|
SubtitleTracks: []models.SubtitleTrack{{Language: "eng"}},
|
|
ExternalSubtitles: []models.ExternalSubtitle{{Language: "jpn"}},
|
|
}, true},
|
|
}
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
if got := detectMultiSub(tc.file); got != tc.want {
|
|
t.Errorf("detectMultiSub = %v, want %v", got, tc.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestNormalizeReleaseType(t *testing.T) {
|
|
cases := []struct {
|
|
path, want string
|
|
}{
|
|
{"", ""},
|
|
{"/movies/Foo.2020.1080p.REMUX.mkv", "REMUX"},
|
|
{"/movies/Foo.2020.1080p.WEB-DL.mkv", "WEB-DL"},
|
|
{"/movies/Foo.2020.1080p.BluRay.mkv", "BluRay"},
|
|
{"/movies/Foo.2020.480p.DVDRip.mkv", "DVD"},
|
|
// Word-boundary check: "dvd" embedded in a benign word must not match.
|
|
{"/movies/goodvideos/Foo.2020.1080p.mkv", ""},
|
|
}
|
|
for _, tc := range cases {
|
|
if got := normalizeReleaseType(tc.path); got != tc.want {
|
|
t.Errorf("normalizeReleaseType(%q) = %q, want %q", tc.path, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestBuildSummaryAggregatesNewFields(t *testing.T) {
|
|
file := &models.MediaFile{
|
|
Resolution: "1080p",
|
|
CodecVideo: "hevc",
|
|
Container: "mkv",
|
|
VideoTracks: []models.VideoTrack{{
|
|
Codec: "hevc",
|
|
Width: 1920,
|
|
Height: 1080,
|
|
AspectRatio: "16:9",
|
|
}},
|
|
AudioTracks: []models.AudioTrack{
|
|
{Language: "eng", Channels: 8, Default: true, Title: "Atmos"},
|
|
{Language: "spa", Channels: 6},
|
|
},
|
|
SubtitleTracks: []models.SubtitleTrack{{Language: "eng"}},
|
|
}
|
|
got := BuildSummary([]*models.MediaFile{file})
|
|
if got == nil {
|
|
t.Fatal("expected non-nil summary")
|
|
}
|
|
if got.Resolution != "1080p" {
|
|
t.Errorf("Resolution = %q, want %q", got.Resolution, "1080p")
|
|
}
|
|
if got.VideoCodec != "H.265" {
|
|
t.Errorf("VideoCodec = %q, want %q", got.VideoCodec, "H.265")
|
|
}
|
|
if got.AudioChannels != "7.1" {
|
|
t.Errorf("AudioChannels = %q, want %q", got.AudioChannels, "7.1")
|
|
}
|
|
if got.Container != "MKV" {
|
|
t.Errorf("Container = %q, want %q", got.Container, "MKV")
|
|
}
|
|
if got.AspectRatio != "16:9" {
|
|
t.Errorf("AspectRatio = %q, want %q", got.AspectRatio, "16:9")
|
|
}
|
|
if got.Audio != "Atmos" {
|
|
t.Errorf("Audio = %q, want %q", got.Audio, "Atmos")
|
|
}
|
|
if !got.MultiAudio {
|
|
t.Error("MultiAudio = false, want true")
|
|
}
|
|
if !got.MultiSub {
|
|
t.Error("MultiSub = false, want true")
|
|
}
|
|
}
|