discovery-media-player 0.1.14 → 0.1.16
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.
- package/bin/serve.js +5 -1
- package/context/storage.js +11 -0
- package/docs/HOST-CONTRACT.md +3 -2
- package/package.json +1 -1
- package/server/handler.js +23 -3
package/bin/serve.js
CHANGED
|
@@ -50,7 +50,11 @@ function versParametres(url) {
|
|
|
50
50
|
return q;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
-
|
|
53
|
+
// ⚠️ DÉRIVÉE, PAS RECOPIÉE. Cette liste disait encore `.svg` après son retrait de la table des
|
|
54
|
+
// types en 0.1.7 : la page de premier contact proposait un format que la visionneuse ne sait plus
|
|
55
|
+
// ouvrir. Un clic, un téléchargement, et l'impression que le projet ne marche pas — sur l'écran
|
|
56
|
+
// qui ne se rejoue pas.
|
|
57
|
+
const AFFICHABLES = new Set(require("../context/storage").EXTENSIONS_AFFICHABLES);
|
|
54
58
|
|
|
55
59
|
/**
|
|
56
60
|
* Page d'accueil du mode dossier : ce qu'il y a à lire, et où le prendre.
|
package/context/storage.js
CHANGED
|
@@ -229,7 +229,18 @@ async function fetchAllowedFile(url, { range } = {}, { origins, hostBase, root,
|
|
|
229
229
|
return fetch(url, { headers });
|
|
230
230
|
}
|
|
231
231
|
|
|
232
|
+
/**
|
|
233
|
+
* Les extensions que le player sait AFFICHER — dérivées de la table ci-dessus, jamais recopiées.
|
|
234
|
+
*
|
|
235
|
+
* ⚠️ `bin/serve.js` en tenait une seconde liste pour sa page d'accueil, et elle contenait encore
|
|
236
|
+
* `.svg` après son retrait en 0.1.7. La page de PREMIER CONTACT proposait donc un format que la
|
|
237
|
+
* visionneuse ne sait plus ouvrir : on clique, et on reçoit un téléchargement. Deux listes du même
|
|
238
|
+
* fait finissent toujours par diverger — celle-ci se déduit.
|
|
239
|
+
*/
|
|
240
|
+
const EXTENSIONS_AFFICHABLES = Object.freeze(Object.keys(TYPES));
|
|
241
|
+
|
|
232
242
|
module.exports = {
|
|
243
|
+
EXTENSIONS_AFFICHABLES,
|
|
233
244
|
PUBLIC_OBJECT_PREFIX,
|
|
234
245
|
storageOrigins,
|
|
235
246
|
hostFetchBase,
|
package/docs/HOST-CONTRACT.md
CHANGED
|
@@ -25,11 +25,12 @@ need.
|
|
|
25
25
|
{
|
|
26
26
|
"product": "discovery-media-player",
|
|
27
27
|
"contract": 1,
|
|
28
|
-
"version": "
|
|
29
|
-
"capabilities": ["docshare", "presentations", "embed-denied", "host-fetch", "brand-reference", "host-auth", "host-share"],
|
|
28
|
+
"version": "<the running version>",
|
|
29
|
+
"capabilities": ["docshare", "presentations", "embed-denied", "host-fetch", "brand-reference", "host-auth", "host-share", "host-mail"],
|
|
30
30
|
"frameAncestors": ["'self'", "https://*.vercel.app", "https://app.example.com"],
|
|
31
31
|
"separateIssuer": true,
|
|
32
32
|
"hostShare": true,
|
|
33
|
+
"hostMail": true,
|
|
33
34
|
"plugins": { "bot": false, "visitors": false, "brandIntro": false, "botBrowser": false, "providerQuotas": false }
|
|
34
35
|
}
|
|
35
36
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "discovery-media-player",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.16",
|
|
4
4
|
"description": "Self-hosted document viewer: per-recipient tracked links, reading analytics, live presentation. The core knows nothing about the application hosting it — everything it borrows arrives through an injected context.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pdf-viewer",
|
package/server/handler.js
CHANGED
|
@@ -1407,7 +1407,27 @@ function viewerHtml(share, nonce, logoUrl, pitch) {
|
|
|
1407
1407
|
// INTÉGRÉ (?embed=1) : la visionneuse est en surimpression dans une page hôte de MÊME
|
|
1408
1408
|
// ORIGINE (le plan d'un lot dans une expérience 3D). L'hôte n'a alors plus besoin de sa
|
|
1409
1409
|
// propre barre de titre : celle-ci porte la croix, et la sortie remonte en postMessage.
|
|
1410
|
+
// ⚠️ DEUX QUESTIONS DIFFÉRENTES, QU'UNE SEULE VARIABLE CONFONDAIT.
|
|
1411
|
+
//
|
|
1412
|
+
// `embed` décide de l'HABILLAGE : la visionneuse porte sa propre croix, et l'hôte retire sa
|
|
1413
|
+
// barre de titre. L'aperçu a déjà la sienne (`closeBtn`, à droite) — lever la condition
|
|
1414
|
+
// afficherait donc DEUX croix. Le `!preview` n'était pas arbitraire.
|
|
1415
|
+
//
|
|
1416
|
+
// `embedded` répond à l'autre question : cette page est-elle SERVIE dans un cadre ? C'est elle
|
|
1417
|
+
// qui commande la poignée de main. Le serveur le sait déjà — il en tire les `frame-ancestors`
|
|
1418
|
+
// de la réponse — mais le navigateur ne l'apprenait qu'en aperçu de lien tracé.
|
|
1419
|
+
//
|
|
1420
|
+
// ⚠️ Conséquence signalée par le second hôte, et c'est exactement la panne que le contrat
|
|
1421
|
+
// décrit : `embed-ready` n'était jamais émis en aperçu, donc l'hôte recevait un SILENCE. Or le
|
|
1422
|
+
// silence couvre deux cas opposés — instance absente, ou player vivant. Leur guetteur de
|
|
1423
|
+
// démarrage a conclu « injoignable » et remplacé un player qui fonctionnait par la visionneuse
|
|
1424
|
+
// du navigateur, six secondes après l'ouverture, sous les yeux du lecteur. Et l'aperçu est
|
|
1425
|
+
// précisément le mode qu'un hôte utilise pour SES documents.
|
|
1426
|
+
//
|
|
1427
|
+
// La page était pourtant encadrable et parlait déjà (`share`, `close` en postMessage) : il ne
|
|
1428
|
+
// lui manquait que de dire qu'elle était là.
|
|
1410
1429
|
const embed = !preview && !!share.embed;
|
|
1430
|
+
const embedded = !!share.embed;
|
|
1411
1431
|
const fileUrl = preview ? share.stream_url : `/api/doc?slug=${encodeURIComponent(share.slug)}&file=1`;
|
|
1412
1432
|
// Logo de MARQUE du loader : celui du promoteur (brand_logo, ex. MJ
|
|
1413
1433
|
// Développement) s'il est renseigné, avec la mention de l'éditeur dessous (si configurée)
|
|
@@ -1421,7 +1441,7 @@ function viewerHtml(share, nonce, logoUrl, pitch) {
|
|
|
1421
1441
|
// En aperçu interne, on embarque de quoi démarrer une présentation live (URL Storage brute + métadonnées).
|
|
1422
1442
|
// `fileName` : c'est LUI qui dit la nature du document côté page. L'URL publique est
|
|
1423
1443
|
// `/api/doc?slug=…&file=1`, sans extension — sans ce champ, une image partait dans pdf.js.
|
|
1424
|
-
const cfg = JSON.stringify({ brand: PLAYER.branding.name, slug: preview ? "" : share.slug, fileUrl, fileName: share.file_name || "", pdfjs: PDFJS, title, preview, embed, bot: botOn, botGuided: !preview && !!share.bot_enabled && share.bot_guided !== false, botAv: (!preview && share.bot_enabled && share.bot_avatar) || "", botName: (!preview && share.bot_enabled && share.bot_name) || "", botGreet: (!preview && share.bot_enabled && share.bot_greeting) || "", botGreetDoc: (!preview && share.bot_enabled && share.bot_greeting_doc) || "", dl: share.allow_download !== false, autoPresent: !!share.auto_present, botAnim: share.bot_page_anim !== false, botVoice: !preview && !!share.bot_enabled && !!process.env.ELEVENLABS_API_KEY, vIcOn: ICONS.sound, vIcOff: ICONS.mute, kStyle: (!preview && share.bot_enabled && share.bot_karaoke) || "classic", vLayout: (!preview && share.bot_enabled && share.video_layout) || "", vClips: !preview && !!share.bot_vclips, botVAv: (!preview && share.bot_enabled && share.bot_vphoto) || "", resumeSlug: preview ? (share.resume_slug || "") : "", supaUrl: preview ? (share.supa_url || "") : "", supaKey: preview ? (share.supa_key || "") : "", internal: preview && share.internal_email ? { email: share.internal_email, name: share.presenter_name || "", docId: share.doc_id || "" } : null, present: preview ? { url: share.raw_url || "", name: share.file_name || "", title: share.doc_title || "", docId: share.doc_id || "", by: share.presenter_name || "", email: share.internal_email || "", av: share.presenter_avatar || "" } : null });
|
|
1444
|
+
const cfg = JSON.stringify({ brand: PLAYER.branding.name, slug: preview ? "" : share.slug, fileUrl, fileName: share.file_name || "", pdfjs: PDFJS, title, preview, embed, embedded, bot: botOn, botGuided: !preview && !!share.bot_enabled && share.bot_guided !== false, botAv: (!preview && share.bot_enabled && share.bot_avatar) || "", botName: (!preview && share.bot_enabled && share.bot_name) || "", botGreet: (!preview && share.bot_enabled && share.bot_greeting) || "", botGreetDoc: (!preview && share.bot_enabled && share.bot_greeting_doc) || "", dl: share.allow_download !== false, autoPresent: !!share.auto_present, botAnim: share.bot_page_anim !== false, botVoice: !preview && !!share.bot_enabled && !!process.env.ELEVENLABS_API_KEY, vIcOn: ICONS.sound, vIcOff: ICONS.mute, kStyle: (!preview && share.bot_enabled && share.bot_karaoke) || "classic", vLayout: (!preview && share.bot_enabled && share.video_layout) || "", vClips: !preview && !!share.bot_vclips, botVAv: (!preview && share.bot_enabled && share.bot_vphoto) || "", resumeSlug: preview ? (share.resume_slug || "") : "", supaUrl: preview ? (share.supa_url || "") : "", supaKey: preview ? (share.supa_key || "") : "", internal: preview && share.internal_email ? { email: share.internal_email, name: share.presenter_name || "", docId: share.doc_id || "" } : null, present: preview ? { url: share.raw_url || "", name: share.file_name || "", title: share.doc_title || "", docId: share.doc_id || "", by: share.presenter_name || "", email: share.internal_email || "", av: share.presenter_avatar || "" } : null });
|
|
1425
1445
|
return `<!doctype html><html lang=fr><head><meta charset=utf-8>
|
|
1426
1446
|
<meta name=viewport content="width=device-width,initial-scale=1,maximum-scale=3,viewport-fit=cover,interactive-widget=resizes-content">
|
|
1427
1447
|
<meta name=robots content="noindex,nofollow">
|
|
@@ -1744,7 +1764,7 @@ ${LEGAL_CSS}
|
|
|
1744
1764
|
// qu'on est là — sans cette poignée de main elle ne peut pas savoir si la croix
|
|
1745
1765
|
// existe (visionneuse d'une version antérieure) et garde la sienne par sécurité.
|
|
1746
1766
|
// Le format du fil, le targetOrigin et la validation vivent dans player/src/bridge.ts.
|
|
1747
|
-
if(CFG.
|
|
1767
|
+
if(CFG.embedded){
|
|
1748
1768
|
Player.bridge.sendToHost({type:'embed-ready'});
|
|
1749
1769
|
var _xb=document.getElementById('embedCloseBtn');
|
|
1750
1770
|
if(_xb) _xb.addEventListener('click',function(){ Player.bridge.sendToHost({type:'close'}); });
|
|
@@ -2810,7 +2830,7 @@ async function handler(req, res) {
|
|
|
2810
2830
|
}
|
|
2811
2831
|
const supaUrl = (PLAYER.config && PLAYER.config.supabaseUrl) || "";
|
|
2812
2832
|
const supaKey = (PLAYER.config && PLAYER.config.supabasePublishableKey) || "";
|
|
2813
|
-
const pseudo = { preview: true, slug: "", file_name: String(q.name || "document.pdf"), doc_title: String(q.title || q.name || "Document"), raw_url: url, doc_id: String(q.docId || ""), presenter_name: String(q.by || ""), presenter_avatar: String(q.av || ""), internal_email: String(q.uemail || ""), supa_url: supaUrl, supa_key: supaKey, auto_present: String(q.autopresent || "") === "1", resume_slug: String(q.resume || ""), stream_url: `/api/doc?preview=1&stream=1&url=${encodeURIComponent(url)}&name=${encodeURIComponent(String(q.name || ""))}` };
|
|
2833
|
+
const pseudo = { preview: true, embed, slug: "", file_name: String(q.name || "document.pdf"), doc_title: String(q.title || q.name || "Document"), raw_url: url, doc_id: String(q.docId || ""), presenter_name: String(q.by || ""), presenter_avatar: String(q.av || ""), internal_email: String(q.uemail || ""), supa_url: supaUrl, supa_key: supaKey, auto_present: String(q.autopresent || "") === "1", resume_slug: String(q.resume || ""), stream_url: `/api/doc?preview=1&stream=1&url=${encodeURIComponent(url)}&name=${encodeURIComponent(String(q.name || ""))}` };
|
|
2814
2834
|
let plogo = ""; try { plogo = await PLAYER.branding.logo(); } catch { /* sans logo */ }
|
|
2815
2835
|
const pnonce = crypto.randomBytes(16).toString("base64");
|
|
2816
2836
|
// Aperçu interne : CSP relâchée (supabase-js jsdelivr + Realtime wss) pour la présence + le chat live,
|