Patreon Streamable Into Clipboard

Adds a button to turn patreon streamable posts into custom text on your clipboard

当前为 2024-06-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Patreon Streamable Into Clipboard
  3. // @license MIT
  4. // @namespace rtonne
  5. // @match https://www.patreon.com/*
  6. // @icon https://www.google.com/s2/favicons?sz=64&domain=patreon.com
  7. // @version 1.2
  8. // @author Rtonne
  9. // @description Adds a button to turn patreon streamable posts into custom text on your clipboard
  10. // @run-at document-end
  11. // @grant GM.setClipboard
  12. // ==/UserScript==
  13.  
  14. function getClipboardContent(post_title, post_url, streamable_url) {
  15. return `
  16. ### ${post_title} [](${post_url})
  17.  
  18. - [ ] :LiEye:
  19.  
  20. <iframe src="${streamable_url}" style="width: 100%; max-height: 70vh; aspect-ratio: 16 / 9;" allowfullscreen></iframe>
  21. `;
  22. }
  23.  
  24. const clipboard_svg = /*svg*/ `
  25. <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" >
  26. <!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.-->
  27. <path d="M280 64h40c35.3 0 64 28.7 64 64V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V128C0 92.7 28.7 64 64 64h40 9.6C121 27.5 153.3 0 192 0s71 27.5 78.4 64H280zM64 112c-8.8 0-16 7.2-16 16V448c0 8.8 7.2 16 16 16H320c8.8 0 16-7.2 16-16V128c0-8.8-7.2-16-16-16H304v24c0 13.3-10.7 24-24 24H192 104c-13.3 0-24-10.7-24-24V112H64zm128-8a24 24 0 1 0 0-48 24 24 0 1 0 0 48z"/>
  28. </svg>`;
  29.  
  30. const url_regex = /^https:\/\/www.patreon.com\/[^\/]+?\/posts.*$/;
  31. const observer = new MutationObserver(async () => {
  32. if (!url_regex.test(window.location.href)) {
  33. return;
  34. }
  35.  
  36. const elements = await waitForElements(
  37. document,
  38. "li > div[data-tag='post-card']"
  39. );
  40.  
  41. for (const element of elements) {
  42. if (element.querySelector(".rtonne-patreon-streamable-button")) {
  43. continue;
  44. }
  45. const post_video_figure = element.querySelector(
  46. "figure[title='video thumbnail']"
  47. );
  48. if (!post_video_figure) {
  49. continue;
  50. }
  51. const more_actions_button = element.querySelector(
  52. "button[data-tag='more-actions-button']"
  53. );
  54.  
  55. const clipboard_button = document.createElement("button");
  56. clipboard_button.className = more_actions_button.className;
  57. clipboard_button.classList.add("rtonne-patreon-streamable-button");
  58. more_actions_button.before(clipboard_button);
  59. const clipboard_button_svg_container = document.createElement("div");
  60. clipboard_button_svg_container.className =
  61. more_actions_button.children[0].className;
  62. clipboard_button_svg_container.innerHTML = clipboard_svg;
  63. clipboard_button.append(clipboard_button_svg_container);
  64.  
  65. clipboard_button.onclick = async () => {
  66. post_video_figure
  67. .querySelector("div[data-tag='media-container']")
  68. .click();
  69. const [streamable_iframe] = await waitForElements(element, "iframe");
  70. const iframe_search_url = streamable_iframe.src.substring(
  71. streamable_iframe.src.indexOf("?") + 1
  72. );
  73. const iframe_search_params = new URLSearchParams(iframe_search_url);
  74. const iframe_url = iframe_search_params.get("src");
  75.  
  76. const post_title = element.querySelector(
  77. "span[data-tag='post-title'] > a"
  78. );
  79. const post_title_text = post_title.innerText.trim();
  80. const post_url = post_title.href;
  81.  
  82. GM.setClipboard(
  83. getClipboardContent(post_title_text, post_url, iframe_url)
  84. );
  85. };
  86. }
  87. });
  88. observer.observe(document.body, {
  89. childList: true,
  90. subtree: true,
  91. });
  92.  
  93. /**
  94. * Uses a MutationObserver to wait until the element we want exists.
  95. * This function is required because elements take a while to appear sometimes.
  96. * https://stackoverflow.com/questions/5525071/how-to-wait-until-an-element-exists
  97. * @param {HTMLElement} node The element we want to search in.
  98. * @param {string} selector A string for node.querySelector describing the elements we want.
  99. * @returns {Promise<HTMLElement[]>} The list of elements found.
  100. */
  101. function waitForElements(node, selector) {
  102. return new Promise((resolve) => {
  103. if (node.querySelector(selector)) {
  104. return resolve(node.querySelectorAll(selector));
  105. }
  106.  
  107. const observer = new MutationObserver(() => {
  108. if (node.querySelector(selector)) {
  109. observer.disconnect();
  110. resolve(node.querySelectorAll(selector));
  111. }
  112. });
  113.  
  114. observer.observe(document.body, {
  115. childList: true,
  116. subtree: true,
  117. });
  118. });
  119. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址