From 988f59543569413c7d45a425d7059608f1c9ee53 Mon Sep 17 00:00:00 2001 From: BTMuli Date: Wed, 29 Mar 2023 13:53:47 +0800 Subject: [PATCH] =?UTF-8?q?fix(parser):=20=E5=AE=8C=E5=96=84=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/Home.vue | 5 +- src/pages/News.vue | 5 +- src/plugins/Mys/index.ts | 14 ++ src/plugins/Mys/interface/post.ts | 2 +- src/plugins/Mys/utils/parser.ts | 204 +++++++++++++++++++----------- 5 files changed, 152 insertions(+), 78 deletions(-) create mode 100644 src/plugins/Mys/index.ts diff --git a/src/pages/Home.vue b/src/pages/Home.vue index dd5b03e8..c6301428 100644 --- a/src/pages/Home.vue +++ b/src/pages/Home.vue @@ -62,9 +62,10 @@ import TLoading from "../components/t-loading.vue"; import { fs, http } from "@tauri-apps/api"; // store import useAppStore from "../store/modules/app"; +// plugin +import Mys_Oper from "../plugins/Mys"; // utils import { createTGWindow } from "../utils/TGWindow"; -import { StructuredPostParser } from "../plugins/Mys/utils/parser"; // interface import { GachaResponse, @@ -180,7 +181,7 @@ async function toPost(post_id: string) { return res.data.post.post; }); // 结构化渲染 - const parseDoc = StructuredPostParser(post.structured_content); + const parseDoc = Mys_Oper.PostParser(post.structured_content); // 将解析后的 doc 保存到 文件 await fs.writeTextFile( `${appStore.dataPath.temp}\\${post_id}_home.html`, diff --git a/src/pages/News.vue b/src/pages/News.vue index dc462b8e..f063b4e0 100644 --- a/src/pages/News.vue +++ b/src/pages/News.vue @@ -132,9 +132,10 @@ import useAppStore from "../store/modules/app"; // tools // @ts-ignore import "../tools/svg-inject.js"; +// plugin +import Mys_Oper from "../plugins/Mys"; // utils import { createTGWindow } from "../utils/TGWindow"; -import { StructuredPostParser } from "../plugins/Mys/utils/parser"; // interface import { Post, @@ -215,7 +216,7 @@ async function toPost(post_id: string) { // 获取渲染模式 if (renderMode.value) { // 结构化渲染 - parseDoc = StructuredPostParser(post.structured_content); + parseDoc = Mys_Oper.PostParser(post.structured_content); } else { // 原始渲染 parseDoc = new DOMParser().parseFromString(post.content, "text/html"); diff --git a/src/plugins/Mys/index.ts b/src/plugins/Mys/index.ts new file mode 100644 index 00000000..bdeec19f --- /dev/null +++ b/src/plugins/Mys/index.ts @@ -0,0 +1,14 @@ +/** + * @file plugins Mys index.ts + * @description Mys plugin index + * @author BTMuli + * @since Alpha + */ + +import { PostParser } from "./utils/parser"; + +const Mys_Oper = { + PostParser, +}; + +export default Mys_Oper; diff --git a/src/plugins/Mys/interface/post.ts b/src/plugins/Mys/interface/post.ts index 0010eadd..4ffc1cd6 100644 --- a/src/plugins/Mys/interface/post.ts +++ b/src/plugins/Mys/interface/post.ts @@ -322,4 +322,4 @@ export interface PostStructuredContent { color?: string; link?: string; }; -} \ No newline at end of file +} diff --git a/src/plugins/Mys/utils/parser.ts b/src/plugins/Mys/utils/parser.ts index 00821d4a..c4d782a3 100644 --- a/src/plugins/Mys/utils/parser.ts +++ b/src/plugins/Mys/utils/parser.ts @@ -4,7 +4,6 @@ * @author BTMuli * @since Alpha */ - import { PostStructuredContent } from "../interface/post"; /** @@ -13,87 +12,26 @@ import { PostStructuredContent } from "../interface/post"; * @description 为了安全考虑,不会解析所有的属性,只会解析几个常用的属性 * @returns {Document} 解析后的 HTML 文档 */ -export function StructuredPostParser(data: string): Document { +export function PostParser(data: string): Document { // Json 化 let jsonData: PostStructuredContent[] = JSON.parse(data); // 创建 HTML 文档 const doc = document.implementation.createHTMLDocument(); // 遍历 Json 数据 jsonData.forEach((item: any) => { - if (item.insert.image) { - // 创建 div - const div = document.createElement("div"); - // 创建图片 - const img = document.createElement("img"); - img.src = item.insert.image; - // 设置图片属性 - img.height = item.attributes.height; // 设置高度 - img.width = item.attributes.width; // 设置宽度 - // 如果宽度超过 800,将其设置为 800,图片自适应 - if (img.width > 800) img.width = 800; - // 高度自适应 - img.style.height = "auto"; - // 插入图片 - div.appendChild(img); - // 设置 div 属性 - div.style.display = "center"; // 居中 - div.style.margin = "20px auto"; // 设置 margin - // 插入 div - doc.body.appendChild(div); + if (typeof item.insert === "string") { + const text = TextParser(item); + doc.body.appendChild(text); + } else if (item.insert.image) { + const img = ImageParser(item); + doc.body.appendChild(img); } else if (item.insert.vod) { // 创建 div - const div = document.createElement("div"); - // 创建视频 - const video = document.createElement("video"); - // 获取最高分辨率的视频 - let resolution; - // 获取 resolutions中definition="1080P"的视频 - resolution = item.insert.vod.resolutions.find( - (resolution: any) => resolution.definition === "1080P" - ); - if (!resolution) { - // 如果没有找到,就获取720P的视频 - resolution = item.insert.vod.resolutions.find( - (resolution: any) => resolution.definition === "720P" - ); - } - if (!resolution) { - // 如果还是没有找到,就获取第一个 - resolution = item.insert.vod.resolutions[0]; - } - // 设置一些属性 - video.poster = item.insert.vod.cover; // 设置封面 - video.width = resolution.width > 800 ? 800 : resolution.width; // 设置宽度(取最高分辨率的宽度) - video.height = resolution.width > 800 ? 450 : resolution.height; // 设置高度(取最高分辨率的高度) - video.controls = true; // 设置 controls - // 添加 source - const source = document.createElement("source"); - source.src = resolution.url; - source.type = resolution.format === ".mp4" ? "video/mp4" : "video/webm"; - video.appendChild(source); - // 添加 controls - video.controls = true; - // 插入 video - div.appendChild(video); - // 设置 div 属性 - div.style.display = "center"; // 居中 - div.style.margin = "20px auto"; // 设置 margin + const video = VideoParser(item); // 插入 div - doc.body.appendChild(div); - } else if (typeof item.insert === "string") { - // 创建文本 - const text = document.createElement("span"); - // 设置文本属性 - // 创建 style string - if (item.attributes) { - let styleString = ""; - if (item.attributes.color) styleString += `color: ${item.attributes.color};`; - // 设置 style - text.style.cssText = styleString; - } - text.innerText = item.insert; // 设置文本 - // 插入文本 - doc.body.appendChild(text); + doc.body.appendChild(video); + } else if (item.insert.backup_text) { + // TODO: 折叠内容 } }); // doc 宽度设为 800,居中 @@ -101,3 +39,123 @@ export function StructuredPostParser(data: string): Document { doc.body.style.margin = "20px auto"; return doc; } + +/** + * @description 解析文本 + * @since Alpha + * @param {PostStructuredContent} data Mys数据 + * @returns {HTMLSpanElement} 解析后的文本 + */ +function TextParser(data: PostStructuredContent): HTMLSpanElement { + // 检查数据 + if (typeof data.insert !== "string") { + throw new Error("data.insert is not a string"); + } + // 创建文本 + const text = document.createElement("span"); + // 设置文本属性 + if (data.attributes) { + if (data.attributes.bold) text.style.fontWeight = "bold"; + if (data.attributes.color) text.style.color = data.attributes.color; + if (data.attributes.link) { + const a = document.createElement("a"); + a.href = data.attributes.link; + a.target = "_blank"; + a.innerText = data.insert; + return a; + } + } + // 行间距 + text.style.lineHeight = "2"; + // 设置 span 内容 + text.innerText = data.insert; + // 返回文本 + return text; +} + +/** + * @description 解析图片 + * @since Alpha + * @param {PostStructuredContent} data Mys数据 + * @returns {HTMLDivElement} 解析后的图片 + */ +function ImageParser(data: PostStructuredContent): HTMLDivElement { + // 检查数据 + if (typeof data.insert === "string") { + throw new Error("data.insert is a string"); + } + if (!data.insert.image) { + throw new Error("data.insert.image is not defined"); + } + if (!data.attributes) { + throw new Error("data.attributes is not defined"); + } + if (!data.attributes.width) { + throw new Error("data.attributes.width is not defined"); + } + if (!data.attributes.height) { + throw new Error("data.attributes.height is not defined"); + } + const div = document.createElement("div"); + // 创建图片 + const img = document.createElement("img"); + img.src = data.insert.image; + // 设置图片属性,窗口宽度 900,页面宽度 800 + img.style.height = "auto"; // 高度自适应 + img.width = 800; // 设置宽度 + // 判断是否是 cover + if (data.attributes.width === 690 && data.attributes.height === 320) { + // 添加 border-radius + img.style.borderRadius = "10px"; + } + // 插入图片 + div.appendChild(img); + // 设置 div 属性 + div.style.display = "center"; // 居中 + div.style.margin = "20px auto"; // 设置 margin + // 返回 div + return div; +} + +/** + * @description 解析视频 + * @since Alpha + * @param {PostStructuredContent} data Mys数据 + * @returns {HTMLDivElement} 解析后的视频 + */ +function VideoParser(data: PostStructuredContent): HTMLDivElement { + // 检查数据 + if (typeof data.insert === "string") { + throw new Error("data.insert is a string"); + } + if (!data.insert.vod) { + throw new Error("data.insert.vod is not defined"); + } + // 创建 div + const div = document.createElement("div"); + // 创建视频 + const video = document.createElement("video"); + // 获取 resolutions中size最大的视频 + const resolution = data.insert.vod.resolutions.reduce((prev: any, curr: any) => { + if (prev.size > curr.size) return prev; + return curr; + }); + // 设置视频属性 + video.poster = data.insert.vod.cover; // 设置封面 + video.width = 800; // 设置宽度 + video.height = 450; // 设置高度 + video.controls = true; // 设置 controls + // 添加 source + const source = document.createElement("source"); + source.src = resolution.url; + source.type = resolution.format === ".mp4" ? "video/mp4" : "video/webm"; + // 插入 source + video.appendChild(source); + // 插入 video + div.appendChild(video); + // 设置 div 属性 + div.style.display = "center"; // 居中 + div.style.margin = "20px auto"; // 设置 margin + // 返回 div + return div; +}