Add Library filter to Stats
All checks were successful
/ release (push) Successful in 1m7s

This commit is contained in:
speatzle 2024-10-08 11:30:04 +02:00
parent 9d2519c085
commit 7d037be106
4 changed files with 58 additions and 21 deletions

View file

@ -104,6 +104,7 @@ func Start(_conf config.Config, tmplFS embed.FS, staticFS embed.FS, migrationsFS
mux.HandleFunc("/ffmpeg_commands", handleFfmpegCommands) mux.HandleFunc("/ffmpeg_commands", handleFfmpegCommands)
mux.HandleFunc("/queue_enable", HandleSetQueueEnable) mux.HandleFunc("/queue_enable", HandleSetQueueEnable)
mux.HandleFunc("/stats", handleStats) mux.HandleFunc("/stats", handleStats)
mux.HandleFunc("/stats/{id}", handleStats)
mux.HandleFunc("/", handleIndex) mux.HandleFunc("/", handleIndex)

View file

@ -18,7 +18,9 @@ import (
) )
type StatsDisplay struct { type StatsDisplay struct {
Charts []ChartData Charts []ChartData
Libraries []Library
SelectedLibrary string
} }
type ChartData struct { type ChartData struct {
@ -83,14 +85,14 @@ func generatePie(name string, data []opts.PieData) ChartData {
} }
} }
func generateStats(ctx context.Context) ([]ChartData, error) { func generateStats(ctx context.Context, library_id string) ([]ChartData, error) {
data := []ChartData{} data := []ChartData{}
rows, err := db.Query(ctx, rows, err := db.Query(ctx,
`SELECT COALESCE(jsonb_path_query_first(ffprobe_data, '$.streams[*] ? (@.codec_type == "video") ? (@.disposition.attached_pic == 0).codec_name')::text, 'Unknown') AS name, COUNT(*) AS value `SELECT COALESCE(jsonb_path_query_first(ffprobe_data, '$.streams[*] ? (@.codec_type == "video") ? (@.disposition.attached_pic == 0).codec_name')::text, 'Unknown') AS name, COUNT(*) AS value
FROM files FROM files
WHERE ffprobe_data IS NOT NULL WHERE ffprobe_data IS NOT NULL AND ($1 = "" OR library_id = $1)
GROUP BY 1;`) GROUP BY 1;`, library_id)
if err != nil { if err != nil {
return nil, fmt.Errorf("Query Codecs: %w", err) return nil, fmt.Errorf("Query Codecs: %w", err)
} }
@ -111,8 +113,8 @@ func generateStats(ctx context.Context) ([]ChartData, error) {
rows, err = db.Query(ctx, rows, err = db.Query(ctx,
`SELECT COALESCE(jsonb_path_query_first(ffprobe_data, '$.streams[*] ? (@.codec_type == "video") ? (@.disposition.attached_pic == 0).width')::text, 'Unknown') AS name, COUNT(*) AS value `SELECT COALESCE(jsonb_path_query_first(ffprobe_data, '$.streams[*] ? (@.codec_type == "video") ? (@.disposition.attached_pic == 0).width')::text, 'Unknown') AS name, COUNT(*) AS value
FROM files FROM files
WHERE ffprobe_data IS NOT NULL WHERE ffprobe_data IS NOT NULL AND ($1 = "" OR library_id = $1)
GROUP BY 1;`) GROUP BY 1;`, library_id)
if err != nil { if err != nil {
return nil, fmt.Errorf("Query Resolution: %w", err) return nil, fmt.Errorf("Query Resolution: %w", err)
} }
@ -133,8 +135,8 @@ func generateStats(ctx context.Context) ([]ChartData, error) {
rows, err = db.Query(ctx, rows, err = db.Query(ctx,
`SELECT COALESCE(jsonb_path_query_first(ffprobe_data, '$.format.format_name')::text, 'Unknown') AS name, COUNT(*) AS value `SELECT COALESCE(jsonb_path_query_first(ffprobe_data, '$.format.format_name')::text, 'Unknown') AS name, COUNT(*) AS value
FROM files FROM files
WHERE ffprobe_data IS NOT NULL WHERE ffprobe_data IS NOT NULL AND ($1 = "" OR library_id = $1)
GROUP BY 1;`) GROUP BY 1;`, library_id)
if err != nil { if err != nil {
return nil, fmt.Errorf("Query Container: %w", err) return nil, fmt.Errorf("Query Container: %w", err)
} }
@ -154,8 +156,8 @@ func generateStats(ctx context.Context) ([]ChartData, error) {
rows, err = db.Query(ctx, rows, err = db.Query(ctx,
`SELECT health AS id, COUNT(*) AS value `SELECT health AS id, COUNT(*) AS value
FROM files FROM files WHERE ($1 = "" OR library_id = $1)
GROUP BY 1;`) GROUP BY 1;`, library_id)
if err != nil { if err != nil {
return nil, fmt.Errorf("Query Health: %w", err) return nil, fmt.Errorf("Query Health: %w", err)
} }
@ -175,8 +177,8 @@ func generateStats(ctx context.Context) ([]ChartData, error) {
rows, err = db.Query(ctx, rows, err = db.Query(ctx,
`SELECT transcode AS id, COUNT(*) AS value `SELECT transcode AS id, COUNT(*) AS value
FROM files FROM files WHERE ($1 = "" OR library_id = $1)
GROUP BY 1;`) GROUP BY 1;`, library_id)
if err != nil { if err != nil {
return nil, fmt.Errorf("Query Transcode: %w", err) return nil, fmt.Errorf("Query Transcode: %w", err)
} }
@ -195,9 +197,10 @@ func generateStats(ctx context.Context) ([]ChartData, error) {
data = append(data, generatePie("Transcode Status", res)) data = append(data, generatePie("Transcode Status", res))
rows, err = db.Query(ctx, rows, err = db.Query(ctx,
`SELECT status AS id, COUNT(*) AS value `SELECT tasks.status AS id, COUNT(*) AS value
FROM tasks FROM tasks INNER JOIN files ON files.id = tasks.file_id
GROUP BY 1;`) WHERE ($1 = "" OR files.library_id = $1)
GROUP BY 1;`, library_id)
if err != nil { if err != nil {
return nil, fmt.Errorf("Query Task Status: %w", err) return nil, fmt.Errorf("Query Task Status: %w", err)
} }
@ -222,11 +225,11 @@ func generateStats(ctx context.Context) ([]ChartData, error) {
} }
rows, err = db.Query(ctx, rows, err = db.Query(ctx,
`SELECT date_trunc('day', updated_at) date, status, COUNT(*) AS count `SELECT date_trunc('day', tasks.updated_at) date, tasks.status, COUNT(*) AS count
FROM tasks FROM tasks INNER JOIN files ON files.id = tasks.file_id
WHERE updated_at > current_date - 7 AND (status = $1 OR status = $2) WHERE ($1 = "" OR files.library_id = $1) AND tasks.updated_at > tasks.current_date - 7 AND (tasks.status = $2 OR tasks.status = $3)
GROUP BY 1,2 GROUP BY 1,2
ORDER BY date;`, constants.TASK_STATUS_SUCCESS, constants.TASK_STATUS_FAILED) ORDER BY date;`, library_id, constants.TASK_STATUS_SUCCESS, constants.TASK_STATUS_FAILED)
if err != nil { if err != nil {
return nil, fmt.Errorf("Query Task Status Day: %w", err) return nil, fmt.Errorf("Query Task Status Day: %w", err)
} }
@ -289,17 +292,36 @@ func generateStats(ctx context.Context) ([]ChartData, error) {
} }
func handleStats(w http.ResponseWriter, r *http.Request) { func handleStats(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
data, err := generateStats(r.Context()) data, err := generateStats(r.Context(), id)
if err != nil { if err != nil {
slog.ErrorContext(r.Context(), "Generate Stats:", "err", err) slog.ErrorContext(r.Context(), "Generate Stats:", "err", err)
http.Error(w, "Generate Stats: "+err.Error(), http.StatusInternalServerError) http.Error(w, "Generate Stats: "+err.Error(), http.StatusInternalServerError)
return return
} }
rows, err := db.Query(r.Context(),
`SELECT *
FROM libraries;`)
if err != nil {
slog.ErrorContext(r.Context(), "Query Libraries", "err", err)
http.Error(w, "Query Libraries: "+err.Error(), http.StatusInternalServerError)
return
}
libraries, err := pgx.CollectRows(rows, pgx.RowToStructByName[Library])
if err != nil {
slog.ErrorContext(r.Context(), "Collect Libraries", "err", err)
http.Error(w, "Collect Libraries: "+err.Error(), http.StatusInternalServerError)
return
}
buf := bytes.Buffer{} buf := bytes.Buffer{}
err = templates.ExecuteTemplate(&buf, constants.STATS_TEMPLATE_NAME, StatsDisplay{ err = templates.ExecuteTemplate(&buf, constants.STATS_TEMPLATE_NAME, StatsDisplay{
Charts: data, Libraries: libraries,
SelectedLibrary: id,
Charts: data,
}) })
if err != nil { if err != nil {
slog.ErrorContext(r.Context(), "Executing Stats Template", "err", err) slog.ErrorContext(r.Context(), "Executing Stats Template", "err", err)

View file

@ -0,0 +1,7 @@
function setLibrary(library_id) {
if (library_id === "") {
} else {
window.location.href = "/stats";
}
window.location.href = "/stats/" + library_id;
}

View file

@ -1,5 +1,12 @@
{{template "head"}} {{template "head"}}
<h2>Stats</h2> <h2>Stats</h2>
<label for="library">Library:</label>
<select id="library" name="library" onchange="setLibrary(this.value)" class="short-button">
<option {{if e .SelectedLibrary "" }} selected {{end}}value="">ALL</option>
{{range $l := .Libraries}}
<option {{if e .SelectedLibrary $l.ID }} selected {{end}}value="{{$l.ID}}">{{$l.Name}}</option>
{{end}}
</select>
<div class="stats"> <div class="stats">
{{range $c := .Charts}} {{range $c := .Charts}}
{{$c.Element}} {{$c.Script}} {{$c.Element}} {{$c.Script}}