PageSourceSearch

https://www.revolt.tv/scripts/reels-revolt-player.js

js revolt.tv collected 2026-10-01 10:11:53 UTC 13,400 bytes, 337 lines download raw bytes

1/*
2 * REVOLT.TV Floating Video Player
3 * This script is used to embed the revolt.tv promo video player on a website.
4 *
5 * COPYRIGHT (C) 2024 YOUR NAME. ALL RIGHTS RESERVED.
6 */
7
8/* eslint-disable no-undef */
9import { getPlayerProp } from '../templates/shorts/shorts.js';
10import { applyPrivacyToAdTag, resolveAdMode } from './privacy.js';
11import { suppressedAdvertising } from './jw-ads.js';
12
13const PLAYLIST_ID = 'STIJxPse';
14const PLAYER_DELAY = 3000; // 3 seconds delay before showing player on page
15const params = new URLSearchParams(window.location.search);
16
17// Load stylesheet from /styles/revolt-player.css
18const styleSheet = document.createElement('link');
19styleSheet.rel = 'stylesheet';
20styleSheet.href = '/styles/reels-revolt-player.css';
21document.head.appendChild(styleSheet);
22
23async function embedReelsRevoltPlayer() {
24  // Player wrapper div
25  const containerDiv = document.createElement('div');
26  containerDiv.classList.add('reels-revolt-player-container');
27
28  // Heading div
29  const headingDiv = document.createElement('div');
30  headingDiv.classList.add('reels-revolt-player-heading');
31
32  // Mobile only link div
33  const mobileLinkDiv = document.createElement('div');
34  mobileLinkDiv.classList.add('reels-revolt-player-mobile-link');
35  const mobileLinkElement = document.createElement('a');
36
37  mobileLinkDiv.appendChild(mobileLinkElement);
38  headingDiv.appendChild(mobileLinkDiv);
39  containerDiv.appendChild(headingDiv);
40
41  const playerDiv = document.createElement('div');
42  playerDiv.id = 'reels-revolt-player';
43  containerDiv.appendChild(playerDiv);
44
45  const linkDiv = document.createElement('div');
46  linkDiv.classList.add('reels-revolt-player-link');
47
48  const linkElement = document.createElement('a');
49  linkDiv.appendChild(linkElement);
50
51  const nextImageLinkElement = document.createElement('a');
52  nextImageLinkElement.href = 'https://www.revolt.tv'; // Default url used before video starts
53  nextImageLinkElement.classList.add('reels-revolt-player-next');
54  const nextImage = document.createElement('img');
55  nextImage.src = '/icons/revolt-player-next.png';
56  nextImage.alt = 'Watch Episode';
57  nextImageLinkElement.appendChild(nextImage);
58  linkDiv.appendChild(nextImageLinkElement);
59
60  const learnMoreButton = document.createElement('a');
61  learnMoreButton.classList.add('learn-more-btn');
62  learnMoreButton.innerHTML = 'Learn More';
63
64  const handleAdRedirection = (event, adClickURL) => {
65    if (event.currentTarget.innerText === learnMoreButton.innerHTML) {
66      window.open(adClickURL, '_blank');
67    }
68  };
69
70  containerDiv.appendChild(linkDiv);
71
72  document.body.appendChild(containerDiv);
73
74  // Fetch safetags from the current page
75  let safetags = '';
76  const maxRetries = 10;
77  const retryInterval = 250; // 500ms
78  function getSafetags() {
79    return new Promise((resolve) => {
80      let retryCount = 0;
81
82      function checkSafetags() {
83        if (window.safetag_lists && window.safetag_lists.Exclude) {
84          if (window.safetag_lists.Exclude.length > 0) {
85            safetags = window.safetag_lists.Exclude.join(',');
86            safetags = encodeURIComponent(safetags);
87          }
88          // eslint-disable-next-line no-console
89          console.log('player safetags (encoded)', safetags);
90
91          resolve();
92        } else if (retryCount < maxRetries) {
93          retryCount += 1;
94          setTimeout(checkSafetags, retryInterval);
95        } else {
96          // eslint-disable-next-line no-console
97          console.warn('Failed to get safetags after maximum retries');
98          resolve();
99        }
100      }
101
102      checkSafetags();
103    });
104  }
105
106  const playerDelay = typeof jwplayer === 'undefined' ? 1000 : 0;
107  setTimeout(async () => {
108    getSafetags();
109
110    const param = {
111      safetags: safetags || '',
112    };
113
114    // Resolve ad-serving eligibility BEFORE building/stamping tags below, so
115    // the schedule decision and the tag's privacy stamps read the same
116    // consent/opt-out snapshot instead of racing each other. Only attach an
117    // ad schedule when the visitor has Targeting (C0004) consent or is an
118    // eligible US opt-out visitor — otherwise the player never requests
119    // GAM/VAST ad tags.
120    const { load, nonPersonalized } = await resolveAdMode();
121
122    let preRolladTagUrl;
123    let midRolladTagUrl;
124
125    preRolladTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?iu=/21787171759/revolt.tv/site/preroll_ai_float_640x480v&description_url={currentPage}&sz=640x480&gdfp_req=1&output=xml_vast2&unviewed_position_start=1&env=vp&cust_params=safetag_exclude_list%3D{safetags}%26platform%3Dfloating';
126    midRolladTagUrl = 'https://pubads.g.doubleclick.net/gampad/ads?iu=/21787171759/revolt.tv/site/midroll_ai_float_640x480v&description_url={currentPage}&sz=640x480&gdfp_req=1&output=xml_vast2&unviewed_position_start=1&env=vp&cust_params=safetag_exclude_list%3D{safetags}%26platform%3Dfloating';
127
128    preRolladTagUrl = preRolladTagUrl.replace('{currentPage}', encodeURIComponent(window.location.href));
129    midRolladTagUrl = midRolladTagUrl.replace('{currentPage}', encodeURIComponent(window.location.href));
130
131    // Replace placeholders dynamically
132    Object.keys(param).forEach((key) => {
133      preRolladTagUrl = preRolladTagUrl.replace(`{${key}}`, param[key]);
134      midRolladTagUrl = midRolladTagUrl.replace(`{${key}}`, param[key]);
135    });
136
137    const rngCorrelator = Math.floor(Math.random() * 9999999).toString();
138    preRolladTagUrl = `${preRolladTagUrl}&correlator=${rngCorrelator}`;
139    midRolladTagUrl = `${midRolladTagUrl}&correlator=${rngCorrelator}`;
140
141    // Remove unused parameters in one go using regex
142    preRolladTagUrl = preRolladTagUrl.replace(/%26[^=]+%3D(&|$)/g, '$1').replace(/(&|\?)$/, '');
143    midRolladTagUrl = midRolladTagUrl.replace(/%26[^=]+%3D(&|$)/g, '$1').replace(/(&|\?)$/, '');
144
145    // Stamp the tags with the SAME opt-out determination resolveAdMode()
146    // above already made, rather than an independent (and possibly
147    // inconsistent) second read of GPC/usprivacy
148    preRolladTagUrl = applyPrivacyToAdTag(preRolladTagUrl, nonPersonalized);
149    midRolladTagUrl = applyPrivacyToAdTag(midRolladTagUrl, nonPersonalized);
150    const playerConfig = {
151      playlist: `https://cdn.jwplayer.com/v2/playlists/${PLAYLIST_ID}?format=json`,
152      autostart: true,
153      mute: true,
154    };
155    if (window.location.pathname !== '/shorts') {
156      // Always set the key. Leaving it unset on the blocked path let JW fall
157      // back to its cloud-hosted schedule and serve raw personalized tags to
158      // exactly the visitors this gate excludes.
159      playerConfig.advertising = !load ? suppressedAdvertising('vast') : {
160        client: 'vast',
161        schedule: [
162          { offset: 'pre', tag: preRolladTagUrl },
163          { offset: 30, tag: midRolladTagUrl },
164          { offset: 60, tag: midRolladTagUrl },
165          { offset: 90, tag: midRolladTagUrl },
166          { offset: 180, tag: midRolladTagUrl },
167          { offset: 150, tag: midRolladTagUrl },
168          { offset: 210, tag: midRolladTagUrl },
169          { offset: 120, tag: midRolladTagUrl },
170          { offset: 240, tag: midRolladTagUrl },
171          { offset: 270, tag: midRolladTagUrl },
172        ],
173      };
174    }
175    const shortsadconfig = await getPlayerProp();
176
177    if (window.location.pathname === '/shorts') {
178      playerConfig.advertising = shortsadconfig.advertising ?? suppressedAdvertising();
179    }
180    const player = window.jwplayer('reels-revolt-player').setup(playerConfig);
181    window.revoltFloatingPlayer = player;
182    window.revoltFloatingPlayerWasPlaying = false;
183
184    window.pauseFloatingPlayer = () => {
185      window.revoltFloatingPlayerWasPlaying = true;
186      window.revoltFloatingPlayer.pause(true);
187    };
188
189    window.resumeFloatingPlayer = () => {
190      if (window.revoltFloatingPlayerWasPlaying) {
191        window.revoltFloatingPlayer.play(true);
192        window.revoltFloatingPlayerWasPlaying = false;
193      }
194    };
195
196    function stylePlayIcon() {
197      // JW Player's volume/mute button class
198      const muteButton = document.querySelector('.jw-icon.jw-icon-volume');
199      if (muteButton) {
200        muteButton.style.display = 'flex';
201        muteButton.style.zIndex = '9'; // stay on top
202      }
203    }
204
205    function styleAdVolumeIcon() {
206      const volumeIcon = document.querySelector('.jwplayer.jw-flag-ads .jw-icon-volume');
207      if (volumeIcon) {
208        volumeIcon.style.top = '0px';
209        volumeIcon.style.display = 'flex';
210      }
211    }
212
213    function togglePlayIconLowPowerMode(show) {
214      setTimeout(() => {
215        const playIcon = document.querySelector(
216          '.jw-icon.jw-icon-display.jw-button-color.jw-reset',
217        );
218        if (!playIcon) return;
219
220        playIcon.style.setProperty('display', show ? 'flex' : 'none', 'important');
221      }, show ? 1000 : 0);
222    }
223
224    player.on('ready', () => {
225      if (window.location.pathname === '/shorts') {
226        player.setControls(true);
227        // Show play icon (Low Power Mode / iOS)
228        togglePlayIconLowPowerMode(true);
229        mobileLinkElement.style.display = '-webkit-box';
230        mobileLinkElement.innerText = player.getPlaylistItem().title;
231        mobileLinkElement.href = player.getPlaylistItem().articleURL;
232      }
233    });
234
235    player.on('play', () => {
236      if (window.location.pathname === '/shorts') {
237        stylePlayIcon();
238        // Hide play icon
239        togglePlayIconLowPowerMode(window.innerWidth > 711);
240      }
241      window.reelsPlayers?.forEach((p) => {
242        if (p.getState() === 'playing') {
243          p.pause(true);
244        }
245      });
246      mobileLinkElement.style.display = '-webkit-box';
247      mobileLinkElement.innerText = player.getPlaylistItem().title;
248      mobileLinkElement.href = player.getPlaylistItem().articleURL;
249    });
250
251    player.on('adPlay', () => {
252      if (window.location.pathname === '/shorts') {
253        styleAdVolumeIcon();
254      }
255      window.reelsPlayers?.forEach((p) => {
256        if (p.getState() === 'playing') {
257          p.pause(true);
258        }
259      });
260    });
261
262    // Advertisment Start Event - Update title, link
263    jwplayer('reels-revolt-player').on('adImpression', (event) => {
264      if (window.location.pathname === '/shorts') {
265        const videoView = document.querySelector(`#${playerDiv.id}_googima .jw-ads-view video`);
266        videoView.style.zIndex = 9;
267        const controlbar = document.querySelector('.jw-controlbar');
268        controlbar.style.zIndex = 99;
269      }
270      const adClickURL = window.location.pathname === '/shorts' ? event?.ima?.ad?.data?.clickThroughUrl : event.clickThroughUrl;
271
272      // Heading background class toggle for mobile
273      if (!headingDiv.classList.contains('reels-revolt-player-active-ad')) {
274        headingDiv.classList.add('reels-revolt-player-active-ad');
275      }
276      mobileLinkDiv.addEventListener('click', (clickEvent) => handleAdRedirection(clickEvent, adClickURL));
277
278      mobileLinkElement.style.display = 'none';
279      mobileLinkDiv.appendChild(learnMoreButton);
280
281      // Hide links
282      if (!linkDiv.classList.contains('hide-link')) {
283        linkDiv.classList.add('hide-link');
284      }
285    });
286
287    // Advertisement End Event - Update title, link
288    jwplayer('reels-revolt-player').on('adComplete adSkipped', () => {
289      // Heading background class toggle for mobile
290      if (headingDiv.classList.contains('reels-revolt-player-active-ad')) {
291        headingDiv.classList.remove('reels-revolt-player-active-ad');
292      }
293
294      mobileLinkDiv.removeEventListener('click', () => handleAdRedirection());
295
296      mobileLinkElement.style.display = '-webkit-box';
297      learnMoreButton.remove();
298      // Show links
299      if (linkDiv.classList.contains('hide-link')) {
300        linkDiv.classList.remove('hide-link');
301      }
302
303      mobileLinkElement.innerText = player.getPlaylistItem().title;
304
305      // Set link to show url and video title
306      const playlistItem = jwplayer('reels-revolt-player').getPlaylistItem();
307      linkElement.textContent = player.getPlaylistItem().title;
308      linkElement.href = player.getPlaylistItem().articleURL;
309
310      mobileLinkElement.textContent = playlistItem.seriesTitle;
311      mobileLinkElement.href = player.getPlaylistItem().articleURL;
312      nextImageLinkElement.href = player.getPlaylistItem().articleURL;
313    });
314  }, playerDelay);
315}
316
317// Call revolt floating video player
318const additionalPlayerDelay = window.revoltAddDelay || 0;
319const totalPlayerDelay = window.location.pathname.includes('/shorts') ? 100 : PLAYER_DELAY + additionalPlayerDelay;
320setTimeout(() => {
321  embedReelsRevoltPlayer();
322  const headerWrapper = document.querySelector('.header-wrapper');
323  const playerContainer = document.querySelector('.reels-revolt-player-container');
324
325  if ((window.location.pathname.includes('/clips') || window.location.pathname.includes('/reels') || window.location.pathname.includes('/shorts')) && !params.has('v5')) {
326    playerContainer.style.top = 'unset';
327  } else if ((window.location.pathname.includes('/clips') || window.location.pathname.includes('/reels') || window.location.pathname.includes('/shorts')) && params.has('v5')) {
328    playerContainer.style.top = '100%';
329  }
330  // Move playerContainer right after headerWrapper
331  headerWrapper.appendChild(playerContainer);
332
333  if (playerContainer) {
334    const headerTitle = document.querySelector('.article-head-wrapper');
335    headerTitle.style.marginTop = '100px';
336  }
337}, totalPlayerDelay);

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.