Plex External Player

Play plex videos in an external player

当前为 2016-01-17 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Plex External Player
  3. // @namespace https://github.com/Kayomani/PlexExternalPlayer
  4. // @version 1.3
  5. // @description Play plex videos in an external player
  6. // @author Kayomani
  7. // @include /^https?://.*:32400/web.*
  8. // @include http://*:32400/web/index.html*
  9. // @require http://code.jquery.com/jquery-1.11.3.min.js
  10. // @grant GM_xmlhttpRequest
  11. // ==/UserScript==
  12.  
  13. var makeRequest = function(url){
  14. return new Promise( function (resolve, reject) {
  15. GM_xmlhttpRequest({
  16. method: "GET",
  17. headers: {
  18. "X-Plex-Token":localStorage["myPlexAccessToken"]
  19. },
  20. url: url,
  21. onload: resolve,
  22. onerror: reject
  23. });
  24. });
  25. };
  26.  
  27. var logMessage = function(msg){
  28. console.log('Plex External: ' + msg);
  29. };
  30.  
  31. var markAsPlayedInPlex = function(id) {
  32. logMessage('Marking ' + id + ' as played');
  33. return makeRequest(window.location.origin + '/:/scrobble?key='+ id +'&identifier=com.plexapp.plugins.library');
  34. };
  35.  
  36. var openItemOnAgent = function(path, id, openFolder) {
  37. if(openFolder){
  38. var fwd = path.lastIndexOf('/');
  39. var bck = path.lastIndexOf('\\');
  40. var best = fwd>bck?fwd:bck;
  41. if(best>-1){
  42. path = path.substr(0, best);
  43. }
  44. }
  45. logMessage('Playing ' + path);
  46. var url = 'http://localhost:7251/?protocol=1&item=' + encodeURIComponent(path);
  47. return new Promise(function (resolve, reject) {
  48. makeRequest(url).then(function(){
  49. markAsPlayedInPlex(id).then(resolve, reject);
  50. },reject);
  51. });
  52. };
  53.  
  54. var clickListener = function(e) {
  55. e.preventDefault();
  56. e.stopPropagation();
  57. var a = jQuery(e.target).closest('a');
  58. var link = a.attr('href');
  59. var openFolder = a.attr('data-type') === 'folder';
  60. var url = link;
  61. if (link === '#' || link === undefined) {
  62. url = window.location.hash;
  63. }
  64.  
  65. if (url.indexOf('%2Fmetadata%2F') > -1) {
  66. var idx = url.indexOf('%2Fmetadata%2F');
  67. var id = url.substr(idx + 14);
  68.  
  69. // Get metadata
  70. makeRequest(window.location.origin + '/library/metadata/' + id + '?checkFiles=1&includeExtras=1')
  71. .then(function(response){
  72. // Play the first availible part
  73. var parts = response.responseXML.getElementsByTagName('Part');
  74. for (var i = 0; i < parts.length; i++) {
  75. if (parts[i].attributes['file'] !== undefined) {
  76. openItemOnAgent(parts[i].attributes['file'].value, id, openFolder);
  77. return;
  78. }
  79. }
  80.  
  81. if (parts.length === 0) {
  82. // If we got a directory/Season back then get the files in it
  83. var dirs = response.responseXML.getElementsByTagName('Directory');
  84. if (dirs.length > 0) {
  85. makeRequest(window.location.origin + dirs[0].attributes['key'].value)
  86. .then(function(response){
  87. var videos = response.responseXML.getElementsByTagName('Video');
  88. var file = null;
  89. var id = null;
  90. for (var i = 0; i < videos.length; i++) {
  91. var vparts = videos[i].getElementsByTagName('Part');
  92. if (vparts.length > 0) {
  93. file = vparts[0].attributes['file'].value;
  94. id = vparts[0].attributes['id'].value;
  95. if (videos[i].attributes['lastViewedAt'] === null || videos[i].attributes['lastViewedAt'] === undefined) {
  96. break;
  97. }
  98. }
  99. }
  100.  
  101. if (file !== null) {
  102. openItemOnAgent(file, id, openFolder);
  103. }
  104. });
  105. }
  106. }
  107. });
  108. }
  109. };
  110.  
  111. var bindClicks = function() {
  112. jQuery(".glyphicon.play").each(function(i, e) {
  113. e = jQuery(e);
  114. if (!e.hasClass('plexextplayer')) {
  115. if (!e.parent().hasClass('hidden')) {
  116. e.addClass('plexextplayer');
  117. var parent = e.parent().parent();
  118. if (parent.is('li')) {
  119. var template = jQuery('<li><a class="btn-gray" href="#" title="Play Externally" data-toggle="Play Externally" data-original-title="Play Externally"><i class="glyphicon play plexextplayer plexextplayerico"></i></a></li><li><a class="btn-gray" href="#" title="Open containing folder" data-type="folder" data-toggle="Play Externally" data-original-title="Open containing folder"><i class="glyphicon play plexextplayer plexfolderextplayerico"></i></a></li>');
  120. parent.after(template);
  121. template.click(clickListener);
  122. } else if (parent.is('div') && parent.hasClass('media-poster-actions')) {
  123. var template = jQuery('<button class="play-btn media-poster-btn btn-link" tabindex="-1"><i class="glyphicon play plexextplayer plexextplayerico"></i></button>');
  124. parent.prepend(template);
  125. template.click(clickListener);
  126. }
  127. }
  128. }
  129. });
  130. };
  131.  
  132. // Make buttons smaller
  133. jQuery('body').append('<style>.media-poster-btn { padding: 8px !important; } .glyphicon.plexfolderextplayerico:before { content: "\\e343"; } .glyphicon.plexextplayerico:before { content: "\\e161"; }</style>');
  134.  
  135. // Bind buttons and check for new ones every 100ms
  136. setInterval(bindClicks, 100);
  137. bindClicks();