140 lines
3.3 KiB
Go
140 lines
3.3 KiB
Go
package web
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log/slog"
|
|
"net/http"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"coda/internal/jobs"
|
|
"coda/internal/models"
|
|
)
|
|
|
|
func (h *Handler) Library(w http.ResponseWriter, r *http.Request) {
|
|
entries, err := h.scanner.Browse("")
|
|
if err != nil {
|
|
slog.Warn("library page browse failed, showing empty view", "error", err)
|
|
h.render.Render(w, "library.html", nil)
|
|
return
|
|
}
|
|
h.render.Render(w, "library.html", entries)
|
|
}
|
|
|
|
func (h *Handler) LibraryBrowse(w http.ResponseWriter, r *http.Request) {
|
|
relPath := r.URL.Query().Get("path")
|
|
|
|
entries, err := h.scanner.Browse(relPath)
|
|
if err != nil {
|
|
slog.Warn("library browse failed", "path", relPath, "error", err)
|
|
writeJSON(w, http.StatusOK, []any{})
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, entries)
|
|
}
|
|
|
|
func (h *Handler) LibraryScan(w http.ResponseWriter, r *http.Request) {
|
|
albums, err := h.scanner.Scan()
|
|
if err != nil {
|
|
slog.Error("library scan failed", "error", err)
|
|
http.Error(w, "internal error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
if r.Header.Get("HX-Request") != "" {
|
|
h.render.Render(w, "scan_results.html", albums)
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusOK, albums)
|
|
}
|
|
|
|
type convertRequest struct {
|
|
Paths []string `json:"paths"`
|
|
}
|
|
|
|
func (h *Handler) LibraryConvert(w http.ResponseWriter, r *http.Request) {
|
|
var paths []string
|
|
|
|
if strings.HasPrefix(r.Header.Get("Content-Type"), "application/json") {
|
|
var req convertRequest
|
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
http.Error(w, "bad request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
paths = req.Paths
|
|
} else {
|
|
if err := r.ParseForm(); err != nil {
|
|
http.Error(w, "bad request", http.StatusBadRequest)
|
|
return
|
|
}
|
|
paths = r.Form["paths"]
|
|
}
|
|
|
|
if len(paths) == 0 {
|
|
http.Error(w, "no paths provided", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
seen := map[string]struct{}{}
|
|
resolved := make([]string, 0, len(paths))
|
|
for _, relPath := range paths {
|
|
relPath = strings.TrimSpace(relPath)
|
|
if relPath == "" {
|
|
continue
|
|
}
|
|
if _, ok := seen[relPath]; ok {
|
|
continue
|
|
}
|
|
seen[relPath] = struct{}{}
|
|
|
|
absPath, err := h.scanner.Resolve(relPath)
|
|
if err != nil {
|
|
slog.Warn("library convert: invalid path", "path", relPath, "error", err)
|
|
http.Error(w, "invalid path: "+relPath, http.StatusBadRequest)
|
|
return
|
|
}
|
|
resolved = append(resolved, absPath)
|
|
}
|
|
if len(resolved) == 0 {
|
|
http.Error(w, "no paths provided", http.StatusBadRequest)
|
|
return
|
|
}
|
|
|
|
var jobIDs []string
|
|
for _, absPath := range resolved {
|
|
album := filepath.Base(absPath)
|
|
artist := filepath.Base(filepath.Dir(absPath))
|
|
|
|
id, err := h.enqueuer.Enqueue(r.Context(), jobs.EnqueueInput{
|
|
Artist: artist,
|
|
Album: album,
|
|
Path: absPath,
|
|
Source: models.SourceManual,
|
|
})
|
|
if err != nil {
|
|
slog.Error("library convert enqueue failed", "path", absPath, "error", err)
|
|
http.Error(w, "internal error", http.StatusInternalServerError)
|
|
return
|
|
}
|
|
|
|
jobIDs = append(jobIDs, id)
|
|
}
|
|
|
|
slog.Info("library convert completed", "jobs", jobIDs)
|
|
|
|
if r.Header.Get("HX-Request") != "" {
|
|
h.render.Render(w, "convert_result.html", len(jobIDs))
|
|
return
|
|
}
|
|
|
|
writeJSON(w, http.StatusAccepted, map[string]any{"job_ids": jobIDs})
|
|
}
|
|
|
|
func writeJSON(w http.ResponseWriter, status int, v any) {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(status)
|
|
json.NewEncoder(w).Encode(v)
|
|
}
|