Jellyfin with Potplayer

play video with Potplayer

  1. // ==UserScript==
  2. // @name Jellyfin with Potplayer
  3. // @namespace https://greasyfork.org/zh-CN/scripts/487579-jellyfin-with-potplayer
  4. // @version 1.2
  5. // @license MIT
  6. // @description play video with Potplayer
  7. // @author jasmineamberr 原作者:Tccoin https://github.com/tccoin/Jellyfin-Potplayer
  8. // @match http://192.168.2.21:8096/web/index.html
  9. // @grant GM_registerMenuCommand
  10. // @grant GM_setValue
  11. // @grant GM_getValue
  12. // ==/UserScript==
  13.  
  14. (function() {
  15. 'use strict';
  16. let api_key = "f3ed4fdbc14042b4b8a2a87291726b2f"
  17. async function getMovie(jellyfin_url, userid, itemid) {
  18. let id
  19. let r = await fetch(`${jellyfin_url}/Users/${userid}/Items?ParentId=${itemid}`, {
  20. headers: {
  21. "X-Emby-Authorization": `MediaBrowser Client="tampermonkey", Token="${api_key}"`
  22. }
  23. })
  24. let data = await r.json()
  25. id = data.Items[0].Id
  26. if (["Episode", "Movie"].includes(data.Items[0].Type)) {
  27. return id
  28. } else {
  29. return getMovie(jellyfin_url, userid, id)
  30. }
  31. }
  32. let openPotplayer = async (itemid) => {
  33. let id;
  34. let jellyfin_url = ApiClient._serverAddress // eslint-disable-line
  35. let userid = (await ApiClient.getCurrentUser()).Id; // eslint-disable-line
  36. let r = await ApiClient.getItem(userid, itemid) // eslint-disable-line
  37. if (["Episode", "Movie", "Video"].includes(r.Type)) {
  38. id = itemid
  39. } else if ("Person" == r.Type) {
  40. return
  41. } else {
  42. id = await getMovie(jellyfin_url, userid, itemid)
  43. }
  44. let url = `${jellyfin_url}/Items/${id}/Download?api_key=${api_key}`
  45. // console.log(url)
  46. window.open('potplayer://' + url)
  47. }
  48.  
  49. let bindEvent = async () => {
  50. let buttons = [];
  51. let retry = 6 + 1;
  52. while (buttons.length == 0 && retry > 0) {
  53. await new Promise(resolve => setTimeout(resolve, 1000));
  54. buttons = document.querySelectorAll('[data-mode=play],[data-mode=resume],[data-action=resume]');
  55. retry -= 1;
  56. }
  57. for (let button of buttons) {
  58. let nextElementSibling = button.nextElementSibling;
  59. let parentElement = button.parentElement;
  60. let outerHTML = button.outerHTML;
  61. button.parentElement.removeChild(button);
  62. let newButton = document.createElement('button');
  63. if (nextElementSibling) {
  64. parentElement.insertBefore(newButton, nextElementSibling);
  65. } else {
  66. parentElement.append(newButton);
  67. }
  68. newButton.outerHTML = outerHTML;
  69. }
  70. buttons = document.querySelectorAll('[data-mode=play],[data-mode=resume]');
  71. for (let button of buttons) {
  72. button.removeAttribute('data-mode');
  73. button.addEventListener('click', e => {
  74. e.stopPropagation();
  75. e.preventDefault();
  76. let itemid = /id=(.*?)&serverId/.exec(window.location.hash)[1];
  77. openPotplayer(itemid);
  78. });
  79. }
  80. buttons = document.querySelectorAll('[data-action=resume]');
  81. for (let button of buttons) {
  82. button.removeAttribute('data-action');
  83. button.addEventListener('click', e => {
  84. e.stopPropagation();
  85. e.preventDefault();
  86. let item = e.target;
  87. let itemid;
  88. while (item != document && !item.hasAttribute('data-id')) {
  89. item = item.parentNode
  90. }
  91. if (item != document) {
  92. itemid = item.getAttribute('data-id');
  93. } else {
  94. let option = document.querySelector("#itemDetailPage:not(.hide) [class~='selectSourceContainer'] [is='emby-select'] option:checked")
  95. if (option) {
  96. itemid = option.value
  97. } else {
  98. item = e.target
  99. while (!item.querySelector("button[is='emby-playstatebutton']")) {
  100. item = item.parentNode
  101. }
  102. itemid = item.querySelector("button[is='emby-playstatebutton']").getAttribute('data-id');
  103. }
  104. }
  105. openPotplayer(itemid);
  106. });
  107. }
  108. };
  109.  
  110. let lazyload = async () => {
  111. let items = document.querySelectorAll('[data-src].lazy');
  112. let y = document.scrollingElement.scrollTop;
  113. let intersectinglist = [];
  114. for (let item of items) {
  115. let windowHeight = document.body.offsetHeight;
  116. let itemTop = item.getBoundingClientRect().top;
  117. let itemHeight = item.offsetHeight;
  118. if (itemTop + itemHeight >= 0 && itemTop <= windowHeight) {
  119. intersectinglist.push(item);
  120. }
  121. }
  122. for (let item of intersectinglist) {
  123. item.style.setProperty('background-image', `url("${item.getAttribute('data-src')}")`);
  124. item.classList.remove('lazy');
  125. item.classList.remove('lazy-hidden');
  126. item.removeAttribute('data-src');
  127. };
  128. };
  129.  
  130. let key = "启用PotPlayer"
  131. let enabled = GM_getValue(key)
  132. GM_registerMenuCommand(`${enabled?"已开启":"已关闭"}`, () => {
  133. GM_setValue(key, !enabled)
  134. location.reload()
  135. })
  136. if (enabled) {
  137. const observer = new MutationObserver((mutations, observer) => {
  138. bindEvent();
  139. lazyload()
  140. });
  141. observer.observe(document, {
  142. subtree: true,
  143. childList: true
  144. });
  145. }
  146. })();