Advertisement
lulzScript

FA Conglomerator 3.2.1 [2018-06-12]

May 13th, 2012
4,699
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name           FA Conglomerator
  3. // @namespace      http://lulz.net/
  4. // @version        3.2.1
  5. // @description    Asynchronously extracts all submissions from a FurAffinity gallery or scraps collection and displays them as a list of links in a new window. A button "Conglomerate" is inserted into the gallery/scraps collection page which activates the script. Additional input boxes allow the specification of the maximum number of concurrent requests used, a request delay time and the number of retries. Install guide, updates, changelog and other scripts @ http://pastebin.com/u/lulzScript
  6. // @include        http*://*.furaffinity.net/gallery/*
  7. // @include        http*://*.furaffinity.net/scraps/*
  8. //
  9. // @grant GM_setValue
  10. // @grant GM_getValue
  11. //
  12. // ==/UserScript==
  13.  
  14.  
  15. //Some constants
  16. //Default is 48 submissions per page. Valid values can be taken from the drop-down box on the gallery page.
  17. var congPerPage = 48;
  18. //Default is up to 10 retries for a page/submission to load.
  19. var congRetries = 10;
  20.  
  21. //Maximum number of concurrent requests. Default is 4.
  22. //Note: Your browser may still generally limit the maximum concurrent requests per server according to its system settings.
  23. var maxConcReq = 4;
  24.  
  25. //The polling times in miliseconds for submission requests and submission list requests.
  26. //Each request will check for an available request slot using this interval.
  27. //This is needed to limit concurrent request to maxConcReq.
  28. //The value of submListPollTime should be lower than submPollTime, to give page loading priority over submission loading.
  29. var submPollTime = 40;
  30. var submListPollTime = 20;
  31.  
  32. //Nummber of miliseconds to wait before a request slot is freed for further request
  33. //Can be used to reduce the stress on the server but links will take longer to conglomerate
  34. var congFreeSlotDelay = 500;
  35.  
  36.  
  37. //When each request either loaded or gave an error, the rCount should be back at 0.
  38. //Pending downlaods lead to rCount > 0
  39. function checkIfDone()
  40. {
  41.   if (rCount == 0)
  42.     lStatus.innerHTML = '<b> Done!</b>';
  43. }
  44.  
  45. //Frees a used request slot
  46. function freeRequestSlot()
  47. {
  48.   concReq--;
  49. }
  50.  
  51. //Function called when a submission page was successfully loaded.
  52. //req = XMLHttpRequest object of the loaded page
  53. //linkObj = link object within the new link window
  54. function onSubm (req, linkObj)
  55. {
  56.   //Extract download link from submission page
  57.   var submFile_ = req.responseText.match(/< *a *href=\"([^>\"]*)\" *> *Download *< *\/a *>/i);
  58.   if ( submFile_ )
  59.   {
  60.     var wProto = submFile_[1].replace(/^\/\//, faProto[0]);
  61.     //Insert extracted download link into link list
  62.     //Link displayed as text
  63.     linkObj.innerHTML = wProto;
  64.     //Also set the href attribut, to make the link clickable
  65.     linkObj.setAttribute('href', wProto);
  66.   }
  67.  
  68.   //Request finished successfully. Decrease request counter and check if rCount = 0.
  69.   rCount--;
  70.   checkIfDone();
  71.  
  72. }
  73.  
  74. //Function called when a submission page failed to load.
  75. //req = XMLHttpRequest object of the failed page
  76. //linkObj = link object within the new link window
  77. //retry = counter that indicates the current number of retries.
  78. function onSubmErr (req, linkObj, retry)
  79. {
  80.   //Display "Error" in the linklist if the link couldn't be extracted.
  81.   linkObj.innerHTML = '<b>Error (retry = ' + retry + '/' + congRetries + ')</b>';
  82.  
  83.   //Request finished with error. Decrease request counter and check if rCount = 0.
  84.   rCount--;
  85.   checkIfDone();
  86. }
  87.  
  88. //Loads a submission page asynchronously.
  89. //submURL = Full URL of submission.
  90. //linkObj = link object within the new link window
  91. //retry = counter that indicates the current number of retries.
  92. //After [congRetries] failed retries, the function fails.
  93. function getSubm (submURL, linkObj, retry)
  94. {
  95.   //Check for free request slot
  96.   if (concReq < maxConcReq)
  97.   {
  98.     //Reserve request slot
  99.     concReq++;
  100.  
  101.     //Display loading message and retries needed if any.
  102.     if (retry == 0)
  103.       linkObj.innerHTML = '<b>loading...</b>';
  104.     else
  105.       linkObj.innerHTML = '<b>loading...(retry = ' + retry + '/' + congRetries + ')</b>';
  106.  
  107.     var req = new XMLHttpRequest();
  108.     //Prepare request with full URL, containing the desired page number
  109.     req.open('GET', submURL, true);
  110.     //Function to call when loading state of page changes
  111.     req.onreadystatechange = function (evt)
  112.     {
  113.  
  114.       //Check if page was loaded (or not)
  115.       if (req.readyState == 4)
  116.       {
  117.         //Free reserved request slot
  118.         if (congFreeSlotDelay > 0)
  119.           setTimeout (freeRequestSlot, congFreeSlotDelay);
  120.         else
  121.           freeRequestSlot();
  122.  
  123.         if (req.status == 200)
  124.         {
  125.           //Page was sucessfully loaded
  126.           //Call handler for successful requests.
  127.           onSubm (req, linkObj);
  128.         }
  129.         else
  130.         {
  131.           //If page failed to load, retry up to [congRetries] times.
  132.           if ( retry < congRetries )
  133.           {
  134.             linkObj.innerHTML = 'waiting...[queued for retry ' + (retry+1) + '/' + congRetries + ']'
  135.             //The retry is done with setTimeout, instead of calling getSubm directly.
  136.             //This will prevent multiple recursive calls for each retry
  137.             setTimeout (getSubm, 0, submURL, linkObj, retry+1);
  138.           }
  139.           else
  140.           {
  141.             //Page could not be loaded even after retrying
  142.             //Call error handler.
  143.             onSubmErr (req, linkObj, retry);
  144.           }
  145.         }
  146.       }
  147.     };
  148.  
  149.     //Sends request and starts loading of the page.
  150.     req.send(null);
  151.   }
  152.   else
  153.     //If no request slot is available, poll for free request slot by calling this request again after a delay of [submPollTime] miliseconds.
  154.     setTimeout (getSubm, submPollTime, submURL, linkObj, retry)
  155. }
  156.  
  157. //Function called when a submission list page was successfully loaded.
  158. //req = XMLHttpRequest object of the loaded page
  159. //no = number of loaded page
  160. //retry = counter that indicates the current number of retries.
  161. function onSubmList (req, no, retry)
  162. {
  163.   //Extract all submission links
  164.   var rawSubmLinks = req.responseText.match(/<a href=\"\/view\/\d+\/\">/gi);
  165.  
  166.   //Continue as long as there are submission links found
  167.   //An empty page indicates the end of a gallery/scraps collection
  168.   if ( rawSubmLinks )
  169.   {
  170.     //Display page number
  171.     var div = congLinkWin.document.createElement('div');
  172.  
  173.     //Display page number an retries needed if any.
  174.     if (retry == 0)
  175.       div.innerHTML = '</br></br><b>Page ' + no + '</b></br></br>';
  176.     else
  177.       div.innerHTML = '</br></br><b>Page ' + no + ' (retry = ' + retry + '/' + congRetries + ')</b></br></br>';
  178.  
  179.     congLinkWin.document.body.insertBefore(div, null);
  180.  
  181.     //Iterate through all found links.
  182.     for (var i=0; i < rawSubmLinks.length; i++)
  183.     {
  184.       //Extract actual link from list of matched links
  185.       var submLink = rawSubmLinks[i].match(/<a href=\"\/view\/(\d+)\/\">/);
  186.       if ( submLink )
  187.       {
  188.         //Make full URL of submission page from link.
  189.         var submURL = faBaseURL + '/view/' + submLink[1] + '/';
  190.  
  191.         //Add new line for submission in link window and set status to "loading..."
  192.         var div = congLinkWin.document.createElement('div');
  193.         div.innerHTML = '<b>' + sCount + ': </b>';
  194.         var linkObj = congLinkWin.document.createElement('a');
  195.         linkObj.innerHTML = 'waiting...';
  196.         linkObj.setAttribute('target', '_blank');
  197.         div.insertBefore(linkObj, null);
  198.         congLinkWin.document.body.insertBefore(div, null);
  199.  
  200.         //New request. Increase request counter.
  201.         rCount++;
  202.         getSubm (submURL, linkObj, 0);
  203.  
  204.         //Increase submission counter
  205.         sCount++;
  206.  
  207.       }
  208.     }
  209.  
  210.     //New request. Increase request counter.
  211.     rCount++;
  212.     //Load next submission list page
  213.     getSubmList (submBaseURL, no+1, congPerPage, 0);
  214.   }
  215.   else
  216.   {
  217.     //There are no more pages. Display message.
  218.     var div = congLinkWin.document.createElement('div');
  219.     div.innerHTML = '</br></br><b>No more pages!</b>';
  220.     congLinkWin.document.body.insertBefore(div, null);
  221.   }
  222.  
  223.   //Request finished successfully. Decrease request counter and check if rCount = 0.
  224.   rCount--;
  225.   checkIfDone();
  226.  
  227. }
  228.  
  229. //Function called when a submission list page failed to load.
  230. //req = XMLHttpRequest object of the failed page
  231. //no = number of loaded page
  232. //retry = counter that indicates the current number of retries.
  233. function onSubmListErr (req, no, retry)
  234. {
  235.   //Page could not be loaded. Display Error message.
  236.   var div = congLinkWin.document.createElement('div');
  237.   div.innerHTML = '</br></br><b>Error - cannot load page ' + no + ' (retry = ' + retry + '/' + congRetries + ')!<br/>Further conglomeration stoppped.</b><br/>';
  238.   congLinkWin.document.body.insertBefore(div, null);
  239.  
  240.   //Request finished with error. Decrease request counter and check if rCount = 0.
  241.   rCount--;
  242.   checkIfDone();
  243. }
  244.  
  245. //Loads a submission list page from the gallery/scraps collection asynchronously.
  246. //baseURL = Base URL of gallery/scraps collection page.
  247. //no = Nummer of list page to load
  248. //perpage = submissions per page {24, 36, 48, 60}
  249. //retry = counter that indicates the current number of retries.
  250. //After [congRetries] failed retries, the function fails.
  251. function getSubmList (baseURL, no, perpage, retry)
  252. {
  253.   //Check for free request slot
  254.   if (concReq < maxConcReq)
  255.   {
  256.     //Reserve request slot
  257.     concReq++;
  258.  
  259.     var req = new XMLHttpRequest();
  260.     //Prepare request with full URL, containing the desired page number
  261.     req.open('POST', baseURL + no, true);
  262.     //Set additional request header
  263.     req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  264.  
  265.     //Function to call when loading state of page changes
  266.     req.onreadystatechange = function (evt)
  267.     {
  268.       //Check if page was loaded (or not)
  269.       if (req.readyState == 4)
  270.       {
  271.         //Free reserved request slot
  272.         if (congFreeSlotDelay > 0)
  273.           setTimeout (freeRequestSlot, congFreeSlotDelay);
  274.         else
  275.           freeRequestSlot();
  276.  
  277.         if (req.status == 200)
  278.         {
  279.           //Page was sucessfully loaded
  280.           //Call handler for successful requests.
  281.           onSubmList (req, no, retry);
  282.         }
  283.         else
  284.         {
  285.           //If page failed to load, retry up to [congRetries] times.
  286.           if ( retry < congRetries )
  287.             //The retry is done with setTimeout, instead of calling getSubmList directly.
  288.             //This will prevent multiple recursive calls for each retry and therefore save stack resources.
  289.             setTimeout (getSubmList, 0, baseURL, no, perpage, retry+1);
  290.           else
  291.           {
  292.             //Page could not be loaded even after retrying
  293.             //Call error handler.
  294.             onSubmListErr (req, no, retry);
  295.           }
  296.         }
  297.       }
  298.     };
  299.  
  300.     //Sends request and starts loading of the page.
  301.     req.send('perpage=' + perpage);
  302.   }
  303.   else
  304.     //If no request slot is available, poll for free request slot by calling this request again after a delay of [submListPollTime] miliseconds.
  305.     setTimeout(getSubmList, submListPollTime, baseURL, no, perpage, retry);
  306. }
  307.  
  308. //Global variables:
  309.  
  310. //Holds the object of the drop-down box containing the selected number of submissions per page
  311. var submPerPage;
  312. //Base URL of FA, including protocol and subdomain
  313. var faBaseURL;
  314. //Base URL of gallery/scraps
  315. var submBaseURL;
  316. //Reference to new link window
  317. var congLinkWin;
  318. //Counter for submission index
  319. var sCount;
  320. //Name of FA user whose galery/scraps are being conglomerated
  321. var faUsername;
  322. //Source of submission ("gallery" or "scraps")
  323. var submSrc;
  324. //Reference to status message in link window
  325. var lStatus;
  326. //Request counter. Used to determine when all submissions are loaded.
  327. var rCount;
  328. //Protocol http or https
  329. var faProto;
  330. //Counter for concurrent connections
  331. var concReq;
  332.  
  333. //Main script function. Called when conglomerator button is clicked
  334. function congStart(e)
  335. {
  336.   //Prevents the form's default action for a button
  337.   e.preventDefault();
  338.  
  339.   //Get FA base URL (including protocol and subdomain); gallery/scraps base URL; submission source and username
  340.   var baseURLs = document.location.href.match(/((.*furaffinity\.net)\/(gallery|scraps)\/([^\/]*))(?:\/|)/);
  341.   if ( baseURLs )
  342.   {
  343.     submBaseURL = baseURLs[1] + '/'; //Submission base URL
  344.     faBaseURL = baseURLs[2];         //FA base URL
  345.     submSrc = baseURLs[3];           //submission source
  346.     faUsername = baseURLs[4];        //FA username
  347.  
  348.     faProto = document.location.href.match(/(.*):\/\//);
  349.  
  350.     //Open new window for extracted submission links
  351.     congLinkWin = window.open();
  352.     if ( congLinkWin )
  353.     {
  354.       //Set title of new window
  355.       congLinkWin.document.title = 'FA conglomerator';
  356.       //Add title to link window
  357.       var span = congLinkWin.document.createElement('span');
  358.       span.innerHTML = '<b>List of ' + faUsername + '\'s ' + submSrc + ':</b>';
  359.       congLinkWin.document.body.insertBefore(span, null);
  360.  
  361.       //Add status message to link Window
  362.       lStatus = congLinkWin.document.createElement('span');
  363.       lStatus.innerHTML = '<b> Loading...</b>';
  364.       congLinkWin.document.body.insertBefore(lStatus, null);
  365.  
  366.       //Reset submission-, request and connection counter
  367.       sCount = 1;
  368.       rCount = 1;
  369.       concReq = 0;
  370.  
  371.       //Set maximum concurrent requests according to the input field
  372.       maxConcReq = parseInt(congMCRInput.value, 10);
  373.  
  374.       //Set free slot delay according to the input field
  375.       congFreeSlotDelay = parseInt(congFSDInput.value, 10);
  376.  
  377.       //Set retries according to the input field
  378.       congRetries = parseInt(congRetInput.value, 10);
  379.  
  380.       //Set number sumbissions per page using the selected value of the drop-down box if it is valid, otherwise uses default value
  381.       if (submPerPage)
  382.       {
  383.         var congPerPage_ = parseInt(submPerPage.value, 10);
  384.         if (!isNaN(congPerPage))
  385.           congPerPage = congPerPage_;
  386.       }
  387.  
  388.       //At this point faBaseURL and submBaseURL both matched correctly.
  389.       //Load first submission list page
  390.       getSubmList (submBaseURL, 1, congPerPage, 0, 0);
  391.     }
  392.   }
  393. }
  394.  
  395. function insertButton()
  396. {
  397.   //Find location of gallery control buttons
  398.   var browsebar = document.getElementsByClassName('page-options');
  399.  
  400.   //If found, insert conglomerator button
  401.   if ( browsebar.length >= 1 )
  402.   {
  403.     var bParent = browsebar[0];
  404.     bParent.insertBefore(congButton, bParent.firstChild);
  405.     bParent.insertBefore(congInputSpan, congButton.nextSibling);
  406.     bParent.insertBefore(document.createElement('br'), congInputSpan.nextSibling);
  407.     bParent.insertBefore(document.createElement('br'), congInputSpan.nextSibling);
  408.  
  409.     //Find drop-down box containing the valid values for the number of submissions per page
  410.     var submPerPage_ = document.getElementsByName('perpage');
  411.     if (submPerPage_.length > 0)
  412.     {
  413.       submPerPage = submPerPage_[0];
  414.       var submPerPageTitle = 'NOTE: This input box is part of the original webpage and lets you set the number of submissions you can view per page.\nThe conglomerator will also use this value for the number of submissions per page it will request from the server.';
  415.  
  416.       //Insert oder add tooltip text to original input field
  417.       if (!submPerPage.title)
  418.         submPerPage.title = submPerPageTitle;
  419.       else
  420.         submPerPage.title = submPerPage.title + '\n\n' + submPerPageTitle;
  421.     }
  422.  
  423.     buttonInserted = true;
  424.   }
  425. }
  426.  
  427. //Flag that is later set to true if button was inserted.
  428. var buttonInserted = false;
  429.  
  430. function onCongMCRInputChange(e)
  431. {
  432.   GM_setValue ('maxConcReq', congMCRInput.value);
  433. }
  434.  
  435. //Create button for conglomerator script
  436. var congButton = document.createElement('button');
  437. //Set button styles/attributes
  438. congButton.setAttribute('class', 'button');
  439. congButton.innerHTML = 'Conglomerate';
  440. congButton.style.marginRight = '10px';
  441. congButton.title = 'Start conglomeration';
  442.  
  443. //Set function to call on a click
  444. congButton.addEventListener ('click', congStart, false);
  445.  
  446. //Create span to group all conglomerator input fields
  447. var congInputSpan = document.createElement('span');
  448.  
  449. //Create and insert text label
  450. var congInputLabel = document.createElement('span');
  451. congInputLabel.innerHTML = 'Concurrent requests:';
  452. congInputSpan.insertBefore (congInputLabel, null);
  453.  
  454. //Create input field for maximum number of concurrent requests
  455. var congMCRInput = document.createElement('input');
  456. congInputSpan.insertBefore (congMCRInput, null);
  457.  
  458. //Create and insert text label
  459. congInputLabel = document.createElement('span');
  460. congInputLabel.innerHTML = 'Request delay [ms]:';
  461. congInputSpan.insertBefore (congInputLabel, null);
  462.  
  463. //Create input field for request delay
  464. var congFSDInput = document.createElement('input');
  465. congInputSpan.insertBefore (congFSDInput, null);
  466.  
  467. congInputLabel = document.createElement('span');
  468. congInputLabel.innerHTML = 'Retries:';
  469. congInputSpan.insertBefore (congInputLabel, null);
  470.  
  471. //Create input field for number of retries
  472. var congRetInput = document.createElement('input');
  473. congInputSpan.insertBefore (congRetInput, null);
  474.  
  475.  
  476.  
  477. //Set input styles/attributes
  478. congMCRInput.setAttribute('class', 'button');
  479. congMCRInput.setAttribute ('type', 'number');
  480. congMCRInput.setAttribute ('size', '3');
  481. congMCRInput.setAttribute ('min', '1');
  482. congMCRInput.style.width = '30px';
  483. congMCRInput.style.marginLeft = '5px';
  484. congMCRInput.style.marginRight = '10px';
  485. congMCRInput.title = 'Maximum number of concurrent requests made to the server.\nIf this number is too high it might trigger request restrictions and timeouts on the server side, leading to high numbers of retries and possibly incomplete conglomeration.';
  486.  
  487. //Load last used value from storage
  488. var maxConcReq_ = GM_getValue ('maxConcReq');
  489. //If value is valid, set as initial value
  490. if (maxConcReq_)
  491.   congMCRInput.value = maxConcReq_;
  492. else
  493.   congMCRInput.value = maxConcReq;
  494.  
  495. congMCRInput.addEventListener ('change', onCongMCRInputChange, false);
  496.  
  497.  
  498. function onCongFSDInputChange(e)
  499. {
  500.   GM_setValue ('congFreeSlotDelay', congFSDInput.value);
  501. }
  502.  
  503. //Set input styles/attributes
  504. congFSDInput.setAttribute('class', 'button');
  505. congFSDInput.setAttribute ('type', 'number');
  506. congFSDInput.setAttribute ('size', '3');
  507. congFSDInput.setAttribute ('min', '0');
  508. congFSDInput.style.width = '50px';
  509. congFSDInput.style.marginLeft = '5px';
  510. congFSDInput.style.marginRight = '10px';
  511. congFSDInput.title = 'Number of milliseconds [ms] to wait before a request slot is freed for further requests.\nIncreasing this number can reduce the stress on the server but conglomeration will take longer';
  512.  
  513.  
  514. //Load last used value from storage
  515. var congFreeSlotDelay_ = GM_getValue ('congFreeSlotDelay');
  516. //If value is valid, set as initial value
  517. if (congFreeSlotDelay_)
  518.   congFSDInput.value = congFreeSlotDelay_;
  519. else
  520.   congFSDInput.value = congFreeSlotDelay;
  521.  
  522. congFSDInput.addEventListener ('change', onCongFSDInputChange, false);
  523.  
  524.  
  525. function onCongRetInputChange(e)
  526. {
  527.   GM_setValue ('congRetries', congRetInput.value);
  528. }
  529.  
  530. //Set input styles/attributes
  531. congRetInput.setAttribute('class', 'button');
  532. congRetInput.setAttribute ('type', 'number');
  533. congRetInput.setAttribute ('size', '3');
  534. congRetInput.setAttribute ('min', '0');
  535. congRetInput.style.width = '30px';
  536. congRetInput.style.marginLeft = '5px';
  537. congRetInput.title = 'The number of retries for a single request if an error occurs.\nIf there are many requests with a high number of retries in the conglomerator window, try to decreas the number of concurrent requests and/or increase the request delay.';
  538.  
  539. //Load last used value from storage
  540. var congRetries_ = GM_getValue ('congRetries');
  541. //If value is valid, set as initial value
  542. if (congRetries_)
  543.   congRetInput.value = congRetries_;
  544. else
  545.   congRetInput.value = congRetries;
  546.  
  547. congRetInput.addEventListener ('change', onCongRetInputChange, false);
  548.  
  549.  
  550. //Try to insert button into page if page is already available at this time
  551. insertButton();
  552.  
  553. //Function onDCL is called when DOM tree has completely been loaded.
  554. //This is the safest way to start a script.
  555. document.addEventListener ('DOMContentLoaded',
  556.   function onDCL(e)
  557.   {
  558.     //If the button wasn't inserted before because the page wasn't
  559.     //available at that time, it will be inserted now.
  560.     if ( !buttonInserted )
  561.       insertButton();
  562.  
  563.   }, false
  564. );
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement