CLI: Refactor photoprism/dl test suite

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-08-23 14:31:39 +02:00
parent 37908ca3b5
commit c7e71bbbe2
2 changed files with 200 additions and 216 deletions

View File

@@ -3,16 +3,10 @@ package dl
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/json"
"fmt"
"io" "io"
"os"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
"testing" "testing"
"github.com/photoprism/photoprism/pkg/fs"
) )
const ( const (
@@ -22,74 +16,6 @@ const (
subtitlesTestVideoRawURL = "https://www.youtube.com/watch?v=QRS8MkLhQmM" subtitlesTestVideoRawURL = "https://www.youtube.com/watch?v=QRS8MkLhQmM"
) )
// Todo: Must be fixed, which might require an updated YT-DLP version or different request parameters.
func TestParseInfo(t *testing.T) {
t.Skip("todo: must be fixed, which might require an updated YT-DLP version or different request parameters")
if testing.Short() {
t.Skip("skipping test in short mode.")
}
for _, c := range []struct {
url string
expectedTitle string
}{
{"https://soundcloud.com/avalonemerson/avalon-emerson-live-at-printworks-london-march-2017",
"Avalon Emerson Live at Printworks London 2017"},
{"https://www.infoq.com/presentations/Simple-Made-Easy",
"Simple Made Easy - InfoQ"},
{testVideoRawURL,
"Cinematic Epic Deep Trailer - Background Music for Trailers and Film"},
} {
t.Run(c.url, func(t *testing.T) {
ctx, cancelFn := context.WithCancel(context.Background())
ydlResult, err := NewMetadata(ctx, c.url, Options{
DownloadThumbnail: true,
})
if err != nil {
cancelFn()
t.Errorf("failed to parse: %v", err)
return
}
cancelFn()
yi := ydlResult.Info
results := ydlResult.Formats()
if yi.Title != c.expectedTitle {
t.Errorf("expected title %q got %q", c.expectedTitle, yi.Title)
}
if yi.Thumbnail != "" && len(yi.ThumbnailBytes) == 0 {
t.Errorf("expected thumbnail bytes")
}
var dummy map[string]interface{}
if err := json.Unmarshal(ydlResult.RawJSON, &dummy); err != nil {
t.Errorf("failed to parse RawJSON")
}
if len(results) == 0 {
t.Errorf("expected formats")
}
for _, f := range results {
if f.FormatID == "" {
t.Errorf("expected to have FormatID")
}
if f.Ext == "" {
t.Errorf("expected to have Ext")
}
if (f.ACodec == "" || f.ACodec == "none") &&
(f.VCodec == "" || f.VCodec == "none") &&
f.Ext == "" {
t.Errorf("expected to have some media: audio %q video %q ext %q", f.ACodec, f.VCodec, f.Ext)
}
}
})
}
}
func TestPlaylist(t *testing.T) { func TestPlaylist(t *testing.T) {
t.Skip("skipping test because playlist URL is unreliable.") t.Skip("skipping test because playlist URL is unreliable.")
@@ -152,7 +78,7 @@ func TestChannel(t *testing.T) {
func TestUnsupportedURL(t *testing.T) { func TestUnsupportedURL(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("skipping test in short mode.") t.Skip("skipped download test in short mode")
} }
_, ydlResultErr := NewMetadata(context.Background(), "https://www.google.com", Options{}) _, ydlResultErr := NewMetadata(context.Background(), "https://www.google.com", Options{})
@@ -218,107 +144,9 @@ func TestSubtitles(t *testing.T) {
} }
} }
// Todo: Must be fixed, which might require an updated YT-DLP version or different request parameters.
func TestDownloadSections(t *testing.T) {
t.Skip("todo: must be fixed, which might require an updated YT-DLP version or different request parameters")
fileName := fs.Abs("./testdata/duration_test_file")
duration := 5
defer func() {
_ = os.Remove(fileName)
}()
cmd := exec.Command(FindFFmpegBin(), "-version")
_, err := cmd.Output()
if err != nil {
t.Errorf("failed to check ffmpeg installed: %s", err)
}
ydlResult, ydlResultErr := NewMetadata(
context.Background(),
testVideoRawURL,
Options{
DownloadSections: fmt.Sprintf("*0:0-0:%d", duration),
})
if ydlResult.Options.DownloadSections != "*0:0-0:5" {
t.Errorf("failed to setup --download-sections")
}
if ydlResultErr != nil {
t.Errorf("failed to download: %s", ydlResultErr)
}
dr, err := ydlResult.Download(context.Background(), "")
if err != nil {
t.Fatal(err)
}
f, err := os.Create(fileName)
if err != nil {
t.Fatal(err)
}
defer f.Close()
_, err = io.Copy(f, dr)
if err != nil {
t.Fatal(err)
}
cmd = exec.Command(FindFFprobeBin(), "-v", "quiet", "-show_entries", "format=duration", fileName)
stdout, err := cmd.Output()
if err != nil {
t.Fatal(err)
}
var gotDurationString string
output := string(stdout)
for _, line := range strings.Split(output, "\n") {
if strings.Contains(line, "duration") {
if d, found := strings.CutPrefix(line, "duration="); found {
gotDurationString = d
}
}
}
gotDuration, err := strconv.ParseFloat(gotDurationString, 32)
if err != nil {
t.Fatal(err)
}
seconds := int(gotDuration)
if seconds != duration {
t.Fatalf("did not get expected duration of %d, but got %d", duration, seconds)
}
_ = dr.Close()
}
func TestErrorNotAPlaylist(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
_, ydlResultErr := NewMetadata(context.Background(), testVideoRawURL, Options{
Type: TypePlaylist,
DownloadThumbnail: false,
})
if ydlResultErr != ErrNotAPlaylist {
t.Errorf("expected is playlist error, got %s", ydlResultErr)
}
}
func TestErrorNotASingleEntry(t *testing.T) { func TestErrorNotASingleEntry(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("skipping test in short mode.") t.Skip("skipped download test in short mode")
} }
_, ydlResultErr := NewMetadata(context.Background(), playlistRawURL, Options{ _, ydlResultErr := NewMetadata(context.Background(), playlistRawURL, Options{
@@ -331,40 +159,6 @@ func TestErrorNotASingleEntry(t *testing.T) {
} }
} }
// Todo: Must be fixed, which might require an updated YT-DLP version or different request parameters.
func TestOptionDownloader(t *testing.T) {
t.Skip("todo: must be fixed, which might require an updated YT-DLP version or different request parameters")
if testing.Short() {
t.Skip("skipping test in short mode.")
}
ydlResult, ydlResultErr := NewMetadata(
context.Background(),
testVideoRawURL,
Options{
Downloader: "ffmpeg",
})
if ydlResultErr != nil {
t.Fatalf("failed to download: %s", ydlResultErr)
}
dr, err := ydlResult.Download(context.Background(), ydlResult.Info.Formats[0].FormatID)
if err != nil {
t.Fatal(err)
}
downloadBuf := &bytes.Buffer{}
_, err = io.Copy(downloadBuf, dr)
if err != nil {
t.Fatal(err)
}
dr.Close()
}
func TestInvalidOptionTypeField(t *testing.T) { func TestInvalidOptionTypeField(t *testing.T) {
_, err := NewMetadata(context.Background(), playlistRawURL, Options{ _, err := NewMetadata(context.Background(), playlistRawURL, Options{
Type: 42, Type: 42,
@@ -376,7 +170,7 @@ func TestInvalidOptionTypeField(t *testing.T) {
func TestDownloadPlaylistEntry(t *testing.T) { func TestDownloadPlaylistEntry(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("skipping test in short mode.") t.Skip("skipped download test in short mode")
} }
// Download file by specifying the playlist index // Download file by specifying the playlist index

View File

@@ -1,11 +1,16 @@
//go:build yt
package dl package dl
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/json"
"fmt"
"io" "io"
"os" "os"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
"testing" "testing"
@@ -14,12 +19,200 @@ import (
"github.com/photoprism/photoprism/pkg/fs" "github.com/photoprism/photoprism/pkg/fs"
) )
// Todo: Must be fixed, which might require an updated YT-DLP version or different request parameters. func TestDownloadSections(t *testing.T) {
func TestDownload(t *testing.T) { fileName := fs.Abs("./testdata/duration_test_file")
t.Skip("todo: must be fixed, which might require an updated YT-DLP version or different request parameters") duration := 5
defer func() {
_ = os.Remove(fileName)
}()
cmd := exec.Command(FindFFmpegBin(), "-version")
_, err := cmd.Output()
if err != nil {
t.Errorf("failed to check ffmpeg installed: %s", err)
}
ydlResult, ydlResultErr := NewMetadata(
context.Background(),
testVideoRawURL,
Options{
DownloadSections: fmt.Sprintf("*0:0-0:%d", duration),
})
if ydlResult.Options.DownloadSections != "*0:0-0:5" {
t.Errorf("failed to setup --download-sections")
}
if ydlResultErr != nil {
t.Errorf("failed to download: %s", ydlResultErr)
}
dr, err := ydlResult.Download(context.Background(), "")
if err != nil {
t.Fatal(err)
}
f, err := os.Create(fileName)
if err != nil {
t.Fatal(err)
}
defer f.Close()
_, err = io.Copy(f, dr)
if err != nil {
t.Fatal(err)
}
cmd = exec.Command(FindFFprobeBin(), "-v", "quiet", "-show_entries", "format=duration", fileName)
stdout, err := cmd.Output()
if err != nil {
t.Fatal(err)
}
var gotDurationString string
output := string(stdout)
for _, line := range strings.Split(output, "\n") {
if strings.Contains(line, "duration") {
if d, found := strings.CutPrefix(line, "duration="); found {
gotDurationString = d
}
}
}
gotDuration, err := strconv.ParseFloat(gotDurationString, 32)
if err != nil {
t.Fatal(err)
}
seconds := int(gotDuration)
if seconds != duration {
t.Fatalf("did not get expected duration of %d, but got %d", duration, seconds)
}
_ = dr.Close()
}
func TestErrorNotAPlaylist(t *testing.T) {
if testing.Short() { if testing.Short() {
t.Skip("skipping test in short mode.") t.Skip("skipped download test in short mode")
}
_, ydlResultErr := NewMetadata(context.Background(), testVideoRawURL, Options{
Type: TypePlaylist,
DownloadThumbnail: false,
})
if ydlResultErr != ErrNotAPlaylist {
t.Errorf("expected is playlist error, got %s", ydlResultErr)
}
}
func TestOptionDownloader(t *testing.T) {
if testing.Short() {
t.Skip("skipped download test in short mode")
}
ydlResult, ydlResultErr := NewMetadata(
context.Background(),
testVideoRawURL,
Options{
Downloader: "ffmpeg",
})
if ydlResultErr != nil {
t.Fatalf("failed to download: %s", ydlResultErr)
}
dr, err := ydlResult.Download(context.Background(), ydlResult.Info.Formats[0].FormatID)
if err != nil {
t.Fatal(err)
}
downloadBuf := &bytes.Buffer{}
_, err = io.Copy(downloadBuf, dr)
if err != nil {
t.Fatal(err)
}
dr.Close()
}
func TestParseInfo(t *testing.T) {
if testing.Short() {
t.Skip("skipped download test in short mode")
}
for _, c := range []struct {
url string
expectedTitle string
}{
{"https://soundcloud.com/avalonemerson/avalon-emerson-live-at-printworks-london-march-2017",
"Avalon Emerson Live at Printworks London 2017"},
{"https://www.infoq.com/presentations/Simple-Made-Easy",
"Simple Made Easy - InfoQ"},
{testVideoRawURL,
"Cinematic Epic Deep Trailer - Background Music for Trailers and Film"},
} {
t.Run(c.url, func(t *testing.T) {
ctx, cancelFn := context.WithCancel(context.Background())
ydlResult, err := NewMetadata(ctx, c.url, Options{
DownloadThumbnail: true,
})
if err != nil {
cancelFn()
t.Errorf("failed to parse: %v", err)
return
}
cancelFn()
yi := ydlResult.Info
results := ydlResult.Formats()
if yi.Title != c.expectedTitle {
t.Errorf("expected title %q got %q", c.expectedTitle, yi.Title)
}
if yi.Thumbnail != "" && len(yi.ThumbnailBytes) == 0 {
t.Errorf("expected thumbnail bytes")
}
var dummy map[string]interface{}
if err := json.Unmarshal(ydlResult.RawJSON, &dummy); err != nil {
t.Errorf("failed to parse RawJSON")
}
if len(results) == 0 {
t.Errorf("expected formats")
}
for _, f := range results {
if f.FormatID == "" {
t.Errorf("expected to have FormatID")
}
if f.Ext == "" {
t.Errorf("expected to have Ext")
}
if (f.ACodec == "" || f.ACodec == "none") &&
(f.VCodec == "" || f.VCodec == "none") &&
f.Ext == "" {
t.Errorf("expected to have some media: audio %q video %q ext %q", f.ACodec, f.VCodec, f.Ext)
}
}
})
}
}
func TestDownload(t *testing.T) {
if testing.Short() {
t.Skip("skipped download test in short mode")
} }
// Fetch metadata. // Fetch metadata.
@@ -74,10 +267,7 @@ func TestDownload(t *testing.T) {
} }
} }
// Todo: Must be fixed, which might require an updated YT-DLP version or different request parameters.
func TestDownloadWithoutInfo(t *testing.T) { func TestDownloadWithoutInfo(t *testing.T) {
t.Skip("todo: must be fixed, which might require an updated YT-DLP version or different request parameters")
stderrBuf := &bytes.Buffer{} stderrBuf := &bytes.Buffer{}
dr, err := Download(context.Background(), testVideoRawURL, Options{ dr, err := Download(context.Background(), testVideoRawURL, Options{
StderrFn: func(cmd *exec.Cmd) io.Writer { StderrFn: func(cmd *exec.Cmd) io.Writer {