fix: align uniapp live stream metadata fields
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
ROOT = Path(__file__).resolve().parents[2]
|
ROOT = Path(__file__).resolve().parents[2]
|
||||||
@@ -18,12 +19,33 @@ def main() -> None:
|
|||||||
popup_source = read_text(MATCH_LIVE_POPUP)
|
popup_source = read_text(MATCH_LIVE_POPUP)
|
||||||
api_source = read_text(MATCH_API)
|
api_source = read_text(MATCH_API)
|
||||||
|
|
||||||
assert "live_list" in detail_source, "match detail should handle live_list"
|
assert 'v-if="liveList.length"' in detail_source, "match detail should render live_list block with v-if"
|
||||||
|
assert 'v-else-if="thirdPartyLiveUrl"' in detail_source, "match detail should keep thirdPartyLiveUrl fallback with v-else-if"
|
||||||
assert "MatchLivePopup" in detail_source, "match detail should use MatchLivePopup"
|
assert "MatchLivePopup" in detail_source, "match detail should use MatchLivePopup"
|
||||||
assert "play_url_m3u8" in popup_source, "popup should support play_url_m3u8"
|
assert "fetch_status" in detail_source, "match detail should reference fetch_status"
|
||||||
|
assert "last_fetch_at" in detail_source, "match detail should reference last_fetch_at"
|
||||||
|
assert "update_time" in detail_source, "match detail should reference update_time"
|
||||||
|
assert "default_play_url" in popup_source, "popup should prefer default_play_url"
|
||||||
assert "stream_options" in popup_source, "popup should support stream_options"
|
assert "stream_options" in popup_source, "popup should support stream_options"
|
||||||
assert "/pages/webview/webview?url=" in popup_source, "selected stream should route through existing webview page"
|
assert "/pages/webview/webview?url=" in popup_source, "selected stream should route through existing webview page"
|
||||||
|
|
||||||
|
field_names = [
|
||||||
|
"play_url_m3u8",
|
||||||
|
"play_url_hd_m3u8",
|
||||||
|
"play_url_flv",
|
||||||
|
"play_url_hd_flv",
|
||||||
|
]
|
||||||
|
for field_name in field_names:
|
||||||
|
assert field_name in popup_source, f"popup should cover {field_name}"
|
||||||
|
assert field_name in api_source, f"match live types should cover {field_name}"
|
||||||
|
|
||||||
|
assert re.search(r"const\s+defaultPlayUrl\s*=\s*normalizeUrl\(props\.line\?\.default_play_url\)", popup_source), \
|
||||||
|
"popup should explicitly prefer props.line?.default_play_url"
|
||||||
|
assert re.search(r"defaultPlayUrl.*?resetSelected", popup_source, re.S), \
|
||||||
|
"popup selection reset should prefer default_play_url"
|
||||||
|
assert re.search(r"fetch_status.*last_fetch_at", popup_source, re.S) or "update_time" in popup_source, \
|
||||||
|
"popup metadata should use fetch_status and last_fetch_at/update_time"
|
||||||
|
|
||||||
assert "/match/detail" in api_source, "match detail API path should stay on /match/detail"
|
assert "/match/detail" in api_source, "match detail API path should stay on /match/detail"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
|
||||||
export interface MatchLiveStreamOption {
|
export interface MatchLiveStreamOption {
|
||||||
|
id?: number | string
|
||||||
title?: string
|
title?: string
|
||||||
name?: string
|
name?: string
|
||||||
label?: string
|
label?: string
|
||||||
@@ -9,12 +10,21 @@ export interface MatchLiveStreamOption {
|
|||||||
url?: string
|
url?: string
|
||||||
play_url?: string
|
play_url?: string
|
||||||
play_url_m3u8?: string
|
play_url_m3u8?: string
|
||||||
|
play_url_hd_m3u8?: string
|
||||||
|
play_url_flv?: string
|
||||||
|
play_url_hd_flv?: string
|
||||||
default_play_url?: string
|
default_play_url?: string
|
||||||
live_url?: string
|
live_url?: string
|
||||||
|
source_url?: string
|
||||||
|
fetch_status?: string | number
|
||||||
|
last_fetch_at?: string | number
|
||||||
|
update_time?: string | number
|
||||||
|
stream_options?: MatchLiveStreamOption[]
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MatchLiveLineItem {
|
export interface MatchLiveLineItem {
|
||||||
|
id?: number | string
|
||||||
title?: string
|
title?: string
|
||||||
name?: string
|
name?: string
|
||||||
label?: string
|
label?: string
|
||||||
@@ -28,8 +38,13 @@ export interface MatchLiveLineItem {
|
|||||||
source_url?: string
|
source_url?: string
|
||||||
default_play_url?: string
|
default_play_url?: string
|
||||||
play_url_m3u8?: string
|
play_url_m3u8?: string
|
||||||
|
play_url_hd_m3u8?: string
|
||||||
|
play_url_flv?: string
|
||||||
|
play_url_hd_flv?: string
|
||||||
play_url?: string
|
play_url?: string
|
||||||
live_url?: string
|
live_url?: string
|
||||||
|
fetch_status?: string | number
|
||||||
|
last_fetch_at?: string | number
|
||||||
stream_options?: MatchLiveStreamOption[]
|
stream_options?: MatchLiveStreamOption[]
|
||||||
[key: string]: any
|
[key: string]: any
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,6 +67,16 @@ const normalizeUrl = (value: unknown) => {
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getFetchStatusText = (value: unknown) => {
|
||||||
|
const text = normalizeText(value)
|
||||||
|
if (!text) return ''
|
||||||
|
const normalized = text.toLowerCase()
|
||||||
|
if (normalized === 'success' || normalized === '1') return '已抓取'
|
||||||
|
if (normalized === 'pending' || normalized === '0') return '待抓取'
|
||||||
|
if (normalized === 'failed' || normalized === '-1') return '抓取失败'
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
const formatMetaTime = (value: unknown) => {
|
const formatMetaTime = (value: unknown) => {
|
||||||
const text = normalizeText(value)
|
const text = normalizeText(value)
|
||||||
if (!text) return ''
|
if (!text) return ''
|
||||||
@@ -101,8 +111,8 @@ const lineTitle = computed(() => {
|
|||||||
const lineMetaText = computed(() => {
|
const lineMetaText = computed(() => {
|
||||||
const line = props.line || {}
|
const line = props.line || {}
|
||||||
const parts = [
|
const parts = [
|
||||||
normalizeText(line.status_text) || normalizeText(line.status_desc) || normalizeText(line.status),
|
getFetchStatusText(line.fetch_status),
|
||||||
formatMetaTime(line.updated_at || line.update_time),
|
formatMetaTime(line.last_fetch_at || line.update_time),
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
return parts.length ? parts.join(' · ') : '请选择可用直播线路后打开'
|
return parts.length ? parts.join(' · ') : '请选择可用直播线路后打开'
|
||||||
})
|
})
|
||||||
@@ -111,12 +121,18 @@ const sourceUrl = computed(() => normalizeUrl(props.line?.source_url))
|
|||||||
|
|
||||||
const buildStreamOption = (option: MatchLiveStreamOption | undefined, index: number) => {
|
const buildStreamOption = (option: MatchLiveStreamOption | undefined, index: number) => {
|
||||||
if (!option) return null
|
if (!option) return null
|
||||||
const url =
|
const url = (
|
||||||
normalizeUrl(option.play_url_m3u8) ||
|
[
|
||||||
normalizeUrl(option.play_url) ||
|
option.default_play_url,
|
||||||
normalizeUrl(option.url) ||
|
option.play_url_m3u8,
|
||||||
normalizeUrl(option.default_play_url) ||
|
option.play_url_hd_m3u8,
|
||||||
normalizeUrl(option.live_url)
|
option.play_url_flv,
|
||||||
|
option.play_url_hd_flv,
|
||||||
|
option.play_url,
|
||||||
|
option.url,
|
||||||
|
option.live_url,
|
||||||
|
].map((item) => normalizeUrl(item)).find(Boolean) || ''
|
||||||
|
)
|
||||||
if (!url) return null
|
if (!url) return null
|
||||||
return {
|
return {
|
||||||
...option,
|
...option,
|
||||||
@@ -136,6 +152,15 @@ const streamOptions = computed(() => {
|
|||||||
const line = props.line || {}
|
const line = props.line || {}
|
||||||
const options: PopupStreamOption[] = []
|
const options: PopupStreamOption[] = []
|
||||||
const seen = new Set<string>()
|
const seen = new Set<string>()
|
||||||
|
const fallbackFields: Array<{ field: keyof MatchLiveLineItem; label: string }> = [
|
||||||
|
{ field: 'default_play_url', label: '默认线路' },
|
||||||
|
{ field: 'play_url_m3u8', label: 'M3U8 线路' },
|
||||||
|
{ field: 'play_url_hd_m3u8', label: '高清 M3U8' },
|
||||||
|
{ field: 'play_url_flv', label: 'FLV 线路' },
|
||||||
|
{ field: 'play_url_hd_flv', label: '高清 FLV' },
|
||||||
|
{ field: 'play_url', label: '备用线路' },
|
||||||
|
{ field: 'live_url', label: '直播地址' },
|
||||||
|
]
|
||||||
|
|
||||||
const pushOption = (option: MatchLiveStreamOption | undefined, index: number) => {
|
const pushOption = (option: MatchLiveStreamOption | undefined, index: number) => {
|
||||||
const built = buildStreamOption(option, index)
|
const built = buildStreamOption(option, index)
|
||||||
@@ -148,25 +173,11 @@ const streamOptions = computed(() => {
|
|||||||
line.stream_options.forEach((item, index) => pushOption(item, index))
|
line.stream_options.forEach((item, index) => pushOption(item, index))
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultPlayUrl = normalizeUrl(line.default_play_url)
|
fallbackFields.forEach(({ field, label }) => {
|
||||||
if (defaultPlayUrl && !seen.has(defaultPlayUrl)) {
|
const value = normalizeUrl(line[field])
|
||||||
pushOption({ label: '默认线路', url: defaultPlayUrl }, options.length)
|
if (!value || seen.has(value)) return
|
||||||
}
|
pushOption({ label, [field]: value }, options.length)
|
||||||
|
})
|
||||||
const playUrlM3u8 = normalizeUrl(line.play_url_m3u8)
|
|
||||||
if (playUrlM3u8 && !seen.has(playUrlM3u8)) {
|
|
||||||
pushOption({ label: 'M3U8 线路', play_url_m3u8: playUrlM3u8 }, options.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
const playUrl = normalizeUrl(line.play_url)
|
|
||||||
if (playUrl && !seen.has(playUrl)) {
|
|
||||||
pushOption({ label: '备用线路', play_url: playUrl }, options.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
const liveUrl = normalizeUrl(line.live_url)
|
|
||||||
if (liveUrl && !seen.has(liveUrl)) {
|
|
||||||
pushOption({ label: '直播地址', live_url: liveUrl }, options.length)
|
|
||||||
}
|
|
||||||
|
|
||||||
return options
|
return options
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -514,6 +514,16 @@ const awaySubs = computed(() => lineupList.value.filter((p: any) => p.team_side
|
|||||||
|
|
||||||
const normalizeLiveText = (value: unknown) => String(value || '').trim()
|
const normalizeLiveText = (value: unknown) => String(value || '').trim()
|
||||||
|
|
||||||
|
const getLiveFetchStatusText = (value: unknown) => {
|
||||||
|
const text = normalizeLiveText(value)
|
||||||
|
if (!text) return ''
|
||||||
|
const normalized = text.toLowerCase()
|
||||||
|
if (normalized === 'success' || normalized === '1') return '已抓取'
|
||||||
|
if (normalized === 'pending' || normalized === '0') return '待抓取'
|
||||||
|
if (normalized === 'failed' || normalized === '-1') return '抓取失败'
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
const getLiveLineTitle = (item: MatchLiveLineItem) => {
|
const getLiveLineTitle = (item: MatchLiveLineItem) => {
|
||||||
return (
|
return (
|
||||||
normalizeLiveText(item?.title) ||
|
normalizeLiveText(item?.title) ||
|
||||||
@@ -541,8 +551,8 @@ const formatLiveLineUpdate = (value: unknown) => {
|
|||||||
|
|
||||||
const getLiveLineMeta = (item: MatchLiveLineItem) => {
|
const getLiveLineMeta = (item: MatchLiveLineItem) => {
|
||||||
const parts = [
|
const parts = [
|
||||||
normalizeLiveText(item?.status_text) || normalizeLiveText(item?.status_desc) || normalizeLiveText(item?.status),
|
getLiveFetchStatusText(item?.fetch_status),
|
||||||
formatLiveLineUpdate(item?.updated_at || item?.update_time),
|
formatLiveLineUpdate(item?.last_fetch_at || item?.update_time),
|
||||||
].filter(Boolean)
|
].filter(Boolean)
|
||||||
return parts.length ? parts.join(' · ') : '点击选择直播线路'
|
return parts.length ? parts.join(' · ') : '点击选择直播线路'
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user