feat: support player

This commit is contained in:
yanghao05
2025-04-25 14:38:55 +08:00
parent 11bea6b8c9
commit 75865ae19a
7 changed files with 151 additions and 12 deletions

View File

@@ -13,9 +13,9 @@ import (
"github.com/go-pay/gopay/wechat/v3" "github.com/go-pay/gopay/wechat/v3"
"github.com/gofiber/fiber/v3" "github.com/gofiber/fiber/v3"
"github.com/gofiber/fiber/v3/log"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/samber/lo" "github.com/samber/lo"
log "github.com/sirupsen/logrus"
) )
type ListQuery struct { type ListQuery struct {
@@ -95,8 +95,10 @@ type PostItem struct {
// @Router /api/posts/:id [get] // @Router /api/posts/:id [get]
// @Bind id path // @Bind id path
func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*PostItem, error) { func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*PostItem, error) {
log.Infof("Fetching post with ID: %d", id)
post, err := models.Posts.GetByID(ctx.Context(), id) post, err := models.Posts.GetByID(ctx.Context(), id)
if err != nil { if err != nil {
log.WithError(err).Errorf("GetByID err: %v", err)
return nil, err return nil, err
} }
@@ -126,6 +128,43 @@ func (ctl *posts) Show(ctx fiber.Ctx, id int64) (*PostItem, error) {
}, nil }, nil
} }
type PlayUrl struct {
Url string `json:"url"`
}
// Play
// @Router /api/posts/:id/play [get]
// @Bind id path
// @Bind user local
func (ctl *posts) Play(ctx fiber.Ctx, id int64, user *model.Users) (*PlayUrl, error) {
log.Infof("Fetching play URL for post ID: %d", id)
post, err := models.Posts.GetByID(ctx.Context(), id)
if err != nil {
return nil, err
}
preview := false
bought, err := models.Users.HasBought(ctx.Context(), user.ID, post.ID)
if !bought || err != nil {
preview = true
}
for _, asset := range post.Assets.Data {
if asset.Type == "video/mp4" && asset.Metas.Short == preview {
media, err := models.Medias.GetByID(ctx.Context(), asset.Media)
if err != nil {
return nil, err
}
url, err := ctl.oss.GetSignedUrl(ctx.Context(), media.Path)
if err != nil {
return nil, err
}
return &PlayUrl{Url: url}, nil
}
}
return nil, errors.New("视频不存在")
}
// Mine posts // Mine posts
// @Router /api/posts/mine [get] // @Router /api/posts/mine [get]
// @Bind pagination query // @Bind pagination query

View File

@@ -63,6 +63,12 @@ func (r *Routes) Register(router fiber.Router) {
PathParam[int64]("id"), PathParam[int64]("id"),
)) ))
router.Get("/api/posts/:id/play", DataFunc2(
r.posts.Play,
PathParam[int64]("id"),
Local[*model.Users]("user"),
))
router.Get("/api/posts/mine", DataFunc2( router.Get("/api/posts/mine", DataFunc2(
r.posts.Mine, r.posts.Mine,
Query[requests.Pagination]("pagination"), Query[requests.Pagination]("pagination"),

View File

@@ -283,3 +283,25 @@ func (m *usersModel) GetUsersMapByIDs(ctx context.Context, ids []int64) (map[int
return item.ID, item return item.ID, item
}), nil }), nil
} }
// HasBought
func (m *usersModel) HasBought(ctx context.Context, userID, postID int64) (bool, error) {
tbl := table.UserPosts
stmt := tbl.
SELECT(tbl.ID).
WHERE(
tbl.UserID.EQ(Int64(userID)).AND(
tbl.PostID.EQ(Int64(postID)),
),
)
m.log.Infof("sql: %s", stmt.DebugSql())
var userPost model.UserPosts
if err := stmt.QueryContext(ctx, db, &userPost); err != nil {
m.log.Errorf("error querying user post: %v", err)
return false, err
}
return userPost.ID > 0, nil
}

View File

@@ -9,6 +9,7 @@
"axios": "^1.8.4", "axios": "^1.8.4",
"dplayer": "^1.27.1", "dplayer": "^1.27.1",
"pinia": "^3.0.2", "pinia": "^3.0.2",
"plyr": "^3.7.8",
"tailwindcss": "^4.1.4", "tailwindcss": "^4.1.4",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-icons-plus": "^0.1.8", "vue-icons-plus": "^0.1.8",
@@ -209,8 +210,12 @@
"copy-anything": ["copy-anything@3.0.5", "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="], "copy-anything": ["copy-anything@3.0.5", "https://registry.npmmirror.com/copy-anything/-/copy-anything-3.0.5.tgz", { "dependencies": { "is-what": "^4.1.8" } }, "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w=="],
"core-js": ["core-js@3.41.0", "https://registry.npmmirror.com/core-js/-/core-js-3.41.0.tgz", {}, "sha512-SJ4/EHwS36QMJd6h/Rg+GyR4A5xE0FSI3eZ+iBVpfqf1x0eTSg1smWLHrA+2jQThZSh97fmSgFSU8B61nxosxA=="],
"csstype": ["csstype@3.1.3", "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], "csstype": ["csstype@3.1.3", "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
"custom-event-polyfill": ["custom-event-polyfill@1.0.7", "https://registry.npmmirror.com/custom-event-polyfill/-/custom-event-polyfill-1.0.7.tgz", {}, "sha512-TDDkd5DkaZxZFM8p+1I3yAlvM3rSr1wbrOliG4yJiwinMZN8z/iGL7BTlDkrJcYTmgUSb4ywVCc3ZaUtOtC76w=="],
"delayed-stream": ["delayed-stream@1.0.0", "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], "delayed-stream": ["delayed-stream@1.0.0", "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
"detect-libc": ["detect-libc@2.0.3", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.3.tgz", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], "detect-libc": ["detect-libc@2.0.3", "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.0.3.tgz", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="],
@@ -293,6 +298,8 @@
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.2", "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="], "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.29.2", "https://registry.npmmirror.com/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", { "os": "win32", "cpu": "x64" }, "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA=="],
"loadjs": ["loadjs@4.3.0", "https://registry.npmmirror.com/loadjs/-/loadjs-4.3.0.tgz", {}, "sha512-vNX4ZZLJBeDEOBvdr2v/F+0aN5oMuPu7JTqrMwp+DtgK+AryOlpy6Xtm2/HpNr+azEa828oQjOtWsB6iDtSfSQ=="],
"magic-string": ["magic-string@0.30.17", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], "magic-string": ["magic-string@0.30.17", "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.17.tgz", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="],
"math-intrinsics": ["math-intrinsics@1.1.0", "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "math-intrinsics": ["math-intrinsics@1.1.0", "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
@@ -317,6 +324,8 @@
"pinia": ["pinia@3.0.2", "https://registry.npmmirror.com/pinia/-/pinia-3.0.2.tgz", { "dependencies": { "@vue/devtools-api": "^7.7.2" }, "peerDependencies": { "typescript": ">=4.4.4", "vue": "^2.7.0 || ^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-sH2JK3wNY809JOeiiURUR0wehJ9/gd9qFN2Y828jCbxEzKEmEt0pzCXwqiSTfuRsK9vQsOflSdnbdBOGrhtn+g=="], "pinia": ["pinia@3.0.2", "https://registry.npmmirror.com/pinia/-/pinia-3.0.2.tgz", { "dependencies": { "@vue/devtools-api": "^7.7.2" }, "peerDependencies": { "typescript": ">=4.4.4", "vue": "^2.7.0 || ^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-sH2JK3wNY809JOeiiURUR0wehJ9/gd9qFN2Y828jCbxEzKEmEt0pzCXwqiSTfuRsK9vQsOflSdnbdBOGrhtn+g=="],
"plyr": ["plyr@3.7.8", "https://registry.npmmirror.com/plyr/-/plyr-3.7.8.tgz", { "dependencies": { "core-js": "^3.26.1", "custom-event-polyfill": "^1.0.7", "loadjs": "^4.2.0", "rangetouch": "^2.0.1", "url-polyfill": "^1.1.12" } }, "sha512-yG/EHDobwbB/uP+4Bm6eUpJ93f8xxHjjk2dYcD1Oqpe1EcuQl5tzzw9Oq+uVAzd2lkM11qZfydSiyIpiB8pgdA=="],
"postcss": ["postcss@8.5.3", "https://registry.npmmirror.com/postcss/-/postcss-8.5.3.tgz", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="], "postcss": ["postcss@8.5.3", "https://registry.npmmirror.com/postcss/-/postcss-8.5.3.tgz", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
"postcss-value-parser": ["postcss-value-parser@4.2.0", "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], "postcss-value-parser": ["postcss-value-parser@4.2.0", "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
@@ -325,6 +334,8 @@
"proxy-from-env": ["proxy-from-env@1.1.0", "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], "proxy-from-env": ["proxy-from-env@1.1.0", "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
"rangetouch": ["rangetouch@2.0.1", "https://registry.npmmirror.com/rangetouch/-/rangetouch-2.0.1.tgz", {}, "sha512-sln+pNSc8NGaHoLzwNBssFSf/rSYkqeBXzX1AtJlkJiUaVSJSbRAWJk+4omsXkN+EJalzkZhWQ3th1m0FpR5xA=="],
"rfdc": ["rfdc@1.4.1", "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], "rfdc": ["rfdc@1.4.1", "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
"rollup": ["rollup@4.40.0", "https://registry.npmmirror.com/rollup/-/rollup-4.40.0.tgz", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.40.0", "@rollup/rollup-android-arm64": "4.40.0", "@rollup/rollup-darwin-arm64": "4.40.0", "@rollup/rollup-darwin-x64": "4.40.0", "@rollup/rollup-freebsd-arm64": "4.40.0", "@rollup/rollup-freebsd-x64": "4.40.0", "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", "@rollup/rollup-linux-arm-musleabihf": "4.40.0", "@rollup/rollup-linux-arm64-gnu": "4.40.0", "@rollup/rollup-linux-arm64-musl": "4.40.0", "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-musl": "4.40.0", "@rollup/rollup-linux-s390x-gnu": "4.40.0", "@rollup/rollup-linux-x64-gnu": "4.40.0", "@rollup/rollup-linux-x64-musl": "4.40.0", "@rollup/rollup-win32-arm64-msvc": "4.40.0", "@rollup/rollup-win32-ia32-msvc": "4.40.0", "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w=="], "rollup": ["rollup@4.40.0", "https://registry.npmmirror.com/rollup/-/rollup-4.40.0.tgz", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.40.0", "@rollup/rollup-android-arm64": "4.40.0", "@rollup/rollup-darwin-arm64": "4.40.0", "@rollup/rollup-darwin-x64": "4.40.0", "@rollup/rollup-freebsd-arm64": "4.40.0", "@rollup/rollup-freebsd-x64": "4.40.0", "@rollup/rollup-linux-arm-gnueabihf": "4.40.0", "@rollup/rollup-linux-arm-musleabihf": "4.40.0", "@rollup/rollup-linux-arm64-gnu": "4.40.0", "@rollup/rollup-linux-arm64-musl": "4.40.0", "@rollup/rollup-linux-loongarch64-gnu": "4.40.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-gnu": "4.40.0", "@rollup/rollup-linux-riscv64-musl": "4.40.0", "@rollup/rollup-linux-s390x-gnu": "4.40.0", "@rollup/rollup-linux-x64-gnu": "4.40.0", "@rollup/rollup-linux-x64-musl": "4.40.0", "@rollup/rollup-win32-arm64-msvc": "4.40.0", "@rollup/rollup-win32-ia32-msvc": "4.40.0", "@rollup/rollup-win32-x64-msvc": "4.40.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w=="],
@@ -343,6 +354,8 @@
"update-browserslist-db": ["update-browserslist-db@1.1.3", "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="], "update-browserslist-db": ["update-browserslist-db@1.1.3", "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw=="],
"url-polyfill": ["url-polyfill@1.1.13", "https://registry.npmmirror.com/url-polyfill/-/url-polyfill-1.1.13.tgz", {}, "sha512-tXzkojrv2SujumYthZ/WjF7jaSfNhSXlYMpE5AYdL2I3D7DCeo+mch8KtW2rUuKjDg+3VXODXHVgipt8yGY/eQ=="],
"vite": ["vite@6.3.0", "https://registry.npmmirror.com/vite/-/vite-6.3.0.tgz", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.3", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.12" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-9aC0n4pr6hIbvi1YOpFjwQ+QOTGssvbJKoeYkuHHGWwlXfdxQlI8L2qNMo9awEEcCPSiS+5mJZk5jH1PAqoDeQ=="], "vite": ["vite@6.3.0", "https://registry.npmmirror.com/vite/-/vite-6.3.0.tgz", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.3", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.12" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-9aC0n4pr6hIbvi1YOpFjwQ+QOTGssvbJKoeYkuHHGWwlXfdxQlI8L2qNMo9awEEcCPSiS+5mJZk5jH1PAqoDeQ=="],
"vue": ["vue@3.5.13", "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/compiler-sfc": "3.5.13", "@vue/runtime-dom": "3.5.13", "@vue/server-renderer": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ=="], "vue": ["vue@3.5.13", "https://registry.npmmirror.com/vue/-/vue-3.5.13.tgz", { "dependencies": { "@vue/compiler-dom": "3.5.13", "@vue/compiler-sfc": "3.5.13", "@vue/runtime-dom": "3.5.13", "@vue/server-renderer": "3.5.13", "@vue/shared": "3.5.13" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-wmeiSMxkZCSc+PM2w2VRsOYAZC8GdipNFRTsLSfodVqI9mbejKeXEGr8SckuLnrQPGe3oJN5c3K0vpoU9q/wCQ=="],

View File

@@ -14,6 +14,7 @@
"axios": "^1.8.4", "axios": "^1.8.4",
"dplayer": "^1.27.1", "dplayer": "^1.27.1",
"pinia": "^3.0.2", "pinia": "^3.0.2",
"plyr": "^3.7.8",
"tailwindcss": "^4.1.4", "tailwindcss": "^4.1.4",
"vue": "^3.5.13", "vue": "^3.5.13",
"vue-icons-plus": "^0.1.8", "vue-icons-plus": "^0.1.8",

View File

@@ -10,6 +10,10 @@ export const postApi = {
} }
}); });
}, },
play(id) {
return client.get(`/api/posts/${id}/play`);
},
show(id) { show(id) {
return client.get(`/api/posts/${id}`); return client.get(`/api/posts/${id}`);
}, },

View File

@@ -1,5 +1,7 @@
<script setup> <script setup>
import { onMounted, ref } from 'vue' import Plyr from 'plyr'
import 'plyr/dist/plyr.css'
import { onMounted, onUnmounted, ref } from 'vue'
import { useRoute, useRouter } from 'vue-router' import { useRoute, useRouter } from 'vue-router'
import { postApi } from '../api/postApi' import { postApi } from '../api/postApi'
@@ -7,6 +9,49 @@ const route = useRoute()
const router = useRouter() const router = useRouter()
const article = ref(null) const article = ref(null)
const buying = ref(false) const buying = ref(false)
const player = ref(null)
const videoElement = ref(null)
const mediaLoaded = ref(false)
const initializePlayer = () => {
if (videoElement.value) {
player.value = new Plyr(videoElement.value, {
controls: ['play', 'progress', 'current-time', 'duration', 'settings', 'fullscreen'],
settings: ['speed'],
speed: { selected: 1, options: [0.5, 0.75, 1] },
autoplay: false
})
player.value.on('play', async () => {
if (!mediaLoaded.value) {
await loadVideoSource()
}
})
player.value.on('ended', () => {
mediaLoaded.value = false
videoElement.value.src = ''
})
}
}
const loadVideoSource = async () => {
try {
const { data } = await postApi.play(route.params.id)
if (videoElement.value) {
mediaLoaded.value = true
videoElement.value.src = data.url
await player.value.restart()
await player.value.play()
}
} catch (error) {
console.error('Failed to load video:', error)
// alert('视频加载失败,请稍后重试')
}
}
const handleBuy = async () => { const handleBuy = async () => {
if (buying.value) return if (buying.value) return
@@ -46,23 +91,24 @@ const fetchArticle = async () => {
} }
} }
const formatDate = (dateString) => {
return new Date(dateString).toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long',
day: 'numeric'
})
}
onMounted(async () => { onMounted(async () => {
await fetchArticle() await fetchArticle()
initializePlayer()
})
onUnmounted(() => {
if (player.value) {
player.value.destroy()
}
}) })
</script> </script>
<template> <template>
<div class="min-h-screen bg-gray-50"> <div class="min-h-screen bg-gray-50">
<div v-if="article"> <div v-if="article">
<video controls :poster="article.head_images[0]" class="w-full aspect-video object-cover"> <video ref="videoElement" :poster="article.head_images[0]"
class="w-full aspect-video object-cover plyr-video" playsinline preload="none">
<source type="video/mp4" />
Your browser does not support the video tag. Your browser does not support the video tag.
</video> </video>
<div class="p-4"> <div class="p-4">
@@ -89,3 +135,11 @@ onMounted(async () => {
</div> </div>
</div> </div>
</template> </template>
<style>
.plyr-video {
--plyr-color-main: #2563eb;
width: 100%;
max-height: 100vh;
}
</style>