Step 5. A URL goes through the same pipeline as an upload — it just gains a download step and a source_url. - The host allowlist is checked on the parsed hostname before yt-dlp is invoked, so lookalikes and userinfo tricks are refused too - yt-dlp -J reads metadata synchronously with a 15s timeout; a timeout leaves the fields blank rather than failing the submission - Over-long tracks are refused from that metadata, before a byte is downloaded - Failed URL submissions offer Yritä uudelleen with the typed text intact; uploads cannot retry, so they offer re-upload Prefill takes track then title, and artist then creator then uploader, and leaves a field blank rather than inventing one. testdata/ytdlp-noose.json is a real dump of an ordinary upload, which has none of the music fields. Also fixes a URL-only submit being blocked by the file input's required attribute — HTML cannot express "one of these two", so the server says it. The image now takes yt-dlp from Alpine 3.24 instead of pip, which drops python3 and pip entirely; see decision 19.
82 lines
2.4 KiB
Go
82 lines
2.4 KiB
Go
package main
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestAllowedYouTubeURL(t *testing.T) {
|
|
for _, ok := range []string{
|
|
"https://www.youtube.com/watch?v=XnfMBo4IQ-g",
|
|
"https://youtu.be/XnfMBo4IQ-g",
|
|
"https://music.youtube.com/watch?v=XnfMBo4IQ-g",
|
|
"http://m.youtube.com/watch?v=XnfMBo4IQ-g",
|
|
} {
|
|
if _, allowed := allowedYouTubeURL(ok); !allowed {
|
|
t.Errorf("%q was rejected", ok)
|
|
}
|
|
}
|
|
for _, bad := range []string{
|
|
"",
|
|
"not a url",
|
|
"file:///etc/passwd",
|
|
"https://evil.example.com/watch?v=x",
|
|
// The allowlist is on the host, so a lookalike path or userinfo must not pass.
|
|
"https://evil.example.com/www.youtube.com/watch?v=x",
|
|
"https://youtube.com.evil.example.com/watch?v=x",
|
|
"https://[email protected]/",
|
|
"-oExecuteMe",
|
|
} {
|
|
if _, allowed := allowedYouTubeURL(bad); allowed {
|
|
t.Errorf("%q was allowed", bad)
|
|
}
|
|
}
|
|
}
|
|
|
|
// A real dump of an ordinary upload: no track, no artist, no creator, no album — just a title with
|
|
// double spaces and an uploader. This is why prefill must never invent an "Unknown".
|
|
func TestYouTubeMetaFromOrdinaryUpload(t *testing.T) {
|
|
raw, err := os.ReadFile("testdata/ytdlp-noose.json")
|
|
if err != nil {
|
|
t.Skipf("fixture missing: %v", err)
|
|
}
|
|
meta, err := parseYouTubeMeta(raw)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if meta.Title != "Sentenced Noose" {
|
|
t.Errorf("title = %q, want the cleaned video title", meta.Title)
|
|
}
|
|
if meta.Artist != "Heikki Rokkonen" {
|
|
t.Errorf("artist = %q, want the uploader as the last fallback", meta.Artist)
|
|
}
|
|
if meta.Duration != 245*time.Second {
|
|
t.Errorf("duration = %v, want 4m5s", meta.Duration)
|
|
}
|
|
}
|
|
|
|
func TestYouTubeMetaPrefersMusicFields(t *testing.T) {
|
|
meta, err := parseYouTubeMeta([]byte(`{
|
|
"title": "Sentenced - Noose (Official Video)",
|
|
"track": "Noose", "artist": "Sentenced", "creator": "ignored",
|
|
"uploader": "SentencedVEVO", "duration": 245.0}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if meta.Title != "Noose" || meta.Artist != "Sentenced" {
|
|
t.Errorf("got %q by %q, want the track/artist fields to win", meta.Title, meta.Artist)
|
|
}
|
|
}
|
|
|
|
// Empty stays empty: a blank field prompts the submitter, a plausible "Unknown" does not.
|
|
func TestYouTubeMetaLeavesBlanksBlank(t *testing.T) {
|
|
meta, err := parseYouTubeMeta([]byte(`{"duration": 10.0}`))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if meta.Title != "" || meta.Artist != "" {
|
|
t.Errorf("got %q by %q, want both empty", meta.Title, meta.Artist)
|
|
}
|
|
}
|