mirror of
https://github.com/BTMuli/TeyvatGuide.git
synced 2026-04-23 22:09:51 +08:00
✨ B站视频基本信息获取,支持分享图生成
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<!-- todo 优化 -->
|
||||
<template>
|
||||
<div class="tp-video-box">
|
||||
<div class="tp-video-box" v-if="videoData">
|
||||
<!-- todo https://socialsisteryi.github.io/bilibili-API-collect/docs/video/videostream_url.html#%E8%A7%86%E9%A2%91%E4%BC%B4%E9%9F%B3%E9%9F%B3%E8%B4%A8%E4%BB%A3%E7%A0%81 -->
|
||||
<iframe
|
||||
class="tp-video-container"
|
||||
@@ -9,26 +8,32 @@
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
|
||||
sandbox="allow-forms allow-same-origin allow-popups allow-presentation allow-scripts"
|
||||
:id="`tp-video-${props.data.insert.video}`"
|
||||
>
|
||||
</iframe>
|
||||
<div class="tp-video-cover" v-if="videoCover">
|
||||
<img alt="cover" :src="videoCover" />
|
||||
<img src="/source/UI/video_play.svg" alt="icon" />
|
||||
<span>{{ getVideoTime() }}</span>
|
||||
/>
|
||||
<div class="tp-video-share">
|
||||
<img alt="cover" :src="videoCover" class="tp-video-cover" />
|
||||
<img alt="icon" src="/source/UI/video_play_bili.png" class="tp-video-icon" />
|
||||
<div class="tp-video-info">
|
||||
<span>{{ videoData.bvid }}|{{ timestampToDate(videoData.ctime * 1000) }}</span>
|
||||
<span>{{ videoData.title }}</span>
|
||||
</div>
|
||||
<div class="tp-video-view">
|
||||
<v-icon size="12">mdi-eye</v-icon>
|
||||
<span>{{ videoData.stat.view }}</span>
|
||||
</div>
|
||||
<div class="tp-video-time">
|
||||
<v-icon size="12">mdi-clock-time-four-outline</v-icon>
|
||||
<span>{{ getVideoDuration(videoData.duration) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
// todo https://artplayer.org/?libs=https://cdnjs.cloudflare.com/ajax/libs/dashjs/4.5.2/dash.all.min.js&example=dash
|
||||
// todo flv
|
||||
// https://artplayer.org/document/library/flv.html
|
||||
// https://api.bilibili.com/x/player/playurl?avid=666064953&cid=1400018762&qn=64&otype=json
|
||||
import Bili from "@Bili/index.js";
|
||||
import { getCurrentWindow } from "@tauri-apps/api/window";
|
||||
import { onBeforeMount, onMounted, onUnmounted, ref, shallowRef, useTemplateRef } from "vue";
|
||||
import showSnackbar from "@comp/func/snackbar.js";
|
||||
import { onMounted, onUnmounted, ref, shallowRef } from "vue";
|
||||
|
||||
import TGLogger from "@/utils/TGLogger.js";
|
||||
import { saveImgLocal } from "@/utils/TGShare.js";
|
||||
import { getImageBuffer } from "@/utils/TGShare.js";
|
||||
import { getVideoDuration, timestampToDate } from "@/utils/toolFunc.js";
|
||||
|
||||
type TpVideo = { insert: { video: string } };
|
||||
type TpVideoProps = { data: TpVideo };
|
||||
@@ -37,60 +42,28 @@ const props = defineProps<TpVideoProps>();
|
||||
const videoAspectRatio = ref<number>(16 / 9);
|
||||
const videoCover = ref<string>();
|
||||
const videoData = shallowRef<TGApp.Plugins.Bili.Video.ViewData>();
|
||||
const videoRef = useTemplateRef<HTMLIFrameElement>(`#tp-video-${props.data.insert.video}`);
|
||||
const coverBuffer = shallowRef<Uint8Array | null>(null);
|
||||
|
||||
console.log("tpVideo", props.data.insert.video);
|
||||
|
||||
onBeforeMount(async () => {
|
||||
onMounted(async () => {
|
||||
const url = new URL(props.data.insert.video);
|
||||
const aid = url.searchParams.get("aid") ?? undefined;
|
||||
const bvid = url.searchParams.get("bvid") ?? undefined;
|
||||
try {
|
||||
videoData.value = await Bili.video.view(aid, bvid);
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
await TGLogger.Error(`获取视频信息失败: ${e.message}`);
|
||||
} else await TGLogger.Error(`获取视频信息失败: ${e}`);
|
||||
}
|
||||
videoData.value = await Bili.video.view(aid, bvid);
|
||||
if (!videoData.value) {
|
||||
console.error("videoData is null");
|
||||
showSnackbar.error(`获取B站视频信息失败:${props.data.insert.video}`);
|
||||
return;
|
||||
}
|
||||
const meta = videoData.value.dimension;
|
||||
if (meta.width > meta.height) videoAspectRatio.value = meta.width / meta.height;
|
||||
else videoAspectRatio.value = meta.height / meta.width;
|
||||
coverBuffer.value = await getImageBuffer(videoData.value.pic);
|
||||
videoCover.value = URL.createObjectURL(new Blob([coverBuffer.value], { type: "image/png" }));
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
if (videoData.value && videoData.value.pic) {
|
||||
videoCover.value = await saveImgLocal(videoData.value.pic);
|
||||
}
|
||||
if (videoRef.value === null) return;
|
||||
videoRef.value.addEventListener("fullscreenchange", listenFullScreen);
|
||||
});
|
||||
|
||||
async function listenFullScreen(): Promise<void> {
|
||||
if (document.fullscreenElement) await getCurrentWindow().setFullscreen(true);
|
||||
else await getCurrentWindow().setFullscreen(false);
|
||||
}
|
||||
|
||||
function getVideoTime(): string {
|
||||
const duration = videoData.value?.duration ?? 0;
|
||||
const seconds = duration % 60;
|
||||
const minutes = Math.floor(duration / 60) % 60;
|
||||
const hours = Math.floor(duration / 3600);
|
||||
let result = "";
|
||||
if (hours > 0) result += `${hours.toString().padStart(2, "0")}:`;
|
||||
result += `${minutes.toString().padStart(2, "0")}:`;
|
||||
result += `${seconds.toString().padStart(2, "0")}`;
|
||||
return result;
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
if (videoCover.value) URL.revokeObjectURL(videoCover.value);
|
||||
if (videoRef.value !== null) {
|
||||
videoRef.value.removeEventListener("fullscreenchange", listenFullScreen);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<style lang="css" scoped>
|
||||
@@ -102,6 +75,8 @@ onUnmounted(() => {
|
||||
}
|
||||
|
||||
.tp-video-container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
@@ -110,7 +85,7 @@ onUnmounted(() => {
|
||||
aspect-ratio: v-bind(videoAspectRatio);
|
||||
}
|
||||
|
||||
.tp-video-cover {
|
||||
.tp-video-share {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
top: 0;
|
||||
@@ -124,28 +99,64 @@ onUnmounted(() => {
|
||||
aspect-ratio: v-bind(videoAspectRatio);
|
||||
}
|
||||
|
||||
.tp-video-cover :first-child {
|
||||
width: 100%;
|
||||
.tp-video-cover {
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.tp-video-cover :nth-child(2) {
|
||||
.tp-video-icon {
|
||||
position: absolute;
|
||||
top: calc(50% - 40px);
|
||||
left: calc(50% - 40px);
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.tp-video-cover :nth-child(3) {
|
||||
.tp-video-time {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
padding: 0 5px;
|
||||
left: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
background: rgb(0 0 0/50%);
|
||||
color: var(--tgc-white-4);
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
.tp-video-info {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 5px;
|
||||
border-radius: 5px;
|
||||
background: rgb(0 0 0 / 50%);
|
||||
color: var(--tgc-white-1);
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.tp-video-view {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 5px;
|
||||
border-radius: 5px;
|
||||
background: rgb(0 0 0/50%);
|
||||
color: var(--tgc-white-4);
|
||||
font-family: var(--font-title);
|
||||
font-size: 12px;
|
||||
gap: 5px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user