1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-11 18:24:43 +02:00

Desktop, Mobile: Add support for media player for video and audio files

Co-authored-by: Bryan <bryan.r.gerlach@gmail.com>
This commit is contained in:
Laurent Cozic 2020-08-02 12:03:49 +01:00
parent 7f73931530
commit 13280ce1b3
10 changed files with 54 additions and 10 deletions

View File

@ -145,6 +145,7 @@ ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
ReactNativeClient/lib/hooks/usePrevious.js ReactNativeClient/lib/hooks/usePrevious.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/media.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
ReactNativeClient/lib/JoplinServerApi.js ReactNativeClient/lib/JoplinServerApi.js

1
.gitignore vendored
View File

@ -136,6 +136,7 @@ ReactNativeClient/lib/hooks/useImperativeHandlerDebugger.js
ReactNativeClient/lib/hooks/usePrevious.js ReactNativeClient/lib/hooks/usePrevious.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/checkbox.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/fence.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/media.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/mermaid.js
ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js ReactNativeClient/lib/joplin-renderer/MdToHtml/rules/sanitize_html.js
ReactNativeClient/lib/JoplinServerApi.js ReactNativeClient/lib/JoplinServerApi.js

View File

@ -58,7 +58,7 @@ class HtmlToHtml {
html = htmlUtils.processImageTags(html, data => { html = htmlUtils.processImageTags(html, data => {
if (!data.src) return null; if (!data.src) return null;
const r = utils.imageReplacement(this.ResourceModel_, data.src, options.resources, this.resourceBaseUrl_); const r = utils.resourceReplacement(this.ResourceModel_, data.src, options.resources, this.resourceBaseUrl_);
if (!r) return null; if (!r) return null;
if (typeof r === 'string') { if (typeof r === 'string') {

View File

@ -17,6 +17,7 @@ const rules = {
code_inline: require('./MdToHtml/rules/code_inline'), code_inline: require('./MdToHtml/rules/code_inline'),
fountain: require('./MdToHtml/rules/fountain'), fountain: require('./MdToHtml/rules/fountain'),
mermaid: require('./MdToHtml/rules/mermaid').default, mermaid: require('./MdToHtml/rules/mermaid').default,
media: require('./MdToHtml/rules/media').default,
}; };
const setupLinkify = require('./MdToHtml/setupLinkify'); const setupLinkify = require('./MdToHtml/setupLinkify');

View File

@ -3,7 +3,7 @@ const htmlUtils = require('../../htmlUtils.js');
const utils = require('../../utils'); const utils = require('../../utils');
function renderImageHtml(before, src, after, ruleOptions) { function renderImageHtml(before, src, after, ruleOptions) {
const r = utils.imageReplacement(ruleOptions.ResourceModel, src, ruleOptions.resources, ruleOptions.resourceBaseUrl); const r = utils.resourceReplacement(ruleOptions.ResourceModel, src, ruleOptions.resources, ruleOptions.resourceBaseUrl);
if (typeof r === 'string') return r; if (typeof r === 'string') return r;
if (r) return `<img ${before} ${htmlUtils.attributesHtml(r)} ${after}/>`; if (r) return `<img ${before} ${htmlUtils.attributesHtml(r)} ${after}/>`;
return `[Image: ${src}]`; return `[Image: ${src}]`;

View File

@ -14,9 +14,9 @@ function installRule(markdownIt, mdOptions, ruleOptions) {
if (!Resource.isResourceUrl(src) || ruleOptions.plainResourceRendering) return defaultRender(tokens, idx, options, env, self); if (!Resource.isResourceUrl(src) || ruleOptions.plainResourceRendering) return defaultRender(tokens, idx, options, env, self);
const r = utils.imageReplacement(ruleOptions.ResourceModel, src, ruleOptions.resources, ruleOptions.resourceBaseUrl); const r = utils.resourceReplacement(ruleOptions.ResourceModel, src, ruleOptions.resources, ruleOptions.resourceBaseUrl);
if (typeof r === 'string') return r; if (typeof r === 'string') return r;
if (r) return `<img data-from-md ${htmlUtils.attributesHtml(Object.assign({}, r, { title: title }))}/>`; if (r && r.type === 'image') return `<img data-from-md ${htmlUtils.attributesHtml(Object.assign({}, r, { title: title }))}/>`;
return defaultRender(tokens, idx, options, env, self); return defaultRender(tokens, idx, options, env, self);
}; };

View File

@ -0,0 +1,30 @@
const utils = require('../../utils');
// @ts-ignore: Keep the function signature as-is despite unusued arguments
function installRule(markdownIt:any, mdOptions:any, ruleOptions:any, context:any) {
const defaultRender = markdownIt.renderer.rules.link_open;
markdownIt.renderer.rules.link_open = (tokens: { [x: string]: any; }, idx: string | number, options: any, env: any, self: any) => {
const Resource = ruleOptions.ResourceModel;
const token = tokens[idx];
const src = utils.getAttr(token.attrs, 'href');
if (!Resource.isResourceUrl(src) || ruleOptions.plainResourceRendering) return defaultRender(tokens, idx, options, env, self);
const r = utils.resourceReplacement(ruleOptions.ResourceModel, src, ruleOptions.resources, ruleOptions.resourceBaseUrl);
if (typeof r === 'string') return r;
if (r && r.type === 'audio') return `<audio controls><source src='${r.src}'></audio><a href=# onclick=ipcProxySendToHost('joplin://${src.substring(2)}')>`;
if (r && r.type === 'video') return `<video style="width:100%" controls><source src='${r.src}'></video>`;
console.log(context);
return defaultRender(tokens, idx, options, env, self);
};
}
export default function(context:any,ruleOptions:any) {
return function(md:any, mdOptions:any) {
installRule(md, mdOptions, ruleOptions, context);
};
}

View File

@ -122,7 +122,8 @@ utils.resourceStatus = function(ResourceModel, resourceInfo) {
return resourceStatus; return resourceStatus;
}; };
utils.imageReplacement = function(ResourceModel, src, resources, resourceBaseUrl) { utils.resourceReplacement = function(ResourceModel, src, resources, resourceBaseUrl) {
if (!ResourceModel) return null;
if (!ResourceModel || !resources) return null; if (!ResourceModel || !resources) return null;
if (!ResourceModel.isResourceUrl(src)) return null; if (!ResourceModel.isResourceUrl(src)) return null;
@ -138,13 +139,15 @@ utils.imageReplacement = function(ResourceModel, src, resources, resourceBaseUrl
} }
const mime = resource.mime ? resource.mime.toLowerCase() : ''; const mime = resource.mime ? resource.mime.toLowerCase() : '';
if (ResourceModel.isSupportedImageMimeType(mime)) { const type = ResourceModel.mimeTypeToMediaType(mime);
if (type != 'unknown') {
let newSrc = `./${ResourceModel.filename(resource)}`; let newSrc = `./${ResourceModel.filename(resource)}`;
if (resourceBaseUrl) newSrc = resourceBaseUrl + newSrc; if (resourceBaseUrl) newSrc = resourceBaseUrl + newSrc;
newSrc += `?t=${resource.updated_time}`; newSrc += `?t=${resource.updated_time}`;
return { return {
'data-resource-id': resource.id, 'data-resource-id': resource.id,
src: newSrc, src: newSrc,
type: type,
}; };
} }

View File

@ -26,9 +26,16 @@ class Resource extends BaseItem {
return this.encryptionService_; return this.encryptionService_;
} }
static isSupportedImageMimeType(type) { static mimeTypeToMediaType(type) {
const imageMimeTypes = ['image/jpg', 'image/jpeg', 'image/png', 'image/gif', 'image/svg+xml', 'image/webp']; if (type.startsWith('image/')) {
return imageMimeTypes.indexOf(type.toLowerCase()) >= 0; return 'image';
} else if (type.startsWith('audio/')) {
return 'audio';
} else if (type.startsWith('video/')) {
return 'video';
} else {
return 'unknown';
}
} }
static fetchStatuses(resourceIds) { static fetchStatuses(resourceIds) {
@ -205,7 +212,7 @@ class Resource extends BaseItem {
let tagAlt = resource.alt ? resource.alt : resource.title; let tagAlt = resource.alt ? resource.alt : resource.title;
if (!tagAlt) tagAlt = ''; if (!tagAlt) tagAlt = '';
const lines = []; const lines = [];
if (Resource.isSupportedImageMimeType(resource.mime)) { if (Resource.mimeTypeToMediaType(resource.mime) === 'image') {
lines.push('!['); lines.push('![');
lines.push(markdownUtils.escapeTitleText(tagAlt)); lines.push(markdownUtils.escapeTitleText(tagAlt));
lines.push(`](:/${resource.id})`); lines.push(`](:/${resource.id})`);

View File

@ -477,6 +477,7 @@ class Setting extends BaseModel {
'markdown.plugin.emoji': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable markdown emoji')}${wysiwygNo}` }, 'markdown.plugin.emoji': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable markdown emoji')}${wysiwygNo}` },
'markdown.plugin.insert': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ++insert++ syntax')}${wysiwygNo}` }, 'markdown.plugin.insert': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable ++insert++ syntax')}${wysiwygNo}` },
'markdown.plugin.multitable': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable multimarkdown table extension')}${wysiwygNo}` }, 'markdown.plugin.multitable': { value: false, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable multimarkdown table extension')}${wysiwygNo}` },
'markdown.plugin.media': { value: true, type: Setting.TYPE_BOOL, section: 'plugins', public: true, appTypes: ['mobile', 'desktop'], label: () => `${_('Enable media players (audio and video)')}${wysiwygNo}` },
// Tray icon (called AppIndicator) doesn't work in Ubuntu // Tray icon (called AppIndicator) doesn't work in Ubuntu
// http://www.webupd8.org/2017/04/fix-appindicator-not-working-for.html // http://www.webupd8.org/2017/04/fix-appindicator-not-working-for.html