Add per-disk profiles with video transcoding support
Each disk stores .jukebox/profile.json with copy parameters (dest
folder, overwrite mode, file select, reserve space, auto-copy) and
optional transcoding limits for the target player (codec, resolution,
bitrate, FPS, audio channels, output format).
On copy, video files are probed with ffprobe; if they exceed the
profile limits they are transcoded via ffmpeg (-threads 0 for full
CPU usage), otherwise copied as-is. Scale filter never upscales.
New: internal/disk/profile.go, internal/transcoder/{detect,transcoder}.go
API: GET/PUT /api/disks/profile?mount_path=
UI: disk profile panel in dashboard for known disks
Dockerfile: adds ffmpeg to the runtime image
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"jukebox_maker/internal/config"
|
||||
"jukebox_maker/internal/disk"
|
||||
)
|
||||
|
||||
@@ -15,6 +16,7 @@ func (s *Server) diskResponse(info disk.DiskInfo) map[string]any {
|
||||
"total_bytes": info.TotalBytes,
|
||||
"free_bytes": info.FreeBytes,
|
||||
"mount_path": info.MountPath,
|
||||
"profile": info.Profile,
|
||||
}
|
||||
if info.DiskID != "" {
|
||||
if s.deps.OnDiskInit != nil {
|
||||
@@ -113,3 +115,39 @@ func (s *Server) handleDiskInit(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
jsonOK(w, map[string]string{"disk_id": diskID})
|
||||
}
|
||||
|
||||
func (s *Server) handleGetProfile(w http.ResponseWriter, r *http.Request) {
|
||||
mountPath := config.NormalizeMediaPath(r.URL.Query().Get("mount_path"))
|
||||
if mountPath == "" {
|
||||
jsonErr(w, http.StatusBadRequest, "mount_path is required")
|
||||
return
|
||||
}
|
||||
p, err := disk.LoadProfile(mountPath)
|
||||
if err != nil {
|
||||
jsonErr(w, http.StatusNotFound, "profile not found")
|
||||
return
|
||||
}
|
||||
jsonOK(w, p)
|
||||
}
|
||||
|
||||
func (s *Server) handlePutProfile(w http.ResponseWriter, r *http.Request) {
|
||||
mountPath := config.NormalizeMediaPath(r.URL.Query().Get("mount_path"))
|
||||
if mountPath == "" {
|
||||
jsonErr(w, http.StatusBadRequest, "mount_path is required")
|
||||
return
|
||||
}
|
||||
var p disk.DiskProfile
|
||||
if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
|
||||
jsonErr(w, http.StatusBadRequest, "invalid JSON: "+err.Error())
|
||||
return
|
||||
}
|
||||
if err := disk.SaveProfile(mountPath, &p); err != nil {
|
||||
jsonErr(w, http.StatusInternalServerError, "save profile: "+err.Error())
|
||||
return
|
||||
}
|
||||
// Обновляем информацию о диске в watcher если он там есть
|
||||
if s.deps.Watcher != nil {
|
||||
s.deps.Watcher.ProbeNow()
|
||||
}
|
||||
jsonOK(w, &p)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user