Reset Steam Workshop Subscriptions

Unsubscribe from all Steam Workshop Addons with a single click.

  1. // ==UserScript==
  2. // @name Reset Steam Workshop Subscriptions
  3. // @name:nl Reset Steam Workshop Abonnementen
  4. // @namespace https://timmy.ws
  5. // @version 1.1.4
  6. // @license MIT
  7. // @supportURL hello@timmy.ws
  8. // @description Unsubscribe from all Steam Workshop Addons with a single click.
  9. // @description:nl Zeg al je Steam Workshop abonnementen op met één enkele klik.
  10. // @author Timmy
  11. // @compatible firefox
  12. // @compatible chrome
  13. // @compatible opera
  14. // @compatible safari
  15. // @match *://steamcommunity.com/id/*/myworkshopfiles/*mysubscriptions*
  16. // @match *://steamcommunity.com/profiles/*/myworkshopfiles/*mysubscriptions*
  17. // @grant GM_addStyle
  18. // @run-at document-end
  19. // ==/UserScript==
  20.  
  21. /*jslint browser: true*/
  22.  
  23. (function () {
  24. 'use strict';
  25.  
  26. var addons = [], addonTotal, pageTotal, sessionId;
  27.  
  28. // Returns the number of addons the user is subscribed to
  29. function getAddonTotal() {
  30. var regex, workshopInfo;
  31. regex = /([0-9]+)/g;
  32. workshopInfo = document.getElementsByClassName('workshopBrowsePagingInfo')[0].innerHTML;
  33. return parseInt(workshopInfo.match(regex)[2], 10);
  34. }
  35.  
  36. // Return the number of pages we have to analyse
  37. function getPageTotal(addonTotal) {
  38. return Math.ceil(addonTotal / 30);
  39. }
  40.  
  41. // Returns true if we are on a secured page
  42. function usingSSL() {
  43. return (document.location.protocol === 'https:') ? true : false;
  44. }
  45.  
  46. // Returns query variable (https://css-tricks.com/snippets/javascript/get-url-variables/)
  47. function getQueryVariable(variable) {
  48. var query, vars, i, pair;
  49.  
  50. query = window.location.search.substring(1);
  51. vars = query.split('&');
  52. for (i = 0; i < vars.length; i = i + 1) {
  53. pair = vars[i].split('=');
  54. if (pair[0] === variable) { return pair[1]; }
  55. }
  56. return false;
  57. }
  58.  
  59. // Returns the users' profile id
  60. function getBaseURL() {
  61. return document.querySelector(".user_avatar").href;
  62. }
  63.  
  64. // Returns the users' session id
  65. function getSessionId() {
  66. return document.getElementById('PublishedFileUnsubscribe').getElementsByTagName('input')[2].value;
  67. }
  68.  
  69. // Returns the appId and name of the game if a filter is applied, or false
  70. function getAppFilter() {
  71. var id, name;
  72.  
  73. id = getQueryVariable('appid');
  74. if (!id || id === "0") { return false; }
  75.  
  76. name = document.getElementById('searchedForApp').childNodes[0].nodeValue;
  77. return [ id, name ];
  78. }
  79.  
  80. // Display feedback to user
  81. function feedback(id, message) {
  82. var element = document.getElementById(id);
  83.  
  84. if (!element) {
  85. element = document.createElement('div');
  86. element.id = id;
  87. document.getElementById('unsub-progress').appendChild(element);
  88. } else if (element.firstChild) {
  89. while (element.firstChild) {
  90. element.removeChild(element.firstChild);
  91. }
  92. }
  93.  
  94. element.appendChild(document.createTextNode(message));
  95. }
  96.  
  97. // Unsubscribe from all addons
  98. function processAllAddons(xmlhttp) {
  99. if (addons.length < addonTotal) { return false; }
  100.  
  101. var i, j, handleStateChange, url;
  102.  
  103. feedback('ANALYSE_PROG', 'Analysing subscriptions... Done!');
  104.  
  105. j = 0;
  106. handleStateChange = function (index) {
  107. return function () {
  108. if (xmlhttp[index].readyState === 4) {
  109. j = j + 1;
  110. feedback('UNSUB_PROGRESS', 'Unsubscribing from addons... (' + Math.round(j / addonTotal * 100) + '%)');
  111. if (j === addonTotal) {
  112. feedback('UNSUB_PROGRESS', 'Unsubscribing from addons... Done!');
  113. feedback('END', 'Reloading page...');
  114. setTimeout(function () {
  115. window.location.reload();
  116. }, 1000);
  117. }
  118. }
  119. };
  120. };
  121.  
  122. xmlhttp = [];
  123.  
  124. url = (usingSSL()) ? 'https' : 'http';
  125. url += '://steamcommunity.com/sharedfiles/unsubscribe';
  126.  
  127. for (i = 0; i < addons.length; i = i + 1) {
  128. xmlhttp[i] = new XMLHttpRequest();
  129. xmlhttp[i].open('POST', url);
  130. xmlhttp[i].setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
  131. xmlhttp[i].onreadystatechange = handleStateChange(i);
  132. xmlhttp[i].send('id=' + addons[i][0] + '&appid=' + addons[i][1] + '&sessionid=' + sessionId);
  133. }
  134. }
  135.  
  136. // Get subscribed addons for each page and add them to the addons array
  137. function processSinglePage(xmlhttp, index) {
  138. if (xmlhttp[index].status === 200) {
  139. var container, unsubscribeItem, i, itemDetails;
  140.  
  141. container = document.implementation.createHTMLDocument().documentElement;
  142. container.innerHTML = xmlhttp[index].responseText;
  143. unsubscribeItem = container.querySelectorAll('a[id^="UnsubscribeItemBtn"]');
  144.  
  145. for (i = 0; i < unsubscribeItem.length; i = i + 1) {
  146. itemDetails = decodeURI(unsubscribeItem[i].href).match(/[0-9]+/g); // Extract WorkshopID and AppID
  147. addons.push([itemDetails[0], itemDetails[1]]);
  148. }
  149.  
  150. processAllAddons(xmlhttp);
  151. }
  152. }
  153.  
  154. function getAddons() {
  155. var url, appFilter, xmlhttp, handleStateChange, i;
  156.  
  157. appFilter = getAppFilter();
  158.  
  159. url = getBaseURL();
  160. url += '/myworkshopfiles/?browsefilter=mysubscriptions';
  161. url += appFilter ? '&appid=' + appFilter[0] : '';
  162. url += '&numperpage=30&p=';
  163.  
  164. xmlhttp = [];
  165.  
  166. handleStateChange = function (index) {
  167. return function () {
  168. if (xmlhttp[index].readyState === 4) {
  169. feedback('ANALYSE_PROG', 'Analysing subscriptions... (' + index + '/' + pageTotal + ')');
  170. processSinglePage(xmlhttp, index);
  171. }
  172. };
  173. };
  174.  
  175. for (i = 1; i <= pageTotal; i = i + 1) {
  176. xmlhttp[i] = new XMLHttpRequest();
  177. xmlhttp[i].onreadystatechange = handleStateChange(i);
  178. xmlhttp[i].open('GET', url + i);
  179. xmlhttp[i].send();
  180. }
  181. }
  182.  
  183. // Injects our element in the DOM
  184. function injectUnsubscriber() {
  185. if (document.getElementById('no_items')) {
  186. return false;
  187. }
  188.  
  189. addonTotal = getAddonTotal();
  190. pageTotal = getPageTotal(addonTotal);
  191. sessionId = getSessionId();
  192.  
  193. var reference, parent, wrap, appFilter, titleText, title, button, clear, leftContents, progress;
  194.  
  195. reference = document.getElementById('tabs_basebg_workshop');
  196. parent = reference.parentNode;
  197.  
  198. wrap = document.createElement('div');
  199. wrap.id = 'unsubscriber';
  200. parent.insertBefore(wrap, reference);
  201.  
  202. appFilter = getAppFilter();
  203. if (appFilter) {
  204. titleText = 'Reset Workshop Subscriptions for ' + appFilter[1];
  205. } else {
  206. titleText = 'Reset ALL Workshop Subscriptions';
  207. }
  208.  
  209. title = document.createElement('div');
  210. title.className = 'title';
  211. title.appendChild(document.createTextNode(titleText));
  212. wrap.appendChild(title);
  213.  
  214. button = document.createElement('div');
  215. button.className = 'button';
  216. button.innerHTML = 'Unsubscribe All';
  217. button.addEventListener('click', function () {
  218. var confirm = window.confirm("Are you sure you want to " + titleText + "?");
  219.  
  220. if (confirm) {
  221. button.style.pointerEvents = 'none';
  222.  
  223. leftContents = document.getElementById('leftContents');
  224. while (leftContents.firstChild) {
  225. leftContents.removeChild(leftContents.firstChild);
  226. }
  227.  
  228. progress = document.createElement('div');
  229. progress.id = 'unsub-progress';
  230. progress.className = 'generic-block';
  231. leftContents.appendChild(progress);
  232.  
  233. feedback("START", "Task: " + titleText);
  234.  
  235. getAddons();
  236. }
  237. });
  238. wrap.appendChild(button);
  239.  
  240. clear = document.createElement('div');
  241. clear.className = 'clear';
  242. wrap.appendChild(clear);
  243.  
  244. GM_addStyle('#unsubscriber {font-size: 13px; width: 100%; margin-top: 12px; background: rgba(0, 0, 0, 0.4); color: rgb(143, 152, 160); padding: 10px; box-sizing: border-box;}');
  245. GM_addStyle('#unsubscriber .title {color: #67c1f5; font-size: 16px; font-family: \'Motiva Sans Light\', Arial, Tahoma; float: left;}');
  246. GM_addStyle('#unsubscriber .button {color: rgba(255, 255, 255, 0.8);background: rgba(231, 76, 60, 0.4); font-size: 13px; padding: 3px 9px; float: right; border-radius: 2px; cursor: pointer;}');
  247. GM_addStyle('#unsubscriber .button:hover {color: #fff; background: rgba(231, 76, 60, 0.75);}');
  248. GM_addStyle('#unsubscriber .clear {clear: both;}');
  249. GM_addStyle('.generic-block {margin-top: 10px; padding: 10px; color: rgb(143, 152, 160); background: rgba(0, 0, 0, 0.4); width: 100%; font-size: 13px; box-sizing: border-box; line-height: 1.5em;}');
  250. GM_addStyle('.disabled {pointer-events: none; background: rgba(0, 0, 0, 0.7);}');
  251. }
  252.  
  253. injectUnsubscriber();
  254. }());