feat: support play
This commit is contained in:
Binary file not shown.
16
frontend/package-lock.json
generated
16
frontend/package-lock.json
generated
@@ -9,6 +9,7 @@
|
|||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
|
"hls.js": "^0.8.5",
|
||||||
"pinia": "^2.2.6",
|
"pinia": "^2.2.6",
|
||||||
"vant": "^4.9.9",
|
"vant": "^4.9.9",
|
||||||
"vue": "^3.5.12",
|
"vue": "^3.5.12",
|
||||||
@@ -1371,6 +1372,15 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/hls.js": {
|
||||||
|
"version": "0.8.5",
|
||||||
|
"resolved": "https://npm.hub.ipao.vip/repository/npm/hls.js/-/hls.js-0.8.5.tgz",
|
||||||
|
"integrity": "sha512-hJBxUAsJInjXNqDPPA6646h/IulN2OUAK9OT6/0gO1oygQ4ZF3pB3j/Uyk2UWO0cDKyAs/SDTclVWMGurt4glw==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"url-toolkit": "^2.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/hookable": {
|
"node_modules/hookable": {
|
||||||
"version": "5.5.3",
|
"version": "5.5.3",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
@@ -2485,6 +2495,12 @@
|
|||||||
"browserslist": ">= 4.21.0"
|
"browserslist": ">= 4.21.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/url-toolkit": {
|
||||||
|
"version": "2.2.5",
|
||||||
|
"resolved": "https://npm.hub.ipao.vip/repository/npm/url-toolkit/-/url-toolkit-2.2.5.tgz",
|
||||||
|
"integrity": "sha512-mtN6xk+Nac+oyJ/PrI7tzfmomRVNFIWKUbG8jdYFt52hxbiReFAXIjYskvu64/dvuW71IcB7lV8l0HvZMac6Jg==",
|
||||||
|
"license": "Apache-2.0"
|
||||||
|
},
|
||||||
"node_modules/vant": {
|
"node_modules/vant": {
|
||||||
"version": "4.9.9",
|
"version": "4.9.9",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
|
"hls.js": "^0.8.5",
|
||||||
"pinia": "^2.2.6",
|
"pinia": "^2.2.6",
|
||||||
"vant": "^4.9.9",
|
"vant": "^4.9.9",
|
||||||
"vue": "^3.5.12",
|
"vue": "^3.5.12",
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<RouterView />
|
<router-view v-slot="{ Component }">
|
||||||
|
<keep-alive>
|
||||||
|
<component :is="Component" />
|
||||||
|
</keep-alive>
|
||||||
|
</router-view>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import NotFound from '@/views/NotFound.vue'
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
import HomeView from '../views/tabs/HomeView.vue'
|
import HomeView from '../views/tabs/HomeView.vue'
|
||||||
import TabView from '../views/TabView.vue'
|
import TabView from '../views/TabView.vue'
|
||||||
@@ -5,13 +6,14 @@ import TabView from '../views/TabView.vue'
|
|||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
routes: [
|
routes: [
|
||||||
|
{ path: '/:pathMatch(.*)*', name: 'NotFound', component: NotFound },
|
||||||
{
|
{
|
||||||
path: '/tab',
|
path: '/t/:tenant',
|
||||||
name: 'tab',
|
name: 'tab',
|
||||||
component: TabView,
|
component: TabView,
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'home',
|
path: '',
|
||||||
name: 'tab.home',
|
name: 'tab.home',
|
||||||
component: HomeView,
|
component: HomeView,
|
||||||
},
|
},
|
||||||
@@ -21,8 +23,8 @@ const router = createRouter({
|
|||||||
component: () => import('../views/tabs/BoughtView.vue'),
|
component: () => import('../views/tabs/BoughtView.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'me',
|
path: 'user',
|
||||||
name: 'tab.me',
|
name: 'tab.user',
|
||||||
// route level code-splitting
|
// route level code-splitting
|
||||||
// this generates a separate chunk (About.[hash].js) for this route
|
// this generates a separate chunk (About.[hash].js) for this route
|
||||||
// which is lazy-loaded when the route is visited.
|
// which is lazy-loaded when the route is visited.
|
||||||
@@ -31,7 +33,7 @@ const router = createRouter({
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/play/{:id}',
|
path: '/t/:tenant/play/:hash',
|
||||||
name: 'play',
|
name: 'play',
|
||||||
component: () => import('../views/PlayView.vue'),
|
component: () => import('../views/PlayView.vue'),
|
||||||
},
|
},
|
||||||
@@ -40,12 +42,6 @@ const router = createRouter({
|
|||||||
|
|
||||||
router.beforeEach((to, from) => {
|
router.beforeEach((to, from) => {
|
||||||
console.log("from", from, "goto: ", to)
|
console.log("from", from, "goto: ", to)
|
||||||
|
|
||||||
if (to.path === "/" && from.path === "/") {
|
|
||||||
console.log("redirecting to tab.home")
|
|
||||||
return { name: "tab.home" }
|
|
||||||
}
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import axios from 'axios'; // 引入axios
|
import axios from 'axios'; // 引入axios
|
||||||
|
|
||||||
console.log("__GA: ", __GA)
|
|
||||||
const service = axios.create({
|
const service = axios.create({
|
||||||
baseURL: "/v1",
|
baseURL: "/v1",
|
||||||
timeout: 30,
|
timeout: 30 * 1000,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Authorization': 'Bearer ' + __GA,
|
'Authorization': 'Bearer ' + __GA,
|
||||||
@@ -16,15 +15,18 @@ service.interceptors.request.use(
|
|||||||
return config
|
return config
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// http response 拦截器
|
// http response 拦截器
|
||||||
service.interceptors.response.use(
|
service.interceptors.response.use(
|
||||||
(response) => {
|
(response) => {
|
||||||
|
console.log(response)
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
export default service
|
export default service
|
||||||
3
frontend/src/views/NotFound.vue
Normal file
3
frontend/src/views/NotFound.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<h1>找不到页面</h1>
|
||||||
|
</template>
|
||||||
@@ -1,3 +1,143 @@
|
|||||||
<template>
|
<template>
|
||||||
<h1>Player</h1>
|
<van-nav-bar :title="item.title" left-text="返回" left-arrow @click-left="onClickLeft" />
|
||||||
|
<div style="background-color: black;">
|
||||||
|
<video id="video" :poster="item.poster"></video>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<van-notice-bar v-if="false === item.bought" left-icon="volume-o" text="未购买的视频、音频默认播放时长为1分钟左右。" />
|
||||||
|
|
||||||
|
|
||||||
|
<div id="container">
|
||||||
|
<van-space direction="vertical" fill size="2rem">
|
||||||
|
|
||||||
|
<van-row gutter="20" v-show="playing">
|
||||||
|
<van-col span="24">
|
||||||
|
<van-progress :percent="(currentTime / duration) * 100" />
|
||||||
|
</van-col>
|
||||||
|
</van-row>
|
||||||
|
|
||||||
|
<van-row gutter="20">
|
||||||
|
<van-col span="12">
|
||||||
|
<van-button type="primary" plain round @click="play(item.hash, 'video')" size="large" block>看视频</van-button>
|
||||||
|
</van-col>
|
||||||
|
<van-col span="12">
|
||||||
|
<van-button type="warning" plain round="" @click="play(item.hash, 'audio')" size="large"
|
||||||
|
block>听音频</van-button>
|
||||||
|
</van-col>
|
||||||
|
</van-row>
|
||||||
|
|
||||||
|
<van-row gutter="20" v-show="playing">
|
||||||
|
<van-col span="24">
|
||||||
|
<van-button round type="danger" @click="stop()" size="large" block>结束播放</van-button>
|
||||||
|
</van-col>
|
||||||
|
</van-row>
|
||||||
|
</van-space>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import request from "@/utils/request";
|
||||||
|
import Hls from "hls.js";
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const item = ref({
|
||||||
|
title: "加载中...",
|
||||||
|
})
|
||||||
|
|
||||||
|
const currentTime = ref(0);
|
||||||
|
const duration = ref(0);
|
||||||
|
const playing = ref(false);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
loadMedia(route.params.hash);
|
||||||
|
const player = document.getElementById('video');
|
||||||
|
player.addEventListener('timeupdate', updateTime);
|
||||||
|
player.addEventListener('loadedmetadata', () => {
|
||||||
|
duration.value = player.duration;
|
||||||
|
});
|
||||||
|
player.addEventListener('ended', function () {
|
||||||
|
console.log("Video ended");
|
||||||
|
playing.value = false;
|
||||||
|
});
|
||||||
|
player.addEventListener('pause', () => {
|
||||||
|
playing.value = false;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
const onClickLeft = () => {
|
||||||
|
router.back();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
const loadMedia = (hash) => {
|
||||||
|
console.log("loadMedia: ", hash);
|
||||||
|
setTimeout(() => {
|
||||||
|
request
|
||||||
|
.get(`/medias/${hash}`)
|
||||||
|
.then((res) => {
|
||||||
|
console.log(res)
|
||||||
|
item.value = res.data;
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error("ERROR", err);
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
|
||||||
|
const play = (hash, type) => {
|
||||||
|
playing.value = true;
|
||||||
|
|
||||||
|
const player = document.getElementById('video');
|
||||||
|
const source = `/v1/medias/${hash}/${type}`
|
||||||
|
if (Hls.isSupported()) {
|
||||||
|
var hls = new Hls({
|
||||||
|
xhrSetup: function (xhr, url) {
|
||||||
|
xhr.setRequestHeader('Authorization', 'Bearer ' + __GA);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
hls.loadSource(source);
|
||||||
|
hls.attachMedia(player);
|
||||||
|
hls.on(Hls.Events.MANIFEST_PARSED, function () {
|
||||||
|
player.play();
|
||||||
|
});
|
||||||
|
} else if (player.canPlayType('application/vnd.apple.mpegurl')) {
|
||||||
|
player.src = source;
|
||||||
|
player.addEventListener('loadedmetadata', function () {
|
||||||
|
player.play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTime = () => {
|
||||||
|
const player = document.getElementById('video');
|
||||||
|
currentTime.value = player.currentTime;
|
||||||
|
};
|
||||||
|
|
||||||
|
const stop = () => {
|
||||||
|
const player = document.getElementById('video');
|
||||||
|
player.pause();
|
||||||
|
player.currentTime = 0;
|
||||||
|
currentTime.value = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatTime = (time) => {
|
||||||
|
const minutes = Math.floor(time / 60);
|
||||||
|
const seconds = Math.floor(time % 60);
|
||||||
|
return `${minutes}:${seconds < 10 ? '0' : ''}${seconds}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
#container {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#video {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 4 / 3;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -2,16 +2,14 @@
|
|||||||
<RouterView />
|
<RouterView />
|
||||||
|
|
||||||
<van-tabbar route v-model="active">
|
<van-tabbar route v-model="active">
|
||||||
<van-tabbar-item replace name="home" icon="home-o" to="/tab/home"
|
<van-tabbar-item replace name="home" icon="home-o" to="/tab/home">主页
|
||||||
>主页
|
|
||||||
</van-tabbar-item>
|
</van-tabbar-item>
|
||||||
<van-tabbar-item replace name="bought" icon="like-o" to="/tab/bought"
|
<van-tabbar-item replace name="bought" icon="like-o" to="/tab/bought">已购</van-tabbar-item>
|
||||||
>已购</van-tabbar-item
|
|
||||||
>
|
|
||||||
<van-tabbar-item replace name="me" icon="contact-o" to="/tab/me">我</van-tabbar-item>
|
<van-tabbar-item replace name="me" icon="contact-o" to="/tab/me">我</van-tabbar-item>
|
||||||
</van-tabbar>
|
</van-tabbar>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { RouterView } from "vue-router";
|
import { RouterView } from "vue-router";
|
||||||
|
const active = ref("home");
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
|
|
||||||
<van-back-top bottom="80" />
|
<van-back-top bottom="80" />
|
||||||
|
|
||||||
|
<van-button :to="{ name: 'play', params: { tenant: 'ypl', hash: '123' } }" type="primary">主要按钮</van-button>
|
||||||
|
|
||||||
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" offset="100" @load="loadData">
|
<van-list v-model:loading="loading" :finished="finished" finished-text="没有更多了" offset="100" @load="loadData">
|
||||||
<van-card v-for="item in items" :key="item" :desc="item.title" :thumb="item.poster" @click="play(item)">
|
<van-card v-for="item in items" :key="item" :desc="item.title" :thumb="item.poster" @click="play(item)">
|
||||||
<template #title>
|
<template #title>
|
||||||
@@ -51,10 +53,11 @@ const offset = ref("");
|
|||||||
const play = (item) => {
|
const play = (item) => {
|
||||||
// vue router goto play view
|
// vue router goto play view
|
||||||
console.log("play -", item);
|
console.log("play -", item);
|
||||||
router.push({ name: "play", params: { id: item } });
|
router.push({ name: "play", params: { hash: item.hash } });
|
||||||
};
|
};
|
||||||
|
|
||||||
const loadData = () => {
|
const loadData = () => {
|
||||||
|
return
|
||||||
// request /v1/medias
|
// request /v1/medias
|
||||||
const data = {
|
const data = {
|
||||||
offset: offset.value,
|
offset: offset.value,
|
||||||
|
|||||||
Reference in New Issue
Block a user