mirror of
https://github.com/photoprism/photoprism.git
synced 2025-09-27 05:08:13 +08:00
CLI: Refactor photoprism/dl test suite
Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
@@ -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
|
||||||
|
@@ -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 {
|
||||||
|
Reference in New Issue
Block a user