PageSourceSearch

https://radiox.com/wp-content/plugins/player-rnc/assets/js/rnc-player-core.js?ver=0.7.0

js radiox.com collected 2026-10-02 03:16:05 UTC 11,510 bytes, 265 lines download raw bytes

1/**
2 * rnc-player-core.js — logique commune aux players RNC (live + VOD).
3 * Moteur maison remplaçant RMP : chargement Google IMA (pub VAST/GAM),
4 * events GA, debug, et exécution d'un pré-roll réutilisable.
5 *
6 * Config par site injectée par le plugin PHP dans window.RNC_PLAYER_CONFIG :
7 *   adTag   URL du tag VAST (GAM) du site — '' = lecture sans pub.
8 *           Le littéral __timestamp__ y est remplacé par Date.now() à chaque requête.
9 *   debug   true = logs console préfixés [rnc-player].
10 *   live    config du module live-audio (voir rnc-player-live-audio.js).
11 *
12 * Exposé en global : window.RNCPlayer
13 */
14window.RNCPlayer = (function () {
15  'use strict';
16
17  var CFG = window.RNC_PLAYER_CONFIG || {};
18  // Logs : config du site, OU interrupteur local pour diagnostiquer en prod
19  // sans redéploiement : localStorage.setItem('rnc-debug','1') dans la console.
20  var DEBUG = !!CFG.debug;
21  try { if (window.localStorage && localStorage.getItem('rnc-debug') === '1') { DEBUG = true; } } catch (_) {}
22
23  function log() {
24    if (DEBUG && window.console && console.log) {
25      console.log.apply(console, ['[rnc-player]'].concat([].slice.call(arguments)));
26    }
27  }
28
29  var IMA_SDK = 'https://imasdk.googleapis.com/js/sdkloader/ima3.js';
30  var AD_TAG = CFG.adTag || '';
31
32  function adTag() {
33    return AD_TAG.replace('__timestamp__', Date.now());
34  }
35
36  function ga(event, title) {
37    log('GA →', event, title || '');
38    try {
39      if (typeof window.gtag === 'function') {
40        window.gtag('event', event, { video_title: title || '' });
41      } else if (window.dataLayer && window.dataLayer.push) {
42        window.dataLayer.push({ event: 'rnc_player', player_event: event, video_title: title || '' });
43      }
44    } catch (e) { /* GA absent */ }
45  }
46
47  // --- Google IMA (chargé une fois, tôt, pour être prêt au clic) ---
48  var imaOk = false, imaTried = false;
49  function loadIMA() {
50    if (!AD_TAG) { log('IMA: aucun tag configuré → pas de pub'); return; }
51    if (window.google && window.google.ima) { imaOk = true; return; }
52    if (imaTried) { return; }
53    imaTried = true;
54    log('IMA: chargement du SDK…');
55    var s = document.createElement('script');
56    s.src = IMA_SDK; s.async = true;
57    s.onload = function () {
58      imaOk = !!(window.google && window.google.ima);
59      log('IMA:', imaOk ? ('chargé — version ' + google.ima.VERSION) : 'google.ima absent');
60    };
61    s.onerror = function () { imaOk = false; log('IMA: BLOQUÉ (adblock/DNS/navigateur) → lecture sans pub'); };
62    document.head.appendChild(s);
63  }
64  function imaReady() { return imaOk; }
65
66  // --- HLS (.m3u8 — live TV Cloudflare Stream, VOD Stream éventuel) ---
67  // hls.js est vendu avec le plugin et chargé PARESSEUSEMENT : seulement au
68  // premier attachSource() d'une URL .m3u8 sur un navigateur sans HLS natif.
69  // CFG.hlsJs = URL du fichier (injectée par le plugin PHP).
70  var hlsLoading = null;
71  function loadHls() {
72    if (window.Hls) { return Promise.resolve(window.Hls); }
73    if (hlsLoading) { return hlsLoading; }
74    hlsLoading = new Promise(function (resolve, reject) {
75      if (!CFG.hlsJs) { reject(new Error('hlsJs non configuré')); return; }
76      log('hls: chargement de hls.js…');
77      var s = document.createElement('script');
78      s.src = CFG.hlsJs; s.async = true;
79      s.onload = function () { window.Hls ? resolve(window.Hls) : reject(new Error('Hls absent')); };
80      s.onerror = function () { reject(new Error('hls.js introuvable')); };
81      document.head.appendChild(s);
82    });
83    return hlsLoading;
84  }
85
86  function isHlsUrl(url) { return /\.m3u8($|\?)/.test(url || ''); }
87
88  /**
89   * Branche une source sur un média. URL .m3u8 → HLS natif (Safari/iOS) ou
90   * hls.js ; sinon src direct. onReady est appelé quand la source est prête à
91   * être jouée (manifest analysé pour hls.js). Retourne { destroy } pour
92   * libérer l'instance hls.js (arrête le buffering du live).
93   */
94  function attachSource(media, url, onReady) {
95    if (!isHlsUrl(url)) {
96      media.src = url;
97      if (onReady) { onReady(); }
98      return { destroy: function () { media.removeAttribute('src'); media.load(); } };
99    }
100    if (media.canPlayType('application/vnd.apple.mpegurl')) {
101      log('hls: natif (Safari)');
102      media.src = url;
103      if (onReady) { onReady(); }
104      return { destroy: function () { media.removeAttribute('src'); media.load(); } };
105    }
106    var hls = null, dead = false;
107    loadHls().then(function (Hls) {
108      if (dead) { return; }
109      if (!Hls.isSupported()) { log('hls: MSE indisponible → src direct (échec probable)'); media.src = url; if (onReady) { onReady(); } return; }
110      hls = new Hls();
111      hls.on(Hls.Events.MANIFEST_PARSED, function () { log('hls: manifest analysé'); if (onReady) { onReady(); } });
112      hls.on(Hls.Events.ERROR, function (_e, data) {
113        if (!data.fatal) { return; }
114        log('hls: erreur fatale', data.type, '→', data.details);
115        if (data.type === Hls.ErrorTypes.NETWORK_ERROR) { hls.startLoad(); }
116        else if (data.type === Hls.ErrorTypes.MEDIA_ERROR) { hls.recoverMediaError(); }
117        else { hls.destroy(); }
118      });
119      hls.loadSource(url);
120      hls.attachMedia(media);
121    }, function (e) {
122      log('hls:', e && e.message, '→ src direct (échec probable)');
123      media.src = url;
124      if (onReady) { onReady(); }
125    });
126    return {
127      destroy: function () {
128        dead = true;
129        if (hls) { try { hls.destroy(); } catch (_) {} hls = null; }
130      }
131    };
132  }
133
134  // --- Exclusivité de lecture : UN SEUL lecteur actif à la fois sur la page ---
135  // (ex. page d'extrait : le live du header et le lecteur d'extrait cohabitent).
136  // Chaque lecteur s'enregistre avec sa fonction de mise au repos (pause du média
137  // + annulation d'une éventuelle pub en cours), et appelle handle.claim() au
138  // moment où l'utilisateur le démarre → tous les autres se mettent au repos.
139  var players = [];
140  function registerPlayer(standDown) {
141    var h = {
142      standDown: standDown,
143      claim: function () {
144        for (var i = 0; i < players.length; i++) {
145          if (players[i] !== h) { try { players[i].standDown(); } catch (_) {} }
146        }
147      }
148    };
149    players.push(h);
150    return h;
151  }
152
153  /**
154   * Joue un pré-roll VAST puis rend la main au contenu.
155   * DOIT être appelé dans un geste utilisateur (clic) pour IMA/autoplay mobile.
156   * @param {Object} o
157   *   o.container    élément conteneur d'annonce (div)
158   *   o.content      élément média repris après la pub (<audio>/<video>)
159   *   o.gaTitle      titre pour les events GA
160   *   o.sizeEl       élément mesuré pour dimensionner la pub (ex. la scène 16:9) ;
161   *                  suivi sur resize. Défaut : 640×360.
162   *   o.onPause      appelé quand la pub prend la main (mettre le contenu en pause)
163   *   o.onAdStarted  appelé au démarrage de chaque annonce avec l'objet Ad IMA
164   *                  (permet de distinguer pub vidéo / audio et d'ouvrir la scène)
165   *   o.onResume     appelé pour (re)démarrer le contenu (fin de pub OU pas de pub/erreur)
166   * @returns {Object} { stop, resize, cancel } pour piloter la pub en cours (ou null).
167   *   cancel() = abandon SILENCIEUX : détruit la pub sans déclencher onResume
168   *   (⚠️ adsManager.stop() déclencherait CONTENT_RESUME → redémarrage du contenu).
169   */
170  function runPreroll(o) {
171    if (!imaOk) { log('pub: IMA indisponible → contenu direct'); o.onResume(); return null; }
172    var adsManager = null, cancelled = false;
173    function adSize() {
174      var w = 0, h = 0;
175      if (o.sizeEl) {
176        w = o.sizeEl.clientWidth || (o.sizeEl.parentNode ? o.sizeEl.parentNode.clientWidth : 0);
177        h = o.sizeEl.clientHeight; // hauteur réelle de la scène (son ratio suit le poster)
178      }
179      if (!w) { w = 640; }
180      if (!h) { h = Math.round(w * 9 / 16); }
181      return { w: w, h: h };
182    }
183    function resize() {
184      if (!adsManager) { return; }
185      var s = adSize();
186      try { adsManager.resize(s.w, s.h, google.ima.ViewMode.NORMAL); } catch (_) {}
187    }
188    function unbind() { window.removeEventListener('resize', resize); }
189    window.addEventListener('resize', resize);
190    try {
191      var adDisplayContainer = new google.ima.AdDisplayContainer(o.container, o.content);
192      adDisplayContainer.initialize(); // dans le geste utilisateur
193      var adsLoader = new google.ima.AdsLoader(adDisplayContainer);
194      adsLoader.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onErr, false);
195      adsLoader.addEventListener(google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED, onLoaded, false);
196      var req = new google.ima.AdsRequest();
197      req.adTagUrl = adTag();
198      var s0 = adSize();
199      req.linearAdSlotWidth = s0.w; req.linearAdSlotHeight = s0.h;
200      req.nonLinearAdSlotWidth = s0.w; req.nonLinearAdSlotHeight = 90;
201      log('pub: requête');
202      adsLoader.requestAds(req);
203    } catch (e) {
204      log('pub: exception → contenu direct', e && e.message);
205      unbind();
206      o.onResume();
207      return null;
208    }
209
210    function onLoaded(e) {
211      if (cancelled) { log('pub: annulée avant chargement'); return; }
212      adsManager = e.getAdsManager(o.content);
213      adsManager.addEventListener(google.ima.AdErrorEvent.Type.AD_ERROR, onErr);
214      adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED, function () { if (cancelled) { return; } log('pub: pause contenu'); if (o.onPause) o.onPause(); });
215      adsManager.addEventListener(google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED, function () { if (cancelled) { return; } log('pub: reprise contenu'); unbind(); o.onResume(); });
216      adsManager.addEventListener(google.ima.AdEvent.Type.LOADED, function () { log('pub: chargée'); });
217      adsManager.addEventListener(google.ima.AdEvent.Type.STARTED, function (e2) {
218        log('pub: démarrée'); ga('adimpression', o.gaTitle);
219        if (o.onAdStarted) { try { o.onAdStarted(e2.getAd()); } catch (_) {} }
220        resize(); // la scène a pu s'ouvrir/changer de taille au signalement de la pub
221      });
222      adsManager.addEventListener(google.ima.AdEvent.Type.CLICK, function () { ga('adclick', o.gaTitle); });
223      adsManager.addEventListener(google.ima.AdEvent.Type.ALL_ADS_COMPLETED, function () { log('pub: toutes complétées'); if (o.onAllDone) o.onAllDone(); });
224      try {
225        var s = adSize();
226        adsManager.init(s.w, s.h, google.ima.ViewMode.NORMAL);
227        adsManager.start();
228      } catch (err) { log('pub: start échec →', err && err.message); unbind(); o.onResume(); }
229    }
230    function onErr(e) {
231      if (cancelled) { return; }
232      log('pub: erreur →', (e && e.getError) ? e.getError().toString() : e);
233      ga('adloaderror', o.gaTitle);
234      if (adsManager) { try { adsManager.destroy(); } catch (_) {} }
235      unbind();
236      o.onResume();
237    }
238
239    return {
240      stop: function () { if (adsManager) { try { adsManager.stop(); } catch (_) {} } },
241      resize: resize,
242      cancel: function () {
243        cancelled = true;
244        unbind();
245        if (adsManager) { try { adsManager.destroy(); } catch (_) {} }
246      }
247    };
248  }
249
250  loadIMA(); // eager
251
252  return {
253    CONFIG: CFG,
254    DEBUG: DEBUG,
255    log: log,
256    ga: ga,
257    adTag: adTag,
258    loadIMA: loadIMA,
259    imaReady: imaReady,
260    isHlsUrl: isHlsUrl,
261    attachSource: attachSource,
262    registerPlayer: registerPlayer,
263    runPreroll: runPreroll
264  };
265})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.