localav

Lightweight avfun video dumper

  1. // ==UserScript==
  2. // @name localav
  3. // @namespace https://github.com/doomred
  4. // @description Lightweight avfun video dumper
  5. // @version 0.22
  6. // @encoding utf-8
  7. // @license GPLv3
  8. // @copyleft dye `Eric' jarhoo
  9. // @author dye `Eric' jarhoo
  10. // @homepageURL htpp://saltyremix.com
  11. // @icon https://raw.github.com/doomred/localav/master/localav_icon.png
  12. // @include http://*.acfun.*/v/ac*
  13. // @run-at document-end
  14. // @grant GM_getValue
  15. // @grant GM_setValue
  16. // @grant GM_xmlhttpRequest
  17. // @grant GM_openInTab
  18. // @grant GM_registerMenuCommand
  19. // ==/UserScript==
  20.  
  21. /***********
  22. * Many thanks for the followings, without you the mates,
  23. * `localav' would not make sense.
  24. * zjworks for debuging & testing
  25. * 持续飞翔的胖子 & acfun_tv & 冷色猫咪 for suggestion alternative solution
  26. *
  27. \*********************************************************/
  28.  
  29.  
  30.  
  31. function forceupdate() {
  32. 'use strict';
  33. GM_openInTab('https://raw.github.com/doomred/localav/master/localav.user.js');
  34. return 0;
  35. }
  36.  
  37. var lvDebug, lvLeft, lvBt, lvEasterEgg;
  38. lvDebug = 0; // debug usage, value: 0/1/2
  39. lvLeft = GM_getValue('gm_lv_left', '1em');
  40. lvBt = GM_getValue('gm_lv_bt', '1em');
  41. lvEasterEgg = 0;
  42. GM_registerMenuCommand('localav| Force Update!', forceupdate, 'a');
  43.  
  44. var lvDiv, lvCloseDiv, lvContDiv;
  45. /* init lv-box */
  46. lvDiv = document.createElement('div');
  47. lvDiv.style.border = '2px solid';
  48. lvDiv.style.left = lvLeft;
  49. lvDiv.style.bottom = lvBt;
  50. lvDiv.style.display = 'none'; //init
  51. lvDiv.style.lineHeight = '1em'; //fix inherit
  52. lvDiv.style.zIndex = '9999';
  53. lvDiv.style.position = 'fixed';
  54. lvDiv.style.textAlign = 'right';
  55. lvDiv.style.backgroundColor = '#ffe'; //may use BG-image
  56. lvDiv.id = 'lv-box';
  57. lvDiv.setAttribute('draggable', 'true');
  58. window.document.body.appendChild(lvDiv);
  59.  
  60. lvCloseDiv = document.createElement('span');
  61. lvCloseDiv.id = 'lv-topbar';
  62. lvCloseDiv.innerHTML = '[X]';
  63. lvCloseDiv.style.cursor = 'pointer';
  64. lvCloseDiv.style.fontSize = '16pt';
  65. lvCloseDiv.style.color = 'white';
  66. lvCloseDiv.style.background = '#810400';
  67. lvCloseDiv.onclick = function() {
  68. lvDiv.style.display = 'none';
  69. };
  70. lvDiv.appendChild(lvCloseDiv);
  71.  
  72. lvContDiv = document.createElement('div');
  73. lvContDiv.id = 'lv-contbox';
  74. lvDiv.appendChild(lvContDiv);
  75.  
  76. var lvTarget, anchorBtn, i, lvName;
  77. anchorBtn = document.getElementsByTagName('a');
  78. lvTarget = [];
  79. window.lvName = [];
  80. for(i = anchorBtn.length; i--;) {
  81. if(anchorBtn[i].getAttribute('title')) {
  82.  
  83. if(anchorBtn[i].getAttribute('title').search('Part') !== -1) { // dirty hack, may fail
  84. lvTarget.push(anchorBtn[i].getAttribute('data-vid'));
  85. window.lvName.push(anchorBtn[i].innerHTML);
  86. }
  87. }
  88. }
  89.  
  90.  
  91. for(i = lvTarget.length; i--;) {
  92. playerDLURL = "https://ssl.acfun.tv//aliyun/index.php?&type=mobileclient&vid=" + lvTarget[i];
  93. // key core of localav, another API: jiexi.avfun.info/index.php?vid=xxx
  94. if(lvDebug) {window.alert(playerDLURL);}
  95.  
  96. function dynaparts(obj, id) {
  97. var key, lvContBox, lvContSize, lvContName, temp, i, lvContURL, contDiv, lvContQuality, lvContType, lvTowerBuilder;
  98. contDiv = window.top.document.getElementById('lv-contbox');
  99. lvContName = document.createElement('div');
  100. lvContName.style.margin = '1em 0 0 0';
  101. lvContName.style.textAlign = 'center';
  102. lvContName.innerHTML = window.lvName[id];
  103. contDiv.appendChild(lvContName);
  104. for(key in obj) {
  105. if(obj.hasOwnProperty(key)) {
  106. lvContBox = document.createElement('div');
  107. lvContBox.classList.add('lv-content');
  108. lvContBox.style.margin = '0 1.5em 0 1.5em';
  109. lvContBox.style.cursor = 'pointer';
  110. lvContBox.style.cssFloat = 'left';
  111. lvContQuality = obj[key].quality;
  112. lvContType = obj[key].files[0].type;
  113. temp = 0;
  114. lvContURL = '';
  115. for(i = 0; i < obj[key].files.length; i++) {
  116. lvContURL += obj[key].files[i].url;
  117. lvContURL += '^'; // split urls
  118. temp += obj[key].files[i].bytes; // add up bytes
  119. }
  120. lvContBox.setAttribute('data-lv', lvContURL);
  121. if(lvDebug) {window.alert(lvContURL);}
  122. temp = temp / 1024 / 1024 * 100; // leave two float point
  123. lvContSize = parseInt(temp, 10) / 100 + 'MB';
  124. if(lvContURL.search('.hlv?') !== -1) { // sina hlv fix
  125. lvContType = 'hlv';
  126. }
  127. if(!parseInt(lvContSize, 10)) {lvContSize = '大小仍是个迷';}
  128. lvContBox.innerHTML = lvContQuality + ' [' + lvContType + ']<br />' + lvContSize;
  129. lvContBox.addEventListener('click', function (e) { // download with purejs, midified via SO
  130. var iframe, eventSender, cSrc, arraySrc, i;
  131. if (!e && window.event) {e = window.event;}
  132. eventSender = (window.event) ? e.srcElement : e.target;
  133. cSrc = eventSender.getAttribute('data-lv');
  134. if(lvDebug > 1) {window.alert(lvContURL);}
  135. arraySrc = cSrc.split('^');
  136. if(cSrc.search('.hlv?') !== -1) { // sina hlv fix
  137. for(i = 0; i < arraySrc.length - 1; i++) { //dirty hack, trim tailling one
  138. if(lvDebug > 1) {alert(arraySrc[i]);}
  139. GM_openInTab(arraySrc[i]);
  140. }
  141. temp = null;
  142. } else {
  143. for(i = 0; i < arraySrc.length - 1; i++) {
  144. iframe = document.createElement('iframe');
  145. iframe.style.display = 'none';
  146. iframe.style.position = 'absoulte';
  147. iframe.style.top = '0px';
  148. iframe.style.left = '0px';
  149. iframe.src = arraySrc[i];
  150. document.body.appendChild(iframe);
  151. }
  152. }
  153. }, false);
  154. if(lvContURL.search('&id=tudou') !== -1 || 0) { // add broken parsed urls here, blame API
  155. lvContBox.style.backgroundColor = 'grey';
  156. lvContBox.innerHTML += "<br />可能失效";
  157. }
  158. contDiv.appendChild(lvContBox);
  159. lvContBox.id = 'lv_' + id;
  160. /* fix sina duplicate generated API*/
  161. if(lvContURL.search('v.iask.com') !== -1) {
  162. break;
  163. }
  164. }
  165. }
  166. lvTowerBuilder = document.createElement('hr');
  167. lvTowerBuilder.style.clear = 'both';
  168. contDiv.appendChild(lvTowerBuilder);
  169. }
  170. GM_xmlhttpRequest({
  171. method: "GET",
  172. url: playerDLURL,
  173. headers: {
  174. "Accept": "application/json"
  175. },
  176. onload: function(response) {
  177. var foo = JSON.parse(response.responseText)
  178. var bar = response.finalUrl.search('mobileclient&vid=');
  179. var index = lvTarget.indexOf(response.finalUrl.substr(bar + 17));
  180. dynaparts(foo.result, index);
  181.  
  182. }
  183. });
  184. }
  185.  
  186. if(lvTarget.length > 4 ) { //side style trigger
  187. var lvSidemode;
  188. window.lvSidemode = 1;
  189. }
  190.  
  191. function feedbugback() { //make a dirty joke here
  192. lvCloseDiv.style.height = '100px';
  193. lvFB = document.createElement('div');
  194. lvFB.innerHTML = '[feedback]';
  195. lvFB.style.height = '50px';
  196. lvFB.style.width = '300%';
  197. lvFB.style.borderRadius = '100px';
  198. lvFB.style.backgroundColor = 'brown';
  199. lvFB.style.cursor = 'pointer';
  200. lvFB.style.textAlign = 'right';
  201. lvFB.style.width = '100%';
  202. lvFB.style.textDecoration = 'underline';
  203. lvFB.onclick = function() {
  204. window.open('https://github.com/doomred/localav/issues', 'FEED_ME_BUG');
  205. };
  206. lvDiv.appendChild(lvFB);
  207. }
  208.  
  209. /* HTML5 drag & drop element
  210. * Modified via http://jsfiddle.net/robertc/kKuqH/
  211. * More info https://stackoverflow.com/questions/6230834/html5-drag-and-drop-anywhere-on-the-screen */
  212. function drag_start(event) {
  213. var style = window.getComputedStyle(event.target, null);
  214. event.dataTransfer.setData("text/plain",
  215. parseInt(style.getPropertyValue("left"),10) + ',' + parseInt(event.clientX) + ',' +
  216. parseInt(style.getPropertyValue("bottom"),10) + ',' + parseInt(event.clientY));
  217. }
  218. function drag_over(event) {
  219. event.preventDefault();
  220. return false;
  221. }
  222. function drop(event) {
  223. var offset = event.dataTransfer.getData("text/plain").split(',');
  224. var dg = document.getElementById('lv-box');
  225. var dgRel = document.getElementById('lv-topbar');
  226. dg.style.left = (event.clientX + parseInt(offset[0],10) - parseInt(offset[1],10)) + 'px';
  227. dg.style.bottom = (-event.clientY + parseInt(offset[2],10) + parseInt(offset[3],10)) + 'px';
  228. if(window.lvSidemode) {
  229. dgRel.style.left = (event.clientX + parseInt(offset[0],10) - parseInt(offset[1],10)) + 'px';
  230. dgRel.style.top = (event.clientY + parseInt(dgRel.style.top,10) - parseInt(offset[3],10)) + 'px';
  231. } else {
  232. GM_setValue('gm_lv_left', dg.style.left);
  233. GM_setValue('gm_lv_bt', dg.style.bottom);
  234. }
  235. event.preventDefault();
  236. return false;
  237. }
  238. if(document.getElementById('lv_' + (lvTarget.length - 1))) {
  239.  
  240. }
  241.  
  242. function checkload() {
  243. // 'use strict';
  244. if(document.getElementById('lv_' + (lvTarget.length - 1))) { //sidebar mode on, rewrite lvCloseDiv style
  245. document.getElementById('lv-box').style.display = 'block';
  246. document.getElementById('lv-box').addEventListener('dragstart',drag_start,false);
  247. document.body.addEventListener('dragover',drag_over,false);
  248. document.body.addEventListener('drop',drop,false);
  249. if(window.lvSidemode) {
  250. lvCloseDiv = window.top.document.getElementById('lv-topbar');
  251. lvDiv = window.top.document.getElementById('lv-box');
  252. lvDiv.style.position = 'fixed';
  253. lvDiv.style.left = lvDiv.style.bottom = 0;
  254. lvDiv.style.height = '100%';
  255. lvDiv.style.overflowY = 'scroll';
  256. lvCloseDiv.style.position = 'fixed';
  257. lvCloseDiv.style.left = lvCloseDiv.style.top = 0;
  258. }
  259. if(lvEasterEgg) {feedbugback()}
  260. } else {
  261. window.setTimeout(checkload, 500); //api load
  262. }
  263. }
  264. window.setTimeout(checkload, 500);