Advertisement
lulzScript

FA NewSubs Conglomerator 1.1.0 [2018-06-26]

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