mirror of
https://github.com/zadam/trilium.git
synced 2025-01-16 20:21:43 +08:00
implemented audio/video in note content renderer + streaming API #886
This commit is contained in:
parent
1fdf889ccf
commit
79bb249f3b
6 changed files with 10251 additions and 25 deletions
10206
package-lock.json
generated
10206
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -39,6 +39,7 @@
|
||||||
"electron-find": "1.0.6",
|
"electron-find": "1.0.6",
|
||||||
"electron-window-state": "5.0.3",
|
"electron-window-state": "5.0.3",
|
||||||
"express": "4.17.1",
|
"express": "4.17.1",
|
||||||
|
"express-partial-content": "^1.0.2",
|
||||||
"express-session": "1.17.1",
|
"express-session": "1.17.1",
|
||||||
"fs-extra": "9.1.0",
|
"fs-extra": "9.1.0",
|
||||||
"helmet": "4.4.1",
|
"helmet": "4.4.1",
|
||||||
|
|
|
@ -39,7 +39,7 @@ async function getRenderedContent(note, options = {}) {
|
||||||
.css("max-width", "100%")
|
.css("max-width", "100%")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else if (!options.tooltip && (type === 'file' || type === 'pdf')) {
|
else if (!options.tooltip && ['file', 'pdf', 'audio', 'video']) {
|
||||||
const $downloadButton = $('<button class="file-download btn btn-primary" type="button">Download</button>');
|
const $downloadButton = $('<button class="file-download btn btn-primary" type="button">Download</button>');
|
||||||
const $openButton = $('<button class="file-open btn btn-primary" type="button">Open</button>');
|
const $openButton = $('<button class="file-open btn btn-primary" type="button">Open</button>');
|
||||||
|
|
||||||
|
@ -57,6 +57,22 @@ async function getRenderedContent(note, options = {}) {
|
||||||
|
|
||||||
$content.append($pdfPreview);
|
$content.append($pdfPreview);
|
||||||
}
|
}
|
||||||
|
else if (type === 'audio') {
|
||||||
|
const $audioPreview = $('<audio controls></audio>')
|
||||||
|
.attr("src", openService.getUrlForDownload("api/notes/" + note.noteId + "/open"))
|
||||||
|
.attr("type", note.mime)
|
||||||
|
.css("width", "100%");
|
||||||
|
|
||||||
|
$content.append($audioPreview);
|
||||||
|
}
|
||||||
|
else if (type === 'video') {
|
||||||
|
const $videoPreview = $('<video controls></video>')
|
||||||
|
.attr("src", openService.getUrlForDownload("api/notes/" + note.noteId + "/open"))
|
||||||
|
.attr("type", note.mime)
|
||||||
|
.css("width", "100%");
|
||||||
|
|
||||||
|
$content.append($videoPreview);
|
||||||
|
}
|
||||||
|
|
||||||
$content.append(
|
$content.append(
|
||||||
$('<div style="display: flex; justify-content: space-evenly; margin-top: 5px;">')
|
$('<div style="display: flex; justify-content: space-evenly; margin-top: 5px;">')
|
||||||
|
@ -110,6 +126,10 @@ function getRenderingType(note) {
|
||||||
|
|
||||||
if (type === 'file' && note.mime === 'application/pdf') {
|
if (type === 'file' && note.mime === 'application/pdf') {
|
||||||
type = 'pdf';
|
type = 'pdf';
|
||||||
|
} else if (type === 'file' && note.mime.startsWith('audio/')) {
|
||||||
|
type = 'audio';
|
||||||
|
} else if (type === 'file' && note.mime.startsWith('video/')) {
|
||||||
|
type = 'video';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (note.isProtected) {
|
if (note.isProtected) {
|
||||||
|
|
|
@ -757,6 +757,10 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href
|
||||||
height: 50em; /* PDF is rendered in iframe and it's not possible to put full height so at least a large height */
|
height: 50em; /* PDF is rendered in iframe and it's not possible to put full height so at least a large height */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.include-note-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.alert-warning, .alert-info {
|
.alert-warning, .alert-info {
|
||||||
color: var(--main-text-color) !important;
|
color: var(--main-text-color) !important;
|
||||||
background-color: var(--accented-background-color) !important;
|
background-color: var(--accented-background-color) !important;
|
||||||
|
|
|
@ -6,6 +6,7 @@ const utils = require('../../services/utils');
|
||||||
const noteRevisionService = require('../../services/note_revisions');
|
const noteRevisionService = require('../../services/note_revisions');
|
||||||
const tmp = require('tmp');
|
const tmp = require('tmp');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
const { Readable } = require('stream');
|
||||||
|
|
||||||
function updateFile(req) {
|
function updateFile(req) {
|
||||||
const {noteId} = req.params;
|
const {noteId} = req.params;
|
||||||
|
@ -73,6 +74,38 @@ function openFile(req, res) {
|
||||||
return downloadNoteFile(noteId, res, false);
|
return downloadNoteFile(noteId, res, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function fileContentProvider(req) {
|
||||||
|
// Read file name from route params.
|
||||||
|
const note = repository.getNote(req.params.noteId);
|
||||||
|
const fileName = getFilename(note);
|
||||||
|
let content = note.getContent();
|
||||||
|
|
||||||
|
if (typeof content === "string") {
|
||||||
|
content = Buffer.from(content, 'utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
const totalSize = content.byteLength;
|
||||||
|
const mimeType = note.mime;
|
||||||
|
|
||||||
|
const getStream = range => {
|
||||||
|
if (!range) {
|
||||||
|
// Request if for complete content.
|
||||||
|
return Readable.from(content);
|
||||||
|
}
|
||||||
|
// Partial content request.
|
||||||
|
const { start, end } = range;
|
||||||
|
|
||||||
|
return Readable.from(content.slice(start, end + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
fileName,
|
||||||
|
totalSize,
|
||||||
|
mimeType,
|
||||||
|
getStream
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function saveToTmpDir(req) {
|
function saveToTmpDir(req) {
|
||||||
const noteId = req.params.noteId;
|
const noteId = req.params.noteId;
|
||||||
|
|
||||||
|
@ -95,6 +128,7 @@ function saveToTmpDir(req) {
|
||||||
module.exports = {
|
module.exports = {
|
||||||
updateFile,
|
updateFile,
|
||||||
openFile,
|
openFile,
|
||||||
|
fileContentProvider,
|
||||||
downloadFile,
|
downloadFile,
|
||||||
downloadNoteFile,
|
downloadNoteFile,
|
||||||
saveToTmpDir
|
saveToTmpDir
|
||||||
|
|
|
@ -48,6 +48,8 @@ const sql = require('../services/sql');
|
||||||
const protectedSessionService = require('../services/protected_session');
|
const protectedSessionService = require('../services/protected_session');
|
||||||
const entityChangesService = require('../services/entity_changes.js');
|
const entityChangesService = require('../services/entity_changes.js');
|
||||||
const csurf = require('csurf');
|
const csurf = require('csurf');
|
||||||
|
const {createPartialContentHandler} = require("express-partial-content");
|
||||||
|
|
||||||
|
|
||||||
const csrfMiddleware = csurf({
|
const csrfMiddleware = csurf({
|
||||||
cookie: true,
|
cookie: true,
|
||||||
|
@ -175,7 +177,10 @@ function register(app) {
|
||||||
route(PUT, '/api/notes/:noteId/file', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware],
|
route(PUT, '/api/notes/:noteId/file', [auth.checkApiAuthOrElectron, uploadMiddleware, csrfMiddleware],
|
||||||
filesRoute.updateFile, apiResultHandler);
|
filesRoute.updateFile, apiResultHandler);
|
||||||
|
|
||||||
route(GET, '/api/notes/:noteId/open', [auth.checkApiAuthOrElectron], filesRoute.openFile);
|
route(GET, '/api/notes/:noteId/open', [auth.checkApiAuthOrElectron],
|
||||||
|
createPartialContentHandler(filesRoute.fileContentProvider, {
|
||||||
|
debug: (string, extra) => { console.log(string, extra); }
|
||||||
|
}));
|
||||||
route(GET, '/api/notes/:noteId/download', [auth.checkApiAuthOrElectron], filesRoute.downloadFile);
|
route(GET, '/api/notes/:noteId/download', [auth.checkApiAuthOrElectron], filesRoute.downloadFile);
|
||||||
// this "hacky" path is used for easier referencing of CSS resources
|
// this "hacky" path is used for easier referencing of CSS resources
|
||||||
route(GET, '/api/notes/download/:noteId', [auth.checkApiAuthOrElectron], filesRoute.downloadFile);
|
route(GET, '/api/notes/download/:noteId', [auth.checkApiAuthOrElectron], filesRoute.downloadFile);
|
||||||
|
|
Loading…
Reference in a new issue