Skip to content

Commit adbe8ab

Browse files
Add basic html page to test audio streaming
1 parent a48b3cd commit adbe8ab

File tree

3 files changed

+75
-5
lines changed

3 files changed

+75
-5
lines changed

cmd/internal/routes/routes.go

Lines changed: 73 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ package routes
33
import (
44
"context"
55
"fmt"
6+
"html/template"
67
"net/http"
8+
"os"
9+
"path"
710
"strconv"
811

912
"github.com/jackc/pgx/v5/pgxpool"
@@ -19,15 +22,46 @@ type messageResponse struct {
1922
}
2023

2124
func RegisterRoutes(mux *http.ServeMux, logger logging.Logger) {
22-
mux.Handle("GET /api/v1", rootGetHandler(logger))
23-
mux.Handle("GET /api/v1/health", rootGetHandler(logger))
25+
mux.Handle("GET /api/v1", healthcheckHandler(logger))
26+
mux.Handle("GET /api/v1/health", healthcheckHandler(logger))
2427
mux.Handle("POST /api/v1/recitals", createRecitalHandler(logger))
2528
mux.Handle("GET /api/v1/recitals", listRecitalsHandler(logger))
2629
mux.Handle("GET /api/v1/recitals/{id}", getRecitalHandler(logger))
2730
mux.Handle("DELETE /api/v1/recitals/{id}", deleteRecitalHandler(logger))
31+
mux.Handle("GET /api/v1/recitals/{id}/listen", mainpageHandler(logger))
32+
mux.Handle("GET /api/v1/recitals/{id}/audio", streamRecitalAudioHandler(logger))
2833
}
2934

30-
func rootGetHandler(logger logging.Logger) http.Handler {
35+
func mainpageHandler(logger logging.Logger) http.Handler {
36+
page := template.Must(template.New("listen").Parse(`
37+
<!doctype html>
38+
<meta charset="utf-8" />
39+
<title>Listen</title>
40+
<body style="font-family: system-ui; padding: 2rem;">
41+
<h1>Listen</h1>
42+
<p>Now playing: {{.ID}}</p>
43+
<audio controls preload="auto" style="width: 100%;" src="/api/v1/recitals/{{.ID}}/audio"></audio>
44+
</body>
45+
`))
46+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
47+
id, err := strconv.Atoi(r.PathValue("id"))
48+
if err != nil {
49+
res := messageResponse{Message: "invalid id"}
50+
if err := marshal.Encode(r.Context(), w, r, http.StatusBadRequest, res); err != nil {
51+
writeErrHeader(w, err, logger)
52+
}
53+
return
54+
}
55+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
56+
w.WriteHeader(http.StatusOK)
57+
if err := page.Execute(w, struct{ ID int }{ID: id}); err != nil {
58+
http.Error(w, err.Error(), http.StatusInternalServerError)
59+
return
60+
}
61+
})
62+
}
63+
64+
func healthcheckHandler(logger logging.Logger) http.Handler {
3165
return http.HandlerFunc(
3266
func(w http.ResponseWriter, r *http.Request) {
3367
w.WriteHeader(http.StatusOK)
@@ -188,6 +222,42 @@ func deleteRecitalHandler(logger logging.Logger) http.Handler {
188222
})
189223
}
190224

225+
func streamRecitalAudioHandler(logger logging.Logger) http.Handler {
226+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
227+
id, err := strconv.Atoi(r.PathValue("id"))
228+
if err != nil {
229+
res := messageResponse{Message: "invalid id"}
230+
if err := marshal.Encode(r.Context(), w, r, http.StatusBadRequest, res); err != nil {
231+
writeErrHeader(w, err, logger)
232+
}
233+
return
234+
}
235+
236+
filename := path.Join(services.BaseOutputPath, fmt.Sprintf("%d_out.wav", id))
237+
file, err := os.Open(filename)
238+
if err != nil {
239+
if os.IsNotExist(err) {
240+
http.NotFound(w, r)
241+
} else {
242+
http.Error(w, "open failed", http.StatusInternalServerError)
243+
}
244+
return
245+
}
246+
defer file.Close()
247+
248+
stat, err := file.Stat()
249+
if err != nil {
250+
http.Error(w, "stat failed", http.StatusInternalServerError)
251+
return
252+
}
253+
254+
w.Header().Set("Content-Type", "audio/wav")
255+
w.Header().Set("Accept-Ranges", "bytes")
256+
w.Header().Set("Cache-Control", "public, max-age=3600")
257+
http.ServeContent(w, r, stat.Name(), stat.ModTime(), file)
258+
})
259+
}
260+
191261
func repondWithErrorMessage(w http.ResponseWriter, r *http.Request, message string, code int, logger logging.Logger) {
192262
res := messageResponse{
193263
Message: message,

cmd/internal/services/constants.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import "path"
44

55
const maxArticleContentCharLength int = 3500
66

7-
var baseOutputPath string = path.Join("data", "output", "generations", "audio")
7+
var BaseOutputPath string = path.Join("data", "output", "generations", "audio")
88

99
type Status string
1010

cmd/internal/services/recital_service.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ func generateRecital(id int32, url string, pool *pgxpool.Pool, logger logging.Lo
9494
defer stream.Close()
9595

9696
logger.Out.Println("Persisting stream")
97-
outPath := path.Join(baseOutputPath, fmt.Sprintf("%d_out.wav", id))
97+
outPath := path.Join(BaseOutputPath, fmt.Sprintf("%d_out.wav", id))
9898
if err := audio.Persist(stream, outPath); err != nil {
9999
return fmt.Errorf("failed persisting audio: %w", err)
100100
}

0 commit comments

Comments
 (0)