video.go
1 package utils 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "log" 8 "net/http" 9 "os" 10 11 "github.com/h2non/filetype" 12 "github.com/labstack/echo/v4" 13 "github.com/minio/minio-go/v7" 14 "github.com/minio/minio-go/v7/pkg/credentials" 15 ) 16 17 type ( 18 MediaController struct { 19 s3 S3 20 } 21 ) 22 23 func NewMediaController(s3 S3) *MediaController { 24 return &MediaController{s3} 25 } 26 27 func (mc MediaController) ExtractFromS3(c echo.Context) error { 28 media_id := c.Param("id") 29 fmt.Println("media_id : ", media_id) 30 31 if _, err := os.Stat(mc.s3.Tmp + media_id + ".lock"); err == nil { 32 fmt.Println("File is being writing") 33 return c.File(mc.s3.Directory + "static/videos/tmp.mp4") 34 } 35 36 if _, err := os.Stat(mc.s3.Tmp + media_id); err == nil { 37 fmt.Printf("File exists\n") 38 tmpFile, err := os.OpenFile(mc.s3.Tmp+media_id, os.O_RDONLY, 0644) 39 defer tmpFile.Close() 40 if err != nil { 41 log.Println("error on open file : ", err) 42 } 43 return c.File(tmpFile.Name()) 44 } else { 45 fmt.Printf("File does not exist\n") 46 47 f, err := os.Create(mc.s3.Tmp + media_id + ".lock") 48 f.Close() 49 defer os.Remove(mc.s3.Tmp + media_id + ".lock") 50 51 endpoint := mc.s3.Endpoint 52 accessKeyID := mc.s3.AccessKeyID 53 secretAccessKey := mc.s3.SecretAccessKey 54 region := mc.s3.Region 55 useSSL := true 56 57 bucketName := mc.s3.Bucket 58 objectName := mc.s3.Directory + media_id 59 60 // Initialize minio client object. 61 minioClient, err := minio.New(endpoint, &minio.Options{ 62 Region: region, 63 Creds: credentials.NewStaticV4(accessKeyID, secretAccessKey, ""), 64 Secure: useSSL, 65 }) 66 if err != nil { 67 log.Println(err) 68 return c.String(http.StatusNotFound, "media not found") 69 } 70 71 object, err := minioClient.GetObject(context.Background(), bucketName, objectName, minio.GetObjectOptions{}) 72 if err != nil { 73 log.Println(err) 74 return c.String(http.StatusNotFound, "media not found") 75 } 76 defer object.Close() 77 78 tmpFile, err := os.OpenFile(mc.s3.Tmp+media_id, os.O_RDWR|os.O_CREATE, 0644) 79 defer tmpFile.Close() 80 // Check for errors when opening the file 81 if err != nil { 82 log.Println("error on open file : ", err) 83 return c.String(http.StatusInternalServerError, "Error on open file: "+err.Error()) 84 } 85 86 //fmt.Println("before copy S3 file to tmpFile") 87 if _, err := io.Copy(tmpFile, object); err != nil { 88 os.Remove(tmpFile.Name()) 89 log.Println(err) 90 return c.String(http.StatusNotFound, "media not found") 91 } 92 //fmt.Println("after copy S3 file to tmpFile") 93 return c.File(tmpFile.Name()) 94 } 95 } 96 97 // https://medium.com/@radhian.amri/video-streaming-using-http-206-partial-content-in-go-4e89d96abdd0 98 func (mc MediaController) PlayFrontMediaFromHTTP(c echo.Context) error { 99 media_id := c.Param("id") 100 //fmt.Println("media_id : ", media_id) 101 102 req, _ := http.NewRequest("GET", "http://127.0.0.1:8079/media/"+media_id, nil) 103 client := &http.Client{} 104 req.Header.Set("Range", c.Request().Header.Get("Range")) 105 resp, err := client.Do(req) 106 if err != nil { 107 log.Println(err) 108 return c.Redirect(http.StatusNotFound, "/404") 109 } 110 if resp.StatusCode == http.StatusLocked { 111 log.Println("media is LOCKED") 112 return c.Redirect(http.StatusLocked, "/") 113 } 114 if resp.StatusCode == http.StatusNotFound { 115 log.Println("media NOT found") 116 return c.Redirect(http.StatusTemporaryRedirect, "/404") 117 } 118 119 var l_contentType_ext, l_contentType_mime string 120 if _, err := os.Stat(mc.s3.Tmp + media_id + ".lock"); err == nil { 121 fmt.Println("using tmp.mp4") 122 l_contentType_mime = "video/mp4" 123 } else { 124 l_contentType_ext, l_contentType_mime = mc.getMimeType(media_id) 125 fmt.Printf("File type: %s. MIME: %s\n", l_contentType_ext, l_contentType_mime) 126 } 127 128 c.Response().Header().Set(echo.HeaderContentLength, resp.Header.Get("Content-Length")) 129 contentRange := resp.Header.Get("Content-Range") 130 if contentRange != "" { 131 c.Response().Header().Set("Content-Range", contentRange) 132 } 133 defer resp.Body.Close() 134 return c.Stream(resp.StatusCode, l_contentType_mime, resp.Body) 135 } 136 137 func (mc MediaController) getMimeType(media_id string) (string, string) { 138 dStream, err := os.OpenFile("/tmp/"+media_id, os.O_RDONLY, 0644) 139 defer dStream.Close() 140 // Check for errors when opening the file 141 if err != nil { 142 log.Println("error on open file : ", err.Error()) 143 return "", "" 144 } 145 146 fileBytes := make([]byte, 261) 147 if _, err := dStream.Read(fileBytes); err != nil { 148 panic(err) 149 } 150 contentType, _ := filetype.Match(fileBytes) 151 152 if contentType == filetype.Unknown { 153 fmt.Println("Unknown file type") 154 } 155 156 return contentType.Extension, contentType.MIME.Value 157 }