package trailer import ( "context" "io" "net/http" "strings" "testing" ) type roundTripFunc func(*http.Request) (*http.Response, error) func (fn roundTripFunc) RoundTrip(request *http.Request) (*http.Response, error) { return fn(request) } func TestYouTubeVideoID(t *testing.T) { for _, raw := range []string{ "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "https://youtu.be/dQw4w9WgXcQ", "https://www.youtube.com/embed/dQw4w9WgXcQ", "https://youtube.com/shorts/dQw4w9WgXcQ", } { if got := youtubeVideoID(raw); got != "dQw4w9WgXcQ" { t.Fatalf("youtubeVideoID(%q) = %q", raw, got) } } if got := youtubeVideoID("https://example.com/watch?v=dQw4w9WgXcQ"); got != "" { t.Fatalf("accepted a non-YouTube host: %q", got) } } func TestProviderHostMatchingRejectsLookalikeDomains(t *testing.T) { if newAppleProvider(http.DefaultClient).Supports("https://notapple.com/trailer.mov") { t.Fatal("lookalike Apple host was accepted") } if youtubeVideoID("https://notyoutube.com/watch?v=dQw4w9WgXcQ") != "" { t.Fatal("lookalike YouTube host was accepted") } } func TestYouTubeResolverReturnsValidatedProgressiveStream(t *testing.T) { client := &http.Client{Transport: roundTripFunc(func(request *http.Request) (*http.Response, error) { body := "" status := http.StatusOK headers := http.Header{} switch request.URL.Host { case "www.youtube.com": body = `{"playabilityStatus":{"status":"OK"},"streamingData":{"formats":[` + `{"url":"https://media.example/trailer.mp4","mimeType":"video/mp4; codecs=avc1,mp4a","height":720,"bitrate":1000}]}}` headers.Set("Content-Type", "application/json") case "media.example": status = http.StatusPartialContent headers.Set("Content-Type", "video/mp4") default: t.Fatalf("unexpected request to %s", request.URL) } return &http.Response{ StatusCode: status, Header: headers, Body: io.NopCloser(strings.NewReader(body)), Request: request, }, nil })} resolver := New(client) result, err := resolver.Resolve(context.Background(), Source{ Provider: "youtube", URL: "https://youtu.be/dQw4w9WgXcQ", }) if err != nil { t.Fatal(err) } if result.URL != "https://media.example/trailer.mp4" || result.MimeType != "video/mp4" { t.Fatalf("unexpected result: %+v", result) } } func TestApplePageChoosesBestValidatedStream(t *testing.T) { client := &http.Client{Transport: roundTripFunc(func(request *http.Request) (*http.Response, error) { body := "" headers := http.Header{} status := http.StatusOK if request.URL.Path == "/page" { body = `720` + `1080` headers.Set("Content-Type", "text/html") } else { status = http.StatusPartialContent headers.Set("Content-Type", "video/quicktime") } return &http.Response{ StatusCode: status, Header: headers, Body: io.NopCloser(strings.NewReader(body)), Request: request, }, nil })} resolver := New(client) result, err := resolver.Resolve(context.Background(), Source{ Provider: "apple", URL: "https://trailers.apple.com/page", }) if err != nil { t.Fatal(err) } if !strings.Contains(result.URL, "1080") { t.Fatalf("did not choose the best stream: %+v", result) } } func TestBalancedJSONObjectIgnoresBracesInsideStrings(t *testing.T) { body := `before marker = {"value":"}" ,"nested":{"ok":true}} after` if got := balancedJSONObject(body, "marker = "); got != `{"value":"}" ,"nested":{"ok":true}}` { t.Fatalf("balanced object = %q", got) } }