Improve initial playlist data

This commit is contained in:
Carl Pearson
2024-10-10 15:10:32 -06:00
parent 93eb5ca130
commit b8a56100f3
2 changed files with 109 additions and 38 deletions

View File

@@ -2,7 +2,6 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
@@ -181,24 +180,30 @@ func getYtdlpTitle(url string, args []string) (string, error) {
return strings.TrimSpace(string(stdout)), nil return strings.TrimSpace(string(stdout)), nil
} }
func getYtdlpPlaylistTitle(url string) (string, error) { type PlaylistEntry struct {
URL string `json:"url"`
Title string `json:"title"`
}
type PlaylistData struct {
Title string `json:"title"`
Entries []PlaylistEntry `json:"entries"`
}
func getYtdlpPlaylist(url string) (PlaylistData, error) {
var data PlaylistData
stdout, _, err := runYtdlp("--flat-playlist", "--dump-single-json", url) stdout, _, err := runYtdlp("--flat-playlist", "--dump-single-json", url)
if err != nil { if err != nil {
log.Errorln(err) log.Errorln(err)
return "", err return data, err
} }
var data map[string]interface{}
err = json.Unmarshal(stdout, &data) err = json.Unmarshal(stdout, &data)
if err != nil { if err != nil {
return "", err return data, err
}
title, ok := data["title"].(string)
if !ok {
return "", errors.New("title field not found or not a string")
} }
return title, nil return data, nil
} }
func getYtdlpArtist(url string, args []string) (string, error) { func getYtdlpArtist(url string, args []string) (string, error) {
@@ -684,30 +689,23 @@ func startDownload(originalID uint, videoURL string, audioOnly bool) {
func startPlaylist(id uint, url string, audioOnly bool) { func startPlaylist(id uint, url string, audioOnly bool) {
// retrieve playlist metadata // retrieve playlist metadata
title, err := getYtdlpPlaylistTitle(url) pl, err := getYtdlpPlaylist(url)
if err != nil { if err != nil {
SetPlaylistStatus(id, Failed) SetPlaylistStatus(id, Failed)
return return
} }
err = db.Model(&Playlist{}).Where("id = ?", id).Updates(map[string]interface{}{ err = db.Model(&Playlist{}).Where("id = ?", id).Updates(map[string]interface{}{
"title": title, "title": pl.Title,
}).Error }).Error
if err != nil { if err != nil {
SetPlaylistStatus(id, Failed) SetPlaylistStatus(id, Failed)
return return
} }
// populate playlist entries for _, entry := range pl.Entries {
stdout, _, err := runYtdlp("--get-id", "--flat-playlist", url)
if err != nil {
SetPlaylistStatus(id, Failed)
return
}
for _, line := range strings.Split(string(stdout), "\n") {
line = strings.TrimSpace(line)
if line != "" {
original := Original{ original := Original{
URL: fmt.Sprintf("https://www.youtube.com/watch?v=%s", line), Title: entry.Title,
URL: entry.URL,
Status: Pending, Status: Pending,
Video: !audioOnly, Video: !audioOnly,
Audio: audioOnly, Audio: audioOnly,
@@ -720,7 +718,6 @@ func startPlaylist(id uint, url string, audioOnly bool) {
return return
} }
} }
}
SetPlaylistStatus(id, Completed) SetPlaylistStatus(id, Completed)
} }
@@ -1088,11 +1085,23 @@ func processHandler(c echo.Context) error {
} }
func playlistHandler(c echo.Context) error { func playlistHandler(c echo.Context) error {
referrer := c.Request().Referer()
if referrer == "" { id := c.Param("id")
referrer = "/videos"
var originals []Original
err := db.Where("playlist = ?", true).
Where("playlist_id = ?", id).
Find(&originals).Error
if err != nil {
return c.String(http.StatusInternalServerError, fmt.Sprintf("%v", err))
} }
return c.Redirect(http.StatusSeeOther, referrer)
return c.Render(http.StatusOK, "playlist.html",
map[string]interface{}{
"originals": originals,
"Footer": makeFooter(),
})
} }
func deletePlaylistHandler(c echo.Context) error { func deletePlaylistHandler(c echo.Context) error {

62
templates/playlist.html Normal file
View File

@@ -0,0 +1,62 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="refresh" content="10">
<title>Playlist</title>
<link rel="stylesheet" href="/static/style/common.css">
<link rel="stylesheet" href="/static/style/videos.css">
{{template "footer-css" .}}
</head>
<body>
<h1>Playlist</h1>
<div class="video-list">
{{range .originals}}
<div class="video-card">
<div class="video-title">
{{if or (eq .Status "download completed") (eq .Status "transcoding") (eq .Status "completed")}}
<a href="/video/{{.ID}}">{{.Title}}</a>
{{else}}
{{.Title}}
{{end}}
</div>
<div class="video-info">{{.Artist}}</div>
<div class="video-info"><a href="{{.URL}}">{{.URL}}</a></div>
<div class="video-info">{{.Status}}</div>
<div class="video-info">
{{if .Audio}}
Audio
{{end}}
{{if .Video}}
Video
{{end}}
</div>
<div class="video-options">
{{if eq .Status "completed"}}
<form action="/video/{{.ID}}/process" method="post" style="display:inline;">
<button type="submit">Reprocess</button>
</form>
{{else if eq .Status "failed"}}
<form action="/video/{{.ID}}/restart" method="post" style="display:inline;">
<button type="submit">Restart</button>
</form>
{{else if eq .Status "downloading"}}
{{end}}
<form action="/video/{{.ID}}/delete" method="post" style="display:inline;">
<button type="submit">Delete</button>
</form>
</div>
</div>
{{end}}
</div>
<p><a href="/logout">Logout</a></p>
{{template "footer" .}}
</body>
</html>