SmartChute

BitChute.com Enhancer. Adds missing features. Makes you feel warm.

安裝腳本?
作者推薦腳本

您可能也會喜歡 SeenChute

安裝腳本
  1. // ==UserScript==
  2. // @name SmartChute
  3. // @version 20.8.28
  4. // @description BitChute.com Enhancer. Adds missing features. Makes you feel warm.
  5. // @license MIT
  6. // @author S-Marty
  7. // @compatible firefox
  8. // @compatible chrome
  9. // @compatible opera
  10. // @namespace https://github.com/s-marty/SmartChute
  11. // @homepageURL https://github.com/s-marty/SmartChute
  12. // @supportURL https://github.com/s-marty/SmartChute/wiki
  13. // @icon https://raw.githubusercontent.com/s-marty/SmartChute/master/images/smartChute2.png
  14. // @contributionURL https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QHFFSLZ7ENUQN&source=url
  15. // @include /^https?://www\.bitchute\.com.*$/
  16. // @grant GM.getValue
  17. // @grant GM.setValue
  18. // @grant GM.info
  19. // @grant GM.xmlHttpRequest
  20. // @run-at document-end
  21. // @noframes
  22. // ==/UserScript==
  23.  
  24. // @compatible Firefox 16+, Chrome 36+, Opera 15+, Brave 1.0.1+, Vivaldi 2.2+
  25. // @compatible TamperMonkey 4.10+, ViolentMonkey 2.12+, Greasemonkey 4.7+
  26.  
  27. /* greasyfork.org jshint syntax checking hacks */
  28. /* jshint asi: true */
  29. /* jshint boss: true */
  30. /* jshint esversion: 6 */
  31. /* jshint loopfunc: true */
  32. /* jshint multistr: true */
  33.  
  34. /** ********************** Features **********************
  35. *** Accelerated Bit Chute user experience ahead
  36. *** Floating mini video player is visible when scrolling
  37. *** Floating mini video player position is draggable
  38. *** Floating mini video player size is mouse resizeable
  39. *** Downloader for your favorite videos and posters.
  40. *** Up to 25 playlist choices in sidebar when viewing playlists.
  41. *** Blacklist annoying channels with one click if enabled
  42. *** De-blacklist channels with two clicks using Smarty menu
  43. *** Double-click the Smarty menu button for additional options.
  44. *** Unfix top header to show only while up-scrolling
  45. *** Hide the upper channel carousel using Smarty menu
  46. *** Hide unsafe ads to avoid viruses or other malware
  47. *** Hide or unhide comments section - make up your own mind or not
  48. *** Scrolls down to video player automatically if header is unfixed
  49. *** Seek by single frame using <> (,.) keys while video is paused.
  50. *** Mouse-over the progress bar for frame preview. (not for slow conn.)
  51. *** *Requires ~8mB additional bandwidth for a 640x360 video.
  52. *** Volume indicator visible when using ↑↓ keys.
  53. *** Full screen forward/back navigation, autoplay or not.
  54. *** Take and edit a downloadable screenshot of the current video frame.
  55. *** Autoplay videos or not is now selectable
  56. *** Video volume is persisted - No More 100% volume on the first play
  57. *** Theme night/day is persisted when clicking the sun/moon icon
  58. *** Three additional night themes for your tired eyes
  59. *** Play Next persists when clicking the "PLAYING NEXT" button
  60. *** Persistence lasts across browser instantiations too
  61. *** Top ten most viewed channel video playlist on video page option
  62. *** Channel owner-created playlists displayed on video page option
  63. *** 32 More video choices on Video watch page vs. 6
  64. *** Unlimited video choices using "SHOW MORE" button, vs. 6
  65. *** OpenSearch browser search to search from address or search bar
  66. *** Rss channel feed subscribe link (Browser extension now needed)
  67. *** All browsers have now dropped live bookmarks/rss feeds
  68. *** The rss url format is:
  69. *** https://www.bitchute.com/feeds/rss/channel/[CHANNEL_NAME]/
  70. *** Smarty menu always available
  71.  
  72. *** *** Does not & will not work well with IE and IEdge ***/
  73.  
  74. /* Editable options */
  75. const BC_Debug = false;
  76. /* End Editable options */
  77.  
  78.  
  79. (function() {
  80. "use strict";
  81.  
  82. const w = window;
  83. const d = document;
  84. const name = "SmartChute";
  85. const scriptHandler = GM.info.scriptHandler;
  86. const isChrome = navigator.userAgent.indexOf('Chrome') !=-1;
  87. const BC = {
  88.  
  89. miniPlayerX: 0,
  90. miniPlayerY: 0,
  91. miniPlayerW: 0,
  92. miniPlayerH: 0,
  93. startButton: 0,
  94. isFullScreen: false,
  95. listingsAllHeight: 0,
  96. listingsPopHeight: 0,
  97. miniSiz: {h: 0, hd: 0},
  98. miniPos: {x:0,y:0,xd:0,yd:0},
  99. origVid: {w:698,h:393,r:1.778},
  100. miniplayer: { x:0,y:0,w:350,h:197 },
  101.  
  102.  
  103. chuteMePlease: (e) => {
  104.  
  105. BC.url = w.location.href;
  106. BC.host = w.location.hostname;
  107. BC.path = w.location.pathname;
  108. BC.searchpage = BC.url.indexOf('/search') !=-1;
  109. BC.watchpage = BC.path.indexOf('/video') !=-1;
  110. BC.profilepage = BC.path.indexOf('/profile/') !=-1;
  111. BC.channelpage = BC.path.indexOf('/channel/') !=-1;
  112. BC.categorypage = BC.path.indexOf('/category/') !=-1;
  113. BC.playlistpage = BC.path.indexOf('/playlist/') !=-1;
  114. BC.homepage = BC.url.match(/^https?:\/\/www\.bitchute\.com\/?$/);
  115. if (BC.watchpage && BC.url.indexOf('list=') !=-1) BC.playlist = BC.url;
  116. else if (BC.watchpage && qs(".sidebar-next a") && qs(".sidebar-next a").href.indexOf('list=') !=-1) BC.playlist = qs(".sidebar-next a").href;
  117. else BC.playlist = null;
  118.  
  119. if (!BC.loaded) {
  120. if (!BC.loader) {
  121. if (BC.loader = qs("title")) {
  122. addListener(BC.loader, (e) => {
  123. if (isChrome && BC.settings.hidemenubar) {
  124. if (! document.activeElement.href) {w.scrollTo(0, 0)}
  125. }
  126. BC.chuteMePlease(e);
  127. },{ childList: true });
  128. }
  129. qs("#loader-container").addEventListener('dblclick', (e) => {
  130. e.stopPropagation();
  131. w.stop();
  132. e.target.style.display = "none";
  133. }, false);
  134. }
  135. if (! (BC.homepage || BC.watchpage || BC.channelpage || BC.categorypage || BC.profilepage || BC.searchpage || BC.playlistpage)) return;
  136. if (!BC.themes) {
  137. setTimeout(BC.addThemeListeners, 2000);
  138. if (isChrome && BC.settings.hidemenubar) {
  139. w.addEventListener('beforeunload', function(e){
  140. if (!document.activeElement.href){w.scrollTo(0, 0)}
  141. }, false);
  142. }
  143. BC.setTheme();
  144. BC.setPreferencesCookie("autoplay", BC.settings.playnext);
  145. BC.themes = true;
  146. }
  147. if (BC.searchpage || BC.profilepage || BC.playlistpage) return;
  148. let style = dce("style");
  149. style.type = "text/css";
  150. style.innerText = `
  151. .nav-tabs-list {min-width: 500px !important; width: 100%;} .sidebar-recent .video-card.active {box-shadow: 0 0 1em 0.2em #f37835; border-radius:5px;} .playlist-card.active {border-top: 1px solid #f37835bb; box-shadow: 0 2px 1em 0.2em #f37835; border-radius:5px;}
  152. svg.smarty-donate {float:right;cursor: pointer; color:#209227;display: block;} svg.smarty-donate:hover {-webkit-transform:rotate(14deg) scale(1.2);-khtml-transform:rotate(14deg) scale(1.2);transform:rotate(14deg) scale(1.2);color:#30a247;}
  153. #loader-container {opacity: 0.5;} span.add-to-blacklist { position: absolute; top: 4px; left: 4px; z-index: 50; width:30px; height:30px; } a.side-toggle {cursor: pointer; }
  154. svg.smarty-donate {-webkit-transition: transform 0.25s ease-in, color 0.25s; -moz-transition: transform 0.25s ease-in, color 0.25s; -khtml-transition: transform 0.25s ease-in, color 0.25s; transition: transform 0.25s ease-in, color 0.25s;}
  155. span.blacklist-tooltip { position: absolute; font-size: 14px;padding: 0 4px; height: 22px; left: 2px; top: 38px; line-height: 1.6; background-color: #000 ;display:none;} #smarty_tab label:hover, #smarty_tab #blacklistedchannels span:hover {color:#ef4136;}
  156. #smarty_tab #smartymm > div, #smarty_tab #blacklistedchannels, #smarty_tab #smartyam {padding: 8px; border: 1px solid #333; border-radius:3px;} #smarty_tab #blacklistedchannels span{width: 137px; max-height: 16px;cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block;}
  157. span.add-to-blacklist svg {cursor: pointer;} html.noblacklist span.add-to-blacklist {display:none;} #channel-list div.item div.channel-card:hover .add-to-blacklist {opacity: 1;} .video-views, .video-duration {color: #272727; opacity: 0.8;}
  158. span.add-to-blacklist:hover span.blacklist-tooltip { color:#fff; display:inline; } #carousel {${BC.settings.hidecarousel ? "display:none" : "width: 100%; min-height: 210px"};} .plyr__tooltip {color: #000;}
  159. #carousel .hidden-md > div .channel-card:hover .action-button {opacity:1;} .channel-banner .name a.userisblacklisted {text-decoration: line-through red;} .night .video-views, .night .video-duration {color: #dbdbdb;} html.night .jquery-comments .textarea-wrapper .textarea {background-color: #000 !important;}
  160. .night .jquery-comments ul.main li.comment .comment-wrapper {border-top-color: #dddddd33 !important;}, .channel-banner .name .add-to-blacklist {position: relative;left: 10px;} .channel-banner .name:hover .add-to-blacklist {opacity: 1;}
  161. .smartybox { position: relative; display: block; width: 100%; } .smartybox:nth-child(2) { width: 132px; } .smartybox input[type="checkbox"], .smartybox input[type="radio"] { width: auto; opacity: 0.00000001; position: absolute; left: 0; margin-left: -20px; }
  162. .cbhelper, .radiohelper { top: -4px; left: -8px; display: block; cursor: pointer; position: absolute; user-select: none; -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none;} .cbhelper:before, .radiohelper:before { content: ''; position: absolute; left: 0; top: 0;
  163. margin: 6px; width: 18px; height: 18px; transition: transform 0.28s ease; border-radius: 5px; border: 1px solid #4d4b4e; }
  164. .radiohelper:before { border-radius: 9px; } .radiohelper.r1:before { left: 48px; } .radiohelper.r2:before { left: 88px; } .radiohelper.r3:before { left: 128px; } .cbhelper:after, .radiohelper:after { content: ''; display: block; width: 10px; height: 5px;
  165. border-bottom: 2px solid #7bbe72; border-left: 2px solid #7bbe72; -webkit-transform: rotate(-45deg) scale(0); -moz-transform: rotate(-45deg) scale(0); -khtml-transform: rotate(-45deg) scale(0); transform: rotate(-45deg) scale(0); position: absolute; top: 12px; left: 10px; }
  166. .radiohelper.r1:after { left: 58px; } .radiohelper.r2:after { left: 98px; } .radiohelper.r3:after { left: 138px; } .smartybox input[type="checkbox"]:checked ~ .cbhelper::before, .smartybox input[type="radio"]:checked ~ .radiohelper::before { color: #7bbe72;
  167. background: linear-gradient(to bottom, #d5d1d833 0%, #93919566 100%)}
  168. .smartybox input[type="checkbox"]:checked ~ .cbhelper::after, .smartybox input[type="radio"]:checked ~ .radiohelper::after { -webkit-transform: rotate(-45deg) scale(1); -moz-transform: rotate(-45deg) scale(1); -khtml-transform: rotate(-45deg) scale(1); transform:rotate(-45deg) scale(1); }
  169. .smartybox label { cursor: pointer; padding-left: 25px; white-space: nowrap;} .smartybox.radios label { padding-left: 0px; } .smartybox.radios label.r1 { margin-left: 14px; color:#f0af5a !important; } .smartybox.radios label.r2 { margin-left: 18px; color: #559bcc !important; }
  170. .smartybox.radios label.r3 { margin-left: 18px; color:#55a47c !important; } .smartybox input[type="checkbox"]:focus + label::before, .smartybox input[type="radio"]:focus + label::before { outline: rgb(59, 153, 252) auto 5px; }
  171. .sidebar-recent .playlist_sidebar {margin-bottom: 14px;-webkit-transition: all 1s cubic-bezier(1, 1, 0.5, 1); -moz-transition: all 1s cubic-bezier(1, 1, 0.5, 1); transition: all 1s cubic-bezier(1, 1, 0.5, 1);}
  172. .sidebar-recent .playlist_sidebar.slidein {overflow: hidden; max-height: 0px !important; } .sidebar-heading.playlists {position: relative; cursor: pointer; user-select: none; -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none;}
  173. .sidebar-heading.playlists:after {content: ''; display: block; position: absolute; right: 6px; bottom: 15px; width: 0; height: 0; border-bottom-width: 10px; border-bottom-style: solid; border-top: 10px solid transparent; border-left: 10px solid transparent; border-right: 10px solid transparent; }
  174. .sidebar-heading.playlists:after {border-bottom-color: white;} .sidebar-heading.playlists:hover:after {border-bottom-color: lightgray;} .sidebar-heading.playlists:after {-webkit-transition: all 0.5s ease; -moz-transition: all 0.5s ease; transition: all 0.5s ease;}
  175. .sidebar-heading.playlists.slidein:after {content: ''; display: block; position: absolute; right: 6px; bottom: 5px; width: 0; height: 0; border-top-width: 10px; border-top-style: solid; border-bottom: 10px solid transparent; border-left: 10px solid transparent; border-right: 10px solid transparent; }
  176. .sidebar-heading.playlists.slidein:after {border-top-color: white;} .sidebar-heading.playlists.slidein:hover:after {border-top-color: lightgray;} .plyr.plyr--hide-controls {cursor:none}
  177. #volumometer {position: absolute;right: 40px;width: 20px;height: calc(100% - 120px);bottom: 40px;border: 2px solid #aaa;border-right-color: #888;display: flex;background: linear-gradient(to right, #000 0%, #111 60%, #111 60%, #333 100%);opacity: 0;transition: opacity 2s;}
  178. #volumometer.change {opacity: 0.8;transition: 0.1s;} #volumometer .vol {bottom: 0px;width: 100%;height: 0px;position: absolute;transition: height 0.1s ease-in 0s;box-shadow: 2px 0px 2px #555;background: linear-gradient(to right, #cdcdcd 0%, #fff 50%, #fff 50%, #888 100%);}
  179. #volumometer .percent {position: absolute;right: -30px;width: 80px;height: 80px;top: -60px;text-align: center;color: #333333dd;font-weight: bold;font-size: 2em;text-shadow: 1px 1px #dddddd;pointer-events: none;}
  180. #volumometer .percent::after {content: "";display: block;position: absolute;width: 100%;height: 100%;bottom: 15px;left: 0;pointer-events: none;background-image: radial-gradient(circle, #555 10%, #cacaca 20%, transparent 10.01%);background-position: 50%;transform: scale(0, 0);opacity: 0;transition: 0s;}
  181. #volumometer.change .percent:after {transform: scale(6, 4);opacity: .2;transition: transform .5s, opacity .35s;}
  182. .plyr #autoplay-details {z-index:22; color:#ee3333 !important;} .plyr.plyr--paused button.plyr__control--overlaid.plyr__control--pressed[aria-label="Play"] {opacity:0;visibility:hidden;}
  183. #autoplay-next {position:absolute;width: 800px;top: 50%;left: 50%;margin: -100px 0 0 -400px;z-index: 1;} #autoplay-next .video-card-image {width: 240px; height: 135px;opacity: .9;} #autoplay-next .video-card-image .img-responsive:first-child {opacity: .9; border-radius: 10px;}
  184. #autoplay-next .video-card-text {max-height:88px;} #autoplay-next .video-card-title {font-size: 12px;height: 45px;max-height: 45px;overflow: hidden;} #autoplay-next .video-card-published {font-size: 12px;margin: 2px 0;}
  185. #autoplay-next .video-card {position:absolute;width: 240px;border-radius: 10px;opacity:.9;background:#211f22;box-shadow: 1em 1em 1em 0.7em #000000a0, -0.05em -0.05em 0.1em 0.1em white;}
  186. .autoplay-prev {left:0px;} .autoplay-prev img.play-overlay {width: 64px !important;height: 64px !important;transform: scaleX(-0.7) !important;} .autoplay-prev:hover img.play-overlay {transform: scaleX(-1) !important;}
  187. .autoplay-next {right:0px;} .autoplay-next img.play-overlay {width: 64px !important;height: 64px !important;transform: scaleX(0.7) !important;} .autoplay-next:hover img.play-overlay {transform: scaleX(1) !important;}
  188. #bcframes {opacity: 0; height: 90px; position: absolute; border-radius: 3px; border: 1px solid #555; background-color: #222222aa; overflow: hidden; transform: translate(50%,10px) scale(0); transform-origin: -50% 100%; transition: transform .2s .1s ease,opacity .2s .1s ease; z-index: 130;}
  189. #bcframes.visible {opacity: 1; transform: translate(0,0) scale(1); transform-origin: 50% 100%; transition: transform .2s .1s ease,opacity .2s .1s ease;}
  190. #screenshot {transition: opacity .5s; background: #222222b3; text-align: center; position: fixed; overflow: hidden; z-index: 2000; opacity: 1; bottom: 0; right: 0; left: 0; top: 0;}
  191. #screenshot canvas {top: 50%; z-index: 2001; max-width: 80%; max-height: 80%; position: relative; transform: translateY(-50%); box-shadow: 1em 1em 1em 0.7em #000000a0, -0.01em -0.01em 0.03em 0.03em #aaa;} #screenshot .action {left: calc(50% - 114px); position: absolute;
  192. width: 228px; height: 74px; top: 70%; border-radius: 7px; background: linear-gradient(to top, #ffffff88 10%, #22222288 60%); border-color: #222 #555 #555 #222; border-width: 1px; border-style: solid; border-bottom-right-radius: 39px; border-bottom-left-radius: 39px;}
  193. #screenshot .cancel, #screenshot .save, #screenshot .edit {width: 100px; height: 28px; display: inline-block; background: linear-gradient(to bottom, #333 0%, #000 100%); border: 1px solid #555; border-radius: 5px; text-align: center; line-height: 1.8em; margin: 6px; cursor: pointer;
  194. box-shadow: 0.5em 0.5em 1em 0.1em #00000070, -0.02em -0.02em 0.03em 0.03em #aaaaaaaa;} #screenshot .save a {width: 100%; height: 100%; display: block;} a#takeScreenShot.disabled {pointer-events: none; opacity: 0.3; cursor: default;} #screenshot .cancel:hover, #screenshot .save a:hover {color:#d01105;}
  195. #screenshot .edit {width: 28px; font-weight: bold; font-size: 18px; line-height: 1.5em; margin: 2px 4px;} #screenshot .action .edit.two {opacity:0; position: absolute; top: 4px;}
  196. #screenshot #editInfo {z-index: 2005; cursor: default; font-size: 22px; padding-left: 1px; line-height: 1.1em; position: absolute; border-radius: 14px; margin: 24px 0 0 28px; user-select: none; -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none; box-shadow: 1em 1em 1em 0.1em #00000090;}
  197. #screenshot #editInfo:hover:after {top: 100%; z-index: 2011; width: 200px; font-size: 14px; margin-top: 10px;
  198. padding: 4px 6px; color: #ffffffdd; position: absolute; border-radius: 4px; content: attr(data); background: linear-gradient(to top, #22222288 10%, #00000088 60%); transform: translateX(-90%); border: 2px solid #555555aa; box-shadow: 0.5em 0.5em 1em 0.2em #000000a0;}
  199. #screenshot .action {opacity: 1; z-index: 2010; cursor: move; -webkit-transition: height .5s, opacity .5s; -moz-transition: height .5s, opacity .5s; transition: height .5s, opacity .5s;}
  200. #screenshot .action .cancel, #screenshot .action .save, #screenshot .action .edit {visibility: visible; -webkit-transition: visibility 0s, opacity 0.5s linear; -moz-transition: visibility 0s, opacity 0.5s linear; transition: visibility 0s, opacity 0.5s linear;
  201. user-select: none; -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none;} #screenshot .action.gone {height:40px !important;}
  202. #screenshot .action .cancel.gone, #screenshot .action .save.gone {opacity: 0; visibility: hidden; transition: visibility 0s 0.5s, opacity 0.5s linear}
  203. #screenshot .action .edit.one.gone {opacity:0 !important; visibility: hidden; width:0px !important; transition: visibility 0s 0.5s, opacity 0.5s linear}
  204. #screenshot .action .edit.two {opacity:1;}
  205. #screenshot .action .edit.disabled, #screenshot .action input#zoomfactor[disabled] {pointer-events: none; opacity: 0.5; cursor: default;}
  206. #screenshot .action .edit.two.gone {opacity:0 !important; visibility: hidden; transition: visibility 0s 0.5s, opacity 0.5s linear}
  207. #screenshot .action input#zoomfactor {width:75px; margin-left: -8px;} #screenshot .action input#zoomfactor.gone {display: none}
  208. .drag_resize {z-index: 2003; display: none; position: absolute;} .drag_resize.active {display: block;}
  209. .drag_resize_overlay, .drag_resize_overlay tbody {width: 100%; height: 100%; z-index: 2004; user-select: none; -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none;}
  210. .drag_resize_overlay td {border-width: 0px;} .drag_resize_overlay td.corner {width: 21px; height: 21px;${(isChrome) ? ' display: -webkit-box;' : ''}}
  211. .drag_resize_overlay td.corner.tl {cursor: nwse-resize; border-left: 1px solid #eeeeeeaa; border-top: 1px solid #eeeeeeaa;} .drag_resize_overlay td.corner.tr {cursor: nesw-resize; border-top: 1px solid #eeeeeeaa; border-right: 1px solid #eeeeeeaa;}
  212. .drag_resize_overlay td.corner.br {cursor: nwse-resize; border-right: 1px solid #eeeeeeaa; border-bottom: 1px solid #eeeeeeaa;} .drag_resize_overlay td.corner.bl {cursor: nesw-resize; border-bottom: 1px solid #eeeeeeaa; border-left: 1px solid #eeeeeeaa;}
  213. .drag_resize_overlay td.cropper.ct {height: 21px; cursor: ns-resize; border-left: 1px solid #eeeeeeaa; border-top: 1px solid #eeeeeeaa; border-right: 1px solid #eeeeeeaa; color: #fff; font-size: 14px; overflow: hidden; text-align: center;}
  214. .drag_resize_overlay td.cropper.cr {width: 21px; cursor: ew-resize; border-top: 1px solid #eeeeeeaa; border-right: 1px solid #eeeeeeaa; border-bottom: 1px solid #eeeeeeaa;}
  215. .drag_resize_overlay td.cropper.cb {height: 21px; cursor: ns-resize; border-right: 1px solid #eeeeeeaa; border-bottom: 1px solid #eeeeeeaa; border-left: 1px solid #eeeeeeaa; color: #fff; font-size: 14px; overflow: hidden; text-align: left; padding-left: 15px;}
  216. .drag_resize_overlay td.cropper.cl {width: 21px; cursor: ew-resize; border-bottom: 1px solid #eeeeeeaa; border-left: 1px solid #eeeeeeaa; border-top: 1px solid #eeeeeeaa;}
  217. .drag_resize_overlay td.corner:hover, .drag_resize_overlay td.cropper:hover {background-color:#ffffff1a;} .drag_resize_overlay td.dragger {cursor: move; overflow: visible; display: inline-flex; scrollbar-width: none;
  218. background-image: linear-gradient(90deg, #eeeeeeaa 50%, #11111199 50%), linear-gradient(90deg, #eeeeeeaa 50%, #11111199 50%), linear-gradient(0deg, #eeeeeeaa 50%, #11111199 50%), linear-gradient(0deg, #eeeeeeaa 50%, #11111199 50%);
  219. background-repeat: repeat-x, repeat-x, repeat-y, repeat-y; background-size: 15px 1px, 15px 1px, 1px 15px, 1px 15px; background-position: left top, right bottom, left bottom, right top; animation: borderline 1s infinite linear;}
  220. @keyframes borderline {0% {background-position: left top, right bottom, left bottom, right top;} 100% {background-position: left 15px top, right 15px bottom, left bottom 15px, right top 15px;}} .drag_resize_overlay td.dragger::-webkit-scrollbar {display: none;}
  221. input#zoomfactor[type=range] {-webkit-appearance: none; margin-left: 100px; width: 100px;} input#zoomfactor[type=range][disabled] {opacity: .2; cursor: default;} input#zoomfactor[type=range]:focus {outline: none;}
  222. input#zoomfactor[type=range]::-webkit-slider-runnable-track {width: 100%; height: 6px; cursor: pointer; animate: 0.2s; box-shadow: 1px 1px 1px #000000; background: #aaaaaa77; border-radius: 5px; border: 1px solid #000000;}
  223. input#zoomfactor[type=range]::-webkit-slider-thumb {box-shadow: 1px 1px 1px #000000; border: 1px solid #000000; height: 16px; width: 10px; border-radius: 5px; background: currentColor; cursor: pointer; -webkit-appearance: none; margin-top: -6px;}
  224. input#zoomfactor[type=range]:focus::-webkit-slider-runnable-track {background: #3071A9;} input#zoomfactor[type=range]::-moz-range-track {width: 100%; height: 6px; cursor: pointer; animate: 0.2s; box-shadow: 1px 1px 1px #000000; background: #aaaaaa77; border-radius: 5px; border: 1px solid #000000;}
  225. input#zoomfactor[type=range]::-moz-range-thumb {box-shadow: 1px 1px 1px #000000; border: 1px solid #000000; height: 16px; width: 10px; border-radius: 5px; background: currentColor; cursor: pointer;}
  226. #screenshot #shader {opacity: .6; z-index: 2002; position: absolute; transition: opacity .5s; background-position: left top, right bottom, left bottom, right top;
  227. background-repeat: repeat-x, repeat-x, repeat-y, repeat-y; background-image: linear-gradient(#000, #000), linear-gradient(#000, #000), linear-gradient(#000, #000), linear-gradient(#000, #000);} #screenshot #shader.transparent, #screenshot .action.transparent {opacity: 0 !important;}`;
  228. if (BC.settings.hidemenubar) {
  229. style.innerText += `
  230. #nav-top-menu {position: static; width: 100%; height: 60px;} #nav-menu-buffer {height: 0px; padding-top: 0px !important;}
  231. html.topNavfloat #nav-top-menu, html.tabNavfloat .tab-scroll-outer {-webkit-transition: top 0.5s ease-in-out; -moz-transition: top 0.5s ease-in-out; -khtml-transition: top 0.5s ease-in-out; transition: top 0.5s ease-in-out;}
  232. html.topNavfloat #nav-top-menu {position: fixed;} html.tabNavfloat .tab-scroll-outer {position: fixed; width: 100%; z-index:989; background: #fff;}
  233. html.tabNavfloat.night .tab-scroll-outer {background: #211f22;}
  234. html.topNavfloat #nav-menu {padding-top: 60px;} html.tabNavfloat #page-detail .tab-content {margin-top: 50px;} html.tabNavfloat #page-detail #listing-trending {margin-top: -50px;} html.tabNavfloat #nav-side-menu {z-index:999;}`;
  235. }
  236. if (BC.settings.playlists || BC.settings.mvplaylist) {
  237. style.innerText += `
  238. .mvplaylist.row, .playlist.row {width: 723px;margin-top:20px;margin-bottom:20px;} .plslider, .mvslider {width:100%;max-width: 878px; padding-left:35px;margin: auto 0px; overflow:hidden;display: inline;}
  239. .mvplaylist .playlist-title, .playlist .playlist-title {display:inline-block; width: auto !important; margin: 20px 37px 10px;} #comment-frm-container {margin-top: 20px !important;}
  240. .mvplaylist .playlistbt, .playlist .playlistbt {width: 30px; height: 195px; padding-top: 85px; background-color: #f3f3f3; position: absolute; z-index: 78; } .night .mvplaylist .playlistbt, .night .playlist .playlistbt {background-color: #211f22;}
  241. .plslider, .mvslider {-webkit-transition: margin-left 0.25s ease-in-out; -moz-transition: margin-left 0.25s ease-in-out; -khtml-transition: margin-left 0.25s ease-in-out; transition: margin-left 0.25s ease-in-out;}
  242. .playlistup {margin-left:693px; background: linear-gradient(to right, #BBB 0%,#DDD 100%); border-bottom-right-radius: 20px 40px; border-top-right-radius: 20px 40px;}
  243. .playlistdn {background: linear-gradient(to left, #BBB 0%,#DDD 100%); border-bottom-left-radius: 20px 40px; border-top-left-radius: 20px 40px;}
  244. .night .playlistup {background: linear-gradient(to right, #2c2a2d 0%,#544e53 100%);} .night .playlistdn {background: linear-gradient(to left, #2c2a2d 0%,#544e53 100%);}
  245. .playlistbtn:not(.disabled):hover {background: radial-gradient(ellipse at center, #BBB 0%,#DDD 100%);} .night .playlistbtn:not(.disabled):hover {background: radial-gradient(ellipse at center, #2c2a2d 0%,#544e53 100%);}
  246. .playlistbtn {cursor:pointer;width: 30px;height:195px;padding-top:85px;background-color: #ddd;text-align:center;position: absolute; z-index: 80;margin-top: 5px;}
  247. .playlistbtn b {cursor:pointer; user-select: none; -moz-user-select: none; -khtml-user-select: none; -webkit-user-select: none;} .playlistbtn.disabled {cursor:default; opacity:0.3;} .playlistbtn.disabled b {color: #ddd;cursor:default;}
  248. .playlist-title span {margin-left:16px;} .playlist-title span:hover {color:#ffaa00;} .video-card-published.sequence {position: absolute;bottom: 0px;right: 3px; z-index:50;} .playlist svg.fa-square {opacity: 0.4;}
  249. .mvplaylist .playlist-card, .playlist .playlist-card {width: 208px;height:195px;margin: 5px;} .night .playlistbtn {background-color: #2c2a2d; background:}
  250. .playlist-card .video-card-title {height: 52px; width: 200px; max-height: 52px; max-width: 200px; cursor: pointer; overflow: hidden; display: block;} .playlist-card .video-card-title a {font-size: 13px; font-weight: 500;}
  251. .playlist-card.active .video-card-title {max-height: 47px;}
  252. .night .playlistbtn.disabled b {color: #2c2a2d;} @media (min-width: 768px) {.plslider, .mvslider {max-width: 660px;} .playlistup {margin-left:475px;}.mvplaylist.row, .playlist.row {width: 505px;}}
  253. @media (min-width: 992px) {.plslider, .mvslider {max-width: 878px;} .playlistup {margin-left:693px;}.mvplaylist.row, .playlist.row {width: 723px;}}`;
  254. }
  255. if (BC.settings.hidedonationbar) style.innerText += '.video-container .text-center {display: none !important;}';
  256. if (BC.settings.hidecookienotice) style.innerText += '#alert-cookie {display: none !important;}';
  257. if (BC.settings.hidesignupnotice) style.innerText += '#alert-signup {display: none !important;}';
  258. if (BC.settings.usesquareicons) style.innerText += '.channel-banner .image-container {border-radius:0px !important;}';
  259. if (BC.settings.hideadverts) {
  260. style.innerText += '.sidebar .rcad-container, .sidebar > div:not(.sidebar-video) {display:none !important;}';
  261. let affiliates = null;
  262. if (affiliates = qs('.affiliate-container')) {
  263. affiliates.outerHTML = ''
  264. }
  265. }
  266. if (BC.settings.useminiplayer) style.innerText += 'button.plyr__control[data-plyr="pip"] {display: none !important;}';
  267. d.documentElement.appendChild(style);
  268. if (BC.settings.hidemenubar) w.addEventListener('scroll', BC.floatHeaders);
  269. BC.addBrowserSearch();
  270. BC.loaded = 1;
  271. debug('>>>>>>>>>>>>>> BC load <<<<<<<<<<=');
  272. } else debug('>>>>>>>>>>>>>> BC reload <<<<<<<<<<=');
  273.  
  274. if (BC.watchpage) {
  275. BC.page = 'watchpage';
  276. BC.player.api = qs('video#player');
  277. if (isChrome) {
  278. BC.player.api.crossOrigin = "Anonymous";/* for webkit CORS*/
  279. let src = qs("source", BC.player.api).src;
  280. qs("source", BC.player.api).src = src;
  281. BC.player.api.load();
  282. if (BC.player.autoplay) BC.player.api.play();
  283. }
  284. /* Provide mini player */
  285. if (BC.player.api !== null) {
  286. if (!BC.player.rect) {
  287. w.scrollTo(0, 0);
  288. BC.player.rect = BC.player.api.getBoundingClientRect();
  289. }
  290. BC.player.api.mini_point = BC.player.rect.top + ((BC.player.rect.height / 2) + 50);
  291. BC.origVid = {
  292. w: Math.round(BC.player.rect.width * 10) / 10,
  293. h: Math.round(BC.player.rect.height * 10) / 10,
  294. r: Math.round(BC.player.rect.width/BC.player.rect.height * 1000) / 1000
  295. };
  296. if (!BC.miniPlayerIni && BC.settings.useminiplayer) {
  297. GM.getValue('miniplayer', "{}").then( (value) => {
  298. if (value && value != '{}') {
  299. BC.miniplayer = JSON.parse(value);
  300. w.addEventListener("scroll", BC.miniPlayer, false);
  301. d.addEventListener("fullscreenchange", (e) => { BC.onFullScreen(e)});
  302. d.addEventListener("mozfullscreenchange", (e) => { BC.onFullScreen(e)});
  303. d.addEventListener("webkitfullscreenchange", (e) => { BC.onFullScreene(e)});
  304. let style = dce("style");
  305. style.type = "text/css";
  306. style.innerText = `
  307. html:not(.isfullscreen).s-marty-miniplayer video#player, html:not(.isfullscreen).s-marty-miniplayer .plyr__video-wrapper, html:not(.isfullscreen).s-marty-miniplayer .plyr--video {opacity: 0.94;}
  308. html:not(.isfullscreen).s-marty-miniplayer .video-container > .row > div > .wrapper {position: fixed;z-index: 100;background-color:transparent; border:1px solid rgba(255,255,255,0.3);}
  309. html:not(.isfullscreen).s-marty-miniplayer #s-marty-miniplayer-bar {display : block;cursor: move; height: 40px; left: -3px; right: 5px; top: -6px; position: absolute;z-index: 110;background-color:transparent;}
  310. html:not(.isfullscreen).s-marty-miniplayer #s-marty-miniplayer-bar:hover {background-color:#000; opacity: 0.4; background-clip: padding-box; padding: 6px 0 0 6px;}
  311. html:not(.isfullscreen).s-marty-miniplayer #s-marty-miniplayer-size {display : block;cursor: nesw-resize; width:7px; height: 7px; right: -3px; top: -3px; position: absolute;z-index: 120;background-color:transparent;}
  312. html:not(.isfullscreen).s-marty-miniplayer .plyr__controls button[data-plyr="captions"], html:not(.isfullscreen).s-marty-miniplayer .plyr__controls button[data-plyr="pip"], html:not(.isfullscreen).s-marty-miniplayer .plyr__controls .plyr__menu {display : none !important;}
  313. html:not(.s-marty-miniplayer) #s-marty-miniplayer-bar, html:not(.s-marty-miniplayer) #s-marty-miniplayer-size {display : none;} html:not(.isfullscreen).s-marty-miniplayer .plyr__controls imput[data-plyr="volume"] {/*max-width:12% !important; */width: 12% !important;}
  314. html:not(.isfullscreen).s-marty-miniplayer .plyr__video-embed iframe, html:not(.isfullscreen).s-marty-miniplayer .plyr__video-wrapper--fixed-ratio video {position: relative !important; } html:not(.isfullscreen).s-marty-miniplayer #volumometer {height: calc(100% - 90px);bottom: 20px;}
  315. html.isfullscreen video#player {width: 100% !important; height: !important;}`;
  316. d.documentElement.appendChild(style);
  317. BC.miniPlayerIni = true;
  318. }
  319. }).catch (error => {
  320. error('miniplayer: Error in GM.getValue promise: '+ error)
  321. });
  322. }
  323.  
  324. if (!qs("#s-marty-miniplayer-bar")) {
  325. BC.player.fur = qs(".video-container .wrapper");
  326. let bar = dce("div");
  327. bar.setAttribute('id', 's-marty-miniplayer-bar');
  328. bar.addEventListener("mousedown", BC.moveMiniPlayer, true);
  329. let size = dce("div");
  330. size.setAttribute('id', 's-marty-miniplayer-size');
  331. size.addEventListener("mousedown", BC.sizeMiniPlayer.bind(this), true);
  332. BC.player.fur.insertBefore(bar, BC.player.fur.firstChild);
  333. BC.player.fur.insertBefore(size, BC.player.fur.firstChild);
  334. BC.player.api.volume = BC.player.volume;
  335. BC.player.fur.parentNode.style = `width:${BC.origVid.w}px;height:${BC.origVid.h}px;`;
  336. }
  337.  
  338. /* Autoplay videos */
  339. if (BC.player.autoplay) {
  340. if (! BC.startButton) {
  341. BC.startButton = qs('button[aria-label="Play"]');
  342. BC.player.api.autoplay = true;
  343. if (qs('.plyr__controls imput[data-plyr="volume"]')) {
  344. qs('.plyr__controls imput[data-plyr="volume"]').removeAttribute('hidden'); // Volume
  345. qs('.plyr__controls .plyr__volume button').removeAttribute('hidden'); // Mute
  346. }
  347. }
  348. if (BC.player.api.paused) {
  349. BC.player.api.play()
  350. .catch( function(e) { BC.startButton.dispatchEvent( new MouseEvent('click', {bubbles: true, cancelable: true})) })
  351. }
  352. }
  353. else {
  354. BC.player.api.autoplay = false;
  355. if (!BC.player.api.paused) BC.player.api.pause();
  356. }
  357.  
  358. if (!qs("video#player.bc")) {
  359. /* Video errors */
  360. qs('progress.plyr__progress__buffer').style = '';
  361. qs('div.plyr__progress').title = '';
  362. if (isChrome) {
  363. if (isNaN(BC.player.api.duration)) {
  364. w.setTimeout(() => {
  365. if (BC.player.api.readyState === 0 && isNaN(BC.player.api.duration)) {
  366. let err = null, f = '', n = parseInt(BC.player.api.children[0].attributes.dl.value);
  367. try { err = Bcd.src.videos[n].error; f = Bcd.src.videos[n].url}
  368. catch (e) { err = 'Undefined video error' }
  369. if (err) {
  370. qs('progress.plyr__progress__buffer').style.backgroundColor = 'rgba(255,255,60,.80)';
  371. qs('div.plyr__progress').title = err;
  372. error(`Media error ${err} ${f}`);
  373. }
  374. }
  375. }, 3000)
  376. }
  377. }
  378. else {
  379. BC.player.api.addEventListener('error', function(e) {
  380. let err = '';
  381. if (BC.player.api.error) {
  382. if (BC.player.api.error.code == BC.player.api.error.MEDIA_ERR_NETWORK)
  383. err = 'File error';
  384. else if (BC.player.api.error.code == BC.player.api.error.MEDIA_ERR_DECODE)
  385. err = 'Video file is corrupt';
  386. else if (BC.player.api.error.code == BC.player.api.error.MEDIA_ERR_SRC_NOT_SUPPORTED)
  387. err = 'Video file is incompatable';
  388. }
  389. w.setTimeout((e, err) => {
  390. let f = '';
  391. if (!err) {
  392. try {
  393. let n = parseInt(e.target.attributes.dl.value);
  394. err = Bcd.src.videos[n].error; f = Bcd.src.videos[n].url}
  395. catch (e) { err = 'Undefined video error' }
  396. }
  397. qs('progress.plyr__progress__buffer').style.backgroundColor = 'rgba(255,255,60,.80)';
  398. qs('div.plyr__progress').title = (err || "Undefined video error");
  399. error(`Media error ${err || "undefined"} ${f}`);
  400. }, 3000, e, err)
  401. }, true);
  402. }
  403. if (!qs("#volumometer")) {
  404. let volInd = dce("div");
  405. volInd.id = "volumometer";
  406. volInd.innerHTML = '<div class="vol"></div><div class="percent"></div>';
  407. qs(".wrapper .plyr").appendChild(volInd);
  408. BC.player.api.addEventListener('volumechange', function(e) {
  409. let volume = Math.round(e.target.volume / 0.01) * 0.01;
  410. BC.savePlayerValue('volume', volume);
  411. if (qs(".plyr").classList.contains("plyr--hide-controls")) {
  412. qs("#volumometer .percent").innerText = `${(volume * 100).toFixed()}%`;
  413. qs("#volumometer .vol").style.height = `${volume * 100}%`;
  414. qs("#volumometer").classList.add("change");
  415. setTimeout(()=>{qs("#volumometer").classList.remove("change")},500);
  416. }
  417. }, false);
  418. w.addEventListener('keydown', BC.catchKey, false);
  419. }
  420. BC.player.api.addEventListener('timeupdate', function(e){ BC.onPlayerProgress(e) }, false);
  421. if (BC.settings.useseekbarpreview) {
  422. if (BC.player.api.readyState > 0) BC.getVideoThumbnails({target: BC.player.api});
  423. BC.player.api.addEventListener('loadedmetadata', BC.getVideoThumbnails, false);
  424. }
  425. BC.player.api.classList.add("bc");
  426. }
  427. }
  428. let sidebarnext = qs(".sidebar-next");
  429. let playnext = qs("label.sidebar-autoplay:not(.active)");
  430. if (sidebarnext && playnext) {
  431. playnext.addEventListener('mousedown', function(e) {
  432. if (e.which===1) {
  433. let checked = qs("input#autoplay-toggle").checked;
  434. BC.savePlayerValue('playnext', !checked);
  435. }
  436. }, false);
  437. playnext.classList.add('active')
  438. }
  439. if (BC.settings.hidecomments) setTimeout(BC.hideComments, 2000);
  440. if (BC.playlist) {
  441. BC.playlist = BC.playlist.match( /[&?]+list=([^&]*[a-z0-9_-]+)/i )[1];
  442. BC.addMoreRecentVideos(-1, BC.playlist);
  443. }
  444. else BC.addMoreRecentVideos(8);
  445. if (BC.settings.mvplaylist) BC.addMostViewedPlaylist();
  446. if (BC.settings.playlists) BC.addChannelPlaylists();
  447. if (BC.settings.useblacklist) BC.applyChannelBlacklist();
  448.  
  449. let link = Bcd.addVideoAction("takeScreenShot", "Take Screen Shot", BC.screenshotIcon);
  450. Bcd.addTooltip(link, Bcd.tooltip.length); /* screenshot link */
  451. link.addEventListener('click', BC.takeScreenShot, false);
  452.  
  453. BC.setChannelFeed('add');
  454. BC.addPublishDate();
  455. BC.player.api.focus(); /* keycode listening */
  456. }
  457. else if (BC.channelpage) {
  458. BC.page = 'channelpage';
  459. if (d.cookie.indexOf('sensitivity=') !=-1 && w.location.search.indexOf('showall=1') ==-1) {
  460. d.cookie = "sensitivity=; path=/; expires=Thu, 01 Jan 1970 00:00:01 GMT";
  461. }
  462. let sensitivityWarning;
  463. if (sensitivityWarning = qs('.sensitivity-warning a')) sensitivityWarning.addEventListener('click', BC.addSensitivityCookie, false);
  464. if (BC.settings.useblacklist) BC.applyChannelBlacklist();
  465. BC.setChannelFeed('add');
  466. }
  467. else if (BC.homepage || BC.categorypage) {
  468. BC.page = 'homepage';
  469. if (BC.settings.useblacklist) {
  470. let listingTabs = qs('#listing-tabs.listening');
  471. let listingsAll = qs('#listing-all > div.row');
  472. let listingsPopular = qs('#listing-popular > div.row');
  473. BC.listingsAllHeight = Math.round(listingsAll.getBoundingClientRect().height);
  474. BC.listingsPopHeight = Math.round(listingsPopular.getBoundingClientRect().height);
  475.  
  476. if (!listingTabs) {
  477. qs("ul.nav-tabs-list li a[href='#listing-all']")
  478. .addEventListener('click', function(e){ BC.applyBlacklist('#listing-all > div.row > div', 'all') }, false);
  479. qs("ul.nav-tabs-list li a[href='#listing-popular']")
  480. .addEventListener('click', function(e){ BC.applyBlacklist('#listing-popular > div.row > div', 'popular') }, false);
  481. qs("ul.nav-tabs-list li a[href='#listing-trending']")
  482. .addEventListener('click', function(e){ BC.applyBlacklist('div#trending-day > div.row > div,div#trending-week > div.row > div,div#trending-month > div.row > div', 'trending') }, false);
  483. qs("ul.nav-tabs-list li a[href='#listing-subscribed']")
  484. .addEventListener('click', function(e){ BC.navsIni('subscribedpage') }, false);
  485.  
  486. addListener(listingsAll, function(e) {
  487. let newlistings = qs('#listing-all > div.row');
  488. let newlistingsHeight = Math.round(newlistings.getBoundingClientRect().height);
  489. if (BC.listingsAllHeight < newlistingsHeight) {
  490. BC.listingsAllHeight = newlistingsHeight;
  491. BC.applyBlacklist('#listing-all > div.row > div');
  492. }
  493. },{ childList: true });
  494.  
  495. addListener(listingsPopular, function(e) {
  496. let newlistings = qs('#listing-popular > div.row');
  497. let newlistingsHeight = Math.round(newlistings.getBoundingClientRect().height);
  498. if (BC.listingsPopHeight < newlistingsHeight) {
  499. BC.listingsPopHeight = newlistingsHeight;
  500. BC.applyBlacklist('#listing-popular > div.row > div');
  501. }
  502. },{ childList: true });
  503.  
  504. qs('#listing-tabs').classList.add('listening');
  505. if (!BC.settings.hidecarousel)
  506. BC.applyBlacklist('#carousel #channel-list div.item > div,#carousel .hidden-md > div');
  507. }
  508. }
  509. if (BC.settings.hidecarousel) { // The only way to pause this thing
  510. if (qs('#carousel')) qs('#carousel').innerHTML = '';
  511. }
  512.  
  513. if (BC.settings.homepagegotoall) {
  514. let preferAll = qs("ul.nav-tabs-list li a[href='#listing-all']");
  515. if (preferAll !== null && preferAll.parentNode.className.indexOf('active') ==-1) {
  516. preferAll.dispatchEvent(new MouseEvent('click', {
  517. bubbles: true,
  518. cancelable: true
  519. }));
  520. BC.applyBlacklist('#listing-all > div.row > div', 'all');
  521. }
  522. }
  523. else BC.applyBlacklist('#listing-popular > div.row > div');
  524. BC.setChannelFeed('remove');
  525. }
  526. BC.createSmartyButton();
  527. BC.navsIni();
  528. },
  529.  
  530.  
  531. applyBlacklist: (selector, page) => {
  532.  
  533. BC.previouslisting = selector;
  534. if (page) BC.navsIni();
  535. if (!BC.settings.useblacklist) return;
  536. selector = selector.split(',').join(':not([polled]), ') + ':not([polled])';
  537. let i,
  538. listings = qsa(selector);
  539.  
  540. if (listings.length) {
  541. try {
  542. for (i = 0; i < listings.length; i++) {
  543. let card = qs('.video-card-channel a, .video-trending-channel a, .channel-card a', listings[i]);
  544. if (card) {
  545. let href = card.getAttribute("href");
  546. let channel = href.match( /\/channel\/([a-z0-9_-]+)\//i );
  547. if (channel) {
  548. if (BC.blacklist.find( id => id[0] == channel[1] )) {
  549. listings[i].outerHTML = ''
  550. }
  551. else {
  552. listings[i].setAttribute('polled', channel[1]);
  553. let button = BC.blacklistButton();
  554. let videoCard = qs('.video-card, .video-trending-image, .channel-card', listings[i]);
  555. let name = qs('.channel-card-title', listings[i]);
  556. name = name ? name.innerText : card.innerText;
  557. videoCard.appendChild(button);
  558. button.addEventListener('click', function(e){ BC.blacklistAdd(e, channel[1], name) }, true);
  559. }
  560. }
  561. }
  562. }
  563. } catch (e) {error('applyBlacklist: '+ e)}
  564. }
  565. },
  566.  
  567.  
  568. applyChannelBlacklist: () => {
  569. let card = qs('.channel-banner .name a'),
  570. name = card.innerText;
  571.  
  572. if (card) {
  573. try {
  574. let href = card.getAttribute("href");
  575. let channel = href.match( /\/channel\/([a-z0-9_-]+)\//i );
  576. if (channel) {
  577. if (BC.blacklist.find( id => id[0] == channel[1] )) {
  578. card.setAttribute('title', name +' is blacklisted ☺');
  579. card.classList.add('userisblacklisted')
  580. }
  581. else if (! qs('.add-to-blacklist', card.parentNode)) {
  582. let button = BC.blacklistButton();
  583. card.parentNode.appendChild(button);
  584. button.addEventListener('click', function(e){
  585. BC.blacklistAdd(e, channel[1], name);
  586. this.previousSibling.classList.add('userisblacklisted');
  587. this.previousSibling.setAttribute('title', name +' is blacklisted ☺');
  588. this.style.display = 'none';
  589. }, true);
  590. }
  591. }
  592. } catch (e) {error('applyChannelBlacklist: '+ e)}
  593. }
  594. },
  595.  
  596.  
  597. createSmartyButton: () => {
  598. let i, blacklisted, menu, smarty, colors;
  599. let blContent = '';
  600. let donate = '<svg class="smarty-donate" version="1.0" xmlns="http://www.w3.org/2000/svg" width="14pt" height="14pt" viewBox="0 0 496 512" preserveAspectRatio="xMidYMid meet"><g transform="translate(248 256)"><g transform="translate(0, 0) scale(1, 1) rotate(-14 7 7)">'+
  601. '<path fill="currentColor" d="M248 8C111 8 0 119 0 256s111 248 248 248 248-111 248-248S385 8 248 8zm24 376v16c0 8.8-7.2 16-16 16h-16c-8.8 0-16-7.2-16-16v-16.2c-16.5-.6-32.6-5.8-46.4-15.1-8.7-5.9-10-18.1-2.3-25.2l12-11.3c5.4-5.1 13.3-5.4 19.7-1.6 6.1 3.6 12.9 5.4 19.9 5.4h45c11.3 '+
  602. '0 20.5-10.5 20.5-23.4 0-10.6-6.3-19.9-15.2-22.7L205 268c-29-8.8-49.2-37-49.2-68.6 0-39.3 30.6-71.3 68.2-71.4v-16c0-8.8 7.2-16 16-16h16c8.8 0 16 7.2 16 16v16.2c16.5.6 32.6 5.8 46.4 15.1 8.7 5.9 10 18.1 2.3 25.2l-12 11.3c-5.4 5.1-13.3 5.4-19.7 1.6-6.1-3.6-12.9-5.4-19.9-5.4h-45c-11.3 '+
  603. '0-20.5 10.5-20.5 23.4 0 10.6 6.3 19.9 15.2 22.7l72 21.9c29 8.8 49.2 37 49.2 68.6.2 39.3-30.4 71.2-68 71.4z" transform="translate(-248 -256)"></path></g></g><title>Donate to Smarty</title></svg>';
  604. let tabContent = `<a href="javascript:void(0)">Smarty</a><div id="smarty_tab" class="modal-content" style="display: none; position: absolute; z-index: 200;">
  605. <div id="smartymm" style="display: block;"><div style="width: 170px;">${donate}
  606. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="useblacklist2"><input name="useblacklist" id="useblacklist2" type="checkbox"${BC.settings.useblacklist ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Use Blacklist</label></div>
  607. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="hidemenubar2"><input name="hidemenubar" id="hidemenubar2" type="checkbox"${BC.settings.hidemenubar ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Scroll Menubar</label></div>
  608. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="hidecarousel2"><input name="hidecarousel" id="hidecarousel2" type="checkbox"${BC.settings.hidecarousel ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Hide Carousel</label></div>
  609. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="hidecomments2"><input name="hidecomments" id="hidecomments2" type="checkbox"${BC.settings.hidecomments ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Hide Comments</label></div>
  610. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="mvplaylist2"><input name="mvplaylist" id="mvplaylist2" type="checkbox"${BC.settings.mvplaylist ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Popular Playlist</label></div>
  611. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="playlists2"><input name="playlists" id="playlists2" type="checkbox"${BC.settings.playlists ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;All Playlists</label></div>
  612. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="useminiplayer2"><input name="useminiplayer" id="useminiplayer2" type="checkbox"${BC.settings.useminiplayer ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Use Miniplayer</label></div>
  613. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="autoplay2"><input name="autoplay" id="autoplay2" type="checkbox"${BC.player.autoplay ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Auto Play Video</label></div>
  614. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="hideadverts2"><input name="hideadverts" id="hideadverts2" type="checkbox"${BC.settings.hideadverts ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Hide Unsafe Ads</label></div>
  615. <div class="smartybox radios"><label class="r0" title="Night Color: Default"><input name="color" title="Night Color: None" value="none" type="radio"${BC.settings.color == 'none' ? ' checked':''}><i class="radiohelper r0 tabinput"></i>&nbsp;&nbsp;</label><span style="height:12px;width:12px">
  616. &nbsp;&nbsp;<label class="r1" title="Night Color: Orange"><b>O</b>&nbsp;
  617. <input name="color" value="orange" type="radio"${BC.settings.color == 'orange' ? ' checked':''}><i class="radiohelper r1 tabinput"></i></label></span><span style="height:12px;width:12px;color">&nbsp;&nbsp;<label class="r2" title="Night Color: Blue"><b>B</b>&nbsp;
  618. <input name="color" value="blue" type="radio"${BC.settings.color == 'blue' ? ' checked':''}><i class="radiohelper r2 tabinput"></i></label></span><span style="height:12px;width:12px">&nbsp;&nbsp;<label class="r3" title="Night Color: Green"><b>G</b>&nbsp;
  619. <input name="color" value="green" type="radio"${BC.settings.color == 'green' ? ' checked':''}><i class="radiohelper r3 tabinput"></i></label>&nbsp;</span></div>
  620. </div><div id="blacklistedchannels" style="border-top:none;"><div><em>No Blacklist</em></div></div></div><div id="smartyam" style="width: 198px; display: none;">
  621. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="usesquareicons2"><input name="usesquareicons" id="usesquareicons2" type="checkbox"${BC.settings.usesquareicons ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Use Square Icons</label></div>
  622. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="hidedonationbar2"><input name="hidedonationbar" id="hidedonationbar2" type="checkbox"${BC.settings.hidedonationbar ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Hide Donation Bar</label></div>
  623. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="homepagegotoall2"><input name="homepagegotoall" id="homepagegotoall2" type="checkbox"${BC.settings.homepagegotoall ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Homepage Goto All</label></div>
  624. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="hidecookienotice2"><input name="hidecookienotice" id="hidecookienotice2" type="checkbox"${BC.settings.hidecookienotice ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Hide Cookie Notice</label></div>
  625. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="hidesignupnotice2"><input name="hidesignupnotice" id="hidesignupnotice2" type="checkbox"${BC.settings.hidesignupnotice ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Hide Signup Notice</label></div>
  626. <div class="smartybox"><label class="tabinput" style="margin-bottom: 0px;" for="useseekbarpreview2"><input name="useseekbarpreview" id="useseekbarpreview2" type="checkbox"${BC.settings.useseekbarpreview ? ' checked':''} class="tabinput"> <i class="cbhelper tabinput"></i>&nbsp;Use Seekbar Preview</label></div></div></div>`;
  627.  
  628. menu = qs('ul.nav-tabs-list');
  629. smarty = qs('#smarty_tab');
  630.  
  631. if (smarty === null) {
  632. smarty = dce("li");
  633. smarty.innerHTML = tabContent;
  634. menu.appendChild(smarty);
  635. blacklisted = qs('#blacklistedchannels');
  636.  
  637. if (BC.settings.useblacklist) {
  638. if (BC.blacklist.length ) {
  639. for (i = 0; i < BC.blacklist.length; i++) {
  640. blContent += `<span title="Click to remove ${BC.blacklist[i][1]}" data-name="${BC.blacklist[i][0]}">${BC.blacklist[i][1]}</span>`;
  641. }
  642. }
  643. else blContent = '<div><em>No Blacklist</em></div>';
  644. }
  645. else blContent = '<div><em>Blacklist Off</em></div>';
  646. if (blContent) blacklisted.innerHTML = blContent;
  647.  
  648. qs('a', smarty).addEventListener('click', function(e) {setTimeout(BC.toggleTab,300)}, true);
  649. qs('a', smarty).addEventListener('dblclick', function(e) {e.stopPropagation(); BC.auxMenu = 1; BC.toggleTab(e, false, true)}, true);
  650. qs('#useminiplayer2', smarty).addEventListener('change', function(e) {BC.toggleSettings('useminiplayer',e.target.checked)}, false);
  651. qs('#hidecomments2', smarty).addEventListener('change', function(e) {BC.savePlayerValue('hidecomments',e.target.checked)}, false);
  652. qs('#useblacklist2', smarty).addEventListener('change', function(e) {BC.toggleSettings('useblacklist',e.target.checked)}, false);
  653. qs('#hidecarousel2', smarty).addEventListener('change', function(e) {BC.toggleSettings('hidecarousel',e.target.checked)}, false);
  654. qs('#hidemenubar2', smarty).addEventListener('change', function(e) {BC.toggleSettings('hidemenubar',e.target.checked)}, false);
  655. qs('#hideadverts2', smarty).addEventListener('change', function(e) {BC.toggleSettings('hideadverts',e.target.checked)}, false);
  656. qs('#mvplaylist2', smarty).addEventListener('change', function(e) {BC.savePlayerValue('mvplaylist',e.target.checked)}, false);
  657. qs('#playlists2', smarty).addEventListener('change', function(e) {BC.savePlayerValue('playlists',e.target.checked)}, false);
  658. qs('#autoplay2', smarty).addEventListener('change', function(e) {BC.savePlayerValue('autoplay',e.target.checked)}, false);
  659. qs('#usesquareicons2', smarty).addEventListener('change', function(e) {BC.toggleSettings('usesquareicons',e.target.checked)}, false);
  660. qs('#hidedonationbar2', smarty).addEventListener('change', function(e) {BC.toggleSettings('hidedonationbar',e.target.checked)}, false);
  661. qs('#homepagegotoall2', smarty).addEventListener('change', function(e) {BC.toggleSettings('homepagegotoall',e.target.checked)}, false);
  662. qs('#hidecookienotice2', smarty).addEventListener('change', function(e) {BC.toggleSettings('hidecookienotice',e.target.checked)}, false);
  663. qs('#hidesignupnotice2', smarty).addEventListener('change', function(e) {BC.toggleSettings('hidesignupnotice',e.target.checked)}, false);
  664. qs('#useseekbarpreview2', smarty).addEventListener('change', function(e) {BC.toggleSettings('useseekbarpreview',e.target.checked)}, false);
  665. qs('svg').addEventListener('click', function(e) {w.open('https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QHFFSLZ7ENUQN&source=url', '_blank');}, false);
  666. colors = qsa('input[type=radio][name=color]', smarty);
  667. for (i = 0; i < colors.length; i++) {
  668. colors[i].addEventListener('change', function(e) {BC.toggleSettings('color',this.value)}, false);
  669. }
  670. }
  671. else {
  672. blacklisted = qs('#blacklistedchannels');
  673. if (BC.settings.useblacklist) {
  674. if (BC.blacklist.length ) {
  675. for (i = 0; i < BC.blacklist.length; i++) {
  676. blContent += `<span title="Click to remove ${BC.blacklist[i][1]}" data-name="${BC.blacklist[i][0]}">${BC.blacklist[i][1]}</span>`;
  677. }
  678. }
  679. else blContent = '<div><em>No Blacklist</em></div>';
  680. }
  681. else blContent = '<div><em>Blacklist Off</em></div>';
  682. blacklisted.innerHTML = blContent;
  683. }
  684. if (BC.blacklist.length > 14 && BC.settings.useblacklist) {
  685. blacklisted.style.height = '280px';
  686. blacklisted.style.overflowY = 'scroll';
  687. }
  688. else {
  689. blacklisted.style.height = 'auto';
  690. blacklisted.style.overflowY = 'visible';
  691. }
  692. menu.parentNode.style.overflow = 'visible';
  693. smarty = qsa('#blacklistedchannels > span');
  694. for (i = 0; i < smarty.length; i++) {
  695. smarty[i].addEventListener('click', BC.blacklistRemove, true);
  696. }
  697. },
  698.  
  699.  
  700. blacklistButton: () => {
  701. let span = dce("span");
  702.  
  703. span.className = "action-button add-to-blacklist";
  704. span.innerHTML = '<svg version="1.0" xmlns="http://www.w3.org/2000/svg" width="27" height="27" viewBox="0 0 33 33" preserveAspectRatio="xMidYMid meet"><g transform="translate(0.000000,27.000000) ' +
  705. 'scale(0.100000,-0.100000)" stroke="none"><path fill="currentColor" d="M12 258 c-17 -17 -17 -229 0 -246 17 -17 229 -17 246 0 17 17 17 229 0 246 -17 17 -229 17 -246 0z m233 -123 l0 -110 -110 0 -110 0 -3 99 c-1 55 0 106 2 113 4 11 30 13 113 11 l108 -3 0 -110z"/>' +
  706. '<path fill="currentColor" d="M40 217 c0 -7 16 -26 35 -42 19 -17 35 -35 35 -40 0 -6 -16 -25 -35 -42 -19 -18 -35 -37 -35 -43 0 -22 31 -8 60 29 l32 39 35 -39 c34 -37 63 -51 63 -29 0 6 -16 24 -35 41 -19 17 -35 37 -35 44 0 7 16 25 35 39 35 27 47 56 ' +
  707. '23 56 -7 0 -26 -16 -41 -35 -15 -19 -33 -35 -40 -35 -7 0 -25 16 -41 35 -30 35 -56 46 -56 22z"/></g></svg><span class="blacklist-tooltip">&nbsp;Blacklist&nbsp;</span>';
  708. return span
  709. },
  710.  
  711.  
  712. closeTab: (e) => {e.stopPropagation(); if (!e.target.classList.contains("tabinput")) {BC.toggleTab(null,'close')}},
  713.  
  714. toggleTab: (e, close = false, aux = false) => {
  715. let tab = qs('#smarty_tab');
  716.  
  717. if (!aux && !close && BC.auxMenu) return;
  718. if (tab.style.display == 'block' && aux) return;
  719.  
  720. if (tab.style.display == 'none' && !close) {
  721. if (aux) {
  722. qs("#smartymm").style.display = 'none';
  723. qs("#smartyam").style.display = 'block';
  724. }
  725. else qs("#smartymm").style.display = 'block';
  726. tab.style.display = 'block';
  727. d.body.addEventListener('click', BC.closeTab, false);
  728. }
  729. else {
  730. setTimeout(function(){
  731. BC.auxMenu = !1;
  732. tab.style.display = 'none';
  733. qs("#smartyam").style.display = 'none';
  734. if (BC.reload) w.location.replace(w.location.href);
  735. },200);
  736. d.body.removeEventListener('click', BC.closeTab, false);
  737. }
  738. if (e) {
  739. e.preventDefault();
  740. e.stopPropagation();
  741. return false;
  742. }
  743. },
  744.  
  745.  
  746. blacklistAdd: (e, channel, name) => {
  747. let i,
  748. blocked = qsa('[polled="'+channel+'"]');
  749.  
  750. for (i = 0; i < blocked.length; i++) blocked[i].innerHTML = BC.wait(blocked[i]);
  751. BC.blacklist.push([channel, name]);
  752. /* Sort by User Name*/
  753. BC.blacklist.sort( (a, b) => {
  754. let x = a[1].toUpperCase(), y = b[1].toUpperCase();
  755. if (x > y) return 1;
  756. else if (x < y) return -1;
  757. return 0;
  758. });
  759. GM.setValue('blacklist', JSON.stringify(BC.blacklist));
  760. BC.createSmartyButton();
  761.  
  762. e.preventDefault();
  763. e.stopPropagation();
  764. return false;
  765. },
  766.  
  767.  
  768. blacklistRemove: (e) => {
  769. BC.toggleTab();
  770. let arr = BC.blacklist.filter(function(ele) {
  771. return ele[0] != e.target.getAttribute('data-name');
  772. });
  773. BC.blacklist = arr;
  774. BC.blacklist.sort( (a, b) => {
  775. let x = a[1].toUpperCase(), y = b[1].toUpperCase();
  776. if (x > y) return 1;
  777. else if (x < y) return -1;
  778. return 0;
  779. });
  780. GM.setValue('blacklist', JSON.stringify(BC.blacklist));
  781. BC.createSmartyButton();
  782. e.preventDefault();
  783. e.stopPropagation();
  784. return false;
  785. },
  786.  
  787.  
  788. wait: (o) => {
  789. let waiter = '<div id="loader" style="position: relative;margin: auto;left: 0;top: 90px;width: 10%;"><ul style="width: 10%;"><li></li><li></li><li></li></ul></div>',
  790. dim = o.getBoundingClientRect(),
  791. n = dim.width;
  792.  
  793. o.style.padding = '0';
  794. o.style.width = n +'px';
  795. o.style.height = dim.height +'px';
  796.  
  797. setTimeout(function() {
  798. o.style = '-webkit-transition: width 1s ease-out; -moz-transition: width 1s ease-out; -khtml-transition: width 1s ease-out; transition: width 1s ease-out; width:0%;';
  799. setTimeout(function() {
  800. o.outerHTML = '';
  801. }, 900)
  802. }, 500);
  803. return waiter;
  804. },
  805.  
  806.  
  807. maxY: 0, minY: 0, lastY: 0, tabTop: 0,
  808. topfloat: '', tabfloat: '', upPrev: 0,
  809. tabNav: '', topNav: '',
  810. floatHeaders: (e) => {
  811. let spanY = 0,
  812. scrolled = Math.round(pageYOffset),
  813. up = scrolled < BC.lastY;
  814.  
  815. if (scrolled == BC.lastY) return;
  816. else if (up) {
  817. BC.minY = scrolled;
  818. spanY = BC.minY - BC.maxY;
  819. }
  820. else {
  821. BC.maxY = scrolled;
  822. spanY = BC.maxY - BC.minY;
  823. }
  824. BC.lastY = scrolled;
  825.  
  826. if (up) {
  827. if (!BC.upPrev) BC.maxY = BC.lastY;
  828. if (scrolled < BC.tabTop + 100 || spanY < -150) {
  829. if (BC.topfloat) {
  830. if (scrolled < 1) {
  831. BC.maxY = 0;
  832. BC.topfloat = false;
  833. BC.topNav.style.top = '';
  834. d.documentElement.classList.remove("topNavfloat");
  835. }
  836. else if (scrolled > BC.tabTop + 99) {
  837. BC.topNav.style.top = '0px';
  838. }
  839. }
  840. if (BC.topfloat || scrolled < 1) {
  841. if (scrolled <= BC.tabTop - 60) {
  842. BC.tabfloat = false;
  843. BC.tabNav.style.top = '';
  844. d.documentElement.classList.remove("tabNavfloat");
  845. }
  846. else if (scrolled > BC.tabTop + 99) {
  847. BC.tabNav.style.top = '60px';
  848. }
  849. }
  850. }
  851. }
  852. else if (scrolled > BC.tabTop + 88 && spanY > 149) {
  853. if (BC.upPrev) BC.minY = BC.lastY;
  854. BC.topNav.style.top = '-160px';
  855. if (!BC.topfloat) {
  856. BC.topfloat = true;
  857. d.documentElement.classList.add("topNavfloat");
  858. }
  859. BC.tabNav.style.top = '-102px';
  860. if (!BC.tabfloat) {
  861. BC.tabfloat = true;
  862. d.documentElement.classList.add("tabNavfloat");
  863. }
  864. }
  865. BC.upPrev = up;
  866. },
  867.  
  868.  
  869. navsIni: () => {
  870. if (BC.settings.hidemenubar) {
  871. w.scrollTo(0, 0);
  872. BC.maxY = 0; BC.minY = 0; BC.lastY = 0; BC.tabTop = 0;
  873. BC.topfloat = false; BC.tabfloat = false; BC.upPrev = true;
  874. BC.topNav = qs('#nav-top-menu');
  875. BC.tabNav = qs('.tab-scroll-outer');
  876. d.documentElement.classList.remove("topNavfloat");
  877. d.documentElement.classList.remove("tabNavfloat");
  878. BC.topNav.style.top = '';
  879. if (BC.tabNav && !BC.tabTop) {
  880. BC.tabNav.style.top = '';
  881. setTimeout( function() {
  882. BC.tabTop = BC.tabNav.getBoundingClientRect().top;
  883. if (isChrome) setTimeout( function(){w.scrollTo(0, BC.tabTop)},1000);
  884. else w.scrollTo(0, BC.tabTop);
  885. },1000);
  886. }
  887. } else return
  888. },
  889.  
  890.  
  891. toggleSettings: (arg, val) => {
  892. let previouslisting, carousel, advert;
  893.  
  894. if (arg == 'useblacklist') {
  895. BC.savePlayerValue(arg, val);
  896. BC.createSmartyButton();
  897. if (BC.homepage) {
  898. if (val) {
  899. if (BC.previouslisting) {
  900. previouslisting = BC.previouslisting;
  901. if (previouslisting.indexOf('listing') !=-1 && !BC.settings.hidecarousel)
  902. BC.applyBlacklist('#carousel #channel-list div.item > div,#carousel .hidden-md > div');
  903. BC.applyBlacklist(previouslisting);
  904. }
  905. d.documentElement.classList.remove("noblacklist");
  906. }
  907. else d.documentElement.classList.add("noblacklist");
  908. }
  909. }
  910. else if (arg == 'hidecarousel') {
  911. if (carousel = qs('#carousel')) {
  912. carousel.style.display = (val ? 'none' : 'block');
  913. if (val) carousel.innerHTML = '';
  914. else carousel.innerHTML = '<h3>Refresh window to start carousel</h3>';
  915. }
  916. BC.savePlayerValue(arg, val);
  917. BC.createSmartyButton();
  918. }
  919. else if (arg == 'hidemenubar') {
  920. BC.savePlayerValue(arg, val);
  921. BC.createSmartyButton();
  922. }
  923. else if (arg == 'hideadverts') {
  924. if (advert = qs('.rcad-container')) {
  925. advert.style.display = (val ? 'none' : 'block');
  926. }
  927. BC.savePlayerValue(arg, val);
  928. BC.createSmartyButton();
  929. }
  930. else if (arg == 'usedark') {
  931. if (val) d.documentElement.classList.add("night");
  932. else d.documentElement.classList.remove("night");
  933. BC.savePlayerValue(arg, val);
  934. }
  935. else if (arg == 'useminiplayer' || arg == 'color' || arg == 'usesquareicons' || arg == 'hidedonationbar' ||
  936. arg == 'homepagegotoall' || arg == 'hidecookienotice' || arg == 'hidesignupnotice' || arg == 'useseekbarpreview') {
  937. BC.reload = true;
  938. BC.savePlayerValue(arg, val);
  939. }
  940. },
  941.  
  942.  
  943. savePlayerValue: (arg, val) => {
  944. if (arg == 'volume') BC.player.volume = val;
  945. else if (arg == 'color') BC.settings.color = val;
  946. else if (arg == 'usedark') BC.settings.usedark = val;
  947. else if (arg == 'autoplay') BC.player.autoplay = val;
  948. else if (arg == 'playnext') BC.settings.playnext = val;
  949. else if (arg == 'playlists') BC.settings.playlists = val;
  950. else if (arg == 'mvplaylist') BC.settings.mvplaylist = val;
  951. else if (arg == 'hidemenubar') BC.settings.hidemenubar = val;
  952. else if (arg == 'hideadverts') BC.settings.hideadverts = val;
  953. else if (arg == 'useblacklist') BC.settings.useblacklist = val;
  954. else if (arg == 'hidecarousel') BC.settings.hidecarousel = val;
  955. else if (arg == 'hidecomments') BC.settings.hidecomments = val;
  956. else if (arg == 'useminiplayer') BC.settings.useminiplayer = val;
  957. else if (arg == 'usesquareicons') BC.settings.usesquareicons = val;
  958. else if (arg == 'hidedonationbar') BC.settings.hidedonationbar = val;
  959. else if (arg == 'homepagegotoall') BC.settings.homepagegotoall = val;
  960. else if (arg == 'hidecookienotice') BC.settings.hidecookienotice = val;
  961. else if (arg == 'hidesignupnotice') BC.settings.hidesignupnotice = val;
  962. else if (arg == 'useseekbarpreview') BC.settings.useseekbarpreview = val;
  963. GM.setValue('player', JSON.stringify({
  964. volume : BC.player.volume,
  965. autoplay : BC.player.autoplay,
  966. color : BC.settings.color,
  967. playnext : BC.settings.playnext,
  968. playlists : BC.settings.playlists,
  969. mvplaylist : BC.settings.mvplaylist,
  970. usedark : BC.settings.usedark,
  971. useblacklist : BC.settings.useblacklist,
  972. hidecarousel : BC.settings.hidecarousel,
  973. hidecomments : BC.settings.hidecomments,
  974. hidemenubar : BC.settings.hidemenubar,
  975. hideadverts : BC.settings.hideadverts,
  976. useminiplayer : BC.settings.useminiplayer,
  977. usesquareicons : BC.settings.usesquareicons,
  978. hidedonationbar : BC.settings.hidedonationbar,
  979. homepagegotoall : BC.settings.homepagegotoall,
  980. hidecookienotice : BC.settings.hidecookienotice,
  981. hidesignupnotice : BC.settings.hidesignupnotice,
  982. useseekbarpreview : BC.settings.useseekbarpreview
  983. }));
  984. },
  985.  
  986.  
  987. miniPlayer: () => {
  988. let show_mini = pageYOffset > BC.player.api.mini_point,
  989. is_mini = d.documentElement.classList.contains("s-marty-miniplayer")
  990.  
  991. if (!show_mini && is_mini) {
  992. d.documentElement.classList.remove("s-marty-miniplayer");
  993. BC.player.fur.style.width = '';
  994. BC.player.fur.style.height = '';
  995. BC.player.fur.style.left = '0px';
  996. BC.player.fur.style.bottom = '0px';
  997. BC.player.api.style.width = '';
  998. BC.player.api.style.height = BC.origVid.h +'px';
  999. w.dispatchEvent(new Event("resize"));
  1000. }
  1001. else if (show_mini && !is_mini) {
  1002. d.documentElement.classList.add("s-marty-miniplayer");
  1003. BC.player.fur.style.left = BC.miniplayer.x +'px';
  1004. BC.player.fur.style.bottom = BC.miniplayer.y +'px';
  1005. BC.player.api.style.width = BC.miniplayer.w +'px';
  1006. BC.player.api.style.height = BC.miniplayer.h +'px';
  1007. }
  1008. },
  1009.  
  1010.  
  1011. onFullScreen: (e) => {
  1012. let a, el;
  1013. if (d.fullscreenElement || d.mozFullScreenElement || d.webkitFullscreenElement) {
  1014. BC.isFullScreen = true;
  1015. d.documentElement.classList.add("isfullscreen");
  1016. if (BC.settings.playnext) {
  1017. a = dce("a");
  1018. a.href = "javascript:void(0)";
  1019. a.id = "block_playnext";
  1020. a.style.display = "none";
  1021. el = qs(".sidebar-next .video-card");
  1022. el.insertBefore(a, el.firstChild);
  1023. }
  1024. BC.prevUp = []; BC.nextUp = {}; BC.history = -1; BC.nextUpLoaded = false;
  1025. el = dce("div");
  1026. el.id = "autoplay-next";
  1027. el.className = "hidden";
  1028. qs(".wrapper .plyr").appendChild(el);
  1029. BC.player.api.addEventListener('ended', BC.playNextFullScreen, false);
  1030. BC.player.api.addEventListener('play', BC.hideAutoplayNext, false);
  1031. }
  1032. else {
  1033. BC.isFullScreen = false;
  1034. d.documentElement.classList.remove("isfullscreen");
  1035. if (a = qs(".sidebar-next .video-card a#block_playnext"))
  1036. qs(".sidebar-next .video-card").removeChild(a);
  1037. if (el = qs(".wrapper .plyr #autoplay-next"))
  1038. qs(".wrapper .plyr").removeChild(el);
  1039. BC.player.api.removeEventListener('ended', BC.playNextFullScreen, false);
  1040. BC.player.api.removeEventListener('play', BC.hideAutoplayNext, false);
  1041. BC.prevUp = []; BC.nextUp = {}; BC.history = -1; BC.nextUpLoaded = false;
  1042. }
  1043. },
  1044.  
  1045. catchKey: (e) => {
  1046. let el = d.activeElement,
  1047. fr = 1 / 24, /*23.976,*/
  1048. code = e.keyCode ? e.keyCode : e.which,
  1049. click = new MouseEvent('click', {bubbles: true, cancelable: true});
  1050.  
  1051. if ((typeof code !== "number") ||
  1052. (e.altKey || e.ctrlKey || e.shiftKey || e.metaKey) ||
  1053. (typeof e.target.type !== "undefined" && e.target.type.indexOf('text') !=-1)) return;
  1054. if (code == 188 || code == 190 && BC.player.api.paused) { /* < or > */
  1055. e.preventDefault();
  1056. e.stopPropagation();
  1057. if (code == 188) { /* < one frame back */
  1058. BC.player.api.currentTime =
  1059. Math.max(0, BC.player.api.currentTime - fr);
  1060. }
  1061. else if (code == 190) { /* > one frame forward */
  1062. BC.player.api.currentTime =
  1063. Math.min(BC.player.api.duration, BC.player.api.currentTime + fr);
  1064. } /* hide the red play button */
  1065. setTimeout(()=>{qs('.plyr button.plyr__control--overlaid[aria-label="Play"]')
  1066. .classList.add("plyr__control--pressed")},150);
  1067. }
  1068. else if (!BC.isFullScreen) {
  1069. if (code == 83) { /* s is for screenshot */
  1070. let ss = qs("#screenshot");
  1071. if (!ss)
  1072. qs("#takeScreenShot").dispatchEvent(click)
  1073. else {
  1074. if (qs("#overlay")) ss.removeChild(qs("#overlay"));
  1075. ss.dispatchEvent(click)
  1076. }
  1077. }
  1078. else if (code == 68) { /* d is for download */
  1079. Bcd.toggle_download_dialog();
  1080. }
  1081. }
  1082. },
  1083.  
  1084.  
  1085. sizeMiniPlayer: (e) => {
  1086. let miniPlayerSized = false;
  1087.  
  1088. if (e.type === "mousemove") {
  1089. BC.miniSiz.hd = e.clientY - BC.miniSiz.h;
  1090. BC.miniSiz.h = e.clientY;
  1091. BC.miniPlayerH -= BC.miniSiz.hd;
  1092. if (BC.miniPlayerH < 197) BC.miniPlayerH = 197;
  1093. if (BC.miniPlayerH > BC.origVid.h) BC.miniPlayerH = BC.origVid.h;
  1094. if (BC.miniPlayerH + BC.miniplayer.y > w.innerHeight -15) BC.miniPlayerH = w.innerHeight - BC.miniplayer.y -15;
  1095. BC.miniPlayerW = Math.round(BC.miniPlayerH * BC.origVid.r);
  1096.  
  1097. BC.player.api.style.width = BC.miniPlayerW +'px';
  1098. BC.player.fur.style.width = BC.miniPlayerW +'px';
  1099. BC.player.fur.style.height = BC.miniPlayerH +'px';
  1100. BC.player.api.style.height = BC.miniPlayerH +'px';
  1101. }
  1102. else if (e.type === "mouseup") {
  1103. w.removeEventListener("mouseup", BC.sizeMiniPlayer, true);
  1104. w.removeEventListener("mousemove", BC.sizeMiniPlayer, true);
  1105. if (BC.miniPlayerH != BC.miniplayer.h) {
  1106. BC.miniPlayerH = (BC.miniPlayerH < 197) ? 197 : BC.miniPlayerH;
  1107. BC.miniplayer.h = (BC.miniPlayerH > BC.origVid.h) ? BC.origVid.h : BC.miniPlayerH;
  1108. BC.miniplayer.w = Math.round(BC.miniPlayerH * BC.origVid.r);
  1109. miniPlayerSized = true;
  1110. }
  1111. }
  1112. else if (e.type === "mousedown") {
  1113. BC.miniSiz.h = e.clientY;
  1114. BC.miniPlayerH = BC.miniplayer.h;
  1115. w.addEventListener("mouseup", BC.sizeMiniPlayer, true);
  1116. w.addEventListener("mousemove", BC.sizeMiniPlayer, true);
  1117. }
  1118. if (miniPlayerSized) {
  1119. GM.setValue('miniplayer', JSON.stringify({ x:BC.miniplayer.x,y:BC.miniplayer.y,w:BC.miniplayer.w,h:BC.miniplayer.h }));
  1120. miniPlayerSized = false;
  1121. }
  1122. e.preventDefault();
  1123. e.stopPropagation();
  1124. return false;
  1125. },
  1126.  
  1127.  
  1128. moveMiniPlayer: (e) => {
  1129. let miniPlayerMoved = false;
  1130.  
  1131. if (e.type === "mousemove") {
  1132. BC.miniPos.xd = e.clientX - BC.miniPos.x;
  1133. BC.miniPos.yd = BC.miniPos.y - e.clientY;
  1134. BC.miniPos.x = e.clientX;
  1135. BC.miniPos.y = e.clientY;
  1136. BC.miniPlayerX += BC.miniPos.xd;
  1137. BC.miniPlayerY += BC.miniPos.yd;
  1138. if (BC.miniPlayerX < 0) BC.miniPlayerX = 0;
  1139. if (BC.miniPlayerX + BC.miniplayer.w > w.innerWidth) BC.miniPlayerX = w.innerWidth - BC.miniplayer.w;
  1140. if (BC.miniPlayerY < 0) BC.miniPlayerY = 0;
  1141. if (BC.miniPlayerY + BC.miniplayer.h > w.innerHeight -15) BC.miniPlayerY = w.innerHeight - BC.miniplayer.h -15;
  1142.  
  1143. BC.player.fur.style.left = BC.miniPlayerX +'px';
  1144. BC.player.fur.style.bottom = BC.miniPlayerY +'px';
  1145. }
  1146. else if (e.type === "mouseup") {
  1147. w.removeEventListener("mouseup", BC.moveMiniPlayer, true);
  1148. w.removeEventListener("mousemove", BC.moveMiniPlayer, true);
  1149. if (BC.miniPlayerX != BC.miniplayer.x || BC.miniPlayerY != BC.miniplayer.y) {
  1150. BC.miniplayer.x = (BC.miniPlayerX < 0) ? 0 : BC.miniPlayerX;
  1151. BC.miniplayer.y = (BC.miniPlayerY < 0) ? 0 : BC.miniPlayerY;
  1152. miniPlayerMoved = true;
  1153. }
  1154. }
  1155. else if (e.type === "mousedown") {
  1156. BC.miniPos.x = e.clientX;
  1157. BC.miniPos.y = e.clientY;
  1158. BC.miniPlayerX = BC.miniplayer.x;
  1159. BC.miniPlayerY = BC.miniplayer.y;
  1160. w.addEventListener("mouseup", BC.moveMiniPlayer, true);
  1161. w.addEventListener("mousemove", BC.moveMiniPlayer, true);
  1162. }
  1163. if (miniPlayerMoved) {
  1164. GM.setValue('miniplayer', JSON.stringify({ x:BC.miniplayer.x,y:BC.miniplayer.y,w:BC.miniplayer.w,h:BC.miniplayer.h }));
  1165. miniPlayerMoved = false;
  1166. }
  1167. e.preventDefault();
  1168. e.stopPropagation();
  1169. return false;
  1170. },
  1171.  
  1172.  
  1173. addBrowserSearch: () => {
  1174. let head = qs('head'),
  1175. openSearch = dce('link');
  1176.  
  1177. openSearch.setAttribute('rel', 'search');
  1178. openSearch.setAttribute('href', location.protocol +'//github.com/s-marty/SmartChute/raw/master/inc/search.xml');
  1179. openSearch.setAttribute('type', 'application/opensearchdescription+xml');
  1180. openSearch.setAttribute('title', 'Bit Chute');
  1181. head.appendChild(openSearch);
  1182. },
  1183.  
  1184.  
  1185. setChannelFeed: (action) => {
  1186. let rssLink, title, href, channel;
  1187. let head = qs('head'),
  1188. card = qs('.channel-banner .name a'),
  1189. feed = qs('#rss_feed');
  1190.  
  1191. if (action == 'remove' && feed) {
  1192. head.removeChild(feed)
  1193. }
  1194. else if (action == 'add' && card) {
  1195. title = card.innerText;
  1196. href = card.getAttribute("href");
  1197. channel = href.match( /\/channel\/([a-z0-9_-]+)\//i );
  1198. if (channel) {
  1199. if (feed && feed.title != title) head.removeChild(feed);
  1200. else if (!feed) {
  1201. rssLink = dce('link');
  1202. rssLink.setAttribute('rel', 'alternate');
  1203. rssLink.setAttribute('href', `${location.protocol}//www.bitchute.com/feeds/rss/channel/${channel[1]}/`);
  1204. rssLink.setAttribute('type', 'application/rss+xml');
  1205. rssLink.setAttribute('title', title);
  1206. rssLink.setAttribute('id', 'rss_feed');
  1207. head.appendChild(rssLink);
  1208. }
  1209. }
  1210. }
  1211. },
  1212.  
  1213.  
  1214. setPreferencesCookie: (name, value) => {
  1215. let val, preferences = d.cookie.match(/preferences=(\{[a-z0-9_%:-]+[^;]*\})/i);
  1216. if (preferences) {
  1217. if (name == 'autoplay') {
  1218. val = preferences[1].match(/theme%22:%22([a-z]+)%22/);
  1219. d.cookie = `preferences={%22theme%22:%22${val[1]}%22%2C%22autoplay%22:${value}}; path=/`;
  1220. }
  1221. else if (name == 'theme') {
  1222. val = preferences[1].match(/autoplay%22:([a-z]+)(\}|%2C)/);
  1223. d.cookie = `preferences={%22theme%22:%22${value}%22%2C%22autoplay%22:${val[1]}}; path=/`;
  1224. }
  1225. }
  1226. },
  1227.  
  1228.  
  1229. addSensitivityCookie: (e) => {
  1230. d.cookie = "sensitivity=true; path=/";
  1231. return false
  1232. },
  1233.  
  1234.  
  1235. getCsrftoken: () => {
  1236. let csrftoken = d.cookie.match(/csrftoken=([a-z0-9_-]+[^;]*)/i);
  1237. return csrftoken ? csrftoken[1] : null
  1238. },
  1239.  
  1240.  
  1241. addPublishDate: (update = false) => {
  1242. let u, tn, out = null;
  1243. let vpd = qs("div.video-publish-date");
  1244.  
  1245. if (vpd) {
  1246. u = vpd.innerHTML.match(/(\d\d):(\d\d)\s(UTC)\son\s([a-zA-Z]*)\s(\d?\d)[a-z]{2},\s(\d{4})/);
  1247. //debug('dateString.match', u);
  1248. if (u) {
  1249. if (update) {
  1250. if (out = getPublishDate(u)) {
  1251. vpd = qs("b", vpd);
  1252. tn = d.createTextNode(`${out} ago`);
  1253. vpd.replaceChild(tn, vpd.childNodes[0]);
  1254. setTimeout(BC.addPublishDate, 60000, true)
  1255. }
  1256. }
  1257. else {
  1258. vpd.style.fontSize = "12px";
  1259. out = getPublishDate(u);
  1260. if (out) {
  1261. vpd.innerHTML += `&nbsp;&nbsp; <b>${out} ago</b>`;
  1262. setTimeout(BC.addPublishDate, 60000, true)
  1263. }
  1264. }
  1265. }
  1266. }
  1267. },
  1268.  
  1269.  
  1270. isDark: false, isTheme: false, persistTryDT: 0,
  1271.  
  1272. setTheme: () => {
  1273. let c, theme, version, colours, style;
  1274.  
  1275. if (!BC.isDark && BC.settings.usedark) {
  1276. theme = qs('link#css-theme');
  1277. if (theme !== null) {
  1278. if (theme.href.indexOf('night.css') ==-1) {
  1279. version = theme.href.match( /\/static\/([a-z]+[0-9]+)\//i );
  1280. if (version !== null && version[1] !== null) {
  1281. BC.version = version[1];
  1282. theme.setAttribute('href',`/static/${BC.version}/css/theme-default-night.css`);
  1283. }
  1284. BC.isDark = true;
  1285. }
  1286. BC.setPreferencesCookie("theme", "night");
  1287. d.documentElement.classList.add("night");
  1288. BC.persistTryDT = 0;
  1289. }
  1290. else if (!BC.isDark && BC.persistTryDT++ < 30) {
  1291. w.setTimeout(BC.setTheme, 1000);
  1292. }
  1293. }
  1294. if (!BC.isTheme && BC.settings.color != 'none') {
  1295. BC.isTheme = true;
  1296. colours = {
  1297. 'orange':{dark:'#ef4136',lighter:'#f37835',lightest:'#f0af5a'},
  1298. 'green':{dark:'#46a604',lighter:'#35c453',lightest:'#55a47c'},
  1299. 'blue':{dark:'#2532e0',lighter:'#2567e0',lightest:'#559bcc'}
  1300. };
  1301. c = colours[BC.settings.color];
  1302. style = dce("style");
  1303. style.type = "text/css";
  1304. style.innerText = `
  1305. .night .sidebar-heading, .night .subscribe-button, .night .btn-danger, .night #loader ul li {background-color: ${c.dark};}
  1306. .night .sidebar-recent .video-card.active {box-shadow: 0 0 1em 0.2em ${c.lighter};} .night .nav-tabs>li.active {border-bottom-color:${c.dark};}
  1307. .night .playlist-card.active {border-top: 1px solid ${c.lighter}bb; box-shadow: 0 2px 1em 0.2em ${c.lighter};}
  1308. .night body, .night .video-card .video-card-text, .night .video-card .video-card-text p i, .night .notify-button,
  1309. .night .channel-notify-button, .night .channel-videos-details, .night .channel-videos-title a, .night .channel-videos-text,
  1310. .night .video-trending-details, .night .video-trending-title a, .night .video-trending-channel a, .night .video-trending-text,
  1311. .night .playlist-video .details, .night .playlist-video .title a, .night .playlist-video .channel a, .night .playlist-video .description,
  1312. .night #smarty_tab label, .night #smarty_tab #blacklistedchannels span, .night .jquery-comments ul.main li.comment time,
  1313. .night .video-detail-text p, .night .video-information .sharing-drop span, .night #nav-top-menu .search-box .form-control, .jquery-comments .no-data { color: ${c.lightest};}
  1314. .night a:link, .night a:active, .night a:visited, .night a:focus, .night .scripted-link, .night #nav-top-menu .unauth-link a, .night .jquery-comments ul.navigation li,
  1315. .night .video-card .video-card-text a, .night #nav-top-menu .user-link a, .night #day-theme a svg, .night .search-icon svg { color: ${c.lighter};}
  1316. .night #nav-side-menu .side-toggle:hover, .night #day-theme a svg:hover, .night .search-icon svg:hover, .night #smarty_tab label:hover, .night #smarty_tab #blacklistedchannels span:hover,
  1317. .night a:hover, .night .scripted-link:hover, .night #screenshot .cancel:hover, .night #screenshot .save a:hover, .night .jquery-comments ul.navigation li.active, .night .jquery-comments ul.navigation li:hover {color: ${c.dark} !important;}
  1318. .night #video_download_dialog button[disabled]:not(.download_finished):not(.download_error):hover {background-color: ${c.dark} !important;}
  1319. .night .tags ul li a, .night #show-comments {background-color: #3b383c; border-radius:5px;} .night .tags ul li a:hover {background-color: #4d484e;} .creator-monetization {color: #30a247;}
  1320. .night .channel-banner .name a.userisblacklisted {text-decoration-color: yellow;} .night #video_download_dialog hr {background: linear-gradient(to right, #211f22 0%, ${c.dark} 50%, ${c.dark} 50%, #211f22 100%) !important;}
  1321. .night .radiohelper::before, .night .cbhelper::before {border: 1px solid ${c.dark};}
  1322. .night .smartybox input[type="checkbox"]:checked ~ .cbhelper::before, .night .smartybox input[type="radio"]:checked ~ .radiohelper::before {background: linear-gradient(to bottom, ${c.lightest}33 0%, ${c.dark}66 100%) !important;}`;
  1323. d.documentElement.appendChild(style);
  1324. }
  1325. },
  1326.  
  1327.  
  1328. persistTryATL: 0, buttonFound: false,
  1329.  
  1330. addThemeListeners: () => {
  1331. let toDay = qs('#day-theme a'),
  1332. toNight = qs('#night-theme a');
  1333.  
  1334. if (toDay && toNight && !BC.buttonFound) {
  1335. BC.buttonFound = true;
  1336. toDay.addEventListener('click', function(e) {
  1337. if (e.which===1)
  1338. BC.toggleSettings('usedark', false)
  1339. }, false);
  1340.  
  1341. toNight.addEventListener('click', function(e) {
  1342. if (e.which===1)
  1343. BC.toggleSettings('usedark', true)
  1344. }, false);
  1345. }
  1346. else if (BC.persistTryATL++ < 30 && !BC.buttonFound) setTimeout(BC.addThemeListeners, 1000);
  1347. },
  1348.  
  1349.  
  1350. persistTryHC: 0, showComments: null,
  1351.  
  1352. hideComments: (e) => {
  1353. let comments = qs('#comments-container');
  1354. let container = qs('#comment-frm-container');
  1355. let comment_cnt = qsa('#comments-container li.comment');
  1356. let nocomments = qs('.video-no-comments p') && !qs('.video-no-comments a');
  1357. BC.showComments = qs('#comment-frm-container > #show-comments');
  1358.  
  1359. if (nocomments || BC.showComments) return;
  1360. if (container && comments) {
  1361. if (qs('#comments-container #comment-list') === null) {
  1362. if (BC.persistTryHC++ < 60 && !BC.showComments)
  1363. setTimeout(BC.hideComments, 1000);
  1364. return
  1365. }
  1366. BC.showComments = dce("div");
  1367. BC.showComments.id = 'show-comments';
  1368. BC.showComments.innerHTML = `<span class="scripted-link">Show ${comment_cnt.length} Comment${comment_cnt.length === 1 ? '' : 's'}</span>`;
  1369. BC.showComments.style = "width:100%;height:38px;margin:0px;padding:8px;text-align:center;border-radius:5px;";
  1370. container.insertBefore(BC.showComments, comments);
  1371. comments.style.display = 'none';
  1372. BC.showComments.addEventListener('click', function(e) {
  1373. if (e.which===1) {
  1374. qs('#comments-container').style.display = 'block';
  1375. this.style.display = 'none';
  1376. }
  1377. }, false);
  1378. } else if (BC.persistTryHC++ < 30 && !BC.showComments) setTimeout(BC.hideComments, 2000);
  1379. },
  1380.  
  1381.  
  1382. fetchingMoreRecentVideos: 0,
  1383.  
  1384. addMoreRecentVideos: (offset = -1, playlist = null) => {
  1385. let data, link, xhr, sensitivity;
  1386. let extend = "",
  1387. name = null,
  1388. showall = "";
  1389.  
  1390. if (!playlist) {
  1391. if (BC.fetchingMoreRecentVideos == offset) return;
  1392. BC.fetchingMoreRecentVideos = offset;
  1393. link = qs('.details .name a');
  1394. if (link) name = link.href.match( /\/channel\/([a-z0-9_-]+)\//i );
  1395. }
  1396. else {
  1397. link = `${location.protocol}//${BC.host}/playlist/${playlist}/`;
  1398. }
  1399. sensitivity = d.cookie.match(/sensitivity=((true)|(false))/i);
  1400. if (name || playlist) {
  1401. if (scriptHandler == "Greasemonkey"){xhr = new BC.SM_XMLHttpRequest()}else{xhr = new XMLHttpRequest()}
  1402. if (playlist) {
  1403. data = `csrfmiddlewaretoken=${BC.getCsrftoken()}`;
  1404. xhr.addEventListener("load", (e) => { BC.pare(e, playlist) });
  1405. xhr.addEventListener("error", (e) => { error('XMLHttpRequest playlist videos error: '+ e) });
  1406. }
  1407. else {
  1408. if (sensitivity && sensitivity[1] == 'true') showall = '?showall=1';
  1409. if (offset >= 0) {
  1410. extend = "extend/";
  1411. offset = `&offset=${offset}`;
  1412. }
  1413. data = `csrfmiddlewaretoken=${BC.getCsrftoken()}&name=${name[1]}${offset}`;
  1414. xhr.addEventListener("load", (e) => { BC.pear(e) });
  1415. xhr.addEventListener("error", (e) => { error('XMLHttpRequest recent videos error: '+ e) });
  1416. }
  1417. xhr.open("POST", `${link}${extend}${showall}`, true);
  1418. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  1419. xhr.send(data);
  1420. }
  1421. },
  1422.  
  1423.  
  1424. SM_XMLHttpRequest: function() {
  1425. /* Used only under Greasemonkey due to lack of Referer header */
  1426. this.headers = {"Referer": BC.url}
  1427. this.listeners = {load: null, abort: null, error: null, timeout: null,
  1428. progress: null, readystatechange: null};
  1429. this.addEventListener = function(event, data) {
  1430. if (Object.keys(this.listeners).indexOf(event) !=-1)
  1431. this.listeners[event] = data
  1432. }
  1433. this.setRequestHeader = function(header, data) {
  1434. this.headers[header] = data
  1435. }
  1436. this.open = function(method, url) {
  1437. this.method = method;
  1438. this.url = url;
  1439. }
  1440. this.send = function(data = null) {
  1441. var xhr = GM.xmlHttpRequest({
  1442. data: data,
  1443. url: this.url,
  1444. method: this.method,
  1445. headers: this.headers,
  1446. onload: this.listeners.load,
  1447. onabort: this.listeners.abort,
  1448. onerror: this.listeners.error,
  1449. ontimeout: this.listeners.timeout,
  1450. onprogress: this.listeners.progress,
  1451. onreadystatechange: this.listeners.readystatechange
  1452. });
  1453. }
  1454. },
  1455.  
  1456.  
  1457. pear: (e) => {
  1458. try {
  1459. e = typeof e.target === "undefined" ? {target: e} : e;
  1460. let i, a1, title, pdate, card, cards, active,
  1461. hidden, sidebar, offset, addedoffset, showmore;
  1462. let result = JSON.parse(e.target.responseText);
  1463.  
  1464. if ('undefined' !== result.success && result.success) {
  1465. hidden = dce("div");
  1466. sidebar = qs('.sidebar-recent');
  1467. if (sidebar) {
  1468. hidden.innerHTML = result.html;
  1469. offset = qsa('.video-card', sidebar).length;
  1470. cards = qsa('.channel-videos-container', hidden);
  1471. addedoffset = cards.length;
  1472. showmore = dce("div");
  1473. showmore.className="show-more-container";
  1474. showmore.innerHTML = '<div class="show-more"><span>SHOW MORE</span></div>';
  1475. for (i = 0; i < cards.length; i++) {
  1476. a1 = qs('.channel-videos-image-container', cards[i]).innerHTML
  1477. .replace('channel-videos-image','video-card-image')
  1478. .replace(/_640x360/g,'_320x180');
  1479. title = qs('.channel-videos-title', cards[i]).innerHTML;
  1480. pdate = qs('.channel-videos-details span', cards[i]).innerText;
  1481. card = dce("div");
  1482. card.className="video-card more";
  1483. card.innerHTML = a1 + `
  1484. <div class="video-card-text">
  1485. <p class="video-card-title">${title}</p>
  1486. <p class="video-card-published">${pdate}</p>\n</div>`;
  1487. sidebar.appendChild(card);
  1488. }
  1489. active = qs('.active', sidebar);
  1490. if (active) active.classList.remove("active");
  1491. active = qs(`.video-card > a[href='${BC.path}']`, sidebar);
  1492. if (active) active.parentNode.classList.add("active");
  1493. if (offset == 6 && addedoffset == 25) {
  1494. sidebar.parentNode.appendChild(showmore);
  1495. qs('.show-more').classList.remove("hidden");
  1496. qs('.show-more').addEventListener("click", function(e){ BC.addMoreRecentVideos(offset+addedoffset+2) }, false);
  1497. }
  1498. else if (offset > 6 && addedoffset == 25 && qs('.show-more')) {
  1499. qs('.show-more').outerHTML = showmore.outerHTML;
  1500. qs('.show-more').classList.remove("hidden");
  1501. qs('.show-more').addEventListener("click", function(e){ BC.addMoreRecentVideos(offset+addedoffset+2) }, false);
  1502. }
  1503. }
  1504. }
  1505. else { error('XMLHttpRequest recent videos request error') }
  1506. BC.fetchingMoreRecentVideos = 0;
  1507. } catch (e) { error('XMLHttpRequest recent videos parsing error: '+ e) }
  1508. },
  1509.  
  1510.  
  1511. pare: (e, playlist) => {
  1512. try {
  1513. e = typeof e.target === "undefined" ? {target: e} : e;
  1514. let i, r, a, q, h, a1, br, title, name, icon, pchan, pdate,
  1515. card, active, hidden, content, sidebar, cards;
  1516. let result = JSON.parse(e.target.responseText),
  1517. index = 0,
  1518. seqId = 0,
  1519. cards2 = [];
  1520.  
  1521. if ('undefined' !== result.success && result.success) {
  1522. cards = qs('.sidebar-recent');
  1523. hidden = dce("div");
  1524. content = dce("div");
  1525. content.className = "playlist_sidebar";
  1526. sidebar = dce("h2");
  1527. sidebar.className = "sidebar-heading playlists";
  1528. cards.insertBefore(content, cards.firstChild);
  1529. cards.insertBefore(sidebar, cards.firstChild);
  1530. hidden.innerHTML = result.html;
  1531. cards = qsa('.playlist-video div.row', hidden);
  1532. title = qs('#playlist-title', hidden).innerText;
  1533. name = qs('.details .name a').innerText;
  1534. title = `${cards.length == 25 ? "First 25" : cards.length} videos from ${name}'s \"${title}\" playlist.`;
  1535. icon = qs('.sidebar-recent h2 span');
  1536. icon.setAttribute("data-original-title", title);
  1537. sidebar.innerText = "Playlist Videos";
  1538. sidebar.appendChild(icon);
  1539.  
  1540. a = BC.url.match( /\/video\/([a-z0-9_-]+)\//i )[1];
  1541. r = new RegExp (`/video/${a}/`, 'i');
  1542. for (i = 0; i < cards.length; i++) {
  1543. q = qs('.image-container a', cards[i]).href;
  1544. if (r.test(q)) index = i;
  1545. cards[i].setAttribute("id", i+1);
  1546. cards2.push(cards[i].outerHTML);
  1547. }
  1548.  
  1549. if (index !== 0) {
  1550. cards = new Array(cards2.length);
  1551. cards2.forEach( (item, i) => {
  1552. h = new DOMParser().parseFromString(item, 'text/html');
  1553. h = qs("body > div.row", h);
  1554. if (i < index) cards[cards2.length - index + i] = h;
  1555. else cards[i - index] = h;
  1556. })
  1557. }
  1558.  
  1559. for (i = 0; i < cards.length; i++) {
  1560. if (a1 = qs('.image-container', cards[i])) {
  1561. a1 = a1.innerHTML
  1562. .replace('image','video-card-image')
  1563. .replace(/_640x360/g,'_320x180')
  1564. .replace(/<\/div>/,'')
  1565. .replace(/<\/a>/,'<\/div><\/a>');
  1566. seqId = cards[i].id;
  1567. title = qs('.text-container .title', cards[i]).innerHTML;
  1568. pchan = qs('.text-container .channel', cards[i]).innerHTML;
  1569. pdate = qs('.text-container .details span', cards[i]).innerText;
  1570. q = qs('.image-container a', cards[i]).href.match( /\/video\/([a-z0-9_-]+)\//i )[1];
  1571. card = dce("div");
  1572. card.className="video-card more";
  1573. card.setAttribute("q", q);
  1574. card.innerHTML = a1 + `
  1575. <div class="video-card-text">
  1576. <p class="video-card-title">${title}</p>
  1577. <p class="video-card-channel">${pchan}</p>
  1578. <p class="video-card-published">${pdate}</p>
  1579. <span class="video-card-published sequence">${seqId}</span>\n</div>`;
  1580. content.appendChild(card);
  1581. }
  1582. }
  1583. active = qs('.active', content);
  1584. if (active) active.classList.remove("active");
  1585. active = qs(`.video-card[q='${a}']`, content);
  1586. if (active) active.classList.add("active");
  1587. setTimeout(() => {content.style.maxHeight = `${content.getBoundingClientRect().height}px`;}, 1500);
  1588. sidebar.addEventListener("click", function(e){
  1589. setTimeout(() => {e.target.classList.toggle("slidein");}, 750);
  1590. qs(".playlist_sidebar").classList.toggle("slidein");
  1591. }, false);
  1592. }
  1593. else { error('XMLHttpRequest playlist videos request error') }
  1594. BC.fetchingMoreRecentVideos = 0;
  1595. } catch (e) { error('XMLHttpRequest playlist videos parsing error: '+ e) }
  1596. },
  1597.  
  1598.  
  1599. fetchingMvplaylist: !1,
  1600. mostViewedPlaylist: {slider:null,index:0,length:0,cardWidth:function(){let o = qs('.mvplaylist').getBoundingClientRect();return (!o || !o.width ? 0 : Math.round(o.width/240))}},
  1601.  
  1602. addMostViewedPlaylist: () => {
  1603. let el, comments, parent, row, xhr, data, showall;
  1604. let link = qs('.details .name a'),
  1605. sensitivity = d.cookie.match(/sensitivity=((true)|(false))/i);
  1606.  
  1607. if (qs('.mvplaylist.row') || BC.fetchingMvplaylist) return;
  1608. else {
  1609. parent = qs('.video-container');
  1610. row = dce("div");
  1611. row.className = 'mvplaylist row';
  1612. if (comments = qs('.comments.row')) {
  1613. el = comments;
  1614. }
  1615. else {
  1616. el = parent.children[3];
  1617. el.className = 'comments row';
  1618. }
  1619. parent.insertBefore(row, el);
  1620. }
  1621. if (link) {
  1622. BC.fetchingMvplaylist = 1;
  1623. if (scriptHandler == "Greasemonkey"){xhr = new BC.SM_XMLHttpRequest()}else{xhr = new XMLHttpRequest()}
  1624. data = 'csrfmiddlewaretoken='+ BC.getCsrftoken();
  1625. showall = '';
  1626. if (sensitivity)
  1627. if (sensitivity[1] == 'true') showall = '?showall=1';
  1628.  
  1629. xhr.addEventListener("load", (e) => { BC.peer(e) });
  1630. xhr.addEventListener("error", (e) => { error('XMLHttpRequest most viewed error: '+ e) });
  1631. xhr.open("POST", link + showall, true);
  1632. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  1633. xhr.send(data);
  1634. }
  1635. },
  1636.  
  1637.  
  1638. peer: (e) => {
  1639. try {
  1640. e = typeof e.target === "undefined" ? {target: e} : e;
  1641. let i, hidden, html, active, cards, row, title, content,
  1642. arrow, slider, cardActive, cds, pos, left, right;
  1643. let result = JSON.parse(e.target.responseText);
  1644.  
  1645. if ('undefined' !== result.success && result.success) {
  1646. hidden = dce("div");
  1647. html = result.html.match(/(<div class="video-card"[^<]*?>[\s\S]*<\/div>[\s\n]*?<\/div>)[\s\n]*?<\/div>[\s\n]*?<\/div>/i);
  1648. if (html && html[1]) {
  1649. hidden.innerHTML = html[1];
  1650. active = '';
  1651. cards = qsa('.video-card', hidden);
  1652. row = qs(".mvplaylist.row");
  1653. title = dce("div");
  1654. content = dce("div");
  1655. arrow = dce("div");
  1656. slider = dce("div");
  1657. BC.mostViewedPlaylist.length = cards.length;
  1658. BC.mostViewedPlaylist.slider = slider;
  1659. title.className = 'playlist-title';
  1660. title.innerHTML = `<h2 class="sidebar-heading">Most Viewed (${cards.length})</h2>`;
  1661. row.appendChild(title);
  1662. arrow.className = 'playlistbt';
  1663. content.appendChild(arrow);
  1664. arrow = dce("div");
  1665. arrow.className = 'playlistdn playlistbtn disabled';
  1666. arrow.innerHTML = '<b>&lt;</b>';
  1667. content.style = 'width: 100%;margin: 0px;padding: 0px; overflow:hidden;display: inline-block; max-height: 205px;';
  1668. content.appendChild(arrow);
  1669. arrow = dce("div");
  1670. arrow.className = `playlistup playlistbtn${cards.length > 3 ? '' : ' disabled'}`;
  1671. arrow.innerHTML = '<b>&gt;</b>';
  1672. content.appendChild(arrow);
  1673. slider.className = 'mvslider';
  1674.  
  1675. for (i = 0; i < cards.length; i++) {
  1676. cardActive = qs(`a[href='${BC.path}']`, cards[i]);
  1677. if (cardActive) {
  1678. cds = Math.round( (row.getBoundingClientRect().width - 60) / 218 );
  1679. pos = (i > cards.length - cds) ? cards.length - cds : i;
  1680. BC.mostViewedPlaylist.slider.style.marginLeft = `-${pos * 218}px`;
  1681. if (pos > 0) qs('.playlistdn', content).classList.remove('disabled');
  1682. if (pos >= cards.length - cds) qs('.playlistup', content).classList.add('disabled');
  1683. BC.mostViewedPlaylist.index = pos;
  1684. active = ' active';
  1685. }
  1686. else active = '';
  1687. cards[i].className += ' playlist-card'+ active;
  1688. cards[i].innerHTML = cards[i].innerHTML.replace(/<\/div>(?![\s\S]*<\/div>)/, '\n<span class="video-card-published sequence">'+(i+1)+'</span>\n</div>');
  1689. slider.appendChild(cards[i]);
  1690. }
  1691. content.appendChild(slider);
  1692. row.appendChild(content);
  1693.  
  1694. left = qs('.mvplaylist .playlistdn');
  1695. left.addEventListener("click", function(e) {
  1696. if (this.classList.contains('disabled')) return false;
  1697. if (BC.mostViewedPlaylist.index > 0) {
  1698. BC.mostViewedPlaylist.slider.style.marginLeft = `-${--BC.mostViewedPlaylist.index * 218}px`;
  1699. if (BC.mostViewedPlaylist.index <= 0) this.classList.add('disabled');
  1700. if (BC.mostViewedPlaylist.index < BC.mostViewedPlaylist.length + BC.mostViewedPlaylist.cardWidth()) this.nextSibling.classList.remove('disabled');
  1701. }
  1702. });
  1703. right = qs('.mvplaylist .playlistup');
  1704. right.addEventListener("click", function(e) {
  1705. if (this.classList.contains('disabled')) return false;
  1706. if (BC.mostViewedPlaylist.index <= BC.mostViewedPlaylist.length - BC.mostViewedPlaylist.cardWidth()) {
  1707. BC.mostViewedPlaylist.slider.style.marginLeft = `-${++BC.mostViewedPlaylist.index * 218}px`;
  1708. if (BC.mostViewedPlaylist.index + BC.mostViewedPlaylist.cardWidth() >= BC.mostViewedPlaylist.length) this.classList.add('disabled');
  1709. if (BC.mostViewedPlaylist.index > 0) this.previousSibling.classList.remove('disabled');
  1710. }
  1711. });
  1712. }
  1713. }
  1714. else { error('XMLHttpRequest most viewed request error') }
  1715. BC.fetchingMvplaylist = !1;
  1716. } catch (e) { error('XMLHttpRequest most viewed parsing error: '+ e) }
  1717. },
  1718.  
  1719.  
  1720. playlists: {}, fetchingPlaylists: !1,
  1721.  
  1722. addChannelPlaylists: () => {
  1723. let xhr, data, showall;
  1724. let link = qs('.details .owner a');
  1725.  
  1726. if (qs('.playlist.row') || BC.fetchingPlaylists) return;
  1727. BC.fetchingPlaylists = 1;
  1728. if (link) {
  1729. if (scriptHandler == "Greasemonkey"){xhr = new BC.SM_XMLHttpRequest()}else{xhr = new XMLHttpRequest()}
  1730. data = 'csrfmiddlewaretoken='+ BC.getCsrftoken();
  1731. showall = '';
  1732. xhr.addEventListener("load", (e) => { BC.addPlaylist(e) });
  1733. xhr.addEventListener("error", (e) => { error('XMLHttpRequest most viewed error: '+ e) });
  1734. xhr.open("POST", link, true);
  1735. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  1736. xhr.send(data);
  1737. }
  1738. },
  1739.  
  1740.  
  1741. playAll: null,
  1742. addPlaylist: (e) => {
  1743. try {
  1744. e = typeof e.target === "undefined" ? {target: e} : e;
  1745. let link, plName, xhr, m, re, el, row, comments, parent, data;
  1746. let i = 0,
  1747. result = JSON.parse(e.target.responseText);
  1748.  
  1749. if ('undefined' !== result.success && result.success) {
  1750. parent = qs('.video-container');
  1751. data = 'csrfmiddlewaretoken='+ BC.getCsrftoken();
  1752. re = /class="playlist-card">[\n\s]+<a href="([a-zA-Z0-9\/_-]+)"[\s\S]*?(?!<\/a)+<div class="title">(.*)<\/div>/g
  1753. do {
  1754. m = re.exec(result.html);
  1755. if (m) {
  1756. if (scriptHandler == "Greasemonkey"){xhr = new BC.SM_XMLHttpRequest()}else{xhr = new XMLHttpRequest()}
  1757. plName = 'pl'+ m[1].substr(10, 12);
  1758. BC.playlists[plName] = {item:i++,slider:null,index:0,length:0,cardWidth:function(){let o = qs('#'+ plName +'.playlist').getBoundingClientRect();return (!o || !o.width ? 0 : Math.round(o.width/240))}};
  1759. row = qs('#'+plName+'.playlist.row');
  1760. if (!row) {
  1761. row = dce("div");
  1762. row.className = 'playlist row';
  1763. row.setAttribute('id', plName);
  1764. if (comments = qs('.comments.row')) {
  1765. el = comments;
  1766. }
  1767. else {
  1768. if (BC.settings.mvplaylist)
  1769. el = parent.children[4];
  1770. else el = parent.children[3];
  1771. el.className = 'comments row';
  1772. }
  1773. parent.insertBefore(row, el);
  1774. }
  1775. link = location.protocol +"//"+ BC.host + m[1];
  1776.  
  1777. xhr.addEventListener("load", (e) => { BC.pier(e) });
  1778. xhr.addEventListener("error", (e) => { error('XMLHttpRequest most viewed error: '+ e) });
  1779. xhr.open("POST", link, true);
  1780. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  1781. xhr.send(data);
  1782. }
  1783. } while (m);
  1784. }
  1785. else {
  1786. error('XMLHttpRequest profile page request error');
  1787. BC.fetchingPlaylists = !1;
  1788. }
  1789. } catch (e) { error('XMLHttpRequest profile page parsing error: '+ e) }
  1790. },
  1791.  
  1792.  
  1793. pier: (e) => {
  1794. try {
  1795. e = typeof e.target === "undefined" ? {target: e} : e;
  1796. let i, a1, pdate, link, hidden, html, card, cards, active,
  1797. showMore, plName, row, title, content, arrow, slider,
  1798. href, pos, cds, play, cardActive, left, right;
  1799. let result = JSON.parse(e.target.responseText);
  1800.  
  1801. if ('undefined' !== result.success && result.success) {
  1802. if (!BC.playAll) {
  1803. BC.playAll = result.html.match( /<span class="fa-layers">.*<\/span>/ );
  1804. if (BC.playAll) BC.playAll = BC.playAll[0].replace('<span', '<span title="Play All"')
  1805. }
  1806. link = result.canonical.match( /.*\/playlist\/([a-z0-9_-]+)\//i );
  1807. hidden = dce("div");
  1808. html = result.html.match(/(<div .* class="playlist-video"[^<]*?>[\s\S]*<\/div>[\s\n]*?<\/div>)[\s\n]*?<\/div>[\s\n]*?<\/div>/i);
  1809. if (html && html[1]) {
  1810. hidden.innerHTML = html[1];
  1811. active = '';
  1812. plName = 'pl'+ link[1];
  1813. cards = qsa('.playlist-video > div.row', hidden);
  1814. showMore = qs('.show-more-container .show-more:not(.hidden)', hidden);
  1815. row = qs("#"+ plName +'.row');
  1816. title = dce("div");
  1817. content = dce("div");
  1818. arrow = dce("div");
  1819. slider = dce("div");
  1820. BC.playlists[plName].length = cards.length;
  1821. BC.playlists[plName].slider = slider;
  1822. title.className = 'playlist-title';
  1823.  
  1824. title.innerHTML = `<h2 class="sidebar-heading">${result.title} (${cards.length})${BC.playAll}</h2>`;
  1825. row.appendChild(title);
  1826. if (BC.playAll) {
  1827. href = qs('a', cards[0]).href.match( /(\/video\/[a-z0-9_-]+\/).*/i )[1];
  1828. qs('span', title).addEventListener("click", (e) => {
  1829. w.location.href = `${href}?list=${link[1]}&randomize=false`
  1830. });
  1831. }
  1832.  
  1833. arrow.className = 'playlistbt';
  1834. content.appendChild(arrow);
  1835. arrow = dce("div");
  1836. arrow.className = `playlistdn playlistbtn ${plName} disabled`;
  1837. arrow.innerHTML = '<b>&lt;</b>';
  1838. content.style = 'width: 100%;margin: 0px;padding: 0px; overflow:hidden;display: inline-block; max-height: 205px;';
  1839. content.appendChild(arrow);
  1840. arrow = dce("div");
  1841. arrow.className = `playlistup playlistbtn ${plName + (cards.length > 3 ? '' : ' disabled')}`;
  1842. arrow.innerHTML = '<b>&gt;</b>';
  1843. content.appendChild(arrow);
  1844. slider.className = 'plslider';
  1845.  
  1846. for (i = 0; i < cards.length; i++) {
  1847. if (a1 = qs('.image-container', cards[i])) {
  1848. a1 = a1.innerHTML
  1849. .replace('image','video-card-image')
  1850. .replace(/_640x360/g,'_320x180')
  1851. .replace(/<\/div>/,'')
  1852. .replace(/<\/a>/,'<\/div><\/a>');
  1853. title = qs('.text-container .title', cards[i]).innerHTML;
  1854. pdate = qs('.text-container .details span', cards[i]).innerText;
  1855. card = dce("div");
  1856. card.className="video-card";
  1857. card.innerHTML = a1 + `
  1858. <div class="video-card-text">
  1859. <p class="video-card-title">${title}</p>
  1860. <p class="video-card-published">${pdate}</p>
  1861. <span class="video-card-published sequence">${i+1}</span>\n</div>`;
  1862.  
  1863. play = qs("a", card).getAttribute("href");
  1864. cardActive = play.indexOf(BC.path) !=-1;
  1865. if (cardActive) {
  1866. cds = Math.round( (row.getBoundingClientRect().width - 60) / 218 );
  1867. pos = (i > cards.length - cds) ? cards.length - cds : i;
  1868. BC.playlists[plName].slider.style.marginLeft = `-${pos * 218}px`;
  1869. if (pos > 0) qs(`.playlistdn.${plName}`, content).classList.remove('disabled');
  1870. if (pos >= cards.length - cds) qs(`.playlistup.${plName}`, content).classList.add('disabled');
  1871. BC.playlists[plName].index = pos;
  1872. active = ' active';
  1873. }
  1874. else active = '';
  1875. card.className += ' playlist-card'+ active;
  1876. slider.appendChild(card);
  1877. }
  1878. }
  1879. content.appendChild(slider);
  1880. row.appendChild(content);
  1881.  
  1882. left = qs('.playlistdn.'+ plName);
  1883. left.addEventListener("click", function(e) {
  1884. if (this.classList.contains('disabled')) return false;
  1885. if (BC.playlists[plName].index > 0) {
  1886. BC.playlists[plName].slider.style.marginLeft = `-${--BC.playlists[plName].index * 218}px`;
  1887. if (BC.playlists[plName].index <= 0) this.classList.add('disabled');
  1888. if (BC.playlists[plName].index < BC.playlists[plName].length + BC.playlists[plName].cardWidth()) this.nextSibling.classList.remove('disabled');
  1889. }
  1890. });
  1891. right = qs('.playlistup.'+ plName);
  1892. right.addEventListener("click", function(e) {
  1893. if (this.classList.contains('disabled')) return false;
  1894. if (BC.playlists[plName].index <= BC.playlists[plName].length - BC.playlists[plName].cardWidth()) {
  1895. BC.playlists[plName].slider.style.marginLeft = `-${++BC.playlists[plName].index * 218}px`;
  1896. if (BC.playlists[plName].index + BC.playlists[plName].cardWidth() >= BC.playlists[plName].length) this.classList.add('disabled');
  1897. if (BC.playlists[plName].index > 0) this.previousSibling.classList.remove('disabled');
  1898. }
  1899. });
  1900. }
  1901. }
  1902. else { error('XMLHttpRequest playlists request error') }
  1903. BC.fetchingPlaylists = !1;
  1904. } catch (e) { error('XMLHttpRequest playlists parsing error: '+ e) }
  1905. },
  1906.  
  1907.  
  1908. playNextFullScreenCancel: null, nextUp: {}, prevUp: [], history:-1, nextUpLoaded: false, countsRefreshed: false,
  1909.  
  1910. playNextFullScreen: (vector = null) => {
  1911. let xhr, data;
  1912. if (!BC.isFullScreen) return;
  1913. debug('playNextFullScreen typeof vector: ',typeof vector);
  1914. if (typeof vector === "object") vector = null;
  1915. const fetch = function() {
  1916. return new Promise((resolve, reject) => {
  1917. let url, link = qs('.sidebar-next .video-card a:not(#block_playnext)');
  1918.  
  1919. if (link) {
  1920. url = link.href;
  1921. if (scriptHandler == "Greasemonkey"){xhr = new BC.SM_XMLHttpRequest()}else{xhr = new XMLHttpRequest()}
  1922. data = 'csrfmiddlewaretoken='+ BC.getCsrftoken();
  1923. xhr.addEventListener("error", (e) => { error('XMLHttpRequest playNextFullScreen error: '+ e); reject() });
  1924. xhr.addEventListener("load", (res) => {
  1925. try {
  1926. let e = typeof res.target === "undefined" ? {target: res} : res;
  1927. let content, card, now = qs(".sidebar-next .video-card");
  1928. let result = JSON.parse(e.target.responseText);
  1929.  
  1930. if ('undefined' !== result.success && result.success) {
  1931. const getHistoryItem = (hidden) => {
  1932. card = qs(".sidebar-next .video-card", hidden);
  1933.  
  1934. let nextUp = {
  1935. url: url,
  1936. history: BC.history,
  1937. src: qs("video#player source", hidden).src,
  1938. type: qs("video#player source", hidden).type,
  1939. poster: qs("video#player", hidden).poster,
  1940. tracks: qsa("video#player track", hidden),
  1941. count: qs("#video-view-count", hidden).innerHTML,
  1942. likes: qs("#video-like-count", hidden).innerHTML,
  1943. dislike: qs("#video-dislike-count", hidden).innerHTML,
  1944. pdate: qs(".video-publish-date", hidden).innerText,
  1945. hashtags: qs("#video-hashtags", hidden).innerHTML,
  1946. title: qs("#page-bar #video-title", hidden).innerText,
  1947. tooltip: qs(".sidebar-next h2.sidebar-heading", hidden).innerHTML,
  1948. recent: qsa(".sidebar-recent div.video-card:not(.more)", hidden),
  1949. teaser: qs("#video-description .teaser", hidden).innerHTML,
  1950. full: qs("#video-description .full", hidden).innerHTML,
  1951. actions: qsa(".video-actions .action-list a", hidden),
  1952. appealScript: qs("#appeal-modal + script", hidden),
  1953. reportScript: qs("#report-modal + script", hidden),
  1954. link: qs('.sidebar-next .video-card a:not(#block_playnext)', card).href,
  1955. image: qs(".video-card-image img", card).getAttribute("data-src"),
  1956. views: qs(".video-card-image .video-views", card).innerText,
  1957. duration: qs(".video-card-image .video-duration", card).innerText,
  1958. text: qs(".video-card-text", card).innerHTML,
  1959. now: {
  1960. image: qs(".video-card-image img", now).getAttribute("data-src"),
  1961. arrow: qs(".video-card-image img.play-overlay", now).getAttribute("src"),
  1962. views: qs(".video-card-image .video-views", now).innerText,
  1963. duration: qs(".video-card-image .video-duration", now).innerText,
  1964. text: qs(".video-card-text", now).innerHTML
  1965. }
  1966. }
  1967. return nextUp
  1968. }
  1969. if (!BC.prevUp.length) {
  1970. BC.history += 1;
  1971. BC.prevUp.push(getHistoryItem(qs("#main-content")));
  1972. let mt = new Date(null);
  1973. mt.setSeconds(qs("video#player").duration);
  1974. mt = mt.toISOString().substr(11, 8);
  1975. while (mt[0] == "0" || mt[0] == ":") mt = mt.substr(1);
  1976. BC.prevUp[0].url = BC.url;
  1977. BC.prevUp[0].now.duration = mt;
  1978. BC.prevUp[0].now.image = qs("video#player").poster;
  1979. BC.prevUp[0].now.text = `<p class="video-card-title"><a href="/video/12345678901/">${qs("title").innerText}</a></p><p class="video-card-published">${qs(".video-publish-date b").innerText}</p>`;
  1980. BC.prevUp[0].now.views = qs("#video-view-count").innerText;
  1981. }
  1982. BC.history += 1;
  1983. content = dce("div");
  1984. content.innerHTML = result.html;
  1985. BC.nextUp = getHistoryItem(content);
  1986. BC.prevUp.push(BC.nextUp);
  1987. resolve()
  1988. } else reject()
  1989. } catch (e) { error('XMLHttpRequest playNextFullScreen parsing error: '+ e); reject() }
  1990. });
  1991. xhr.open("POST", url, true);
  1992. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  1993. xhr.send(data);
  1994. }
  1995. });
  1996. }
  1997. const render = (dir, next) => {
  1998. let path,
  1999. card = qs(".sidebar-next .video-card");
  2000. BC.closeItem("details");
  2001. if (qs("#video-view-count")) qs("#video-view-count").setAttribute("id", "fs-view-count");
  2002. if (qs("#video-like-count")) qs("#video-like-count").setAttribute("id", "fs-like-count");
  2003. if (qs("#video-dislike-count")) qs("#video-dislike-count").setAttribute("id", "fs-dislike-count");
  2004. qs("#fs-view-count").innerHTML = next.count;
  2005. qs("#fs-like-count").innerHTML = next.likes;
  2006. qs("#fs-dislike-count").innerHTML = next.dislike;
  2007. qs(".video-publish-date").innerHTML = next.pdate;
  2008. qs("#video-hashtags").innerHTML = next.hashtags;
  2009. if (qs("#disqus_thread")) qs("#disqus_thread").innerHTML = "";
  2010. qs(".sidebar-next h2.sidebar-heading").innerHTMl = next.tooltip;
  2011. qs("#video-description .teaser").innerHTML = next.teaser;
  2012. qs("#video-description .full").innerHTML = next.full;
  2013. qs(".video-actions .action-list").innerHTML = "";
  2014. next.actions.forEach((o) => {qs(".video-actions .action-list").appendChild(o)});
  2015.  
  2016. qsa(".sidebar-recent div.video-card").forEach((o) => {o.parentNode.removeChild(o)});
  2017. next.recent.forEach((o) => {qs(".sidebar-recent").appendChild(o)});
  2018.  
  2019. qs("a:not(#block_playnext)", card).href = next.link;
  2020. qs(".video-card-title a", card).href = next.link;
  2021. qs(".video-card-image img", card).classList.replace("lazyloaded", "lazyload");
  2022. qs(".video-card-image img", card).setAttribute("src", next.image);
  2023. qs(".video-card-image img", card).setAttribute("data-src", next.image);
  2024. qs(".video-card-image img.play-overlay", card).setAttribute("src", next.now.arrow);
  2025. qs(".video-card-image .video-views", card).innerText = next.views;
  2026. qs(".video-card-image .video-duration", card).innerText = next.duration;
  2027. qs(".video-card-text", card).innerHTML = next.text;
  2028. qs("#main-content").replaceChild(next.appealScript, qs("#appeal-modal + script"));
  2029. qs("#main-content").replaceChild(next.reportScript, qs("#report-modal + script"));
  2030. BC.player.api.pause();
  2031. BC.player.api.removeAttribute("autoplay");
  2032. let source = dce("source");
  2033. source.setAttribute("src", next.src);
  2034. source.setAttribute("type", next.type);
  2035. qs("video#player").setAttribute("poster", next.poster);
  2036. qs(".plyr__poster").style.backgroundImage = `url("${next.poster}")`;
  2037. if (BC.player.api.readyState < 2) qs(".plyr").classList.add("plyr--stopped");
  2038. BC.player.api.replaceChild(source, qs("video#player source"));
  2039. qsa("video#player track").forEach((o) => {qs("video#player").removeChild(o)});
  2040. next.tracks.forEach((o) => {qs("video#player").appendChild(o)});
  2041. BC.player.api.load();
  2042. qs("#page-bar #video-title").innerText = next.title;
  2043. path = next.url.match( /(\/video\/[a-z0-9_-]+\/).*/i )[1];
  2044.  
  2045. if (path != BC.path) {
  2046. if (!BC.countsRefreshed) {
  2047. /* Push the page at going fullscreen */
  2048. debug("FullScreen Pushing History 0");
  2049. w.history.pushState({'url': BC.url}, '', BC.url);
  2050. }
  2051. /* Cant pop history states any longer, so just use the next state repeatedly - forward & back */
  2052. debug("FullScreen Replacing History "+ (BC.history));
  2053. w.history.replaceState({'url': next.url}, '', next.url);
  2054. d.title = next.title;
  2055. setTimeout((path)=>{BC.countsRefreshed = false; BC.refreshCounts(path)}, 1000, path);
  2056. BC.nextUpLoaded = false;
  2057. BC.nextUp = {}
  2058. }
  2059. }
  2060. const playsoon = () => {
  2061. let details = qs(".plyr #autoplay-details");
  2062. let detail2 = qs(".wrapper > #autoplay-details");
  2063. if (!details) {
  2064. details = detail2.cloneNode(true);
  2065. qs(".wrapper .plyr").appendChild(details);
  2066. details.addEventListener('click', (e) => {
  2067. e.preventDefault();
  2068. e.stopPropagation();
  2069. BC.closeItem("details");
  2070. return false
  2071. }, false);
  2072. }
  2073. details.classList.remove('hidden');
  2074. qs('.timer .spinner', details).classList.add('animate');
  2075. qs('.timer .filler', details).classList.add('animate');
  2076. qs('.timer .mask', details).classList.add('animate');
  2077. detail2.classList.add("hidden");
  2078. qs('.timer .spinner', detail2).classList.remove('animate');
  2079. qs('.timer .filler', detail2).classList.remove('animate');
  2080. qs('.timer .mask', detail2).classList.remove('animate');
  2081. BC.playNextFullScreenCancel = setTimeout(() => {
  2082. if (BC.playNextFullScreenCancel)
  2083. render("nextUp", BC.nextUp);
  2084. else BC.closeItem("details");
  2085. }, 4800)
  2086. }
  2087. const prevnext = () => {
  2088. let apn;
  2089. let nu = BC.nextUp.now;
  2090. let pu = (("undefined" === typeof BC.prevUp[BC.history-2]) ? null : BC.prevUp[BC.history-2].now);
  2091. let html = `${((!pu) ? "" : `<div class="video-card autoplay-prev" title="Play Previous Video">
  2092. <a class="previmg" href="#"><div class="video-card-image">
  2093. <img class="img-responsive" src="${pu.image}" alt="play previous">
  2094. <img class="img-responsive play-overlay" src="${pu.arrow}" alt="prev">
  2095. <span class="video-views"><svg class="svg-inline--fa fa-eye fa-w-18" aria-hidden="true" focusable="false" data-prefix="far" data-icon="eye" role="img" `+
  2096. `xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" data-fa-i2svg=""><path fill="currentColor" d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 `+
  2097. `0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 `+
  2098. `32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 `+
  2099. `112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"></path></svg><!-- <i class="far fa-eye"></i> --> ${pu.views}</span>
  2100. <span class="video-duration">${pu.duration}</span></div></a>
  2101. <div class="video-card-text">${pu.text.replace(/\/video\/[\w_-]+\//i, "#").replace(/<a href="\/channel.*>(.+)<\/a>/i,"$1")}</div></div>`)}
  2102. <div class="video-card autoplay-next" title="Play Next Video">
  2103. <a class="nextimg" href="#"><div class="video-card-image">
  2104. <img class="img-responsive" src="${nu.image}" alt="play next">
  2105. <img class="img-responsive play-overlay" src="${nu.arrow}" alt="next">
  2106. <span class="video-views"><svg class="svg-inline--fa fa-eye fa-w-18" aria-hidden="true" focusable="false" data-prefix="far" data-icon="eye" role="img" `+
  2107. `xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" data-fa-i2svg=""><path fill="currentColor" d="M288 144a110.94 110.94 0 0 0-31.24 5 55.4 55.4 0 `+
  2108. `0 1 7.24 27 56 56 0 0 1-56 56 55.4 55.4 0 0 1-27-7.24A111.71 111.71 0 1 0 288 144zm284.52 97.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 `+
  2109. `32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400c-98.65 0-189.09-55-237.93-144C98.91 167 189.34 `+
  2110. `112 288 112s189.09 55 237.93 144C477.1 345 386.66 400 288 400z"></path></svg><!-- <i class="far fa-eye"></i> --> ${nu.views}</span>
  2111. <span class="video-duration">${nu.duration}</span></div></a>
  2112. <div class="video-card-text">${nu.text.replace(/\/video\/[\w_-]+\//i, "#").replace(/<a href="\/channel.*>(.+)<\/a>/i,"$1")}</div></div>`;
  2113. apn = qs("#autoplay-next");
  2114. apn.innerHTML = html;
  2115. if (pu) qs(".previmg", apn).addEventListener('click', (e) => {e.stopPropagation();e.preventDefault();BC.playNextFullScreen(BC.history-2)}, false);
  2116. if (pu) qs(".autoplay-prev .video-card-text a", apn).addEventListener('click', (e) => {e.stopPropagation();e.preventDefault();BC.playNextFullScreen(BC.history-2)}, false);
  2117. qs(".nextimg", apn).addEventListener('click', (e) => {e.stopPropagation();e.preventDefault();BC.playNextFullScreen(-1)}, false);
  2118. qs(".autoplay-next .video-card-text a", apn).addEventListener('click', (e) => {e.stopPropagation();e.preventDefault();BC.playNextFullScreen(-1)}, false);
  2119. apn.classList.remove("hidden")
  2120. }
  2121. if (vector === null && BC.settings.playnext) {
  2122. /* video ended
  2123. * use next data if fetched, else
  2124. * get next data, show playing soon & prev/next */
  2125. playsoon();
  2126. if (!BC.nextUpLoaded) {
  2127. fetch().then( () => { prevnext() })
  2128. .catch( (err) => { error("playnext fetch Failed: "+err) });
  2129. } else prevnext()
  2130. }
  2131. else if (vector || vector === 0) {
  2132. /* background pre-auto load */
  2133. if (vector == "load" && !BC.nextUpLoaded) {
  2134. /* view previous was clicked */
  2135. if ("undefined" !== typeof BC.prevUp[BC.history+1]) {
  2136. BC.nextUp = BC.prevUp[BC.history+1];
  2137. BC.nextUpLoaded = true;
  2138. BC.history += 1;
  2139. /* fetch next */
  2140. } else {
  2141. BC.nextUpLoaded = true;
  2142. fetch().then( () => {
  2143. }).catch( (err) => {
  2144. error("load playnext fetch Failed: "+err);
  2145. BC.nextUpLoaded = false;
  2146. });
  2147. }
  2148. /* render next video */
  2149. } else if (vector == -1) {render("nextUp", BC.nextUp);
  2150. /* render previous video */
  2151. } else {
  2152. BC.history = vector;
  2153. render("prevUp", BC.prevUp[vector]);
  2154. }
  2155. }
  2156. /* display only prev/next videos - no timer */
  2157. else {
  2158. if (!BC.nextUpLoaded) {
  2159. fetch().then( () => { prevnext() })
  2160. .catch( (val) => { error("prevnext fetch Failed") });
  2161. } else prevnext()
  2162. }
  2163. },
  2164.  
  2165.  
  2166. onPlayerProgress: (e) => {
  2167. if (BC.isFullScreen && !BC.nextUpLoaded) {
  2168. let t = BC.player.api.currentTime, l = BC.player.api.duration;
  2169. if(t && l && t/l > 0.00005) {
  2170. BC.playNextFullScreen("load");
  2171. }
  2172. }
  2173. },
  2174.  
  2175.  
  2176. closeItem: (item) => {
  2177. if (item == "details") {
  2178. if (BC.playNextFullScreenCancel) {clearTimeout(BC.playNextFullScreenCancel); BC.playNextFullScreenCancel = null}
  2179. if (item = qs('.plyr #autoplay-details')) {
  2180. item.classList.add('hidden');
  2181. qs('.timer .spinner', item).classList.remove('animate');
  2182. qs('.timer .filler', item).classList.remove('animate');
  2183. qs('.timer .mask', item).classList.remove('animate');
  2184. }
  2185. }
  2186. },
  2187.  
  2188.  
  2189. hideAutoplayNext: (e) => {
  2190. if (qs(".plyr #autoplay-next")) {qs(".plyr #autoplay-next").classList.add("hidden")};
  2191. },
  2192.  
  2193.  
  2194. thumbas: null, thumbPlayerUrl: "", thumbs: [], thumbsLoaded: false, thWidth: 0,
  2195.  
  2196. getVideoThumbnails: (e) => {
  2197. let src, context,
  2198. vid = e.target,
  2199. seconds = vid.duration;
  2200. if (vid.id == "thumbPlayer") {
  2201. BC.thumbas = dce("canvas");
  2202. BC.thumbas.style.display = "none";
  2203. BC.thWidth = Math.ceil(vid.videoWidth / vid.videoHeight * 90);
  2204. BC.thumbas.width = BC.thWidth;
  2205. BC.thumbas.height = 90;
  2206. vid.currentTime = 0.0333;
  2207. }
  2208. else {
  2209. BC.thumbs = [];
  2210. BC.thumbsLoaded = false;
  2211. src = dce("source");
  2212. src.src = qs("source", vid).src;
  2213. BC.thumbPlayerUrl = src.src;
  2214. debug("GetVideoThumbnails: "+ src.src);
  2215. vid = dce("video");
  2216. vid.style.display = "none";
  2217. vid.setAttribute("width", BC.thWidth);
  2218. vid.setAttribute("height", "90");
  2219. vid.setAttribute("muted", "muted");
  2220. vid.setAttribute("id", "thumbPlayer");
  2221. vid.setAttribute("crossOrigin", "Anonymous");/* for webkit CORS */
  2222. vid.addEventListener('seeked', function(e) {
  2223. let secs, vid = e.target;
  2224. if (BC.thumbPlayerUrl == e.target.currentSrc) {
  2225. context = BC.thumbas.getContext("2d");
  2226. context.drawImage(vid, 0, 0, BC.thumbas.width, 90);
  2227. let img = new Image();
  2228. img.src = BC.thumbas.toDataURL();
  2229. BC.thumbs.push(img);
  2230. secs = vid.currentTime + vid.duration/75;
  2231. if (secs < vid.duration) vid.currentTime = secs;
  2232. else {
  2233. BC.thumbPlayerUrl = "";
  2234. BC.thumbas = null;
  2235. }
  2236. if (BC.thumbs.length > 6 && !BC.thumbsLoaded) {
  2237. if (qs('#bcframes')) qs('#bcframes').parentNode.removeChild(qs('#bcframes'));
  2238. qs(".plyr__progress").addEventListener('mousemove', function(e) {
  2239. var fr = (BC.isFullScreen) ? qs(".plyr #bcframes") : qs("body > #bcframes");
  2240. var rect = e.target.getBoundingClientRect();
  2241. var key = Math.round(75 / rect.width * (e.pageX - rect.left));
  2242. var x = w.innerWidth || d.documentElement.clientWidth || d.body.clientWidth;
  2243. key = parseInt((key < 0) ? 0 : (key > 74) ? 74 : key);
  2244. var left = (e.pageX - (BC.thWidth / 2) < 20) ? 20 :
  2245. (e.pageX + (BC.thWidth + 20) > x) ? x - (BC.thWidth + 20): e.pageX - (BC.thWidth / 2);
  2246. if (!fr) {
  2247. fr = dce("div");
  2248. fr.id = "bcframes";
  2249. fr.style.width = BC.thWidth +'px';
  2250. if (BC.isFullScreen) qs(".wrapper .plyr").appendChild(fr);
  2251. else qs("body").appendChild(fr);
  2252. }
  2253. if (BC.isFullScreen) fr.style.top = (w.innerHeight - 170) +'px';
  2254. else fr.style.top = (w.pageYOffset + rect.top - 130) +'px';
  2255. fr.style.left = left +'px';
  2256. fr.classList.add("visible");
  2257. if ("undefined" !== typeof BC.thumbs[key]) {
  2258. if (fr.firstChild) fr.replaceChild(BC.thumbs[key], fr.firstChild);
  2259. else fr.appendChild(BC.thumbs[key])
  2260. } else if (fr.firstChild) fr.removeChild(fr.firstChild)
  2261. })
  2262. qs(".plyr__progress").addEventListener('mouseleave', function(e) {
  2263. if (BC.isFullScreen && qs(".plyr #bcframes")) qs(".plyr #bcframes").classList.remove("visible");
  2264. if (qs("body > #bcframes")) qs("body > #bcframes").classList.remove("visible")
  2265. })
  2266. BC.thumbsLoaded = true
  2267. }
  2268. }
  2269. }, false);
  2270. vid.addEventListener('error', (err) => {error(" Error: "+ err)}, false);
  2271. vid.addEventListener('loadedmetadata', BC.getVideoThumbnails, false);
  2272. vid.appendChild(src);
  2273. vid.load()
  2274. }
  2275. },
  2276.  
  2277.  
  2278. takeScreenShot: (e) => {
  2279. if (qs("#takeScreenShot").classList.contains("disabled")) return;
  2280. qs("#takeScreenShot").classList.add("disabled");
  2281. let updateURL = (canv) => {
  2282. canv.toBlob(function(blob) {
  2283. let href = qs("#objectURL").href;
  2284. (w.URL || w.webkitURL || w).revokeObjectURL(href);
  2285. qs("#objectURL").href = (w.URL || w.webkitURL || w).createObjectURL(blob)
  2286. })
  2287. },
  2288. flipImg = (vert = false) => {
  2289. let ctxs, ctxd, canv = dce("canvas");
  2290. canv.width = canvas.width;
  2291. canv.height = canvas.height;
  2292. ctxs = canv.getContext("2d");
  2293. ctxs.drawImage(canvas, 0, 0);
  2294. ctxd = canvas.getContext("2d");
  2295. ctxd.save();
  2296. if (vert) {
  2297. ctxd.translate(0, canvas.height);
  2298. ctxd.scale(1, -1);
  2299. }
  2300. else {
  2301. ctxd.translate(canvas.width, 0);
  2302. ctxd.scale(-1, 1);
  2303. }
  2304. ctxd.drawImage(canv, 0, 0);
  2305. ctxd.restore();
  2306. updateURL(canvas);
  2307. }
  2308. let a, tm, div, blob, save,
  2309. context, close, action,
  2310. canvasOffsetLeft,
  2311. editInfo, canvasOffsetTop,
  2312. vidRotateAngle = 0,
  2313. vidScaleFactor = 1,
  2314. vid = BC.player.api,
  2315. time = vid.currentTime,
  2316. width = vid.videoWidth,
  2317. height = vid.videoHeight,
  2318. title = Bcd.flattenTitle(d.title),
  2319. canvas = dce("canvas"),
  2320. editInfoTitle = ["(Save), reorient (⏎⇔⇕⚫), crop & enlarge (✂), or (Close)",
  2321. "Go back (⇦) to save or close, drag widget border to desired size to crop (✂), or set zoom slider and enlarge (⤧)"];
  2322.  
  2323. canvas.id = "canvas";
  2324. canvas.width = width;
  2325. canvas.height = height;
  2326. context = canvas.getContext("2d");
  2327. context.drawImage(vid, 0, 0, width, height);
  2328. canvas.toBlob(function(blob) {
  2329. let mmactionT, mmactionL, actionT, actionL;
  2330. tm = new Date(null);
  2331. tm.setSeconds(time);
  2332. tm = tm.toISOString().substr(11, 8);
  2333. //while (tm[0] == "0" || tm[0] == ":") tm = tm.substr(1);/* Truncate leading zeros & colons */
  2334. div = dce("div");
  2335. div.setAttribute("id", "screenshot");
  2336. div.addEventListener('click', (e) => {
  2337. e.stopPropagation();
  2338. if (!qs("#overlay")) {
  2339. let delay = (e.target.tagName === "A") ? 5000 : 500;
  2340. setTimeout((url) => {
  2341. (w.URL || w.webkitURL || w).revokeObjectURL(url);
  2342. qs("body").removeChild(qs("#screenshot"));
  2343. qs("#takeScreenShot").classList.remove("disabled");
  2344. }, delay, qs("#objectURL").href);
  2345. qs("#screenshot").style.opacity = 0
  2346. }
  2347. }, false);
  2348. div.appendChild(canvas);
  2349. editInfo = dce("div");
  2350. editInfo.id = "editInfo";
  2351. editInfo.innerText = "!";
  2352. editInfo.className = "edit";
  2353. editInfo.setAttribute('data', editInfoTitle[0]);
  2354. div.appendChild(editInfo);
  2355.  
  2356. action = dce("div");
  2357. action.className = "action";
  2358. action.style.cursor = "move";
  2359. action.addEventListener("click", function(e) {
  2360. if (!e.target.classList.contains('cancel') && e.target.id != 'objectURL') {
  2361. e.stopPropagation(); return false}
  2362. });
  2363. action.addEventListener("mousedown", function(e) {
  2364. e.stopPropagation();
  2365. if (e.which !== 1) return;
  2366. actionT = action.offsetTop;
  2367. actionL = action.offsetLeft;
  2368. mmactionT = e.clientY;
  2369. mmactionL = e.clientX;
  2370.  
  2371. function onActionMove(e) {
  2372. actionL = actionL + e.clientX - mmactionL;
  2373. actionT = actionT + e.clientY - mmactionT;
  2374. if (actionL < 20) actionL = 20;
  2375. else if (actionL > w.innerWidth - 248) actionL = w.innerWidth - 248;
  2376. if (actionT < 20) actionT = 20;
  2377. else if (actionT > w.innerHeight - 90) actionT = w.innerHeight - 90;
  2378. mmactionL = e.clientX;
  2379. mmactionT = e.clientY;
  2380. action.style.left = actionL +'px';
  2381. action.style.top = actionT +'px';
  2382. }
  2383. function onActionMouseUp(e) {
  2384. d.removeEventListener("mouseup", onActionMouseUp, false);
  2385. d.removeEventListener("mousemove", onActionMove, false);
  2386. }
  2387. d.addEventListener("mouseup", onActionMouseUp, false);
  2388. d.addEventListener("mousemove", onActionMove, false);
  2389. });
  2390. close = dce("div");
  2391. close.className = "cancel";
  2392. close.innerText = "Close";
  2393. action.appendChild(close);
  2394. save = dce("div");
  2395. save.className = "save";
  2396. a = d.createElementNS("http://www.w3.org/1999/xhtml", "a");
  2397. a.setAttribute("download", `${title}_(at ${tm}).png`);
  2398. a.setAttribute("id", "objectURL");
  2399. a.setAttribute("type", "image/png");
  2400. a.innerText = "Save";
  2401. a.setAttribute("href", (w.URL || w.webkitURL || w).createObjectURL(blob));
  2402. save.appendChild(a);
  2403. action.appendChild(save);
  2404.  
  2405. let rotateR = dce("div");
  2406. rotateR.className = "edit one rotate-r";
  2407. rotateR.innerText = "⏎";
  2408. rotateR.title = "Rotate Clockwise";
  2409. rotateR.addEventListener("click", function(e) {
  2410. e.stopPropagation();
  2411. let ctxs, ctxd, canv = dce("canvas");
  2412. vidRotateAngle += 90;
  2413. canv.width = canvas.width;
  2414. canv.height = canvas.height;
  2415. ctxs = canv.getContext("2d");
  2416. ctxs.drawImage(canvas, 0, 0);
  2417. canvas.width = canv.height;
  2418. canvas.height = canv.width;
  2419. ctxd = canvas.getContext("2d");
  2420. ctxd.save();
  2421. ctxd.clearRect(0, 0, canvas.width, canvas.height);
  2422. ctxd.translate(canvas.width, 0);
  2423. ctxd.rotate(90 * Math.PI / 180);
  2424. ctxd.drawImage(canv, 0, 0);
  2425. ctxd.restore();
  2426. updateURL(canvas);
  2427. });
  2428. action.appendChild(rotateR);
  2429.  
  2430. let flipH = dce("div");
  2431. flipH.className = "edit one flip-h";
  2432. flipH.innerText = "⇔";
  2433. flipH.title = "Flip Horizontally";
  2434. flipH.addEventListener("click", function(e) {
  2435. e.stopPropagation();
  2436. flipImg()
  2437. });
  2438. action.appendChild(flipH);
  2439.  
  2440. let flipV = dce("div");
  2441. flipV.className = "edit one flip-v";
  2442. flipV.innerText = "⇕";
  2443. flipV.title = "Flip Vertically";
  2444. flipV.addEventListener("click", function(e) {
  2445. e.stopPropagation();
  2446. flipImg(true)
  2447. });
  2448. action.appendChild(flipV);
  2449.  
  2450. let greyscale = dce("div");
  2451. greyscale.className = "edit one greyscale";
  2452. greyscale.innerText = "⚫";
  2453. greyscale.style.color = "#444141";
  2454. greyscale.title = "Black and White";
  2455. greyscale.addEventListener("click", function(e) {
  2456. e.stopPropagation();
  2457. e.target.classList.add("disabled");
  2458. let ctxs = canvas.getContext("2d"),
  2459. imgData = ctxs.getImageData(0, 0, canvas.width, canvas.height),
  2460. pxData = imgData.data;
  2461. ctxs.save();
  2462. for(var x = 0; x < pxData.length; x+=4) {
  2463. var r = pxData[x],
  2464. g = pxData[x + 1],
  2465. b = pxData[x + 2],
  2466. gr = r * .3 + g * .59 + b * .11;
  2467. pxData[x] = gr;
  2468. pxData[x + 1] = gr;
  2469. pxData[x + 2] = gr;
  2470. }
  2471. ctxs.putImageData(imgData, 0, 0);
  2472. ctxs.restore();
  2473. updateURL(canvas);
  2474. });
  2475. action.appendChild(greyscale);
  2476.  
  2477. let cropper = dce("div");
  2478. cropper.className = "edit one cropmenu";
  2479. cropper.innerText = "✂";
  2480. cropper.title = "Crop and Enlarge";
  2481. cropper.addEventListener("click", function(e) {
  2482. e.stopPropagation(); showResize();
  2483. editInfo.setAttribute('data', editInfoTitle[1]);
  2484. qs("#screenshot .action").classList.toggle("gone");
  2485. qs("#screenshot .action .save").classList.toggle("gone");
  2486. qs("#screenshot .action .cancel").classList.toggle("gone");
  2487. qsa("#screenshot .action .edit.one, #screenshot .action .edit.two")
  2488. .forEach((o) => {o.classList.toggle("gone")})
  2489. });
  2490. action.appendChild(cropper);
  2491.  
  2492.  
  2493. let acancel = dce("div");
  2494. acancel.className = "edit two acancel gone";
  2495. acancel.innerText = "⇦";
  2496. acancel.style.left = '16px';
  2497. acancel.title = "Return to previous editor";
  2498. acancel.addEventListener("click", function(e) {
  2499. e.stopPropagation(); closeEditor();
  2500. });
  2501. action.appendChild(acancel);
  2502.  
  2503. let aresize = dce("div");
  2504. aresize.className = "edit two aresize disabled gone";
  2505. aresize.innerText = "✂";
  2506. aresize.style.left = '50px';
  2507. aresize.title = "Crop image to selection";
  2508. aresize.addEventListener("click", function(e) {
  2509. e.stopPropagation(); doCrop(e)
  2510. });
  2511. action.appendChild(aresize);
  2512.  
  2513. let azoom = dce("div");
  2514. azoom.className = "edit two azoom disabled gone";
  2515. azoom.innerText = "⤧";
  2516. azoom.style.left = '84px';
  2517. azoom.style.lineHeight = "1.3em";
  2518. azoom.title = "Enlarge image to 150% at selection";
  2519. azoom.addEventListener("click", function(e) {
  2520. e.stopPropagation(); doZoom(e)
  2521. });
  2522. action.appendChild(azoom);
  2523.  
  2524. let zoomfactor = dce("input");
  2525. zoomfactor.className = "edit two zoomfactor gone";
  2526. zoomfactor.setAttribute("id", "zoomfactor");
  2527. zoomfactor.setAttribute("type", "range");
  2528. zoomfactor.setAttribute("disabled", "");
  2529. zoomfactor.setAttribute("min", "150");
  2530. zoomfactor.setAttribute("max", "300");
  2531. zoomfactor.setAttribute("step", "50");
  2532. zoomfactor.setAttribute("value", "150");
  2533. zoomfactor.setAttribute("autocomplete", "off");
  2534. zoomfactor.title = "Enlarge image to 150% at selection";
  2535. zoomfactor.addEventListener("change", function(e) {
  2536. let zoomTitle = 'Enlarge image to '+e.target.value+'% at selection';
  2537. e.target.title = zoomTitle;
  2538. qs('.action .azoom').title = zoomTitle
  2539. });
  2540. action.appendChild(zoomfactor);
  2541.  
  2542. div.appendChild(action);
  2543. qs("body").appendChild(div);
  2544.  
  2545. var closeEditor = () => {
  2546. if (qs("#overlay")) {
  2547. cropMenu = null;
  2548. qs("#screenshot").removeChild(qs("#overlay"));
  2549. }
  2550. editInfo.setAttribute('data', editInfoTitle[0]);
  2551. qs("#shader").style.backgroundSize = "100% 0px, 100% 0px, 0px 100%, 0px 100%";
  2552. qs("#shader").classList.add("transparent");
  2553. qs("#screenshot .azoom").classList.add("disabled");
  2554. qs("#screenshot .aresize").classList.add("disabled");
  2555. qs("#screenshot #zoomfactor").classList.add("disabled");
  2556. qs("#screenshot #zoomfactor").setAttribute("disabled",'');
  2557. qs("#screenshot .action").classList.toggle("gone");
  2558. qs("#screenshot .action .save").classList.toggle("gone");
  2559. qs("#screenshot .action .cancel").classList.toggle("gone");
  2560. qsa("#screenshot .action .edit.one, #screenshot .action .edit.two")
  2561. .forEach((o) => {o.classList.toggle("gone")})
  2562. }
  2563. var resizer = {}, cropMenu = null;
  2564. var doCrop = (e, f = 1) => {
  2565. e.stopPropagation();
  2566. closeEditor();
  2567. qs("#shader").classList.add("transparent");
  2568. let ctxs, ctxd, canv = dce("canvas");
  2569. let r = resizer.now;
  2570. let dw = Math.round(f * Math.round(r.w/ r.sf)),
  2571. dh = Math.round(f * Math.round(r.h/ r.sf)),
  2572. sl = Math.round(r.l/ r.sf), st = Math.round(r.t/ r.sf),
  2573. sw = Math.round(r.w/ r.sf), sh = Math.round(r.h/ r.sf);
  2574. canv.width = dw;
  2575. canv.height = dh;
  2576. ctxs = canv.getContext("2d");
  2577. ctxs.drawImage(canvas,sl,st,sw,sh,0,0,dw,dh);
  2578. canvas.width = dw;
  2579. canvas.height = dh;
  2580. ctxd = canvas.getContext("2d");
  2581. ctxd.save();
  2582. ctxd.drawImage(canv, 0, 0);
  2583. ctxd.restore();
  2584. updateURL(canvas);
  2585. cropper.classList.add("disabled");
  2586. }
  2587. var doZoom = (e) => {doCrop(e, Math.round(qs("#zoomfactor").value / 100));}
  2588. var displayCoords = (set = 0) => {
  2589. if (set) {setResizerNow()};
  2590. let dspT = Math.max(Math.round(resizer.now.t / resizer.now.sf), 0),
  2591. dspL = Math.max(Math.round(resizer.now.l / resizer.now.sf), 0),
  2592. dspW = Math.max(Math.round(Math.min(resizer.now.w, vid.videoWidth) / resizer.now.sf), 160),
  2593. dspH = Math.max(Math.round(Math.min(resizer.now.h, vid.videoHeight) / resizer.now.sf), 90),
  2594. dspS = Math.round(resizer.now.sf * 1000) / 10;
  2595. qs("#overlay .cropper.ct").innerText = `${dspL}, ${dspT} (${dspW} x ${dspH})`;
  2596. qs("#overlay .cropper.cb").innerText = `(${dspS}%)`
  2597. }
  2598. var setResizerNow = (el = dragger) => {
  2599. let offsetTop, offsetLeft, coords = el.getBoundingClientRect();
  2600.  
  2601. if ("undefined" === typeof resizer.now) {
  2602. offsetTop = coords.top;
  2603. offsetLeft = coords.left;
  2604. if ((vidRotateAngle % 180) === 0)
  2605. vidScaleFactor = coords.width / vid.videoWidth;
  2606. else
  2607. vidScaleFactor = coords.width / vid.videoHeight;
  2608. } else {
  2609. offsetTop = canvasOffsetTop;
  2610. offsetLeft = canvasOffsetLeft;
  2611. }
  2612. resizer.now = {l: coords.left - offsetLeft, t: coords.top - offsetTop, w: coords.width, h: coords.height,
  2613. r: coords.right - offsetLeft, b: coords.bottom - offsetTop, sf: vidScaleFactor};
  2614. }
  2615. var showResize = function(e) {
  2616. if (!qs("#overlay")) {
  2617. /* create resizable overlay */
  2618. setResizerNow(canvas);
  2619. canvasOffsetTop = (qs('#screenshot').getBoundingClientRect().height/ 2) - (resizer.now.h/ 2);
  2620. canvasOffsetLeft = qs('#canvas').getBoundingClientRect().left;
  2621. var tabSizeX = 22, tabSizeY = 22;
  2622. var dragger, limits, isProp = 0, stops = {};
  2623. var overlay = dce("div");
  2624. overlay.id = "overlay";
  2625. overlay.className = "drag_resize active";
  2626. overlay.innerHTML = `
  2627. <table class="drag_resize_overlay">
  2628. <tbody><tr><td class="corner tl">&nbsp;</td><td class="cropper ct">&nbsp;</td><td class="corner tr">&nbsp;</td></tr>
  2629. <tr><td class="cropper cl">&nbsp;</td><td id="dragger" class="dragger">&nbsp;</td><td class="cropper cr">&nbsp;</td></tr>
  2630. <tr><td class="corner bl">&nbsp;</td><td class="cropper cb">&nbsp;</td><td class="corner br">&nbsp;</td></tr></tbody>
  2631. </table>`;
  2632.  
  2633. qs("#screenshot").insertBefore(overlay, qs("#screenshot .action"));
  2634. if (!qs("#screenshot #shader")) {
  2635. let shader = dce("div");
  2636. shader.id = "shader";
  2637. shader.className = "transparent";
  2638. shader.style.top = canvasOffsetTop +'px';
  2639. shader.style.width = resizer.now.w +'px';
  2640. shader.style.height = resizer.now.h +'px';
  2641. shader.style.left = canvas.offsetLeft +'px';
  2642. qs("#screenshot").insertBefore(shader, overlay);
  2643. }
  2644. if (isChrome) {
  2645. tabSizeX = qs(".cropper.cl", overlay).getBoundingClientRect().width//+0.83;
  2646. tabSizeY = qs(".cropper.ct", overlay).getBoundingClientRect().height//-0.2378;
  2647. }
  2648. limits = {l: 0, t: 0, w: resizer.now.w, h: resizer.now.h};
  2649. overlay.style = `left: 0px; top: 0px; margin-left: ${canvasOffsetLeft - tabSizeX}px; margin-top: ${canvasOffsetTop - tabSizeY}px;`;
  2650. dragger = qs("#overlay .dragger");
  2651. dragger.style.width = `${resizer.now.w}px`;
  2652. dragger.style.height = `${resizer.now.h}px`;
  2653. dragger.style.maxWidth = `${resizer.now.w}px`;
  2654. dragger.style.maxHeight = `${resizer.now.h}px`;
  2655. displayCoords();
  2656.  
  2657. function stopSet(key, val = null) {
  2658. let has = "undefined" !== typeof stops[key];
  2659. if (val === null) return (has) ? stops[key] : false;
  2660. if (!has) stops[key] = val;
  2661. return stops[key]
  2662. }
  2663. function dragTo(e) {
  2664. let topNow = resizer.now.t, leftNow = resizer.now.l,
  2665. widthNow = resizer.now.w, heightNow = resizer.now.h,
  2666. newTop = topNow + e.clientY - resizer.e.clientY,
  2667. newLeft = leftNow + e.clientX - resizer.e.clientX;
  2668. if (newTop + heightNow - limits.t > limits.h)
  2669. newTop = limits.h - heightNow + limits.t;
  2670. else if (newTop < limits.t) newTop= limits.t;
  2671. if (newLeft + widthNow - limits.l > limits.w)
  2672. newLeft = limits.w - widthNow + limits.l;
  2673. else if (newLeft < limits.l)newLeft=limits.l;
  2674. overlay.style.top = newTop +"px";
  2675. overlay.style.left = newLeft +"px";
  2676. resizer.e.clientY = e.clientY;
  2677. resizer.e.clientX = e.clientX;
  2678. displayCoords(1)
  2679. }
  2680. function freeCrop(e) {
  2681. if (Object.keys(stops).length) return;
  2682. let newAttr, newProp,
  2683. newTop = null,
  2684. newWidth = null,
  2685. newHeight = null,
  2686. tab = resizer.tab,
  2687. resetTop = 0, resetLeft = 0,
  2688. topNow = resizer.now.t, leftNow = resizer.now.l,
  2689. widthNow = resizer.now.w, heightNow = resizer.now.h,
  2690. mmoved = {X: e.clientX - resizer.e.clientX, Y: e.clientY - resizer.e.clientY};
  2691. if (resizer.pro == "width") {
  2692. newAttr = widthNow + mmoved.X;
  2693. if (isProp && tab == "tr" || tab == "br") {
  2694. if (mmoved.Y !== 0) {
  2695. newProp = (tab == "tr") ? topNow + mmoved.Y : topNow - mmoved.Y;
  2696. newHeight = (tab == "tr") ? heightNow - mmoved.Y : heightNow + mmoved.Y;
  2697. newWidth = newHeight / isProp * 100;
  2698. newAttr = newWidth;
  2699. }
  2700. else return
  2701. }
  2702. if (tab == "tr" && isProp) {
  2703. if (newHeight < 90) {
  2704. newAttr = stopSet("newAttr", 90 / isProp * 100);
  2705. newWidth = stopSet("newWidth", newAttr);
  2706. resetTop = stopSet("resetTop", topNow + heightNow - 90);
  2707. }
  2708. else if (newWidth < 160) {
  2709. newAttr = stopSet("newAttr", 160);
  2710. newWidth = stopSet("newWidth", newAttr);
  2711. resetTop = stopSet("resetTop", topNow + (widthNow * isProp/ 100)) - (newAttr * isProp/ 100);
  2712. }
  2713. else if (newProp < limits.t) {
  2714. newProp = stopSet("newProp", limits.t);
  2715. newAttr = stopSet("newAttr", widthNow - ((0 - topNow) / isProp * 100));
  2716. newWidth = stopSet("newWidth", newAttr);
  2717. resetTop = stopSet("resetTop", newProp);
  2718. }
  2719. }
  2720. else if (newAttr < 160) {
  2721. newAttr = stopSet("newAttr", 160);
  2722. }
  2723. else if (tab == "br" && newHeight < 90) {
  2724. newAttr = stopSet("newAttr", 90 / isProp * 100);
  2725. }
  2726. if (newAttr > limits.w - leftNow + limits.l) {
  2727. newAttr = stopSet("newAttr", limits.w - leftNow + limits.l);
  2728. newProp = stopSet("newProp", topNow);
  2729. newWidth = stopSet("newWidth", newAttr);
  2730. resetTop = (topNow + (widthNow * isProp/ 100)) - (newAttr * isProp/ 100);
  2731. }
  2732. else if (tab == "br" && topNow + heightNow + mmoved.Y > limits.h) {
  2733. newAttr = stopSet("newAttr", (limits.h - topNow) / isProp * 100);
  2734. }
  2735. if (tab == "tr") {
  2736. if (resetTop) {
  2737. newProp = resetTop;
  2738. if (newWidth) newAttr = newWidth;
  2739. }
  2740. overlay.style.top = newProp +"px";
  2741. }
  2742. dragger.style.width = newAttr +"px";
  2743. }
  2744. else if (resizer.pro == "height") {
  2745. newAttr = heightNow + mmoved.Y;
  2746. if (newAttr > limits.h - topNow + limits.t) {
  2747. newAttr = limits.h - topNow + limits.t;
  2748. } else if (newAttr < 90) newAttr = 90;
  2749. dragger.style.height = Math.round(newAttr) +"px";
  2750. }
  2751. else if (resizer.pro == "top") {
  2752. newAttr = topNow + mmoved.Y;
  2753. newProp = heightNow - mmoved.Y;
  2754. if (newProp < 90) {
  2755. newProp = stopSet("newProp", 90);
  2756. newAttr = stopSet("newAttr", topNow + heightNow - 90);
  2757. }
  2758. else if (newAttr < limits.t) {
  2759. newProp = stopSet("newProp", heightNow + topNow);
  2760. newAttr = stopSet("newAttr", limits.t);
  2761. }
  2762. dragger.style.height = Math.round(newProp) +"px";
  2763. overlay.style.top = Math.round(newAttr) +"px";
  2764. }
  2765. else if (resizer.pro == "left") {
  2766. newAttr = leftNow + mmoved.X;
  2767. newProp = widthNow - mmoved.X;
  2768. if (isProp && tab == "tl" || tab == "bl") {
  2769. heightNow = isProp * widthNow / 100;
  2770. newTop = topNow + mmoved.Y;
  2771. newHeight = heightNow - mmoved.Y;
  2772. }
  2773. if (tab == "tl" && isProp) {
  2774. if (newHeight < 90) {
  2775. resetLeft = stopSet("resetLeft", leftNow + widthNow - (90 / isProp * 100));
  2776. resetTop = stopSet("resetTop", topNow + heightNow - 90);
  2777. newProp = stopSet("newProp", 90 / isProp * 100);
  2778. }
  2779. else if (newProp < 160) {
  2780. newProp = stopSet("newProp", 160);
  2781. resetLeft = stopSet("resetLeft", leftNow + widthNow - 160);
  2782. resetTop = stopSet("resetTop", topNow + (widthNow * isProp/ 100)) - (newProp * isProp/ 100);
  2783. }
  2784. else if (newTop < limits.t) {
  2785. newTop = stopSet("newTop", limits.t);
  2786. leftNow = stopSet("leftNow", leftNow);
  2787. newProp = stopSet("newProp", widthNow - ((0 - topNow) / isProp * 100));
  2788. resetLeft = leftNow + widthNow - newProp;
  2789. }
  2790. }
  2791. else if (tab == "bl" && newHeight < 90 && mmoved.Y < 0) {
  2792. newProp = stopSet("newProp", 90 / isProp * 100);
  2793. newAttr = stopSet("newAttr", leftNow + widthNow - (90 / isProp * 100));
  2794. }
  2795. else if (newProp < 160) {
  2796. newProp = stopSet("newProp", 160);
  2797. newAttr = stopSet("newAttr", leftNow + widthNow - newProp);
  2798. }
  2799. if (newAttr < limits.l) {
  2800. newAttr = stopSet("newAttr", limits.l);
  2801. newProp = stopSet("newProp", widthNow - (0 - leftNow));
  2802. resetTop = (topNow + (widthNow * isProp/ 100)) - (newProp * isProp/ 100);
  2803. }
  2804. else if (tab == "bl" && topNow + heightNow + mmoved.Y > limits.h) {
  2805. newProp = stopSet("newProp", (limits.h - topNow) / isProp * 100);
  2806. newAttr = stopSet("newAttr", leftNow + widthNow - newProp);
  2807. }
  2808. if (tab == "tl") {
  2809. if (resetTop || resetLeft) {
  2810. if (resetTop) {newTop = resetTop;}
  2811. if (resetLeft) {newAttr = resetLeft;}
  2812. }
  2813. else {
  2814. newAttr = leftNow + widthNow - (newHeight / isProp * 100);
  2815. newProp = newHeight / isProp * 100;
  2816. }
  2817. overlay.style.top = Math.round(newTop) +"px";
  2818. }
  2819. overlay.style.left = Math.round(newAttr) +"px";
  2820. dragger.style.width = Math.round(newProp) +"px";
  2821. }
  2822. if (!cropMenu) {
  2823. qs(".action .aresize").classList.remove("disabled");
  2824. qs(".action .azoom").classList.remove("disabled");
  2825. qs("#zoomfactor").classList.remove("disabled");
  2826. qs("#zoomfactor").removeAttribute("disabled");
  2827. cropMenu = true
  2828. }
  2829. resizer.e.clientX = e.clientX;
  2830. resizer.e.clientY = e.clientY;
  2831. displayCoords(1)
  2832. }
  2833. function propCrop(e) {
  2834. let bx;
  2835. if (!isProp) {
  2836. bx = dragger.getBoundingClientRect();
  2837. isProp = bx.height / bx.width * 100;
  2838. dragger.style.height = "0";
  2839. dragger.style.paddingTop = isProp +'%';
  2840. }
  2841. freeCrop(e)
  2842. }
  2843. function onMouseMove(e) {resizer.fn(e);}
  2844. overlay.onmousedown = function(e) {
  2845. e.stopPropagation(); e.preventDefault();
  2846. if (e.which !== 1) return;
  2847. stops = {};
  2848. setResizerNow();
  2849. resizer.e = {clientX: e.clientX, clientY: e.clientY};
  2850. shader.classList.add("transparent");
  2851. action.classList.add("transparent");
  2852. let names = e.target.classList;
  2853. if (names.contains("cropper")) {
  2854. resizer.fn = freeCrop;
  2855. if (names.contains("ct")) {resizer.pro = "top"; resizer.tab = "ct"; }
  2856. else if (names.contains("cr")) {resizer.pro = "width"; resizer.tab = "cr"; }
  2857. else if (names.contains("cb")) {resizer.pro = "height"; resizer.tab = "cb";}
  2858. else {resizer.pro = "left"; resizer.tab = "cl";}
  2859. }
  2860. else if (names.contains("corner")) {
  2861. resizer.fn = propCrop;
  2862. if (names.contains("tr")) { resizer.pro = "width"; resizer.tab = "tr";}
  2863. else if (names.contains("br")) {resizer.pro = "width"; resizer.tab = "br";}
  2864. else if (names.contains("bl")) {resizer.pro = "left"; resizer.tab = "bl";}
  2865. else {resizer.pro = "left"; resizer.tab = "tl";}
  2866. }
  2867. else {resizer.fn = dragTo; resizer.tab = "dr";}
  2868.  
  2869. function onMouseUp(e) {
  2870. if (shader.classList.contains("transparent")) {
  2871. d.removeEventListener('mousemove', onMouseMove, false);
  2872. let cvs = qs("#dragger").getBoundingClientRect();
  2873. if (isProp) {
  2874. dragger.style.height = Math.round(cvs.height) +'px';
  2875. dragger.style.removeProperty('padding-top');
  2876. isProp = 0;
  2877. }
  2878. displayCoords(1);
  2879. let shadetop = Math.max(Math.round(resizer.now.t),0),
  2880. shadeleft = Math.max(Math.round(resizer.now.l),0),
  2881. shaderight = Math.round(limits.w - Math.min(resizer.now.r,limits.w)),
  2882. shadebottom = Math.round(limits.h - Math.min(resizer.now.b,limits.h)),
  2883. bgs = `100% ${shadetop}px, 100% ${shadebottom}px, ${shadeleft}px 100%, ${shaderight}px 100%`;
  2884. shader.style.backgroundSize = bgs;
  2885. shader.classList.remove("transparent");
  2886. action.classList.remove("transparent");
  2887. d.removeEventListener('mouseup', onMouseUp, false);
  2888. }
  2889. }
  2890. d.addEventListener('mousemove', onMouseMove, false);
  2891. d.addEventListener('mouseup', onMouseUp, false);
  2892. }
  2893. } else qs("#overlay").classList.toggle("active");
  2894. }
  2895. });
  2896. },
  2897.  
  2898.  
  2899. refreshCounts: (vid) => {
  2900. let xhr, data;
  2901. let link = `${vid}counts/`;
  2902. if (BC.path == vid) {
  2903. if (!BC.countsRefreshed || d.visibilityState == 'visible' || BC.isFullScreen) {
  2904. if (scriptHandler == "Greasemonkey"){xhr = new BC.SM_XMLHttpRequest()}else{xhr = new XMLHttpRequest()}
  2905. data = 'csrfmiddlewaretoken='+ BC.getCsrftoken();
  2906. xhr.addEventListener("error", (e) => { error('XMLHttpRequest refreshCounts error: '+ e) });
  2907. xhr.addEventListener("load", (res) => {
  2908. try {
  2909. let e = typeof res.target === "undefined" ? {target: res} : res;
  2910. let result = JSON.parse(e.target.responseText);
  2911. if ('undefined' !== result.success && result.success) {
  2912. qs('#fs-view-count').innerHTML = result.view_count;
  2913. qs('#fs-like-count').innerHTML = result.like_count;
  2914. qs('#fs-dislike-count').innerHTML = result.dislike_count;
  2915. qs('#subscriber_count').innerHTML = result.subscriber_count;
  2916. BC.countsRefreshed = true;
  2917. setTimeout(BC.refreshCounts, 60000, vid);
  2918. }
  2919. } catch (e) { error('XMLHttpRequest refreshCounts parsing error: '+ e) }
  2920. });
  2921. xhr.open("POST", link, true);
  2922. xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
  2923. xhr.send(data);
  2924. }
  2925. else setTimeout(BC.refreshCounts, 60000, vid)
  2926. }
  2927. },
  2928.  
  2929.  
  2930. screenshotIcon: '<svg class="svg-inline--fa fa-w-18 action-icon fa-fw" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 800" width="0.55in" height="0.55in">'+
  2931. '<path fill="currentColor" stroke="currentColor" stroke-width="2" d="M20,728.816h816.506c11.046,0,20-8.954,20-20V228.013c0-11.046-8.954-20-20-20h-530.81v-60.323c0-11.046-8.954-20-20-20 '+
  2932. 'H114.253c-11.046,0-20,8.954-20,20v60.323H20c-11.046,0-20,8.954-20,20v480.804C0,719.862,8.954,728.816,20,728.816z M427.027,300.181c93.607,0,169.491,75.884,169.491,169.492s-75.884,169.492-169.491,169.492 '+
  2933. 'c-93.608,0-169.492-75.884-169.492-169.492S333.419,300.181,427.027,300.181z" /></svg>',
  2934.  
  2935.  
  2936. init: function() {
  2937. return new Promise( resolve => {
  2938. let settings = {volume:0.5,autoplay:true,color:"none",playnext:false,usedark:true,playlists:true,mvplaylist:true,
  2939. useblacklist:true,hidecarousel:false,hidecomments:false,hidemenubar:true,hideadverts:true,useminiplayer:true,
  2940. usesquareicons:true,hidedonationbar:true,homepagegotoall:true,hidecookienotice:true,hidesignupnotice:true,useseekbarpreview:true};
  2941. GM.getValue('player', "{}").then( value => {
  2942. if (value && value != '{}') {
  2943. let player = Object.assign({}, settings, JSON.parse(value));
  2944. BC.url = null;
  2945. BC.host = null;
  2946. BC.path = null;
  2947. BC.loaded = !1;
  2948. BC.reload = !1;
  2949. BC.auxMenu = !1;
  2950. BC.loader = null;
  2951. BC.themes = null;
  2952. BC.blacklist = [];
  2953. BC.page = 'homepage';
  2954. BC.previouslisting = '';
  2955. BC.miniPlayerIni = false;
  2956. BC.player = {
  2957. api : null,
  2958. fur : null,
  2959. rect : null,
  2960. volume : player.volume,
  2961. autoplay: player.autoplay
  2962. }
  2963. BC.settings = {
  2964. color : player.color,
  2965. usedark : player.usedark,
  2966. playnext : player.playnext,
  2967. playlists : player.playlists,
  2968. mvplaylist : player.mvplaylist,
  2969. hideadverts : player.hideadverts,
  2970. hidemenubar : player.hidemenubar,
  2971. useblacklist : player.useblacklist,
  2972. hidecarousel : player.hidecarousel,
  2973. hidecomments : player.hidecomments,
  2974. useminiplayer : player.useminiplayer,
  2975. usesquareicons : player.usesquareicons,
  2976. hidedonationbar : player.hidedonationbar,
  2977. homepagegotoall : player.homepagegotoall,
  2978. hidecookienotice : player.hidecookienotice,
  2979. hidesignupnotice : player.hidesignupnotice,
  2980. useseekbarpreview : player.useseekbarpreview
  2981. }
  2982. GM.getValue('blacklist', "[]").then( value => {
  2983. BC.blacklist = JSON.parse(value);
  2984. let err = qs("#page-bar .page-title");
  2985. /* Server Error */
  2986. if (!err || (err && !err.innerText.match(/.*((server)+\serror)+$/i))) {
  2987. debug('>>>>>>>>>>>>>> BC init <<<<<<<<<<=');
  2988. resolve(true);
  2989. }
  2990. }).catch ( e => {
  2991. error('S_marty: Error in promise loading blacklist: '+ e);
  2992. });
  2993. }
  2994. else {
  2995. /* Install Database */
  2996. GM.setValue('blacklist', '[]');
  2997. GM.setValue('player', JSON.stringify(settings));
  2998. GM.setValue('miniplayer', "{ \"x\":0,\"y\":0,\"w\":350,\"h\":197 }");
  2999. w.location.replace(w.location.href);
  3000. }
  3001. }).catch ( e => {
  3002. error('S_marty: Error in promise loading dB: '+ e);
  3003. });
  3004. });
  3005. }
  3006. };
  3007.  
  3008.  
  3009. const Bcd = {
  3010.  
  3011. link: null,
  3012. dialog: null,
  3013. loader: null,
  3014. unloader: null,
  3015. playerAPI: null,
  3016. videoButtons: 0,
  3017. videoSources: 0,
  3018. downloadIndex: -1,
  3019. download_poster: false,
  3020. src: {videos: []},
  3021. downloads: [],
  3022. captions: [],
  3023. tooltip: [],
  3024.  
  3025.  
  3026. flattenTitle: (t, title = "") => {
  3027.  
  3028. t = t.replace(/\s?\[mirrored\]/i, "");
  3029. t = t.replace(/(?=\W{3,})/g, "");
  3030. t = t.replace(/\s?["'\[\(\{]*Re.{0,3}upload(ed)?["':\]\)\}]*\s?/i, "");
  3031. [...t].forEach(n => {
  3032. if ( /[^\\/:\*\?'"<>@\|]+/.test(n) ) title += n;
  3033. });
  3034. while (title[0] === "." || title[0] === " ") {title = title.slice(1)}
  3035. while (title.slice(-1) === "." || title.slice(-1) === " ") {title = title.slice(0, -1)}
  3036. title = title.trim();
  3037. if (/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i.test(title)) title = "Video_" + title;
  3038.  
  3039. return title;
  3040. },
  3041.  
  3042.  
  3043. addTooltip: (el, n) => {
  3044. let tooltip, left, br;
  3045. el.addEventListener('mouseover', function(e) {
  3046. Bcd.tooltip[n] = dce("div");
  3047. Bcd.tooltip[n] .className = "tooltip fade bottom in";
  3048. Bcd.tooltip[n] .setAttribute("id", `tooltip${el.id}`);
  3049. Bcd.tooltip[n] .setAttribute("role", "tooltip");
  3050. br = el.getBoundingClientRect();
  3051. Bcd.tooltip[n].style.top = br.bottom + w.scrollY +"px";
  3052. left = br.left + w.scrollX + ((br.right - br.left) / 2)
  3053. Bcd.tooltip[n].innerHTML = `<div class="tooltip-arrow"></div><div class="tooltip-inner">${el.getAttribute("data-original-title")}</div>`;
  3054. qs("body").appendChild(Bcd.tooltip[n]);
  3055. br = Bcd.tooltip[n].getBoundingClientRect();
  3056. Bcd.tooltip[n].style.left = (left - (br.width / 2)) +"px";
  3057. }, false);
  3058.  
  3059. el.addEventListener('mouseout', function(e) {
  3060. Bcd.tooltip[n].classList.remove('in');
  3061. w.setTimeout( function(e) {
  3062. qs(`#tooltip${el.id}`).outerHTML = "";
  3063. }, 150);
  3064. }, false);
  3065. },
  3066.  
  3067.  
  3068. addVideoAction: (id, title, icon) => {
  3069. let spc, action = null,
  3070. menu = qs(".video-actions .action-list");
  3071.  
  3072. action = dce("a");
  3073. action.setAttribute("id", id);
  3074. action.setAttribute("title", "");
  3075. action.setAttribute("data-original-title", title);
  3076. action.setAttribute("data-placement", "bottom");
  3077. action.setAttribute("data-toggle", "tooltip");
  3078. action.className = "disabled";
  3079. action.innerHTML = icon;
  3080. menu.insertBefore(action, menu.firstChild);
  3081. spc = d.createTextNode(" \n \n ");
  3082. menu.insertBefore(spc, menu.firstChild);
  3083. return action
  3084. },
  3085.  
  3086.  
  3087. load: (e, vid) => {
  3088. let style;
  3089.  
  3090. debug(`>>>>>>>>>>>>>> Bcd load <<<<<<<<<<= ${e}:`, vid);
  3091.  
  3092. if (! qs("#downloadLink")) {
  3093. Bcd.link = Bcd.addVideoAction("downloadLink", "Download", Bcd.downloadIcon);
  3094. Bcd.addTooltip(Bcd.link, Bcd.tooltip.length);
  3095. Bcd.link.addEventListener('click', (e) => {
  3096. e.preventDefault();
  3097. if (Bcd.dialog) {
  3098. Bcd.toggle_download_dialog();
  3099. return
  3100. }
  3101. }, false);
  3102. }
  3103.  
  3104. if (! qs("style#video_download")) {
  3105. style = dce("style");
  3106. style.id = "video_download";
  3107. style.type = "text/css";
  3108. style.innerText = `
  3109. a#downloadLink.disabled {pointer-events: none; opacity: 0.3; cursor: default}
  3110. .video-information .video-actions a.download_started {color: #eeee00;}
  3111. .video-information .video-actions a.download_finished {color: #00ee00;}
  3112. .video-information .video-actions a.download_error {color: #ee0000;}
  3113.  
  3114. #video_download_dialog {width: 400px; border: 2px solid #5F5F5F; border-radius: 6px; position: absolute;
  3115. z-index: 99000; background-color: #ddd;}
  3116. html.night #video_download_dialog {background-color: #211f22; border-color: #aaa;}
  3117. #video_download_dialog h2 {text-align: center;}
  3118. #video_download_dialog h4 {padding: 6px; text-align: center; text-shadow: 1px 1px #A1A1A1;}
  3119. #video_download_dialog .download_poster {background-color: transparent;}
  3120. #video_download_dialog .download_poster img {border: 1px solid #5F5F5F; width: 160px; height: 90px; margin-left: 18px;}
  3121. #video_download_dialog .poster_saveas {position: relative; right: 92px; text-align: center; float: right; top: 14px; font-size: 1em;}
  3122. #video_download_dialog #tracks {margin: 6px 16px 0 0; text-align: right;}
  3123. html.night #video_download_dialog .download_poster img {border-color: #aaa;}
  3124. #video_download_dialog button {font-size: 13px; color: #000; border-radius: 4px;
  3125. margin: 4px; right: 10px; position: absolute;-webkit-border-radius: 4; -moz-border-radius: 4; border: 2px solid #555;}
  3126. #video_download_dialog button[disabled] {opacity: 0.5;}
  3127. #video_download_dialog .download_dialog_close_button {color: #990000; background-color: #aaa; top: -16px; right: -16px;
  3128. position: absolute; width: 32px; height: 32px; border-radius: 16px; border: 2px solid #555; cursor: pointer;
  3129. font-size: 24px; font-weight: bold; text-align: center; vertical-align: middle; line-height: 28px;}
  3130. #video_download_dialog .download_dialog_close_button:hover {background-color: #ddd; border-color: #222;}
  3131. #video_download_dialog .download_videos {background-color: transparent;}
  3132. #video_download_dialog hr {height: 1px; width: 80%; margin: 20px auto; border: none;
  3133. background: linear-gradient(to right, #BFBDBD 10%, #000 50%, #000 50%, #BFBDBD 90%);}
  3134. html.night #video_download_dialog hr {background: linear-gradient(to right, #000 0%, #BFBDBD 50%, #BFBDBD 50%, #000 100%);}
  3135. #video_download_dialog .download_videos div {background-color: transparent;}
  3136. #video_download_dialog .download_videos div.download_progress {margin: 2px 0px 2px 16px;}
  3137. #video_download_dialog .download_videos div.download_details {height: 50px; width: 188px; max-width: 200px; float: left;
  3138. padding: 6px; margin-left: 10px;}
  3139. #video_download_dialog button.download_error {background-color: #ff0000 !important;}
  3140. #video_download_dialog button.download_cancel {background-color: #aaaa22;}
  3141. #video_download_dialog button.download_finished {background-color: #8efc8e !important;}
  3142. #video_download_dialog button:not([disabled]):hover {color: #ffffff;}
  3143. #tracks .captions {display: inline-block;font-size: 13px;font-family: sans-serif;font-weight: 500;color: #000;line-height: 1.3;padding: .1em 1em .1em .3em;
  3144. width: 100%;max-width: 100%;box-sizing: border-box;margin: 0;border: 2px solid #555;box-shadow: 0 1px 0 1px rgba(0,0,0,.04);
  3145. border-radius: 5px;-moz-appearance: none;-webkit-appearance: none;appearance: none;background-color: #2532e0;
  3146. background-image: url('data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20`+
  3147. `height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23007CB2%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.`+
  3148. `6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%20`+
  3149. `5.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E'),linear-gradient(to bottom, #525cde 0%,#2532e0 100%);
  3150. background-repeat: no-repeat, repeat;background-position: right .2em top 50%, 0 0;background-size: .65em auto, 100%;outline-style: none;}
  3151. #tracks .captions:hover {color: #fff;} #tracks .captions option {font-weight:normal;}`;
  3152. d.documentElement.appendChild(style);
  3153. }
  3154. Bcd.downloadDialog(vid);
  3155. },
  3156.  
  3157.  
  3158. downloadDialog: (vid) => {
  3159. let downloadVideos, hr, newVid, tracks, captions = "";
  3160.  
  3161. Bcd.dialog = qs("#video_download_dialog");
  3162. debug(`Building downloadDialog (${Bcd.videoButtons})`)
  3163.  
  3164. if (Bcd.dialog) {
  3165. downloadVideos = qs(".download_videos", Bcd.dialog);
  3166.  
  3167. if (downloadVideos) {
  3168. hr = dce("hr");
  3169. newVid = dce("div");
  3170. newVid.style.height = "54px";
  3171. newVid.style.margin = "0px 0px 20px 0px";
  3172. newVid.innerHTML = `
  3173. <div class="download_details v${Bcd.videoButtons}">
  3174. <div id="loader" style="position: relative;margin: auto;left: 0;top: 100%;">
  3175. <ul style="width: 10%;"><li></li><li></li><li></li><li></li><li></li></ul></div>
  3176. </div>
  3177. <div style="width: 180px; height: 26px;">
  3178. <button class="btn-danger download_video_only v${Bcd.videoButtons}" bid="v${Bcd.videoButtons}">Video Only</button><br />
  3179. </div>
  3180. <div style="width: 180px; height: 26px;">
  3181. <button class="btn-danger download_video_and_poster v${Bcd.videoButtons}" bid="v${Bcd.videoButtons}">Video &amp; Poster</button>
  3182. </div>
  3183. <div class="download_progress v${Bcd.videoButtons}"></div>`;
  3184.  
  3185. downloadVideos.insertBefore(newVid, downloadVideos.firstChild);
  3186. downloadVideos.insertBefore(hr, downloadVideos.firstChild);
  3187.  
  3188. qs("button.download_video_only.v"+ Bcd.videoButtons, Bcd.dialog).addEventListener('click', (e) => {
  3189. e.preventDefault();
  3190. let t = e.target;
  3191. let vb = t.getAttribute("bid");
  3192. if (!t.classList.contains("download_cancel")) {
  3193. let i = Bcd.saveToDisk(vid, false);
  3194. debug('download_video: '+i);
  3195. if (i[0] === null) return;
  3196. t.classList.add("download_cancel");
  3197. t.setAttribute("downloadid", i[0]);
  3198. t.innerText = "Cancel "+ t.innerText;
  3199. qs(".download_video_and_poster."+ vb, Bcd.dialog).disabled = "disabled";
  3200. } else {
  3201. let dl = Bcd.downloads[parseInt(t.getAttribute("downloadid"))];
  3202. dl[1].abort();
  3203. msg(`Download Canceled: ${dl[0].name} (${dl[0].n} of ${dl[0].t})`);
  3204. t.classList.remove("download_cancel");
  3205. t.removeAttribute("style");
  3206. t.removeAttribute("downloadid");
  3207. t.innerText = t.innerText.substr(7);
  3208. qs(".download_progress."+ vb, Bcd.dialog).innerHTML = "";
  3209. if (qs("button.download_poster_only", Bcd.dialog).className == "btn-danger download_poster_only") {
  3210. qs(".download_video_and_poster."+ vb, Bcd.dialog).removeAttribute("disabled");
  3211. }
  3212. if (! qs("button.download_cancel", Bcd.dialog)) {
  3213. Bcd.link.classList.remove("download_started");
  3214. }
  3215. }
  3216. });
  3217. qs("button.download_video_and_poster.v"+ Bcd.videoButtons, Bcd.dialog).addEventListener('click', (e) => {
  3218. e.preventDefault();
  3219. let t = e.target;
  3220. let vb = t.getAttribute("bid");
  3221. if (!t.classList.contains("download_cancel")) {
  3222. let i = Bcd.saveToDisk(vid, Bcd.src.poster);
  3223. debug('download_video_and_poster: '+i);
  3224. if (i[0] === null || i[1] === null) return;
  3225. t.classList.add("download_cancel");
  3226. t.setAttribute("downloadid", i[0]+'-'+i[1]);
  3227. t.innerText = "Cancel "+ t.innerText;
  3228. qs("button.download_poster_only", Bcd.dialog).disabled = "disabled";
  3229. qs("button.download_video_only."+ vb, Bcd.dialog).disabled = "disabled";
  3230. qs("button.download_poster_only", Bcd.dialog).setAttribute("downloadid", i[1]);
  3231. qs("button.download_poster_only", Bcd.dialog).classList.add("download_cancel");
  3232. qs("button.download_video_only."+ vb, Bcd.dialog).setAttribute("downloadid", i[0]);
  3233. qsa("button.download_video_and_poster:not(."+ vb +")", Bcd.dialog).forEach( (b) => {
  3234. b.setAttribute("disabled", "disabled");
  3235. })
  3236. } else {
  3237. let p, v;
  3238. let ids = t.getAttribute("downloadid").split('-');
  3239. let dl = Bcd.downloads[parseInt(ids[0])];
  3240. dl[1].abort();
  3241. msg(`Download Canceled: ${dl[0].name} (${dl[0].n} of ${dl[0].t})`);
  3242. dl = Bcd.downloads[parseInt(ids[1])];
  3243. if (qs("button.download_poster_only.download_cancel", Bcd.dialog)) {
  3244. dl[1].abort();
  3245. msg(`Download Canceled: ${dl[0].name} (${dl[0].n} of ${dl[0].t})`);
  3246. }
  3247. t.classList.remove("download_cancel");
  3248. t.removeAttribute("style");
  3249. t.removeAttribute("downloadid");
  3250. t.innerText = t.innerText.substr(7);
  3251. qs(".download_progress."+ vb, Bcd.dialog).innerHTML = "";
  3252. if (qsa("button.download_poster_only.download_finished,"+
  3253. "button.download_poster_only.download_error", Bcd.dialog).length)
  3254. {
  3255. t.disabled = "disabled";
  3256. if (! qs("button.download_cancel", Bcd.dialog)) {
  3257. Bcd.link.classList.replace("download_started", "download_finished");
  3258. }
  3259. }
  3260. else {
  3261. if (p = qs('button.download_poster_only', Bcd.dialog)) {
  3262. p.classList.remove("download_cancel");
  3263. p.removeAttribute("disabled");
  3264. p.removeAttribute("downloadid");
  3265. }
  3266. qsa("button.download_video_and_poster:not(."+ vb +")", Bcd.dialog).forEach( (b) => {
  3267. b.removeAttribute("disabled");
  3268. })
  3269. if (! qs("button.download_cancel", Bcd.dialog)) {
  3270. Bcd.link.classList.remove("download_started");
  3271. }
  3272. }
  3273. if (v = qs('button.download_video_only[downloadid="'+ids[0]+'"]', Bcd.dialog)) {
  3274. v.removeAttribute("disabled")
  3275. v.removeAttribute("downloadid");
  3276. }
  3277. }
  3278. });
  3279. /* ===================================================================================== */
  3280. }
  3281. }
  3282. else {
  3283. try { /* TODO BETA */
  3284. //tracks = [{kind: "captions", src:"/sampleCaptions2.vtt", srclang:"en"},{kind: "captions", src:"/sampleCaptions3.vtt", srclang:"fr"}]
  3285. tracks = qsa("track", Bcd.playerAPI);
  3286. if (tracks.length) {
  3287. captions = '<div id="tracks"><select class="captions btn-danger"><option value="-1">Select Caption</option>';
  3288. tracks.forEach((o, i) => {
  3289. if (o.kind == "captions" || o.kind == "subtitles") {
  3290. Bcd.captions.push({lang: o.srclang, url: o.src, type: o.kind, index: i+1});
  3291. captions += `<option value="${i}">${o.srclang} ${o.kind}</option>`;
  3292. }
  3293. })
  3294. captions += "</select></div>";
  3295. }
  3296. } catch (err) {"Captions Error: "+ err}
  3297. Bcd.dialog = dce("div");
  3298. Bcd.dialog.setAttribute("id", "video_download_dialog");
  3299. Bcd.dialog.innerHTML = `
  3300. <div class="download_poster">
  3301. <h2 class="download_smarty">Smarty Video Download</h2>
  3302. <h4 class="download_title">${Bcd.flattenTitle(d.title)}</h4>
  3303. <img alt="- No Poster -" src="${Bcd.src.poster.url}" title="${Bcd.src.poster.width} x ${Bcd.src.poster.height}"
  3304. onerror="this.nextElementSibling.style.visibility='hidden'; this.onerror=''; this.title=''">
  3305. <button class="btn-danger download_poster_only">Poster Only</button>
  3306. <div class="poster_saveas hidden">Try:<br />right-click<br />Save Image As...</div>${captions}
  3307. </div>
  3308. <div class="download_videos">
  3309. <hr />
  3310. <div style="height: 54px; margin: 0px 0px 20px 0px;">
  3311. <div class="download_details v0">
  3312. <div id="loader" style="position: relative;margin: auto;left: 0;top: 100%;">
  3313. <ul style="width: 10%;"><li></li><li></li><li></li><li></li><li></li></ul></div>
  3314. </div>
  3315. <div style="width: 180px; height: 26px;">
  3316. <button class="btn-danger download_video_only v0" bid="v0">Video Only</button><br />
  3317. </div>
  3318. <div style="width: 180px; height: 26px;">
  3319. <button class="btn-danger download_video_and_poster v0" bid="v0">Video &amp; Poster</button>
  3320. </div>
  3321. <div class="download_progress v0"></div>
  3322. </div>
  3323. </div>
  3324. <div class="download_dialog_close_button" title="Close">X</div>`;
  3325. Bcd.dialog.style.display = "none";
  3326. qs("body").appendChild(Bcd.dialog);
  3327.  
  3328. qs("button.download_poster_only", Bcd.dialog).addEventListener('click', (e) => {
  3329. e.preventDefault();
  3330. let t = e.target;
  3331. if (!t.classList.contains("download_cancel")) {
  3332. let i = Bcd.saveToDisk(false, Bcd.src.poster);
  3333. debug('download_poster: '+i);
  3334. if (i[1] === null) return;
  3335. t.classList.add("download_cancel");
  3336. t.setAttribute("downloadid", i[1]);
  3337. t.innerText = "Cancel "+ t.innerText;
  3338. qsa("button.download_video_and_poster", Bcd.dialog).forEach( (b) => {
  3339. b.setAttribute("disabled", "disabled");
  3340. })
  3341. } else {
  3342. let dl = Bcd.downloads[parseInt(t.getAttribute("downloadid"))];
  3343. dl[1].abort();
  3344. msg(`Download Canceled: ${dl[0].name} (${dl[0].n} of ${dl[0].t})`);
  3345. t.classList.remove("download_cancel");
  3346. t.removeAttribute("style");
  3347. t.removeAttribute("downloadid");
  3348. t.innerText = t.innerText.substr(7);
  3349. qsa("button.download_video_and_poster", Bcd.dialog).forEach( (b) => {
  3350. let cn = "button.download_video_only."+ b.getAttribute("bid");
  3351. if (! qs(cn, Bcd.dialog).classList.contains("download_cancel") &&
  3352. ! qs(cn, Bcd.dialog).classList.contains("download_finished"))
  3353. { b.removeAttribute("disabled"); }
  3354. })
  3355. if (! qs("button.download_cancel", Bcd.dialog)) {
  3356. Bcd.link.classList.remove("download_started");
  3357. }
  3358. }
  3359. });
  3360.  
  3361. qs("button.download_video_only.v0", Bcd.dialog).addEventListener('click', (e) => {
  3362. e.preventDefault();
  3363. let t = e.target;
  3364. if (!t.classList.contains("download_cancel")) {
  3365. let i = Bcd.saveToDisk(vid, false);
  3366. debug('download_video: '+i);
  3367. if (i[0] === null) return;
  3368. t.classList.add("download_cancel");
  3369. t.setAttribute("downloadid", i[0]);
  3370. t.innerText = "Cancel "+ t.innerText;
  3371. qs(".download_video_and_poster.v0", Bcd.dialog).disabled = "disabled";
  3372. } else {
  3373. let dl = Bcd.downloads[parseInt(t.getAttribute("downloadid"))];
  3374. dl[1].abort();
  3375. msg(`Download Canceled: ${dl[0].name} (${dl[0].n} of ${dl[0].t})`);
  3376. t.classList.remove("download_cancel");
  3377. t.removeAttribute("style");
  3378. t.removeAttribute("downloadid");
  3379. t.innerText = t.innerText.substr(7);
  3380. qs(".download_progress.v0", Bcd.dialog).innerHTML = "";
  3381. if (qs("button.download_poster_only", Bcd.dialog).className == "btn-danger download_poster_only") {
  3382. qs(".download_video_and_poster.v0", Bcd.dialog).removeAttribute("disabled");
  3383. }
  3384. if (! qs("button.download_cancel", Bcd.dialog)) {
  3385. Bcd.link.classList.remove("download_started");
  3386. }
  3387. }
  3388. });
  3389. qs("button.download_video_and_poster.v0", Bcd.dialog).addEventListener('click', (e) => {
  3390. e.preventDefault();
  3391. let t = e.target;
  3392. if (!t.classList.contains("download_cancel")) {
  3393. let i = Bcd.saveToDisk(vid, Bcd.src.poster);
  3394. debug('download_video_and_poster: '+i);
  3395. if (i[0] === null || i[1] === null) return;
  3396. t.classList.add("download_cancel");
  3397. t.setAttribute("downloadid", i[0]+'-'+i[1]);
  3398. t.innerText = "Cancel "+ t.innerText;
  3399. qs("button.download_poster_only", Bcd.dialog).disabled = "disabled";
  3400. qs("button.download_video_only.v0", Bcd.dialog).disabled = "disabled";
  3401. qs("button.download_poster_only", Bcd.dialog).setAttribute("downloadid", i[1]);
  3402. qs("button.download_poster_only", Bcd.dialog).classList.add("download_cancel");
  3403. qs("button.download_video_only.v0", Bcd.dialog).setAttribute("downloadid", i[0]);
  3404. qsa("button.download_video_and_poster:not(.v0)", Bcd.dialog).forEach( (b) => {
  3405. b.setAttribute("disabled", "disabled");
  3406. })
  3407. } else {
  3408. let p, v;
  3409. let ids = t.getAttribute("downloadid").split('-');
  3410. let dl = Bcd.downloads[parseInt(ids[0])];
  3411. dl[1].abort();
  3412. msg(`Download Canceled: ${dl[0].name} (${dl[0].n} of ${dl[0].t})`);
  3413. dl = Bcd.downloads[parseInt(ids[1])];
  3414. if (qs("button.download_poster_only.download_cancel", Bcd.dialog)) {
  3415. dl[1].abort();
  3416. msg(`Download Canceled: ${dl[0].name} (${dl[0].n} of ${dl[0].t})`);
  3417. }
  3418. t.classList.remove("download_cancel");
  3419. t.removeAttribute("style");
  3420. t.removeAttribute("downloadid");
  3421. t.innerText = t.innerText.substr(7);
  3422. qs(".download_progress.v0", Bcd.dialog).innerHTML = "";
  3423. if (qsa("button.download_poster_only.download_finished,"+
  3424. "button.download_poster_only.download_error", Bcd.dialog).length)
  3425. {
  3426. t.disabled = "disabled";
  3427. if (! qs("button.download_cancel", Bcd.dialog)) {
  3428. Bcd.link.classList.replace("download_started", "download_finished");
  3429. }
  3430. }
  3431. else {
  3432. if (p = qs('button.download_poster_only', Bcd.dialog)) {
  3433. p.classList.remove("download_cancel");
  3434. p.removeAttribute("disabled");
  3435. p.removeAttribute("downloadid");
  3436. }
  3437. qsa("button.download_video_and_poster:not(.v0)", Bcd.dialog).forEach( (b) => {
  3438. b.removeAttribute("disabled");
  3439. })
  3440. if (! qs("button.download_cancel", Bcd.dialog)) {
  3441. Bcd.link.classList.remove("download_started");
  3442. }
  3443. }
  3444. if (v = qs('button.download_video_only[downloadid="'+ids[0]+'"]', Bcd.dialog)) {
  3445. v.removeAttribute("disabled")
  3446. v.removeAttribute("downloadid");
  3447. }
  3448. }
  3449. });
  3450. if (qs("#tracks .captions")) {
  3451. qs("#tracks .captions", Bcd.dialog).addEventListener('change', (e) => {
  3452. if (e.target.selectedIndex.value!=-1) Bcd.saveToDisk(false, false, e.target.selectedIndex.value);
  3453. })
  3454. }
  3455.  
  3456. qs(".download_dialog_close_button", Bcd.dialog).addEventListener('click', (e) => {
  3457. e.preventDefault();
  3458. Bcd.toggle_download_dialog();
  3459. return false
  3460. });
  3461. qs(".download_dialog_close_button", Bcd.dialog).addEventListener('mouseover', (e) => {
  3462. Bcd.animate({
  3463. duration: 400,
  3464. timing: function(timeFraction) {
  3465. return Math.pow(timeFraction, 2);
  3466. },
  3467. draw: function(progress) {
  3468. e.target.style.borderTopRightRadius = 16 + (progress * 6) + 'px';
  3469. e.target.style.borderBottomLeftRadius = 16 - (progress * 16) + 'px';
  3470. }
  3471. });
  3472. });
  3473. qs(".download_dialog_close_button", Bcd.dialog).addEventListener('mouseout', (e) => {
  3474. Bcd.animate({
  3475. duration: 300,
  3476. timing: function back(timeFraction) {
  3477. return Math.pow(timeFraction, 2)
  3478. }.bind(2.0),
  3479. draw: function(progress) {
  3480. e.target.style.borderBottomLeftRadius = progress * 16 + 'px';
  3481. e.target.style.borderTopRightRadius = 22 - (progress * 6) + 'px';
  3482. }
  3483. });
  3484. });
  3485. Bcd.link.classList.remove("disabled");
  3486. }
  3487. (function (vb) {
  3488. return new Promise( (resolve) => {
  3489. try {
  3490. var url = Bcd.src.videos[vb].url;
  3491. var valid = true;
  3492. var response = "";
  3493. var request = new XMLHttpRequest();
  3494.  
  3495. if ('withCredentials' in request) {
  3496. request.addEventListener('progress', function(e) {
  3497. if (e.total && valid) {
  3498. request.abort();
  3499. valid = false;
  3500. debug('Size request progress', e);
  3501. resolve({size: e.total, vb: vb});
  3502. }
  3503. else {
  3504. if (request.status !== 0) {
  3505. var err = `Error: ${request.status} ${request.statusText}`;
  3506. Bcd.src.videos[vb].error = err;
  3507. valid = false;
  3508. request.abort();
  3509. qs(".download_details.v"+ vb, Bcd.dialog).innerHTML = err;
  3510. }
  3511. }
  3512. });
  3513. request.addEventListener('error', function() {
  3514. error('Size request Error', request.status);
  3515. qs(".download_details.v"+ vb, Bcd.dialog).innerHTML = "";
  3516. });
  3517. request.open('GET', url, true);
  3518. request.responseType = "blob";
  3519. request.send();
  3520. }
  3521. } catch (e) {
  3522. error('Size Promise Error', e);
  3523. }
  3524. })
  3525. }) (Bcd.videoButtons).then( (blob) => {
  3526. let dims = "",
  3527. type = Bcd.src.videos[blob.vb].type,
  3528. size = Bcd.prettyBytes(blob.size),
  3529. width = Bcd.src.videos[blob.vb].width,
  3530. height = Bcd.src.videos[blob.vb].height;
  3531.  
  3532. if (type && type.indexOf('video/') !=-1) {
  3533. type = type.slice(type.lastIndexOf('/') +1);
  3534. type = type[0].toUpperCase() + type.slice(1);
  3535. }
  3536.  
  3537. if (width && height) {
  3538. dims = `<br />&nbsp;&nbsp;${width} x ${height}`;
  3539. }
  3540. qs(".download_details.v"+ blob.vb, Bcd.dialog).innerHTML = `${type} ${size}${dims}`;
  3541. debug('videoButtons Blob response:', blob);
  3542. });
  3543. Bcd.videoButtons += 1;
  3544. },
  3545.  
  3546.  
  3547. toggle_download_dialog: () => {
  3548. if (Bcd.dialog.style.display == "none") {
  3549. Bcd.dialog.style.overflow = "hidden";
  3550. Bcd.dialog.style.top = w.scrollY + 60 +"px";
  3551. Bcd.dialog.style.left = w.innerWidth - 500 +"px";
  3552. Bcd.dialog.style.display ='block';
  3553. Bcd.dialog.style.height ='auto';
  3554.  
  3555. Bcd.animate({
  3556. duration: 400,
  3557. timing: function(timeFraction) {
  3558. return Math.pow(timeFraction, 2);
  3559. },
  3560. draw: function(progress) {
  3561. Bcd.dialog.style.width = progress * 400 + 'px';
  3562. if (progress === 1) {
  3563. Bcd.dialog.style.overflow = 'visible';
  3564. }
  3565. }
  3566. });
  3567. }
  3568. else {
  3569. let height = Bcd.dialog.getBoundingClientRect().height;
  3570. Bcd.dialog.style.overflow = "hidden";
  3571.  
  3572. Bcd.animate({
  3573. duration: 500,
  3574. timing: function back(x, timeFraction) {
  3575. return Math.pow(timeFraction, 2) * ((x + 1) * timeFraction - x)
  3576. }.bind(null, 2.8),
  3577. draw: function(progress) {
  3578. Bcd.dialog.style.width = 400 - (progress * 400) + 'px';
  3579. Bcd.dialog.style.height = height - (progress * height) + 'px';
  3580. if (progress === 1) {
  3581. Bcd.dialog.style.display ='none';
  3582. Bcd.dialog.style.overflow = 'visible';
  3583. }
  3584. }
  3585. });
  3586. }
  3587. },
  3588.  
  3589.  
  3590. buttonDone: (button, state = "finished", txt = "Done") => {
  3591. button.classList.replace("download_cancel", `download_${state}`);
  3592. button.removeAttribute("downloadid");
  3593. button.removeAttribute("style");
  3594. button.disabled = "disabled";
  3595. button.innerText = txt;
  3596. },
  3597.  
  3598.  
  3599. saveToDisk: (asVideo = false, asPoster = false, asAux = false) => {
  3600.  
  3601. // Reset icon color
  3602. Bcd.link.classList.remove('download_error', 'download_finished');
  3603. Bcd.link.classList.add('download_started');
  3604.  
  3605. let el, ext, title, total, button;
  3606. let abo = "",
  3607. dlp = [],
  3608. ab0 = null,
  3609. ab1 = null,
  3610. indx = null,
  3611. update = [],
  3612. progress = [],
  3613. mos = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
  3614. downloadURLs = ((url, name, type, n, t, dli, ab) => {
  3615.  
  3616. msg('Download Started:', name, `(${n} of ${t})`);
  3617.  
  3618. Bcd.downloads[dli] = [];
  3619. Bcd.downloads[dli][0] = {name: name, n: n, t: t};
  3620. Bcd.downloads[dli][1] = SmartyDownload({
  3621.  
  3622. id: dli,
  3623. url: url,
  3624. name: name,
  3625. type: type,
  3626. onload: () => {
  3627. msg(`Download Complete: ${name} (${n} of ${t})`);
  3628. // single video, single image, caption file
  3629. if (! ab) {
  3630. if (button = qs(`button.download_video_only[downloadid="${dli}"]`, Bcd.dialog)) {
  3631. Bcd.buttonDone(button);
  3632. if (dlp[dli]) dlp[dli].innerHTML = "";
  3633. } else if (button = qs(`button.download_poster_only[downloadid="${dli}"]`, Bcd.dialog)) {
  3634. Bcd.buttonDone(button);
  3635. }
  3636. if (indx && qs("#tracks .captions")) {
  3637. qs("#tracks .captions").options[indx].style.backgroundColor = "#8efc8e";
  3638. }
  3639. }
  3640. // ab as video or image
  3641. else {
  3642. if (button = qs(`button.download_poster_only[downloadid="${dli}"]`, Bcd.dialog)) {
  3643. Bcd.buttonDone(button);
  3644. } else if (button = qs(`button.download_video_and_poster[downloadid="${ab}"]`, Bcd.dialog)) {
  3645. Bcd.buttonDone(button);
  3646. if (dlp[dli]) dlp[dli].innerHTML = "";
  3647. }
  3648. }
  3649. if (qsa("button.download_cancel", Bcd.dialog).length === 0) {
  3650. Bcd.link.classList.replace('download_started', 'download_finished');
  3651. }
  3652. },
  3653. onprogress: (dl) => {
  3654. debug(`Download Progress ${dli}:`, dl);
  3655.  
  3656. if (progress[dli] && dlp[dli]) {
  3657. if ((update[dli]++ % 10 === 0) || dl.loaded == dl.total) {
  3658. let now = new Date(),
  3659. pct = Math.round(dl.loaded / dl.total * 100),
  3660. rate = dl.loaded / (now - progress[dli]) * 1000,
  3661. finish = Math.round((dl.total - dl.loaded)/ dl.loaded * (now - progress[dli])),
  3662. crt = `${Math.round(dl.loaded / dl.total * 1000) / 10}.0`;
  3663.  
  3664. crt = crt.slice(0, crt.indexOf(".") + 2);
  3665. //debug('Round (dl.total - dl.loaded: '+(dl.total - dl.loaded)+') / dl.loaded: '+dl.loaded+' * (now - progress: '+(now - progress[dli])+'), yeilds: '+finish)
  3666. finish = new Date(now - finish);
  3667. finish = getPublishDate([ [], [finish.getUTCHours()], [finish.getUTCMinutes()], ["UTC"], [mos[finish.getUTCMonth()]], [finish.getUTCDate()], [finish.getUTCFullYear()], [finish.getUTCSeconds()] ]);
  3668. el.style.background = `linear-gradient(to right, #30a247 ${pct - 10}%, #8efc8e ${pct}%, #ed7f5d 0%, #ed7f5d 100%)`;
  3669. dlp[dli].innerHTML = `<b>${crt}%</b> =&gt; ${Bcd.prettyBytes(dl.loaded)} of ${Bcd.prettyBytes(dl.total)} (${Bcd.prettyBytes(rate)}/s) in ${finish}`;
  3670. }
  3671. }
  3672. else if (dl.total !=-1 && dl.lengthComputable) {
  3673. el = qs(`button.download_cancel[downloadid="${ab ? ab : dli}"]`, Bcd.dialog);
  3674. if (el && ! el.classList.contains("download_poster_only")) {
  3675. progress[dli] = new Date();
  3676. update[dli] = 0;
  3677. dlp[dli] = qs(".download_progress."+ (el.getAttribute("bid")), Bcd.dialog);
  3678. }
  3679. }
  3680. else if (dl.total ==-1) {
  3681. }
  3682. },
  3683. onerror: (dl) => {
  3684. button = qs(`button.download_cancel[downloadid="${ab ? ab : dli}"]`, Bcd.dialog);
  3685. if (! dl.error) {
  3686. msg(`Download Canceled: ${name} (${n} of ${t})`);
  3687. if (button) {
  3688. button.dispatchEvent( new MouseEvent('click', {
  3689. bubbles: true,
  3690. cancelable: true
  3691. }));
  3692. }
  3693. if (qsa("button.download_cancel", Bcd.dialog).length === 0) {
  3694. Bcd.link.classList.remove('download_started');
  3695. }
  3696. if (qsa("button.download_finished", Bcd.dialog).length) {
  3697. Bcd.link.classList.add('download_finished');
  3698. }
  3699. }
  3700. else {
  3701. error(`SmartyDownload (${dli})`, dl.error);
  3702. msg(`Download Canceled: ${name} (${n} of ${t})`);
  3703. Bcd.link.classList.replace('download_started', 'download_error');
  3704. if (dl.ext && ["mp4","ogg","webm"].indexOf(dl.ext) ==-1) {
  3705. button = qs("button.download_poster_only", Bcd.dialog);
  3706. qs(".download_poster .poster_saveas", Bcd.dialog).classList.remove("hidden")
  3707. }
  3708. if (button) {
  3709. Bcd.buttonDone(button, "error", "Error");
  3710. }
  3711. }
  3712. if (indx && qs("#tracks .captions")) {
  3713. qs("#tracks .captions").options[indx].style.backgroundColor = "#f00";
  3714. }
  3715.  
  3716. return
  3717. },
  3718. ontimeout: () => {
  3719. error(`SmartyDownload: ${name} (${n} of ${t}) has timed out`);
  3720. Bcd.link.classList.replace('download_started', 'download_error');
  3721.  
  3722. return
  3723. },
  3724. onabort: () => {
  3725. debug(`SmartyDownload: ${name} (${n} of ${t}) has been aborted`)
  3726.  
  3727. return
  3728. }
  3729. })
  3730. })
  3731. title = Bcd.flattenTitle(d.title);
  3732. total = (asVideo && asPoster) ? 2 : 1;
  3733. if (asVideo) {
  3734. ab0 = ++Bcd.downloadIndex;
  3735. abo = asPoster ? `${ab0}-${ab0 + 1}` : '';
  3736. ext = asVideo.url.slice(asVideo.url.lastIndexOf('.'));
  3737. downloadURLs(asVideo.url, title + ext, asVideo.type, 1, total, ab0, abo);
  3738. }
  3739. if (asPoster) {
  3740. ab1 = ++Bcd.downloadIndex;
  3741. abo = asVideo ? `${ab1 - 1}-${ab1}` : '';
  3742. ext = asPoster.url.slice(asPoster.url.lastIndexOf('.'));
  3743. downloadURLs(asPoster.url, title + ext, asPoster.type, total, total, ab1, abo);
  3744. }
  3745. if (asAux) {
  3746. ab1 = ++Bcd.downloadIndex;
  3747. asAux = Bcd.captions[asAux];
  3748. indx = asAux.index;
  3749. ext = asAux.url.slice(asAux.url.lastIndexOf('.'));
  3750. downloadURLs(asAux.url, `${title}.${asAux.lang + ext}`, asAux.type, 1, 1, ab1, null);
  3751. }
  3752.  
  3753. return [ab0, ab1]
  3754. },
  3755.  
  3756.  
  3757. init: (e) => {
  3758. let ext, source, torrent;
  3759.  
  3760. debug('>>>>>>>>>>>>>> Bcd init <<<<<<<<<<=', e);
  3761.  
  3762. if (qs("title") && ! Bcd.loader) {
  3763. addListener(qs("title"), function(e) {
  3764. Bcd.init("loader");
  3765. },{ childList: true });
  3766. Bcd.loader = true;
  3767. }
  3768.  
  3769. if (! Bcd.unloader) {
  3770. w.addEventListener('beforeunload', (e) => {
  3771. try {
  3772. let vdl = qs("#video_download_dialog");
  3773. let svd = qs("style#video_download");
  3774. if (vdl) vdl.parentNode.removeChild(vdl);
  3775. if (svd) svd.parentNode.removeChild(svd);
  3776. } catch (e) {}
  3777. }, false);
  3778. Bcd.unloader = true;
  3779. }
  3780. Bcd.captions = [];
  3781. Bcd.downloads = [];
  3782. Bcd.videoButtons = 0;
  3783. Bcd.downloadIndex = -1;
  3784. Bcd.download_poster = false;
  3785.  
  3786. if (Bcd.dialog) {
  3787. Bcd.dialog.parentNode.removeChild(Bcd.dialog);
  3788. Bcd.dialog = null;
  3789. }
  3790. if (Bcd.playerAPI = qs("video#player")) {
  3791. Bcd.videoSources = 0;
  3792. Bcd.src = {videos: []};
  3793.  
  3794. if (w.webtorrent) {
  3795. torrent = w.webtorrent.torrents[0];
  3796. torrent.on('ready', () => {
  3797. source = torrent.urlList[0];
  3798. Bcd.addSource({src: source, type: "torrent"});
  3799. })
  3800. }
  3801. if (source = qs("source:not([dl])", Bcd.playerAPI)) {
  3802. Bcd.addSource(source);
  3803. }
  3804.  
  3805. if (!qs("video#player.bcd")) {
  3806. addListener(Bcd.playerAPI, function(e) {
  3807. Bcd.addSource(e.target);
  3808. },{ childList: true, subtree: true });
  3809.  
  3810. if (Bcd.playerAPI.readyState > 0) Bcd.onloadedmetadata();
  3811. Bcd.playerAPI.addEventListener('loadedmetadata', Bcd.onloadedmetadata);
  3812. Bcd.playerAPI.classList.add("bcd");
  3813. }
  3814. }
  3815. else if (w.location.pathname.indexOf('/video') !=-1) {
  3816. if (e == "timed") {
  3817. let err = qs("#page-bar .page-title");
  3818. /* Server Error */
  3819. if (err && err.innerText.match(/.*((server)+\serror)+$/i)) return
  3820. }
  3821. w.setTimeout(Bcd.init, 250, "timed")
  3822. }
  3823. },
  3824.  
  3825.  
  3826. addSource: (source = null) => {
  3827. let vid, ext, s, poster, postersize;
  3828.  
  3829. if (!Bcd.download_poster) {
  3830. if (Bcd.playerAPI.poster) {
  3831. poster = {url: null, type: null, width: 0, height: 0};
  3832. poster.url = new URL(Bcd.playerAPI.poster);
  3833. poster.url = poster.url.href;
  3834. if (postersize = poster.url.match(/_(\d+)x(\d+)\./)) {
  3835. poster.width = postersize[1];
  3836. poster.height = postersize[2];
  3837. }
  3838. poster.type = qs('meta[property="og:image:type"]').content || "image/jpeg";
  3839. Bcd.src.poster = poster;
  3840. Bcd.download_poster = true
  3841. }
  3842. }
  3843. if (source && source.src) {
  3844. ext = source.src.slice(source.src.lastIndexOf('.'));
  3845. s = new URL(source.src);
  3846. if (source.type == "torrent") source.type = "video/mp4 (torrent)";
  3847. else source.setAttribute("dl", Bcd.videoSources);
  3848. vid = {ext: ext, url: s.href, type: source.type, width: 0, height: 0, error: null};
  3849. Bcd.src.videos.push(vid);
  3850. debug(`Video source found: ${source.type}, ${s.href}`);
  3851. Bcd.load("source", vid);
  3852. Bcd.videoSources += 1;
  3853. }
  3854. },
  3855.  
  3856.  
  3857. onloadedmetadata: (e) => {
  3858. let width = Bcd.playerAPI.videoWidth,
  3859. height = Bcd.playerAPI.videoHeight,
  3860. curSrc = Bcd.playerAPI.currentSrc;
  3861.  
  3862. qs("#takeScreenShot").classList.remove("disabled");
  3863. Bcd.src.videos.forEach( (v, i) => {
  3864. debug("url = curSrc ? "+ (v.url == curSrc) +" url: "+v.url);
  3865. if (v.url == curSrc) {
  3866. v.width = width;
  3867. v.height = height;
  3868. if (Bcd.dialog) {
  3869. if (qs(".download_details.v"+ i, Bcd.dialog).innerText.indexOf(" x ") ==-1 &&
  3870. ! qs(".download_details.v"+ i +" ul", Bcd.dialog))
  3871. {
  3872. let dims = `<br />&nbsp;&nbsp;${width} x ${height}`;
  3873. qs(".download_details.v"+ i, Bcd.dialog).innerHTML += dims;
  3874. debug("loadedmetadata Bcd.src.videos: ", Bcd.src.videos);
  3875. debug("Bcd.dialog video("+i+") dimensions added");
  3876. }
  3877. }
  3878. }
  3879. })
  3880. },
  3881.  
  3882.  
  3883. animate: ({timing, draw, duration}) => {
  3884. let start = performance.now();
  3885.  
  3886. requestAnimationFrame(function animate(time) {
  3887. let timeFraction = (time - start) / duration;
  3888. if (timeFraction > 1) timeFraction = 1;
  3889. let progress = timing(timeFraction);
  3890. draw(progress);
  3891. if (timeFraction < 1) {
  3892. requestAnimationFrame(animate);
  3893. }
  3894. });
  3895. },
  3896.  
  3897.  
  3898. prettyBytes: (bytes=0) => {
  3899. let c, i = 0, b = parseInt(Math.abs(bytes)), g = [' B',' kB',' mB',' gB',' tB', ' pB'];
  3900. if (b < 1) return '0.00 B';
  3901. while (b >= 1024) { b /= 1024; i+=1 };
  3902. c = `${Math.round(b * 10) /10}.0`;
  3903. c = c.slice(0, c.indexOf(".") + 2);
  3904. return c + g[i]
  3905. },
  3906.  
  3907.  
  3908. downloadIcon: '<svg class="svg-inline--fa fa-w-18 action-icon fa-fw" focusable="false" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="0.55in" height="0.55in">'+
  3909. '<path fill="currentColor" stroke="currentColor" stroke-width="2" d="M 0.17,37.33 C 3.00,37.11 4.50,44.67 4.83,47.39 4.83,47.39 45.33,47.39 45.33,47.39 45.33,47.39 45.58,37.19 49.61,36.83 '+
  3910. '48.11,43.44 47.30,46.66 47.17,49.72 47.17,49.72 21.39,49.61 21.39,49.61 21.39,49.61 5.50,28.81 5.50,28.81 5.50,28.81 15.31,28.81 15.31,28.81 16.06,6.12 29.50,2.19 42.38,0.62 33.50,4.62 '+
  3911. '23.10,9.76 24.28,28.89 24.28,28.89 34.25,28.88 34.25,28.88 34.25,28.88 18.61,49.67 18.61,49.67 18.61,49.67 3.00,49.67 3.00,49.67 3.22,49.67 1.33,42.56 0.06,37.28M 40.31,-1.94" /></svg>'
  3912. };
  3913.  
  3914.  
  3915. const xhrRequest = function(opt) {
  3916. var xhr = null;
  3917. var saveAs = function(blob, name) {
  3918. return new Promise((resolve, reject) => {
  3919. var url;
  3920. var success = 0;
  3921. var a = d.createElementNS("http://www.w3.org/1999/xhtml", "a");
  3922. const removeLink = (url) => {setTimeout(() => {(w.URL || w.webkitURL || w).revokeObjectURL(url);}, 20000)}
  3923. if ("download" in a) {
  3924. success = 1;
  3925. (url = (w.URL || w.webkitURL || w).createObjectURL(blob), setTimeout(function() {
  3926. a.href = url;
  3927. a.download = name;
  3928. a.dispatchEvent(new MouseEvent('click'));
  3929. removeLink(url);
  3930. debug('download saveAs 1, linkclick');
  3931. resolve();
  3932. }))
  3933. }
  3934. else if ("application/octet-stream" === blob.type && w.FileReader) {
  3935. success = 1;
  3936. var fr = new FileReader;
  3937. fr.onloadend = function() {
  3938. var f = fr.result.replace(/^data:[^;]*;/, "data:attachment/file;");
  3939. w.open(f, "_blank") || (w.location.href = f);
  3940. debug('download saveAs 2, FileReader');
  3941. resolve();
  3942. };
  3943. fr.readAsDataURL(blob);
  3944. }
  3945. else {
  3946. success = 1;
  3947. url = (w.URL || w.webkitURL || w).createObjectURL(blob);
  3948. "application/octet-stream" === blob.type ?
  3949. w.location.href = url :
  3950. w.open(url, "_blank") || (w.location.href = url);
  3951. removeLink(a, url);
  3952. debug('download saveAs 3, w.open');
  3953. resolve();
  3954. }
  3955. if (!success) reject()
  3956. })
  3957. }
  3958.  
  3959. var getFile = function () {
  3960. xhr = new XMLHttpRequest();
  3961. let abort = xhr.abort;
  3962. abort = abort.bind(xhr);
  3963. xhr.timeout = opt.timeout;
  3964. xhr.onerror = (function() {
  3965. if (xhr.readyState === 4 && xhr.status === 0 && xhr.response === null) {
  3966. let ext = opt.url.slice(opt.url.lastIndexOf('.') + 1);
  3967. opt.onerror({error: "Possible Cross-Origin Request Block", ext: `${ext}`})
  3968. } else opt.onerror
  3969. });
  3970. xhr.onabort = opt.onabort;
  3971. xhr.ontimeout = opt.ontimeout;
  3972. xhr.onprogress = opt.onprogress;
  3973. xhr.onload = (e) => {
  3974. e = e.target;
  3975. if (e.status == 200 || e.status == 304 && e.readyState == 4) {
  3976. let type = e.response.type || opt.type || 'application/octet-stream';
  3977. let blob = new Blob([e.response], { type: type });
  3978. saveAs(blob, (opt.name || "download")).then( (val) => {
  3979. debug('saveAs Success');
  3980. opt.onload();
  3981. }).catch( (val) => {
  3982. opt.onerror({error: `saveAs( "${opt.name}" ) Failed`});
  3983. });
  3984. }
  3985. else opt.onerror({error: `${e.status}: ${e.statusText}`});
  3986. }
  3987.  
  3988. xhr.open('GET', opt.url, true);
  3989. xhr.responseType = 'blob';
  3990. xhr.send();
  3991. return {abort: abort}
  3992. }
  3993. return getFile()
  3994. }
  3995.  
  3996.  
  3997. const SmartyDownload = function(params) {return xhrRequest(params);}
  3998.  
  3999.  
  4000. const getPublishDate = (u = []) => {
  4001. let now, days, scs, mis, hrs, dys, wks, yrs, utc, sec;
  4002. let out = "",
  4003. mos = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
  4004.  
  4005. if (u.length) {
  4006. utc = u[3];
  4007. now = new Date();
  4008. sec = typeof u[7] !== 'undefined';
  4009. dys = now.getUTCDate() - parseInt(u[5]);
  4010. hrs = now.getUTCHours() - parseInt(u[1]);
  4011. mis = now.getUTCMinutes() - parseInt(u[2]);
  4012. yrs = now.getUTCFullYear() - parseInt(u[6]);
  4013. mos = now.getUTCMonth() - mos.indexOf(String(u[4]));
  4014. scs = sec ? now.getUTCSeconds() - parseInt(u[7]) : now.getUTCSeconds();
  4015. days = new Date(now.getUTCFullYear(), now.getUTCMonth(), 0).getDate();
  4016.  
  4017. if (mis < 0) {hrs -= 1; mis += 60;}
  4018. if (hrs < 0) {dys -= 1; hrs += 24;}
  4019. if (dys < 0) {mos -= 1; dys += days;}
  4020. if (mos < 0) {yrs -= 1; mos += 12;}
  4021. if (yrs < 0) {yrs = 0;}
  4022.  
  4023. if (yrs) {
  4024. out = `${yrs === 1 ? "a" : yrs} year${yrs > 1 ? 's' : ''}`;
  4025. if (mos > 0) out += `, ${mos} month${mos > 1 ? 's' : ''}`;
  4026. }
  4027. else if (mos > 0) {
  4028. out = `${mos} month${mos > 1 ? 's' : ''}`;
  4029. if (dys > 6) {
  4030. wks = parseInt(dys / 7);
  4031. out += `, ${wks === 1 ? "a" : wks} week${wks > 1 ? 's' : ''}`;
  4032. }
  4033. else if (dys > 0) out += `, ${dys === 1 ? "a" : dys} day${dys > 1 ? 's' : ''}`;
  4034. }
  4035. else if (dys > 0) {
  4036. if (dys > 6) {
  4037. wks = parseInt(dys / 7);
  4038. out = `${wks === 1 ? "a" : wks} week${wks > 1 ? 's' : ''}`;
  4039. if (dys % 7 > 0) out += `, ${dys % 7} day${dys % 7 > 1 ? 's' : ''}`;
  4040. }
  4041. else if (dys > 0) {
  4042. out = `${dys === 1 ? "a" : dys} day${dys > 1 ? 's' : ''}`;
  4043. if (hrs > 0) out += `, ${hrs} hour${hrs > 1 ? 's' : ''}`;
  4044. }
  4045. }
  4046. else if (hrs > 0) {
  4047. out = `${hrs === 1 ? "an" : hrs} hour${hrs > 1 ? 's' : ''}`;
  4048. if (mis > 0) out += `, ${mis} minute${mis > 1 ? 's' : ''}`;
  4049. }
  4050. else if (mis > 0 || (! sec && scs > 30)) {
  4051. if (scs > 30) mis += 1;
  4052. if (mis < 60) out = `${mis === 1 ? "a" : mis} minute${mis > 1 ? 's' : ''}`;
  4053. else out = "an hour";
  4054. }
  4055. else if (scs > 0) out = `${scs} second${scs > 1 ? 's' : ''}`;
  4056. else out = "a moment";
  4057.  
  4058. return out
  4059. }
  4060. }
  4061.  
  4062. const qs = (selector, el=document) => el.querySelector(selector);
  4063. const qsa = (selector, el=document) => el.querySelectorAll(selector);
  4064. const dce = (tag) => document.createElement(tag);
  4065.  
  4066. const addListener = (target, fn, config) => {
  4067. var cfg = Object.assign({}, {attributes:!1, childList:!1,
  4068. characterData:!1, subtree:!1}, config);
  4069. var observer = new MutationObserver((mutations) => {
  4070. mutations.forEach((mutation) => { fn(mutation) })});
  4071. observer.observe(target, cfg);
  4072. return observer
  4073. }
  4074.  
  4075. const msg = function() {console.log(`\u2139\ufe0f %c[${name}] ${Array.prototype.slice.call(arguments).join(" ")}`, "font-weight: 600; color: #0B149E; text-shadow: 1px 1px #DDD;");}
  4076.  
  4077. const error = function() {console.error(`[${name}] ${Array.prototype.slice.call(arguments).join(" ")}`);}
  4078.  
  4079. const debug = function() {
  4080. if (BC_Debug) {
  4081. let n, m = "";
  4082. for (n in arguments) {
  4083. if (typeof arguments[n] === "object" || typeof arguments[n] === "array") {
  4084. m += "%o";
  4085. console.log(`\ud83d\udd28 %c[${name}] ${m}`, "font-weight: 400; color: #853f02; text-shadow: 1px 1px #DDD;", arguments[n]);
  4086. return;
  4087. } else m += arguments[n] + " ";
  4088. }
  4089. console.log(`\ud83d\udd28 %c[${name}] ${m}`, "font-weight: 400; color: #BC5802; text-shadow: 1px 1px #DDD;");
  4090. }
  4091. }
  4092.  
  4093. /* Not in Frames or Cloudflare error pages */
  4094. if (w.self == w.top && !qs("#cf-error-details")) {
  4095. BC.init().then( results => {
  4096. if(results) {BC.chuteMePlease(); Bcd.init("orig")}
  4097. else console.error('S_marty: Error initialising SmartChute');
  4098. });
  4099. }
  4100.  
  4101. }) ();