192 lines
5.0 KiB
Go
192 lines
5.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"git.netflux.io/rob/clipper/media"
|
|
"git.netflux.io/rob/clipper/youtube"
|
|
|
|
youtubev2 "github.com/kkdai/youtube/v2"
|
|
)
|
|
|
|
const (
|
|
ContentTypeApplicationJSON = "application/json"
|
|
DefaultHTTPBindAddr = "0.0.0.0:8888"
|
|
DefaultTimeout = 30 * time.Second
|
|
)
|
|
|
|
func handleRequest(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodGet {
|
|
w.WriteHeader(http.StatusMethodNotAllowed)
|
|
w.Write([]byte("method not allowed"))
|
|
return
|
|
}
|
|
|
|
w.Header().Add("Content-Type", ContentTypeApplicationJSON)
|
|
w.Header().Add("Access-Control-Allow-Origin", "*")
|
|
|
|
if strings.HasPrefix(r.URL.Path, "/api/media_sets") {
|
|
videoID := r.URL.Query().Get("video_id")
|
|
if videoID == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
w.Write([]byte(`{"error": "no video ID provided"}`))
|
|
return
|
|
}
|
|
|
|
mediaSet := new(media.MediaSet)
|
|
mediaSet.ID = videoID
|
|
|
|
if mediaSet.Exists() {
|
|
// just load the metadata.json file:
|
|
if err := mediaSet.Load(); err != nil {
|
|
log.Printf("error loading MediaSet: %v", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(`{"error": "could not fetch media"}`))
|
|
return
|
|
}
|
|
} else {
|
|
// download everything from YouTube:
|
|
var err error
|
|
var youtubeClient youtubev2.Client
|
|
downloader := youtube.NewDownloader(&youtubeClient)
|
|
log.Printf("background context = %p, req context = %p", context.Background(), r.Context())
|
|
mediaSet, err = downloader.Download(r.Context(), videoID)
|
|
if err != nil {
|
|
log.Printf("error downloading MediaSet: %v", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(`{"error": "could not fetch media"}`))
|
|
return
|
|
}
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
if err := json.NewEncoder(w).Encode(mediaSet); err != nil {
|
|
log.Printf("error encoding MediaSet: %v", err)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if strings.HasPrefix(r.URL.Path, "/api/audio") {
|
|
log.Printf("got headers for audio request: %+v", r.Header)
|
|
videoID := r.URL.Query().Get("video_id")
|
|
mediaSet := media.MediaSet{ID: videoID}
|
|
if err := mediaSet.Load(); err != nil {
|
|
log.Printf("error loading MediaSet: %v", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(`{"error": "could not fetch media"}`))
|
|
return
|
|
}
|
|
|
|
// TODO: ensure content-type matches the actual downloaded format.
|
|
w.Header().Set("Content-Type", "audio/webm")
|
|
http.ServeFile(w, r, mediaSet.EncodedAudioPath())
|
|
|
|
return
|
|
}
|
|
|
|
if strings.HasPrefix(r.URL.Path, "/api/video") {
|
|
videoID := r.URL.Query().Get("video_id")
|
|
mediaSet := media.MediaSet{ID: videoID}
|
|
if err := mediaSet.Load(); err != nil {
|
|
log.Printf("error loading MediaSet: %v", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(`{"error": "could not fetch media"}`))
|
|
return
|
|
}
|
|
|
|
http.ServeFile(w, r, mediaSet.VideoPath())
|
|
|
|
return
|
|
}
|
|
|
|
if strings.HasPrefix(r.URL.Path, "/api/thumbnails") {
|
|
videoID := r.URL.Query().Get("video_id")
|
|
mediaSet := media.MediaSet{ID: videoID}
|
|
if err := mediaSet.Load(); err != nil {
|
|
log.Printf("error loading MediaSet: %v", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(`{"error": "could not fetch media"}`))
|
|
return
|
|
}
|
|
|
|
w.Header().Set("Content-Type", "image/jpeg")
|
|
http.ServeFile(w, r, mediaSet.ThumbnailPath())
|
|
|
|
return
|
|
}
|
|
|
|
if strings.HasPrefix(r.URL.Path, "/api/peaks") {
|
|
videoID := r.URL.Query().Get("video_id")
|
|
if videoID == "" {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
w.Write([]byte(`{"error": "no video ID provided"}`))
|
|
return
|
|
}
|
|
|
|
start, err := strconv.ParseInt(r.URL.Query().Get("start"), 0, 64)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
w.Write([]byte(`{"error": "invalid start parameter provided"}`))
|
|
return
|
|
}
|
|
end, err := strconv.ParseInt(r.URL.Query().Get("end"), 0, 64)
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
w.Write([]byte(`{"error": "invalid end parameter provided"}`))
|
|
return
|
|
}
|
|
numBins, err := strconv.Atoi(r.URL.Query().Get("bins"))
|
|
if err != nil {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
w.Write([]byte(`{"error": "invalid bins parameter provided"}`))
|
|
return
|
|
}
|
|
|
|
mediaSet := media.MediaSet{ID: videoID}
|
|
if err = mediaSet.Load(); err != nil {
|
|
log.Printf("error loading MediaSet: %v", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(`{"error": "could not fetch media"}`))
|
|
return
|
|
}
|
|
|
|
peaks, err := mediaSet.Peaks(start, end, numBins)
|
|
if err != nil {
|
|
log.Printf("error generating peaks: %v", err)
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
w.Write([]byte(`{"error": "could not generate peaks"}`))
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
|
|
err = json.NewEncoder(w).Encode(peaks)
|
|
if err != nil {
|
|
log.Printf("error encoding peaks: %v", err)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
w.WriteHeader(http.StatusNotFound)
|
|
w.Write([]byte("page not found"))
|
|
}
|
|
|
|
func main() {
|
|
srv := http.Server{
|
|
ReadTimeout: DefaultTimeout,
|
|
WriteTimeout: DefaultTimeout,
|
|
Addr: DefaultHTTPBindAddr,
|
|
Handler: http.HandlerFunc(handleRequest),
|
|
}
|
|
|
|
log.Fatal(srv.ListenAndServe())
|
|
}
|