This commit is contained in:
kenneth
2023-12-01 02:46:50 +00:00
parent 90e6ba5070
commit 1bb57bc94a
26 changed files with 1312 additions and 169 deletions

View File

@@ -14,9 +14,9 @@ const (
type CtxTypeUser string
type authorize struct {
AuthID string `json:"auth_id"`
AuthName string `json:"auth_name"`
type Authorize struct {
ID string `json:"id"`
Name string `json:"name"`
}
func genId() string {

View File

@@ -1,20 +1,39 @@
package handlers
import (
"log"
"net/http"
"strings"
"github.com/zhang2092/mediahls/internal/db"
)
type pageData struct {
AuthID string
AuthName string
Authorize
Videos []db.Video
}
func (server *Server) home(w http.ResponseWriter, r *http.Request) {
pd := pageData{}
auth, err := server.withCookie(r)
if err == nil {
pd.AuthID = auth.AuthID
pd.AuthName = auth.AuthName
pd.Authorize = *auth
}
ctx := r.Context()
videos, err := server.store.ListVideos(ctx, db.ListVideosParams{
Limit: 100,
Offset: 0,
})
if err == nil {
for _, item := range videos {
if len(item.Description) > 65 {
temp := strings.TrimSpace(item.Description[0:65]) + "..."
item.Description = temp
log.Println(item.Description)
}
pd.Videos = append(pd.Videos, item)
}
}
renderHome(w, pd)
}

View File

@@ -30,14 +30,14 @@ func (server *Server) authorizeMiddleware(next http.Handler) http.Handler {
})
}
func (server *Server) withCookie(r *http.Request) (*authorize, error) {
func (server *Server) withCookie(r *http.Request) (*Authorize, error) {
cookie, err := r.Cookie(AuthorizeCookie)
if err != nil {
log.Printf("get cookie: %v", err)
return nil, err
}
u := &authorize{}
u := &Authorize{}
err = server.secureCookie.Decode(AuthorizeCookie, cookie.Value, u)
if err != nil {
log.Printf("secure decode cookie: %v", err)
@@ -47,19 +47,13 @@ func (server *Server) withCookie(r *http.Request) (*authorize, error) {
return u, nil
}
func withUser(ctx context.Context) *authorize {
var result authorize
func withUser(ctx context.Context) Authorize {
var result Authorize
ctxValue, err := convert.ToByteE(ctx.Value(ContextUser))
log.Printf("ctx: %s", ctxValue)
if err != nil {
log.Printf("1: %v", err)
return nil
}
err = json.Unmarshal(ctxValue, &result)
if err != nil {
log.Printf("2: %v", err)
return nil
return result
}
return &result
json.Unmarshal(ctxValue, &result)
return result
}

View File

@@ -55,6 +55,7 @@ func (server *Server) setupRouter() {
router := mux.NewRouter()
router.Use(mux.CORSMethodMiddleware(router))
router.PathPrefix("/statics/").Handler(http.StripPrefix("/statics/", http.FileServer(http.Dir("web/statics"))))
router.PathPrefix("/upload/imgs").Handler(http.StripPrefix("/upload/imgs/", http.FileServer(http.Dir("upload/imgs"))))
router.HandleFunc("/register", server.registerView).Methods(http.MethodGet)
router.HandleFunc("/register", server.register).Methods(http.MethodPost)
@@ -68,8 +69,17 @@ func (server *Server) setupRouter() {
subRouter := router.PathPrefix("/").Subrouter()
subRouter.Use(server.authorizeMiddleware)
subRouter.HandleFunc("/upload", server.uploadView).Methods(http.MethodGet)
subRouter.HandleFunc("/upload", server.upload).Methods(http.MethodPost)
subRouter.HandleFunc("/me/videos", server.videosView).Methods(http.MethodGet)
subRouter.HandleFunc("/me/videos/p{page}", server.videosView).Methods(http.MethodGet)
subRouter.HandleFunc("/me/videos/create", server.createVideoView).Methods(http.MethodGet)
subRouter.HandleFunc("/me/videos/create/{xid}", server.createVideoView).Methods(http.MethodGet)
subRouter.HandleFunc("/me/videos/create", server.createVideo).Methods(http.MethodPost)
subRouter.HandleFunc("/upload_image", server.uploadImage).Methods(http.MethodPost)
subRouter.HandleFunc("/upload_file", server.uploadVideo).Methods(http.MethodPost)
subRouter.HandleFunc("/transfer/{xid}", server.transferView).Methods(http.MethodGet)
subRouter.HandleFunc("/transfer/{xid}", server.transfer).Methods(http.MethodPost)
server.router = router
}

View File

@@ -1,6 +1,8 @@
package handlers
import (
"bufio"
"errors"
"io"
"log"
"net/http"
@@ -13,17 +15,7 @@ import (
"github.com/zhang2092/mediahls/internal/pkg/fileutil"
)
func (server *Server) uploadView(w http.ResponseWriter, r *http.Request) {
user := withUser(r.Context())
log.Printf("%v", user)
renderUpload(w, nil)
}
func renderUpload(w http.ResponseWriter, data any) {
render(w, data, "web/templates/me/upload.html.tmpl", "web/templates/base/header.html.tmpl", "web/templates/base/footer.html.tmpl")
}
func (server *Server) upload(w http.ResponseWriter, r *http.Request) {
func (server *Server) uploadVideo(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
file, fileHeader, err := r.FormFile("file")
@@ -56,7 +48,8 @@ func (server *Server) upload(w http.ResponseWriter, r *http.Request) {
return
}
dir := path.Join("upload", time.Now().Format("20060102"))
curTime := time.Now()
dir := path.Join("upload", "files", curTime.Format("2006"), curTime.Format("01"), curTime.Format("02"))
exist, _ := fileutil.PathExists(dir)
if !exist {
err := os.MkdirAll(dir, os.ModePerm)
@@ -87,6 +80,60 @@ func (server *Server) upload(w http.ResponseWriter, r *http.Request) {
return
}
w.WriteHeader(http.StatusOK)
_, err = w.Write([]byte("/" + filePath))
if err != nil {
log.Printf("%v", err)
}
}
func (server *Server) uploadImage(w http.ResponseWriter, r *http.Request) {
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(r.Body)
_, fh, err := r.FormFile("file")
if err != nil {
w.WriteHeader(http.StatusBadRequest)
_, err = w.Write([]byte(err.Error()))
if err != nil {
log.Printf("%v", err)
}
return
}
f, err := fh.Open()
if err != nil {
log.Printf("%v", err)
w.WriteHeader(http.StatusInternalServerError)
_, err = w.Write([]byte("读取图片失败"))
if err != nil {
log.Printf("%v", err)
}
return
}
reader := bufio.NewReader(f)
filePath, err := fileutil.UploadImage(reader)
if errors.Is(err, fileutil.ErrUnsupportedFileFormat) {
log.Printf("%v", err)
w.WriteHeader(http.StatusUnsupportedMediaType)
_, err = w.Write([]byte(fileutil.ErrUnsupportedFileFormat.Error()))
if err != nil {
log.Printf("%v", err)
}
return
}
if err != nil {
log.Printf("%v", err)
w.WriteHeader(http.StatusInternalServerError)
_, err = w.Write([]byte(err.Error()))
if err != nil {
log.Printf("%v", err)
}
return
}
w.WriteHeader(http.StatusOK)
_, err = w.Write([]byte(filePath))
if err != nil {

View File

@@ -61,6 +61,7 @@ func viladatorRegister(email, username, password string) (*respErrs, bool) {
}
type respErrs struct {
Authorize
Summary string
Email string
Username string
@@ -191,7 +192,7 @@ func (server *Server) login(w http.ResponseWriter, r *http.Request) {
return
}
encoded, err := server.secureCookie.Encode(AuthorizeCookie, &authorize{AuthID: user.ID, AuthName: user.Username})
encoded, err := server.secureCookie.Encode(AuthorizeCookie, &Authorize{ID: user.ID, Name: user.Username})
if err != nil {
errs.Summary = "请求网络错误,请刷新重试(cookie)"
renderLogin(w, errs)

View File

@@ -1,28 +1,36 @@
package handlers
import (
"context"
"fmt"
"net/http"
"strconv"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/zhang2092/mediahls/internal/db"
"github.com/zhang2092/mediahls/internal/pkg/convert"
"github.com/zhang2092/mediahls/internal/pkg/fileutil"
"github.com/zhang2092/mediahls/internal/pkg/logger"
)
type playData struct {
AuthID string
AuthName string
Url string
Authorize
Url string
Video db.Video
}
func (server *Server) play(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
xid := vars["xid"]
video, _ := server.store.GetVideo(r.Context(), xid)
data := playData{
Url: "/media/" + xid + "/stream/",
Video: video,
}
auth, err := server.withCookie(r)
if err == nil {
data.AuthID = auth.AuthID
data.AuthName = auth.AuthName
data.Authorize = *auth
}
render(w, data, "web/templates/video/play.html.tmpl", "web/templates/base/header.html.tmpl", "web/templates/base/footer.html.tmpl")
}
@@ -43,10 +51,7 @@ http.ServeContent(w, r, "git", time.Now(), video)
func (server *Server) stream(response http.ResponseWriter, request *http.Request) {
vars := mux.Vars(request)
mId := vars["xid"]
fmt.Println(mId)
segName, ok := vars["segName"]
if !ok {
mediaBase := getMediaBase(mId)
m3u8Name := "index.m3u8"
@@ -55,29 +60,261 @@ func (server *Server) stream(response http.ResponseWriter, request *http.Request
mediaBase := getMediaBase(mId)
serveHlsTs(response, request, mediaBase, segName)
}
}
func getMediaBase(mId string) string {
mediaRoot := "media"
return fmt.Sprintf("%s/%s", mediaRoot, mId)
}
func serveHlsM3u8(w http.ResponseWriter, r *http.Request, mediaBase, m3u8Name string) {
fmt.Println("serveHlsM3u8...")
mediaFile := fmt.Sprintf("%s/%s", mediaBase, m3u8Name)
fmt.Println(mediaFile)
http.ServeFile(w, r, mediaFile)
w.Header().Set("Content-Type", "application/x-mpegURL")
}
func serveHlsTs(w http.ResponseWriter, r *http.Request, mediaBase, segName string) {
fmt.Println("serveHlsTs...")
mediaFile := fmt.Sprintf("%s/%s", mediaBase, segName)
fmt.Println(mediaFile)
http.ServeFile(w, r, mediaFile)
w.Header().Set("Content-Type", "video/MP2T")
}
type meVideoData struct {
Authorize
Videos []db.Video
}
func (server *Server) videosView(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
data := meVideoData{
Authorize: withUser(ctx),
}
vars := mux.Vars(r)
page, err := strconv.Atoi(vars["page"])
if err != nil {
page = 1
}
videos, err := server.store.ListVideosByUser(ctx, db.ListVideosByUserParams{
UserID: data.Authorize.ID,
Limit: 16,
Offset: int32((page - 1) * 16),
})
if err == nil {
for _, item := range videos {
if len(item.Description) > 65 {
temp := strings.TrimSpace(item.Description[0:65]) + "..."
item.Description = temp
}
data.Videos = append(data.Videos, item)
}
}
render(w, data, "web/templates/me/videos.html.tmpl", "web/templates/base/header.html.tmpl", "web/templates/base/footer.html.tmpl")
}
func (server *Server) createVideoView(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
xid := vars["xid"]
vm := videoCreateResp{
Authorize: withUser(r.Context()),
}
if len(xid) > 0 {
if v, err := server.store.GetVideo(r.Context(), xid); err == nil {
vm.ID = v.ID
vm.Title = v.Title
vm.Images = v.Images
vm.Description = v.Description
vm.OriginLink = v.OriginLink
vm.Status = int(v.Status)
}
}
renderCreateVideo(w, vm)
}
func renderCreateVideo(w http.ResponseWriter, data any) {
render(w, data, "web/templates/video/edit.html.tmpl", "web/templates/base/header.html.tmpl", "web/templates/base/footer.html.tmpl")
}
type videoCreateResp struct {
Authorize
Summary string
ID string
IDErr string
Title string
TitleErr string
Images string
ImagesErr string
Description string
DescriptionErr string
OriginLink string
OriginLinkErr string
Status int
StatusErr string
}
func viladatorCreateVedio(r *http.Request) (*videoCreateResp, bool) {
ok := true
status, _ := strconv.Atoi(r.PostFormValue("status"))
errs := &videoCreateResp{
Authorize: withUser(r.Context()),
ID: r.PostFormValue("id"),
Title: r.PostFormValue("title"),
Images: r.PostFormValue("images"),
Description: r.PostFormValue("description"),
OriginLink: r.PostFormValue("origin_link"),
Status: status,
}
if len(errs.Title) == 0 {
errs.TitleErr = "请填写正确的标题"
ok = false
}
exist, _ := fileutil.PathExists(strings.TrimPrefix(errs.Images, "/"))
if !exist {
errs.ImagesErr = "请先上传图片"
ok = false
}
if len(errs.Description) == 0 {
errs.DescriptionErr = "请填写描述"
ok = false
}
exist, _ = fileutil.PathExists(strings.TrimPrefix(errs.OriginLink, "/"))
if !exist {
errs.OriginLinkErr = "请先上传视频"
ok = false
}
return errs, ok
}
func (server *Server) createVideo(w http.ResponseWriter, r *http.Request) {
defer r.Body.Close()
if err := r.ParseForm(); err != nil {
renderCreateVideo(w, videoCreateResp{Summary: "请求网络错误, 请刷新重试"})
return
}
vm, ok := viladatorCreateVedio(r)
if !ok {
renderCreateVideo(w, vm)
return
}
curTime := time.Now()
ctx := r.Context()
u := withUser(ctx)
if len(vm.ID) == 0 {
_, err := server.store.CreateVideo(ctx, db.CreateVideoParams{
ID: genId(),
Title: vm.Title,
Description: vm.Description,
Images: vm.Images,
OriginLink: vm.OriginLink,
PlayLink: "",
UserID: u.ID,
CreateBy: u.Name,
})
if err != nil {
vm.Summary = "添加视频失败"
renderCreateVideo(w, vm)
return
}
} else {
_, err := server.store.UpdateVideo(ctx, db.UpdateVideoParams{
ID: vm.ID,
Title: vm.Title,
Description: vm.Description,
Images: vm.Images,
Status: int32(vm.Status),
UpdateAt: curTime,
UpdateBy: u.Name,
})
if err != nil {
vm.Summary = "更新视频失败"
renderCreateVideo(w, vm)
return
}
}
http.Redirect(w, r, "/me/videos", http.StatusFound)
}
type transferData struct {
Authorize
Video db.Video
}
func (server *Server) transferView(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
xid := vars["xid"]
v, _ := server.store.GetVideo(r.Context(), xid)
data := transferData{
Video: v,
}
u, err := server.withCookie(r)
if err == nil {
data.Authorize = *u
}
render(w, data, "web/templates/video/transfer.html.tmpl", "web/templates/base/header.html.tmpl", "web/templates/base/footer.html.tmpl")
}
func (server *Server) transfer(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
xid := vars["xid"]
ctx := r.Context()
v, err := server.store.GetVideo(ctx, xid)
if err != nil {
http.Error(w, "视频信息错误", http.StatusInternalServerError)
return
}
u := withUser(ctx)
v, err = server.store.UpdateVideoStatus(ctx, db.UpdateVideoStatusParams{
ID: v.ID,
Status: 1,
UpdateAt: time.Now(),
UpdateBy: u.Name,
})
if err != nil {
http.Error(w, "视频转码错误", http.StatusInternalServerError)
return
}
go func(v db.Video, name string) {
ctx := context.Background()
err := convert.ConvertHLS("media/"+v.ID+"/", strings.TrimPrefix(v.OriginLink, "/"))
if err != nil {
logger.Logger.Errorf("Convert HLS [%s]-[%s]: %v", v.ID, v.OriginLink, err)
_, _ = server.store.UpdateVideoStatus(ctx, db.UpdateVideoStatusParams{
ID: v.ID,
Status: 2,
UpdateAt: time.Now(),
UpdateBy: name,
})
return
}
// 转码成功
if _, err = server.store.SetVideoPlay(ctx, db.SetVideoPlayParams{
ID: v.ID,
Status: 200,
PlayLink: "/media/" + v.ID + "/stream/",
UpdateAt: time.Now(),
UpdateBy: name,
}); err != nil {
logger.Logger.Errorf("Set Video Play [%s]-[%s]: %v", v.ID, v.OriginLink, err)
return
}
logger.Logger.Infof("[%s]-[%s] 转码完成", v.ID, v.OriginLink)
}(v, u.Name)
w.WriteHeader(http.StatusOK)
w.Write([]byte("视频正在转码中, 请稍后刷新页面"))
}