ep_media_upload 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +4 -2
  2. package/index.js +43 -4
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -27,8 +27,9 @@ Etherpad plugin for secure file uploads via S3 presigned URLs.
27
27
  "expires": 900,
28
28
  "downloadExpires": 300
29
29
  },
30
- "fileTypes": ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "zip"],
31
- "maxFileSize": 52428800
30
+ "fileTypes": ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "zip", "mp3", "mp4", "wav", "mov"],
31
+ "maxFileSize": 52428800,
32
+ "inlineExtensions": ["mp3", "mp4", "wav", "mov", "webm", "ogg"]
32
33
  }
33
34
  ```
34
35
 
@@ -49,6 +50,7 @@ Etherpad plugin for secure file uploads via S3 presigned URLs.
49
50
  |--------|----------|---------|-------------|
50
51
  | `fileTypes` | No | all | Array of allowed extensions (without dots) |
51
52
  | `maxFileSize` | No | unlimited | Max file size in bytes |
53
+ | `inlineExtensions` | No | `[]` | Extensions to open inline in browser (streaming). Files not in this list will download. |
52
54
 
53
55
  ### Environment Variables
54
56
 
package/index.js CHANGED
@@ -133,7 +133,7 @@ const EXTENSION_MIME_MAP = {
133
133
 
134
134
  // Audio
135
135
  mp3: ['audio/mpeg', 'audio/mp3'],
136
- wav: ['audio/wav', 'audio/wave', 'audio/x-wav'],
136
+ wav: ['audio/wav', 'audio/wave', 'audio/x-wav', 'audio/vnd.wave'],
137
137
  ogg: ['audio/ogg'],
138
138
  m4a: ['audio/mp4', 'audio/x-m4a'],
139
139
  flac: ['audio/flac'],
@@ -467,12 +467,50 @@ exports.expressCreateServer = (hookName, context) => {
467
467
  const prefix = keyPrefix || '';
468
468
  const key = `${prefix}${padId}/${fileId}`;
469
469
 
470
+ // Extract file extension to determine inline vs attachment disposition
471
+ const fileExtension = getValidExtension(fileId);
472
+
473
+ // Get inlineExtensions from config (extensions that should open in browser)
474
+ // Default behavior is download (attachment) for all files
475
+ const inlineExtensions = settings.ep_media_upload?.inlineExtensions || [];
476
+ const shouldOpenInline = fileExtension &&
477
+ Array.isArray(inlineExtensions) &&
478
+ inlineExtensions.map(e => e.toLowerCase()).includes(fileExtension.toLowerCase());
479
+
480
+ // Determine Content-Disposition based on extension config
481
+ // Extract filename for Content-Disposition header (UUID.ext -> use as filename)
482
+ const filename = fileId.replace(/[^\w\-_.]/g, '_'); // Sanitize for header
483
+ const disposition = shouldOpenInline
484
+ ? `inline; filename="${filename}"`
485
+ : `attachment; filename="${filename}"`;
486
+
487
+ // Map extensions to canonical MIME types for consistent browser playback
488
+ const EXTENSION_CONTENT_TYPE = {
489
+ mp3: 'audio/mpeg',
490
+ wav: 'audio/wav',
491
+ mp4: 'video/mp4',
492
+ mov: 'video/quicktime',
493
+ webm: 'video/webm',
494
+ ogg: 'audio/ogg',
495
+ m4a: 'audio/mp4',
496
+ pdf: 'application/pdf',
497
+ };
498
+
470
499
  // Generate presigned GET URL with short expiry
500
+ // Use ResponseContentDisposition and ResponseContentType to override stored headers
471
501
  const s3Client = new S3Client({ region });
472
- const getCommand = new GetObjectCommand({
502
+ const commandParams = {
473
503
  Bucket: bucket,
474
504
  Key: key,
475
- });
505
+ ResponseContentDisposition: disposition,
506
+ };
507
+
508
+ // Set canonical Content-Type for inline extensions to ensure browser compatibility
509
+ if (shouldOpenInline && fileExtension && EXTENSION_CONTENT_TYPE[fileExtension.toLowerCase()]) {
510
+ commandParams.ResponseContentType = EXTENSION_CONTENT_TYPE[fileExtension.toLowerCase()];
511
+ }
512
+
513
+ const getCommand = new GetObjectCommand(commandParams);
476
514
 
477
515
  // Use downloadExpires from config, default to 300 seconds (5 minutes)
478
516
  const expiresIn = downloadExpires || 300;
@@ -480,7 +518,8 @@ exports.expressCreateServer = (hookName, context) => {
480
518
 
481
519
  // Log download request for audit trail
482
520
  const username = req.session?.user?.username || 'anonymous';
483
- logger.info(`[ep_media_upload] DOWNLOAD: author="${authorId}" user="${username}" ip="${clientIp}" pad="${padId}" file="${fileId}"`);
521
+ const dispositionType = shouldOpenInline ? 'inline' : 'attachment';
522
+ logger.info(`[ep_media_upload] DOWNLOAD: author="${authorId}" user="${username}" ip="${clientIp}" pad="${padId}" file="${fileId}" disposition="${dispositionType}"`);
484
523
 
485
524
  // Redirect to the presigned URL
486
525
  return res.redirect(302, presignedGetUrl);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ep_media_upload",
3
3
  "description": "beta - Upload files to S3 and insert hyperlinks into the pad. Requires ep_hyperlinked_text.",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
5
5
  "author": {
6
6
  "name": "DCastelone",
7
7
  "url": "https://github.com/dcastelone"