ui: update pdf link processing

This commit is contained in:
undefined committed 2026-07-17 05:11:38 +08:00
1 parent 28286d69b9
commit aba8d9c755
1 file changed
+23 -10
@@ -3,7 +3,6 @@
/* eslint-disable regexp/no-useless-non-capturing-group */ /* eslint-disable regexp/no-useless-non-capturing-group */
/* eslint-disable regexp/optimal-quantifier-concatenation */ /* eslint-disable regexp/optimal-quantifier-concatenation */
import _ from 'lodash';
import type MarkdownIt from 'markdown-it'; import type MarkdownIt from 'markdown-it';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
@@ -77,13 +76,22 @@ function resourceUrl(service: string, src: string, url: string) {
if (service === 'vimeo') return `https://player.vimeo.com/video/${src}`; if (service === 'vimeo') return `https://player.vimeo.com/video/${src}`;
if (service === 'vine') return `https://vine.co/v/${src}/embed/simple`; if (service === 'vine') return `https://vine.co/v/${src}/embed/simple`;
if (service === 'prezi') { if (service === 'prezi') {
return `https://prezi.com/embed/${src}/?bgcolor=ffffff&lock_to_path=0&autoplay=0&autohide_ctrls=0&` return `https://prezi.com/embed/${src}/?bgcolor=ffffff&lock_to_path=0&autoplay=0&autohide_ctrls=0&`
+ 'landing_data=bHVZZmNaNDBIWnNjdEVENDRhZDFNZGNIUE43MHdLNWpsdFJLb2ZHanI5N1lQVHkxSHFxazZ0UUNCRHloSXZROHh3PT0&' + 'landing_data=bHVZZmNaNDBIWnNjdEVENDRhZDFNZGNIUE43MHdLNWpsdFJLb2ZHanI5N1lQVHkxSHFxazZ0UUNCRHloSXZROHh3PT0&'
+ 'landing_sign=1kD6c0N6aYpMUS0wxnQjxzSqZlEB8qNFdxtdjYhwSuI'; + 'landing_sign=1kD6c0N6aYpMUS0wxnQjxzSqZlEB8qNFdxtdjYhwSuI';
} }
return src; return src;
} }
function pdfUrlKind(src: string) {
const normalizedSrc = src.replace(/\s/g, '').replace(/\\/g, '/');
if (/^data:application\/pdf(?:;[^,]*)?,/i.test(normalizedSrc)) return 'data';
if (/^file:\/\//i.test(normalizedSrc)) return 'local';
if (/^https?:\/\//i.test(normalizedSrc)) return 'external';
if (normalizedSrc.startsWith('//') || /^[a-z][a-z\d+.-]*:/i.test(normalizedSrc)) return 'invalid';
return 'local';
}
declare module 'hydrooj' { declare module 'hydrooj' {
interface ModuleInterfaces { interface ModuleInterfaces {
richmedia: { richmedia: {
@@ -95,21 +103,26 @@ declare module 'hydrooj' {
export function Media(md: MarkdownIt, { pdfToolbar = false }: { pdfToolbar?: boolean } = {}) { export function Media(md: MarkdownIt, { pdfToolbar = false }: { pdfToolbar?: boolean } = {}) {
const supported = ['youtube', 'vimeo', 'vine', 'prezi', 'bilibili', 'youku', 'msoffice']; const supported = ['youtube', 'vimeo', 'vine', 'prezi', 'bilibili', 'youku', 'msoffice'];
md.renderer.rules.video = function tokenizeReturn(tokens, idx) { md.renderer.rules.video = function tokenizeReturn(tokens, idx) {
let src = md.utils.escapeHtml(tokens[idx].attrGet('src')); const src = tokens[idx].attrGet('src');
const service = tokens[idx].attrGet('service').replace(/[^A-Z0-9]/gi, '').toLowerCase(); const service = tokens[idx].attrGet('service').replace(/[^A-Z0-9]/gi, '').toLowerCase();
if (Hydro?.module?.richmedia?.[service]) { if (Hydro?.module?.richmedia?.[service]) {
const result = Hydro?.module?.richmedia[service].get(service, src, md); const result = Hydro?.module?.richmedia[service].get(service, src, md);
if (result) return result; if (result) return result;
} }
if (service === 'pdf') { if (service === 'pdf') {
if (['file://', './', '../'].some((i) => src.startsWith(i)) || (src[1] === '/' && src[2] !== '/')) src += src.includes('?') ? '&noDisposition=1' : '?noDisposition=1'; const kind = pdfUrlKind(src);
// A response with has content-disposition header causes the browser to download the file automatically. // A response with has content-disposition header causes the browser to download the file automatically.
// As we cannot control response header from external sites, we block embedding external PDFs. // As we cannot control response header from external sites, we block embedding external PDFs.
else return `<p>Embedding an external PDF is no longer supported.</p> <a href="${_.escape(src)}">Download</a>`; if (kind === 'external') return `<p>Embedding an external PDF is no longer supported.</p> <a href="${md.utils.escapeHtml(src)}">Download</a>`;
if (kind === 'invalid') return '<p>Embedding this PDF URL is not supported.</p>';
const fragmentPos = src.indexOf('#');
const pdfUrl = fragmentPos === -1 ? src : src.slice(0, fragmentPos);
const fragment = fragmentPos === -1 ? '' : src.slice(fragmentPos);
const pdfSrc = kind === 'data' ? src : `${pdfUrl}${pdfUrl.includes('?') ? '&' : '?'}noDisposition=1${fragment}`;
return `\ return `\
<object classid="clsid:${uuid().toUpperCase()}"> <object classid="clsid:${uuid().toUpperCase()}">
<param name="SRC" value="${src}"> <param name="SRC" value="${md.utils.escapeHtml(pdfSrc)}">
<embed type="application/pdf" width="100%" style="min-height:100vh;border:none;" fullscreen="yes" src="${src}#toolbar=${pdfToolbar ? '0' : '1'}&navpanes=0&view=FitH"> <embed type="application/pdf" width="100%" style="min-height:100vh;border:none;" fullscreen="yes" src="${md.utils.escapeHtml(`${pdfSrc}#toolbar=${pdfToolbar ? '0' : '1'}&navpanes=0&view=FitH`)}">
<noembed></noembed> <noembed></noembed>
</embed> </embed>
</object>`; </object>`;
@@ -117,7 +130,7 @@ export function Media(md: MarkdownIt, { pdfToolbar = false }: { pdfToolbar?: boo
if (['url', 'video'].includes(service)) { if (['url', 'video'].includes(service)) {
return `\ return `\
<video width="100%" controls> <video width="100%" controls>
<source src="${src}" type="${src.endsWith('ogg') ? 'video/ogg' : 'video/mp4'}"> <source src="${md.utils.escapeHtml(src)}" type="${src.endsWith('ogg') ? 'video/ogg' : 'video/mp4'}">
Your browser doesn't support video tag. Your browser doesn't support video tag.
</video>`; </video>`;
} }
@@ -125,7 +138,7 @@ export function Media(md: MarkdownIt, { pdfToolbar = false }: { pdfToolbar?: boo
return `\ return `\
<iframe class="embed-responsive-item ${service}-player" type="text/html" \ <iframe class="embed-responsive-item ${service}-player" type="text/html" \
width="100%" style="min-height: 500px" ${allowFullScreen} \ width="100%" style="min-height: 500px" ${allowFullScreen} \
src="${resourceUrl(service, src, tokens[idx].attrGet('url'))}" src="${md.utils.escapeHtml(resourceUrl(service, src, tokens[idx].attrGet('url')))}"
scrolling="no" border="0" frameborder="no" framespacing="0"></iframe>`; scrolling="no" border="0" frameborder="no" framespacing="0"></iframe>`;
} }
return `<div data-${service}>${md.utils.escapeHtml(src)}</div>`; return `<div data-${service}>${md.utils.escapeHtml(src)}</div>`;