mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-02-24 18:53:43 +08:00
Lexical: Added media src conversions
Only actuall added YT in the end. Google had changed URL scheme, and Vimeo seems to just be something else now, can't really browse video pages like before.
This commit is contained in:
parent
958b537a49
commit
d89a2fdb15
@ -16,6 +16,7 @@ import {
|
|||||||
} from "lexical/nodes/common";
|
} from "lexical/nodes/common";
|
||||||
import {$selectSingleNode} from "../../utils/selection";
|
import {$selectSingleNode} from "../../utils/selection";
|
||||||
import {SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode";
|
import {SerializedCommonBlockNode} from "lexical/nodes/CommonBlockNode";
|
||||||
|
import * as url from "node:url";
|
||||||
|
|
||||||
export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio';
|
export type MediaNodeTag = 'iframe' | 'embed' | 'object' | 'video' | 'audio';
|
||||||
export type MediaNodeSource = {
|
export type MediaNodeSource = {
|
||||||
@ -343,11 +344,55 @@ export function $createMediaNodeFromHtml(html: string): MediaNode | null {
|
|||||||
return domElementToNode(tag as MediaNodeTag, el);
|
return domElementToNode(tag as MediaNodeTag, el);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface UrlPattern {
|
||||||
|
readonly regex: RegExp;
|
||||||
|
readonly w: number;
|
||||||
|
readonly h: number;
|
||||||
|
readonly url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These patterns originate from the tinymce/tinymce project.
|
||||||
|
* https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts
|
||||||
|
* License: MIT Copyright (c) 2022 Ephox Corporation DBA Tiny Technologies, Inc.
|
||||||
|
* License Link: https://github.com/tinymce/tinymce/blob/584a150679669859a528828e5d2910a083b1d911/LICENSE.TXT
|
||||||
|
*/
|
||||||
|
const urlPatterns: UrlPattern[] = [
|
||||||
|
{
|
||||||
|
regex: /.*?youtu\.be\/([\w\-_\?&=.]+)/i,
|
||||||
|
w: 560, h: 314,
|
||||||
|
url: 'https://www.youtube.com/embed/$1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /.*youtube\.com(.+)v=([^&]+)(&([a-z0-9&=\-_]+))?.*/i,
|
||||||
|
w: 560, h: 314,
|
||||||
|
url: 'https://www.youtube.com/embed/$2?$4',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regex: /.*youtube.com\/embed\/([a-z0-9\?&=\-_]+).*/i,
|
||||||
|
w: 560, h: 314,
|
||||||
|
url: 'https://www.youtube.com/embed/$1',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
const videoExtensions = ['mp4', 'mpeg', 'm4v', 'm4p', 'mov'];
|
const videoExtensions = ['mp4', 'mpeg', 'm4v', 'm4p', 'mov'];
|
||||||
const audioExtensions = ['3gp', 'aac', 'flac', 'mp3', 'm4a', 'ogg', 'wav', 'webm'];
|
const audioExtensions = ['3gp', 'aac', 'flac', 'mp3', 'm4a', 'ogg', 'wav', 'webm'];
|
||||||
const iframeExtensions = ['html', 'htm', 'php', 'asp', 'aspx', ''];
|
const iframeExtensions = ['html', 'htm', 'php', 'asp', 'aspx', ''];
|
||||||
|
|
||||||
export function $createMediaNodeFromSrc(src: string): MediaNode {
|
export function $createMediaNodeFromSrc(src: string): MediaNode {
|
||||||
|
|
||||||
|
for (const pattern of urlPatterns) {
|
||||||
|
const match = src.match(pattern.regex);
|
||||||
|
if (match) {
|
||||||
|
const newSrc = src.replace(pattern.regex, pattern.url);
|
||||||
|
const node = new MediaNode('iframe');
|
||||||
|
node.setSrc(newSrc);
|
||||||
|
node.setHeight(pattern.h);
|
||||||
|
node.setWidth(pattern.w);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let nodeTag: MediaNodeTag = 'iframe';
|
let nodeTag: MediaNodeTag = 'iframe';
|
||||||
const srcEnd = src.split('?')[0].split('/').pop() || '';
|
const srcEnd = src.split('?')[0].split('/').pop() || '';
|
||||||
const srcEndSplit = srcEnd.split('.');
|
const srcEndSplit = srcEnd.split('.');
|
||||||
@ -360,7 +405,9 @@ export function $createMediaNodeFromSrc(src: string): MediaNode {
|
|||||||
nodeTag = 'embed';
|
nodeTag = 'embed';
|
||||||
}
|
}
|
||||||
|
|
||||||
return new MediaNode(nodeTag);
|
const node = new MediaNode(nodeTag);
|
||||||
|
node.setSrc(src);
|
||||||
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function $isMediaNode(node: LexicalNode | null | undefined): node is MediaNode {
|
export function $isMediaNode(node: LexicalNode | null | undefined): node is MediaNode {
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
|
|
||||||
## Secondary Todo
|
## Secondary Todo
|
||||||
|
|
||||||
- Support media src conversions (https://github.com/tinymce/tinymce/blob/release/6.6/modules/tinymce/src/plugins/media/main/ts/core/UrlPatterns.ts)
|
|
||||||
- Deep check of translation coverage
|
- Deep check of translation coverage
|
||||||
|
|
||||||
## Bugs
|
## Bugs
|
||||||
|
@ -32,7 +32,7 @@ import {
|
|||||||
} from "../../../utils/selection";
|
} from "../../../utils/selection";
|
||||||
import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
|
import {$isDiagramNode, $openDrawingEditorForNode, showDiagramManagerForInsert} from "../../../utils/diagrams";
|
||||||
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
|
import {$createLinkedImageNodeFromImageData, showImageManager} from "../../../utils/images";
|
||||||
import {$showDetailsForm, $showImageForm, $showLinkForm} from "../forms/objects";
|
import {$showDetailsForm, $showImageForm, $showLinkForm, $showMediaForm} from "../forms/objects";
|
||||||
import {formatCodeBlock} from "../../../utils/formats";
|
import {formatCodeBlock} from "../../../utils/formats";
|
||||||
|
|
||||||
export const link: EditorButtonDefinition = {
|
export const link: EditorButtonDefinition = {
|
||||||
@ -168,24 +168,11 @@ export const media: EditorButtonDefinition = {
|
|||||||
label: 'Insert/edit Media',
|
label: 'Insert/edit Media',
|
||||||
icon: mediaIcon,
|
icon: mediaIcon,
|
||||||
action(context: EditorUiContext) {
|
action(context: EditorUiContext) {
|
||||||
const mediaModal = context.manager.createModal('media');
|
|
||||||
|
|
||||||
context.editor.getEditorState().read(() => {
|
context.editor.getEditorState().read(() => {
|
||||||
const selection = $getSelection();
|
const selection = $getSelection();
|
||||||
const selectedNode = $getNodeFromSelection(selection, $isMediaNode) as MediaNode | null;
|
const selectedNode = $getNodeFromSelection(selection, $isMediaNode) as MediaNode | null;
|
||||||
|
|
||||||
let formDefaults = {};
|
$showMediaForm(selectedNode, context);
|
||||||
if (selectedNode) {
|
|
||||||
const nodeAttrs = selectedNode.getAttributes();
|
|
||||||
formDefaults = {
|
|
||||||
src: nodeAttrs.src || nodeAttrs.data || '',
|
|
||||||
width: nodeAttrs.width,
|
|
||||||
height: nodeAttrs.height,
|
|
||||||
embed: '',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaModal.show(formDefaults);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
isActive(selection: BaseSelection | null): boolean {
|
isActive(selection: BaseSelection | null): boolean {
|
||||||
|
@ -186,6 +186,23 @@ export const link: EditorFormDefinition = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function $showMediaForm(media: MediaNode|null, context: EditorUiContext): void {
|
||||||
|
const mediaModal = context.manager.createModal('media');
|
||||||
|
|
||||||
|
let formDefaults = {};
|
||||||
|
if (media) {
|
||||||
|
const nodeAttrs = media.getAttributes();
|
||||||
|
formDefaults = {
|
||||||
|
src: nodeAttrs.src || nodeAttrs.data || '',
|
||||||
|
width: nodeAttrs.width,
|
||||||
|
height: nodeAttrs.height,
|
||||||
|
embed: '',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mediaModal.show(formDefaults);
|
||||||
|
}
|
||||||
|
|
||||||
export const media: EditorFormDefinition = {
|
export const media: EditorFormDefinition = {
|
||||||
submitText: 'Save',
|
submitText: 'Save',
|
||||||
async action(formData, context: EditorUiContext) {
|
async action(formData, context: EditorUiContext) {
|
||||||
@ -215,12 +232,19 @@ export const media: EditorFormDefinition = {
|
|||||||
const height = (formData.get('height') || '').toString().trim();
|
const height = (formData.get('height') || '').toString().trim();
|
||||||
const width = (formData.get('width') || '').toString().trim();
|
const width = (formData.get('width') || '').toString().trim();
|
||||||
|
|
||||||
const updateNode = selectedNode || $createMediaNodeFromSrc(src);
|
// Update existing
|
||||||
updateNode.setSrc(src);
|
if (selectedNode) {
|
||||||
updateNode.setWidthAndHeight(width, height);
|
selectedNode.setSrc(src);
|
||||||
if (!selectedNode) {
|
selectedNode.setWidthAndHeight(width, height);
|
||||||
$insertNodes([updateNode]);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Insert new
|
||||||
|
const node = $createMediaNodeFromSrc(src);
|
||||||
|
if (width || height) {
|
||||||
|
node.setWidthAndHeight(width, height);
|
||||||
|
}
|
||||||
|
$insertNodes([node]);
|
||||||
});
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user