mirror of
https://github.com/sussy-code/smov.git
synced 2024-12-29 16:07:40 +01:00
sanitize html before placing into dom
This commit is contained in:
parent
315c3de3ab
commit
3bee46ff53
5 changed files with 3745 additions and 3587 deletions
|
@ -6,8 +6,8 @@
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formkit/auto-animate": "^1.0.0-beta.5",
|
"@formkit/auto-animate": "^1.0.0-beta.5",
|
||||||
"@headlessui/react": "^1.5.0",
|
"@headlessui/react": "^1.5.0",
|
||||||
"@types/react-helmet": "^6.1.6",
|
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
|
"dompurify": "^3.0.1",
|
||||||
"fscreen": "^1.2.0",
|
"fscreen": "^1.2.0",
|
||||||
"fuse.js": "^6.4.6",
|
"fuse.js": "^6.4.6",
|
||||||
"hls.js": "^1.0.7",
|
"hls.js": "^1.0.7",
|
||||||
|
@ -56,12 +56,14 @@
|
||||||
"@tailwindcss/line-clamp": "^0.4.2",
|
"@tailwindcss/line-clamp": "^0.4.2",
|
||||||
"@types/chromecast-caf-sender": "^1.0.5",
|
"@types/chromecast-caf-sender": "^1.0.5",
|
||||||
"@types/crypto-js": "^4.1.1",
|
"@types/crypto-js": "^4.1.1",
|
||||||
|
"@types/dompurify": "^2.4.0",
|
||||||
"@types/fscreen": "^1.0.1",
|
"@types/fscreen": "^1.0.1",
|
||||||
"@types/lodash.throttle": "^4.1.7",
|
"@types/lodash.throttle": "^4.1.7",
|
||||||
"@types/node": "^17.0.15",
|
"@types/node": "^17.0.15",
|
||||||
"@types/pako": "^2.0.0",
|
"@types/pako": "^2.0.0",
|
||||||
"@types/react": "^17.0.39",
|
"@types/react": "^17.0.39",
|
||||||
"@types/react-dom": "^17.0.11",
|
"@types/react-dom": "^17.0.11",
|
||||||
|
"@types/react-helmet": "^6.1.6",
|
||||||
"@types/react-router": "^5.1.18",
|
"@types/react-router": "^5.1.18",
|
||||||
"@types/react-router-dom": "^5.3.3",
|
"@types/react-router-dom": "^5.3.3",
|
||||||
"@types/react-stickynode": "^4.0.0",
|
"@types/react-stickynode": "^4.0.0",
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
import { mwFetch, proxiedFetch } from "@/backend/helpers/fetch";
|
import { mwFetch, proxiedFetch } from "@/backend/helpers/fetch";
|
||||||
import { MWCaption, MWCaptionType } from "@/backend/helpers/streams";
|
import { MWCaption, MWCaptionType } from "@/backend/helpers/streams";
|
||||||
import toWebVTT from "srt-webvtt";
|
import toWebVTT from "srt-webvtt";
|
||||||
|
import DOMPurify from "dompurify";
|
||||||
|
|
||||||
|
export const sanitize = DOMPurify.sanitize;
|
||||||
export const CUSTOM_CAPTION_ID = "customCaption";
|
export const CUSTOM_CAPTION_ID = "customCaption";
|
||||||
export async function getCaptionUrl(caption: MWCaption): Promise<string> {
|
export async function getCaptionUrl(caption: MWCaption): Promise<string> {
|
||||||
if (caption.type === MWCaptionType.SRT) {
|
if (caption.type === MWCaptionType.SRT) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { sanitize } from "@/backend/helpers/captions";
|
||||||
import { useSettings } from "@/state/settings";
|
import { useSettings } from "@/state/settings";
|
||||||
|
|
||||||
export function Caption({ text }: { text?: string }) {
|
export function Caption({ text }: { text?: string }) {
|
||||||
|
@ -5,13 +6,18 @@ export function Caption({ text }: { text?: string }) {
|
||||||
return (
|
return (
|
||||||
<span
|
<span
|
||||||
className="pointer-events-none mb-1 select-none px-1 text-center"
|
className="pointer-events-none mb-1 select-none px-1 text-center"
|
||||||
/*
|
dir="auto"
|
||||||
WebVTT files may have html elements (such as <i>, <b>) in them
|
// eslint-disable-next-line react/no-danger
|
||||||
but if we want full customization we will have to
|
dangerouslySetInnerHTML={{
|
||||||
remove tags with a regex from raw text
|
__html: sanitize(text || "", {
|
||||||
*/
|
// https://www.w3.org/TR/webvtt1/#dom-construction-rules
|
||||||
dangerouslySetInnerHTML={{ __html: text ?? "" }}
|
ALLOWED_TAGS: ["c", "b", "i", "u", "span", "ruby", "rt"],
|
||||||
|
ADD_TAGS: ["v", "lang"],
|
||||||
|
ALLOWED_ATTR: ["title", "lang"],
|
||||||
|
}),
|
||||||
|
}}
|
||||||
style={{
|
style={{
|
||||||
|
whiteSpace: "pre-wrap",
|
||||||
...captionSettings.style,
|
...captionSettings.style,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -22,7 +22,9 @@ export function CaptionRenderer({
|
||||||
const url = source?.caption?.url;
|
const url = source?.caption?.url;
|
||||||
if (url) {
|
if (url) {
|
||||||
// Is there a better way?
|
// Is there a better way?
|
||||||
const text = await (await fetch(url)).text();
|
const result = await fetch(url);
|
||||||
|
// Uses UTF-8 by default
|
||||||
|
const text = await result.text();
|
||||||
captions.current = parse(text, { strict: false }).cues;
|
captions.current = parse(text, { strict: false }).cues;
|
||||||
} else {
|
} else {
|
||||||
captions.current = [];
|
captions.current = [];
|
||||||
|
@ -43,7 +45,7 @@ export function CaptionRenderer({
|
||||||
{captions.current.map(
|
{captions.current.map(
|
||||||
({ identifier, end, start, text }) =>
|
({ identifier, end, start, text }) =>
|
||||||
isVisible(start, end) && (
|
isVisible(start, end) && (
|
||||||
<Caption key={identifier ?? Math.random() * 9999999} text={text} />
|
<Caption key={identifier || `${start}-${end}`} text={text} />
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
{isControlsShown ? (
|
{isControlsShown ? (
|
||||||
|
|
Loading…
Reference in a new issue