Vimeo.js

  1. /**
  2. * Vimeo website support module.
  3. * @author Moez Bouhlel <bmoez.j@gmail.com>
  4. * @license MPL-2.0
  5. * @copyright 2014-2017 Moez Bouhlel
  6. */
  7. import Module from './Module.js';
  8. import VP from './video-player.js';
  9. import {
  10. asyncGet,
  11. } from './common.js';
  12. /**
  13. * Vimeo website support.
  14. * Vimeo does embed its video player directly into the web page.
  15. *
  16. * URLs support:
  17. * - [x] `http[s]?://vimeo.com/<VIDEO_ID>`
  18. * - [x] `http[s]?://vimeo.com/<USER>`
  19. * - [x] `http[s]?://vimeo.com/channels/<CHANNEL_NAME>`
  20. * - [x] `http[s]?://player.vimeo.com/video/<VIDEO_ID>`
  21. * - [ ] `http[s]?://vimeo.com/couchmode/user<USER_ID>/videos/sort:<SORT_TYPE>/<VIDEO_ID>`
  22. *
  23. * Other:
  24. * - [Milestone](https://github.com/lejenome/html5-video-everywhere/milestones/Vimeo%20Support)
  25. *
  26. * @external
  27. */
  28. class Vimeo extends Module {
  29. constructor() {
  30. super("vimeo");
  31. }
  32. onInteractive() {
  33. this.getConfig().then((conf) => this.getVideoInfo(conf));
  34. }
  35. injectPlayer(conf) {
  36. try {
  37. let playerContainer;
  38. let stl;
  39. if (conf.isEmbed) {
  40. playerContainer = document.body;
  41. } else if (conf.isWatch) {
  42. playerContainer =
  43. document.getElementsByClassName("player_area")[0];
  44. /*
  45. if ((stl = playerContainer.children[0]) && (stl = stl.sheet) &&
  46. (stl.cssRules.length > 0)) {
  47. stl = stl.cssRules[0].cssText;
  48. }
  49. */
  50. } else {
  51. playerContainer = document.getElementById("clip_" + conf.id);
  52. }
  53. if (!playerContainer) return;
  54. let vp = new VP(playerContainer, this.options);
  55. vp.srcs(conf.fmts);
  56. vp.props({
  57. className: conf.className,
  58. autoplay: this.options.isAutoPlay(),
  59. preload: this.options.getPreload(),
  60. loop: this.options.isLoop(),
  61. controls: true,
  62. poster: conf.poster,
  63. volume: this.options.getVolume(),
  64. });
  65. vp.tracksList(conf.tracks.map((l) => l.lang), (lang, resolve, reject) => {
  66. let l = conf.tracks.find((l) => l.lang === lang);
  67. if (l === undefined) {
  68. reject();
  69. } else {
  70. resolve(l.direct_url || l.url);
  71. }
  72. });
  73. if (stl) vp.addCSSRule(stl);
  74. vp.setup();
  75. if (conf.isWatch) this.brozarEvents();
  76. } catch (e) {
  77. this.log("Exception on changePlayer()", e.lineNumber, e.columnNumber, e.message, e.stack);
  78. }
  79. }
  80. getConfig() {
  81. return new Promise((resolve, reject) => {
  82. let isWatch =
  83. /https?:\/\/vimeo.com\/[\d]+/.test(location.href) || this.ogType().indexOf("video") > -1;
  84. let isEmbed = /https?:\/\/player.vimeo.com\/video/.test(location.href);
  85. let isChannel = !isWatch && (/https?:\/\/vimeo.com\/(channels\/|)\w+/.test(location.href) ||
  86. this.ogType().match(/channel|profile/) !== null);
  87. if (!isWatch && !isChannel && !isEmbed) reject();
  88. let playerId;
  89. let playerClass;
  90. if (isWatch) {
  91. playerId = location.pathname.match(/\/([\d]+)/)[1];
  92. playerClass = "player";
  93. } else if (isEmbed) {
  94. playerId = location.pathname.match(/video\/([\d]+)/)[1];
  95. playerClass = "fallback";
  96. } else if (isChannel) {
  97. playerClass = "player";
  98. }
  99. if (!playerId && !isChannel) reject();
  100. resolve({
  101. isWatch: isWatch,
  102. isEmbed: isEmbed,
  103. isChannel: isChannel,
  104. id: playerId,
  105. className: playerClass,
  106. });
  107. });
  108. }
  109. getVideoInfo(conf) {
  110. const INFO_URL = "https://player.vimeo.com/video/";
  111. if (conf.isChannel) {
  112. return Array.map(document.getElementsByClassName("player_container"), (el) => {
  113. let _conf = {};
  114. for (const k of Object.keys(conf)) {
  115. _conf[k] = conf[k];
  116. }
  117. _conf.id = el.id.replace("clip_", "");
  118. return asyncGet(INFO_URL + _conf.id + "/config")
  119. .then(this.processData(_conf))
  120. .then((conf) => this.injectPlayer(conf));
  121. });
  122. } else {
  123. return asyncGet(INFO_URL + conf.id + "/config")
  124. .then(this.processData(conf))
  125. .then((conf) => this.injectPlayer(conf));
  126. }
  127. }
  128. processData(conf) {
  129. return (data) => {
  130. data = JSON.parse(data);
  131. conf.fmts = this.getSrcs(data.request.files.progressive);
  132. conf.poster = data.video.thumbs.base;
  133. conf.tracks = data.request.text_tracks || [];
  134. return Promise.resolve(conf);
  135. };
  136. }
  137. getSrcs(progressive) {
  138. let fmts = {};
  139. let srcs = {};
  140. Array.forEach(progressive, (v) => {
  141. srcs[v.quality] = v.url;
  142. });
  143. for (const [q, fmt] of [
  144. ["270p", "low/mp4"],
  145. ["360p", "medium/mp4"],
  146. ["720p", "high/mp4"],
  147. ["1028p", "higher/mp4"],
  148. ]) {
  149. if (srcs[q]) fmts[fmt] = srcs[q];
  150. }
  151. return fmts;
  152. }
  153. brozarEvents() {
  154. // change Vimeo default click events of items on brozar element
  155. let clips = document.getElementById("clips");
  156. if (clips) {
  157. clips.onclick = function(e) {
  158. if (e.target === e.currentTarget) return;
  159. let li = e.target;
  160. while (li.tagName !== "LI") {
  161. li = li.parentElement;
  162. }
  163. window.location = "/" + li.id.replace("clip_", "");
  164. };
  165. }
  166. let promos = document.getElementsByClassName("js-promo_link");
  167. let promoClick = function(e) {
  168. window.location = "/" + e.currentTarget.dataset.clipId;
  169. };
  170. for (let i = 0; promos && i < promos.length; i++) {
  171. promos[i].onclick = promoClick;
  172. }
  173. }
  174. ogType() {
  175. let t = document.head.querySelector("meta[property=\"og:type\"]");
  176. return (t) ? t.content : "";
  177. }
  178. }
  179. new Vimeo().start();