Plex Freebox

Add a "play on freebox server" button on plex interface.

目前為 2015-01-04 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name Plex Freebox
  3. // @namespace Kalmac
  4. // @description Add a "play on freebox server" button on plex interface.
  5. // @include http://plex.tv/web/app*
  6. // @include https://plex.tv/web/app*
  7. // @version 1
  8. // @grant GM_xmlhttpRequest
  9. // @require https://cdn.jsdelivr.net/jquery.cookie/1.4.1/jquery.cookie.min.js
  10. // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/hmac-sha1.js
  11. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  12. // ==/UserScript==
  13. (function () {
  14. var HOST = 'http://mafreebox.freebox.fr';
  15. var APP_ID = 'fr.freebox.kalmac';
  16. var APP_NAME = 'Plex 4 Freebox';
  17. var APP_VERSION = '0.1';
  18. var DEVICE_NAME = 'Plex remote';
  19. var COOKIE_TRACK_ID = 'freebox_plex_track_id';
  20. var COOKIE_TOKEN = 'freebox_plex_token';
  21. var COOKIE_AIRPLAY_PASS = 'freebox_plex_airplay_pass';
  22. var STATE_UNINITIALIZED = 0;
  23. var STATE_INITIALIZED = 1;
  24. var STATE_UNAUTHORIZED = 2;
  25. var STATE_PENDING = 3;
  26. var STATE_AUTHORIZED = 4;
  27. var STATE_ERROR = 10;
  28. var track_id, token, state = STATE_UNINITIALIZED;
  29. function callFreeboxApi(path, callback, params, method, headers) {
  30. if (typeof(params) != 'object') {
  31. params = {};
  32. }
  33. if (method !== 'POST') {
  34. method = 'GET';
  35. }
  36. if (typeof(headers) != 'object') {
  37. headers = {};
  38. }
  39. GM_xmlhttpRequest({
  40. url: HOST + path,
  41. data: JSON.stringify(params),
  42. method: method,
  43. headers: headers,
  44. onload:function(response) {
  45. callback(JSON.parse(response.responseText));
  46. },
  47. onerror: function(response) {
  48. state = STATE_ERROR;
  49. core();
  50. }
  51. });
  52. }
  53. function init() {
  54. track_id = $.cookie(COOKIE_TRACK_ID);
  55. token = $.cookie(COOKIE_TOKEN);
  56. if (typeof(track_id) != 'undefined' && typeof(token) != 'undefined') {
  57. state = STATE_PENDING;
  58. } else {
  59. state = STATE_UNAUTHORIZED;
  60. }
  61. }
  62. function login() {
  63. callFreeboxApi('/api/v3/login/authorize/', function(response) {
  64. if (response.success) {
  65. $.cookie(COOKIE_TRACK_ID, response.result.track_id, { expires: 1000, path: '/' });
  66. $.cookie(COOKIE_TOKEN, response.result.app_token, { expires: 1000, path: '/' });
  67. state = STATE_INITIALIZED;
  68. core();
  69. }
  70. }, {
  71. 'app_id': APP_ID,
  72. 'app_name': APP_NAME,
  73. 'app_version': APP_VERSION,
  74. 'device_name': DEVICE_NAME }, 'POST');
  75. var airplay_password = prompt("Veuillez autoriser l\'appli sur votre Freebox (fleche de droite) et saisir le mot de passe AirPlay...", "1111");
  76. $.cookie(COOKIE_AIRPLAY_PASS, airplay_password, { expires: 1000, path: '/' });
  77. updateAdminButton('Veuillez autoriser l\'appli sur votre Freebox (fleche de droite).', 'refresh', false);
  78. }
  79. function pending() {
  80. callFreeboxApi('/api/v3/login/authorize/' + track_id, function(response) {
  81. if (response.success) {
  82. switch(response.result.status) {
  83. case 'pending':
  84. break;
  85. case 'granted':
  86. state = STATE_AUTHORIZED;
  87. break;
  88. default:
  89. console.log('Freebox returned ' + response.result.status);
  90. $.removeCookie(COOKIE_TRACK_ID, { path: '/' });
  91. $.removeCookie(COOKIE_TOKEN, { path: '/' });
  92. state = STATE_INITIALIZED;
  93. break;
  94. }
  95. }
  96. });
  97. }
  98. function insertAdminButton() {
  99. var result = false;
  100. if (!$('li#plex-freebox-admin').length) {
  101. var elt = $('li#primary-player-dropdown');
  102. if (elt.length == 1) {
  103. elt.after('<li id="plex-freebox-admin"><a class="settings-btn" href="#" title="Plex 4 Freebox" data-toggle="tooltip" data-original-title="Plex 4 Freebox"><i class="glyphicon refresh"></i></a></li>');
  104. state = STATE_INITIALIZED;
  105. }
  106. }
  107. return result;
  108. }
  109. function updateAdminButton(text, glyph, callback) {
  110. var button = $('li#plex-freebox-admin > a');
  111. var icon = $('li#plex-freebox-admin > a > i');
  112. button.unbind('click');
  113. icon.attr('class', 'glyphicon');
  114. button.attr('data-original-title', text);
  115. button.attr('title', text);
  116. icon.addClass(glyph);
  117. if (callback) {
  118. button.click(callback);
  119. }
  120. }
  121. function decoratePlayer() {
  122. if ($('button#plex-freebox-play').length == 0) {
  123. $('div.video-controls-right span#player-quality-dropdown').before('<button id="plex-freebox-play" class="btn-link recommend-btn pull-left" title="Play 4 Freebox"><i class="glyphicon play">F</i></button>');
  124. $('button#plex-freebox-play').click(play);
  125. }
  126. }
  127. function play(event) {
  128. login(function(challenge) {
  129. session(challenge, function(session_token) {
  130. var pass = $.cookie(COOKIE_AIRPLAY_PASS);
  131. var video = $('video#html-video').attr('src');
  132. var params = {
  133. 'action':'start',
  134. 'media_type': 'video',
  135. 'media': video,
  136. 'password': typeof(pass) == 'undefined'?'1111':pass
  137. };
  138. callFreeboxApi('/api/v3/airmedia/receivers/Freebox%20Player/', function() {
  139. }, params, 'POST', {'X-Fbx-App-Auth': session_token});
  140. });
  141. });
  142. event.preventDefault();
  143. return false;
  144. }
  145. function login(callback) {
  146. callFreeboxApi('/api/v3/login/', function(response) {
  147. callback(response.result.challenge);
  148. });
  149. }
  150. function session(challenge, callback) {
  151. var params = {
  152. 'app_id':APP_ID,
  153. 'password': CryptoJS.HmacSHA1(challenge, token).toString(CryptoJS.enc.Hex)
  154. };
  155. callFreeboxApi('/api/v3/login/session/', function(response) {
  156. callback(response.result.session_token);
  157. }, params, 'POST');
  158. }
  159. function core() {
  160. var retry = false;
  161. switch(state) {
  162. case STATE_UNINITIALIZED:
  163. insertAdminButton();
  164. retry = 1000;
  165. break;
  166. case STATE_INITIALIZED:
  167. updateAdminButton('Plex 4 Freebox en attente ...', 'refresh', false);
  168. init();
  169. retry = 2000;
  170. break;
  171. case STATE_UNAUTHORIZED:
  172. updateAdminButton('Cliquez ici pour authoriser Plex 4 Freebox', 'router', login);
  173. // no retry core until button is clicked.
  174. break;
  175. case STATE_PENDING:
  176. updateAdminButton('Plex 4 Freebox en attente ...', 'refresh', false);
  177. pending();
  178. retry = 2000;
  179. break;
  180. case STATE_AUTHORIZED:
  181. updateAdminButton('Plex 4 Freebox', 'ok', false);
  182. decoratePlayer();
  183. retry = 2000;
  184. break;
  185. case STATE_ERROR:
  186. updateAdminButton('Plex 4 Freebox : ERREUR FATALE!', 'remove', false);
  187. break;
  188. }
  189. console.log('core with state = ' + state);
  190. if (retry) {
  191. setTimeout(core, retry);
  192. }
  193. }
  194. $( window ).load(function() {
  195. core();
  196. });
  197. }) ();