Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"flv.js": "^1.6.2",
"hls.js": "^1.6.13",
"json-server": "^0.17.4",
"mpegts.js": "^1.8.0",
"pinia": "^2.2.6",
"shaka-player": "^4.16.3",
"v-viewer": "^3.0.22",
Expand Down
8 changes: 5 additions & 3 deletions dashboard/src/api/services/live.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,12 @@ class LiveService {

for (let i = 0; i < lines.length; i++) {
const line = lines[i]

// 跳过空行
if (!line) continue;

if (line.startsWith('#EXTINF:')) {
// 解析频道信息 - 修复正则表达式来正确处理属性和频道名称
const match = line.match(/#EXTINF:(-?\d+),(.*)$/)
const match = line.match(/^#EXTINF:([-\.\d]+)\s*(.*)$/);
if (match) {
const duration = match[1]
const fullStr = match[2].trim()
Expand Down Expand Up @@ -483,4 +485,4 @@ class LiveService {
// 创建单例实例
const liveService = new LiveService()

export default liveService
export default liveService
42 changes: 35 additions & 7 deletions dashboard/src/views/Live.vue
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,8 @@
</template>

<script setup>
import { ref, reactive, computed, onMounted, watch, nextTick } from 'vue'
import { ref, reactive, computed, onMounted, watch, nextTick, onUnmounted } from 'vue'
import mpegts from 'mpegts.js'
import { useRouter } from 'vue-router'
import { Message } from '@arco-design/web-vue'
import {
Expand All @@ -236,6 +237,7 @@ import liveService from '@/api/services/live.js'
const router = useRouter()

// 响应式数据
let mpegtsPlayer = null
const loading = ref(false)
const error = ref('')
const liveData = ref(null)
Expand Down Expand Up @@ -332,7 +334,6 @@ const selectGroup = (groupName) => {
const selectChannel = (channel) => {
selectedChannel.value = channel
videoError.value = ''

// 使用nextTick确保DOM更新后再设置线路ID
nextTick(() => {
if (channel && channel.routes && channel.routes.length > 0) {
Expand All @@ -341,15 +342,44 @@ const selectChannel = (channel) => {
} else {
currentRouteId.value = 1
}

// 重置视频播放器
if (videoPlayer.value) {
videoPlayer.value.load()
}
setupMpegtsPlayer()
})
}

// 获取当前频道的播放URL
function setupMpegtsPlayer() {
// 销毁旧播放器
if (mpegtsPlayer) {
mpegtsPlayer.destroy()
mpegtsPlayer = null
}
const url = getCurrentChannelUrl()
if (!url || !videoPlayer.value) return
// 判断是否为 mpegts 流(简单判断 .ts 或 mpegts 协议)
if (url.endsWith('.ts') || url.includes('mpegts') || url.includes('udpxy') ||
url.includes('/udp/') || url.includes('rtp://') || url.includes('udp://')) {
if (mpegts.isSupported()) {
mpegtsPlayer = mpegts.createPlayer({
type: 'mpegts',
url
})
mpegtsPlayer.attachMediaElement(videoPlayer.value)
mpegtsPlayer.load()
mpegtsPlayer.play()
}
}
}

onUnmounted(() => {
if (mpegtsPlayer) {
mpegtsPlayer.destroy()
mpegtsPlayer = null
}
})
const getCurrentChannelUrl = () => {
if (!selectedChannel.value) return ''

Expand All @@ -365,20 +395,17 @@ const getCurrentChannelUrl = () => {
const switchRoute = (event) => {
const routeId = Number(event.target ? event.target.value : event)
if (!selectedChannel.value || !selectedChannel.value.routes) return

const route = selectedChannel.value.routes.find(r => r.id === routeId)
if (route) {
currentRouteId.value = routeId
videoError.value = ''

// 重新加载视频
nextTick(() => {
if (videoPlayer.value) {
videoPlayer.value.src = route.url
videoPlayer.value.load()
}
setupMpegtsPlayer()
})

Message.success(`已切换到${route.name}`)
}
}
Expand Down Expand Up @@ -442,6 +469,7 @@ const retryVideo = () => {
if (videoPlayer.value) {
videoError.value = ''
videoPlayer.value.load()
setupMpegtsPlayer()
}
}

Expand Down