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()) }