191 lines
4.6 KiB
Go
191 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"github.com/aws/aws-sdk-go-v2/config"
|
|
"github.com/aws/aws-sdk-go-v2/service/s3"
|
|
"github.com/bootdotdev/learn-file-storage-s3-golang-starter/internal/database"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"os/exec"
|
|
|
|
"github.com/joho/godotenv"
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
type apiConfig struct {
|
|
db database.Client
|
|
jwtSecret string
|
|
platform string
|
|
filepathRoot string
|
|
assetsRoot string
|
|
s3Client *s3.Client
|
|
s3Bucket string
|
|
s3Region string
|
|
s3CfDistribution string
|
|
port string
|
|
}
|
|
|
|
type thumbnail struct {
|
|
data []byte
|
|
mediaType string
|
|
}
|
|
|
|
func getVideoAspectRatio(filePath string) (string, error) {
|
|
cmd := exec.Command("ffprobe", "-v", "error", "-print_format", "json", "-show_streams", filePath)
|
|
var buffer bytes.Buffer
|
|
cmd.Stdout = &buffer
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
type Stream struct {
|
|
Index int `json:"index"`
|
|
Width int `json:"width"`
|
|
Height int `json:"height"`
|
|
DisplayAspectRatio string `json:"display_aspect_ratio"`
|
|
}
|
|
|
|
type VideoInfo struct {
|
|
Streams []Stream `json:"streams"`
|
|
}
|
|
|
|
var videoInfo VideoInfo
|
|
|
|
err = json.Unmarshal(buffer.Bytes(), &videoInfo)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return "", err
|
|
}
|
|
|
|
return videoInfo.Streams[0].DisplayAspectRatio, nil
|
|
|
|
}
|
|
|
|
func processVideoForFastStart(filePath string) (string, error) {
|
|
newName := filePath + ".processing"
|
|
cmd := exec.Command("ffmpeg", "-i", filePath, "-c", "copy", "-movflags", "faststart", "-f", "mp4", newName)
|
|
var buffer bytes.Buffer
|
|
cmd.Stdout = &buffer
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return newName, nil
|
|
}
|
|
|
|
func main() {
|
|
godotenv.Load(".env")
|
|
|
|
pathToDB := os.Getenv("DB_PATH")
|
|
if pathToDB == "" {
|
|
log.Fatal("DB_URL must be set")
|
|
}
|
|
|
|
db, err := database.NewClient(pathToDB)
|
|
if err != nil {
|
|
log.Fatalf("Couldn't connect to database: %v", err)
|
|
}
|
|
|
|
jwtSecret := os.Getenv("JWT_SECRET")
|
|
if jwtSecret == "" {
|
|
log.Fatal("JWT_SECRET environment variable is not set")
|
|
}
|
|
|
|
platform := os.Getenv("PLATFORM")
|
|
if platform == "" {
|
|
log.Fatal("PLATFORM environment variable is not set")
|
|
}
|
|
|
|
filepathRoot := os.Getenv("FILEPATH_ROOT")
|
|
if filepathRoot == "" {
|
|
log.Fatal("FILEPATH_ROOT environment variable is not set")
|
|
}
|
|
|
|
assetsRoot := os.Getenv("ASSETS_ROOT")
|
|
if assetsRoot == "" {
|
|
log.Fatal("ASSETS_ROOT environment variable is not set")
|
|
}
|
|
|
|
s3Bucket := os.Getenv("S3_BUCKET")
|
|
if s3Bucket == "" {
|
|
log.Fatal("S3_BUCKET environment variable is not set")
|
|
}
|
|
|
|
s3Region := os.Getenv("S3_REGION")
|
|
if s3Region == "" {
|
|
log.Fatal("S3_REGION environment variable is not set")
|
|
}
|
|
|
|
s3CfDistribution := os.Getenv("S3_CF_DISTRO")
|
|
if s3CfDistribution == "" {
|
|
log.Fatal("S3_CF_DISTRO environment variable is not set")
|
|
}
|
|
|
|
port := os.Getenv("PORT")
|
|
if port == "" {
|
|
log.Fatal("PORT environment variable is not set")
|
|
}
|
|
|
|
cfg := apiConfig{
|
|
db: db,
|
|
jwtSecret: jwtSecret,
|
|
platform: platform,
|
|
filepathRoot: filepathRoot,
|
|
assetsRoot: assetsRoot,
|
|
s3Bucket: s3Bucket,
|
|
s3Region: s3Region,
|
|
s3CfDistribution: s3CfDistribution,
|
|
port: port,
|
|
}
|
|
|
|
err = cfg.ensureAssetsDir()
|
|
if err != nil {
|
|
log.Fatalf("Couldn't create assets directory: %v", err)
|
|
}
|
|
|
|
awsCfg, err := config.LoadDefaultConfig(context.Background(), config.WithRegion(cfg.s3Region))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
client := s3.NewFromConfig(awsCfg)
|
|
|
|
cfg.s3Client = client
|
|
|
|
mux := http.NewServeMux()
|
|
appHandler := http.StripPrefix("/app", http.FileServer(http.Dir(filepathRoot)))
|
|
mux.Handle("/app/", appHandler)
|
|
|
|
assetsHandler := http.StripPrefix("/assets", http.FileServer(http.Dir(assetsRoot)))
|
|
mux.Handle("/assets/", cacheMiddleware(assetsHandler))
|
|
|
|
mux.HandleFunc("POST /api/login", cfg.handlerLogin)
|
|
mux.HandleFunc("POST /api/refresh", cfg.handlerRefresh)
|
|
mux.HandleFunc("POST /api/revoke", cfg.handlerRevoke)
|
|
|
|
mux.HandleFunc("POST /api/users", cfg.handlerUsersCreate)
|
|
|
|
mux.HandleFunc("POST /api/videos", cfg.handlerVideoMetaCreate)
|
|
mux.HandleFunc("POST /api/thumbnail_upload/{videoID}", cfg.handlerUploadThumbnail)
|
|
mux.HandleFunc("POST /api/video_upload/{videoID}", cfg.handlerUploadVideo)
|
|
mux.HandleFunc("GET /api/videos", cfg.handlerVideosRetrieve)
|
|
mux.HandleFunc("GET /api/videos/{videoID}", cfg.handlerVideoGet)
|
|
mux.HandleFunc("DELETE /api/videos/{videoID}", cfg.handlerVideoMetaDelete)
|
|
|
|
mux.HandleFunc("POST /admin/reset", cfg.handlerReset)
|
|
|
|
srv := &http.Server{
|
|
Addr: ":" + port,
|
|
Handler: mux,
|
|
}
|
|
|
|
log.Printf("Serving on: http://localhost:%s/app/\n", port)
|
|
log.Fatal(srv.ListenAndServe())
|
|
}
|