85 lines
3.0 KiB
TypeScript
85 lines
3.0 KiB
TypeScript
// src/utils/remarkVideoPlugin.ts
|
|
import { visit } from 'unist-util-visit';
|
|
import type { Plugin } from 'unified';
|
|
import type { Root } from 'hast';
|
|
|
|
|
|
export const remarkVideoPlugin: Plugin<[], Root> = () => {
|
|
return (tree: Root) => {
|
|
visit(tree, 'html', (node: any, index: number | undefined, parent: any) => {
|
|
if (node.value && node.value.includes('<video') && typeof index === 'number') {
|
|
|
|
const srcMatch = node.value.match(/src=["']([^"']+)["']/);
|
|
const titleMatch = node.value.match(/title=["']([^"']+)["']/);
|
|
|
|
if (srcMatch) {
|
|
const originalSrc = srcMatch[1];
|
|
const title = titleMatch?.[1] || 'Video';
|
|
|
|
const finalSrc = processNextcloudUrl(originalSrc);
|
|
|
|
const hasControls = node.value.includes('controls');
|
|
const hasAutoplay = node.value.includes('autoplay');
|
|
const hasMuted = node.value.includes('muted');
|
|
const hasLoop = node.value.includes('loop');
|
|
const hasPreload = node.value.match(/preload=["']([^"']+)["']/);
|
|
|
|
const enhancedHTML = `
|
|
<div class="video-container aspect-16-9">
|
|
<video
|
|
src="${escapeHtml(finalSrc)}"
|
|
${hasControls ? 'controls' : ''}
|
|
${hasAutoplay ? 'autoplay' : ''}
|
|
${hasMuted ? 'muted' : ''}
|
|
${hasLoop ? 'loop' : ''}
|
|
${hasPreload ? `preload="${hasPreload[1]}"` : 'preload="metadata"'}
|
|
style="width: 100%; height: 100%;"
|
|
data-video-title="${escapeHtml(title)}"
|
|
data-original-src="${escapeHtml(originalSrc)}"
|
|
>
|
|
<p>Your browser does not support the video element.</p>
|
|
</video>
|
|
${title !== 'Video' ? `
|
|
<div class="video-metadata">
|
|
<div class="video-title">${escapeHtml(title)}</div>
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
`.trim();
|
|
|
|
parent.children[index] = { type: 'html', value: enhancedHTML };
|
|
|
|
console.log(`[VIDEO] Processed: ${title}`);
|
|
console.log(`[VIDEO] Final URL: ${finalSrc}`);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
};
|
|
|
|
|
|
function processNextcloudUrl(originalUrl: string): string {
|
|
if (isNextcloudShareUrl(originalUrl) && !originalUrl.includes('/download')) {
|
|
const downloadUrl = `${originalUrl}/download`;
|
|
console.log(`[VIDEO] Auto-added /download: ${originalUrl} → ${downloadUrl}`);
|
|
return downloadUrl;
|
|
}
|
|
|
|
return originalUrl;
|
|
}
|
|
|
|
function isNextcloudShareUrl(url: string): boolean {
|
|
const pattern = /\/s\/[a-zA-Z0-9]+/;
|
|
return pattern.test(url) && (url.includes('nextcloud') || url.includes('cloud.'));
|
|
}
|
|
|
|
function escapeHtml(unsafe: string): string {
|
|
if (typeof unsafe !== 'string') return '';
|
|
|
|
return unsafe
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'");
|
|
} |