Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ==UserScript==
- // @name Nexus Download Collection
- // @namespace NDC
- // @version 0.6.7
- // @description Download every mods of a collection in a single click
- // @author Drigtime
- // @match https://next.nexusmods.com/*/collections*
- // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
- // @grant GM.xmlHttpRequest
- // @grant GM_xmlhttpRequest
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM.setValue
- // @grant GM.getValue
- // @connect nexusmods.com
- // @downloadURL https://update.greasyfork.org/scripts/483337/Nexus%20Download%20Collection.user.js
- // @updateURL https://update.greasyfork.org/scripts/483337/Nexus%20Download%20Collection.meta.js
- // ==/UserScript==
- // MDI : https://pictogrammers.com/library/mdi/
- // MDI : https://github.com/MathewSachin/Captura/blob/master/src/Captura.Core/MaterialDesignIcons.cs
- /** CORSViaGM BEGINING */
- const CORSViaGM = document.body.appendChild(Object.assign(document.createElement('div'), { id: 'CORSViaGM' }))
- addEventListener('fetchViaGM', e => GM_fetch(e.detail.forwardingFetch))
- CORSViaGM.init = function (window) {
- if (!window) throw 'The `window` parameter must be passed in!'
- window.fetchViaGM = fetchViaGM.bind(window)
- // Support for service worker
- window.forwardingFetch = new BroadcastChannel('forwardingFetch')
- window.forwardingFetch.onmessage = async e => {
- const req = e.data
- const { url } = req
- const res = await fetchViaGM(url, req)
- const response = await res.blob()
- window.forwardingFetch.postMessage({ type: 'fetchResponse', url, response })
- }
- window._CORSViaGM && window._CORSViaGM.inited.done()
- const info = '🙉 CORS-via-GM initiated!'
- console.info(info)
- return info
- }
- function GM_fetch(p) {
- GM_xmlhttpRequest({
- ...p.init,
- url: p.url, method: p.init.method || 'GET',
- onload: responseDetails => p.res(new Response(responseDetails.response, responseDetails))
- })
- }
- function fetchViaGM(url, init) {
- let _r
- const p = new Promise(r => _r = r)
- p.res = _r
- p.url = url
- p.init = init || {}
- dispatchEvent(new CustomEvent('fetchViaGM', { detail: { forwardingFetch: p } }))
- return p
- }
- CORSViaGM.init(window);
- /** CORSViaGM END */
- class NDCElement {
- constructor(elementName, options = {}) {
- this.element = document.createElement(elementName);
- this.childrens = [];
- if (options.html) {
- this.element.innerHTML = options.html;
- }
- if (options.classes) {
- this.element.className = options.classes;
- }
- if (options.attributes) {
- Object.entries(options.attributes).forEach(([key, value]) => {
- this.element.setAttribute(key, value);
- });
- }
- if (options.events) {
- Object.entries(options.events).forEach(([key, value]) => {
- this.element.addEventListener(key, value);
- });
- }
- }
- remove() {
- this.element.remove();
- }
- hide() {
- this.element.style.display = 'none';
- }
- show() {
- this.element.style.display = '';
- }
- toggle() {
- this.element.style.display = this.element.style.display === 'none' ? '' : 'none';
- }
- appendChild(...elements) {
- elements.forEach(element => {
- this.childrens.push(element);
- this.element.appendChild(element.element);
- });
- }
- removeChild(element) {
- const index = this.childrens.indexOf(element);
- if (index > -1) {
- this.childrens.splice(index, 1);
- }
- element.element.remove();
- }
- }
- class NDC extends NDCElement {
- constructor(gameId, collectionId, options = {}) {
- super('div', options);
- this.pauseBetweenDownload = 5;
- this.loading = true;
- this.forceStop = false;
- this.gameId = gameId;
- this.collectionId = collectionId;
- this.mods = {
- mandatory: [],
- optional: []
- };
- this.downloadButtonContainer = new NDCDownloadButton(this);
- this.progressBarContainer = new NDCProgressBar(this);
- this.console = new NDCLogConsole(this);
- this.init();
- }
- async init() {
- this.pauseBetweenDownload = await GM.getValue('pauseBetweenDownload', 5);
- const loadingContainer = new NDCElement('button', {
- classes: 'w-full font-montserrat font-semibold text-sm leading-none tracking-wider uppercase flex gap-x-2 justify-center items-center transition-colors relative min-h-9 focus:outline focus:outline-2 focus:outline-accent focus:outline-offset-2 px-2 py-1 cursor-pointer bg-primary-moderate fill-font-primary text-font-primary border-transparent focus:bg-primary-strong hover:bg-primary-subdued rounded',
- html: 'Fetching mods list...'
- });
- this.appendChild(loadingContainer);
- const response = await this.fetchMods();
- // const response = await new Promise(resolve => setTimeout(() => resolve({ modFiles: [] }), 1000));
- this.mods.mandatory = response.modFiles.filter(mod => !mod.optional);
- this.mods.optional = response.modFiles.filter(mod => mod.optional);
- this.loading = false;
- loadingContainer.remove();
- this.downloadButtonContainer.render();
- this.downloadButtonContainer.mainBtn.element.addEventListener('click', () => this.downloadMods([...this.mods.mandatory, ...this.mods.optional], "all"));
- this.downloadButtonContainer.menuBtnMandatory.element.addEventListener('click', () => this.downloadMods(this.mods.mandatory, "mandatory"));
- this.downloadButtonContainer.menuBtnOptional.element.addEventListener('click', () => this.downloadMods(this.mods.optional, "optional"));
- this.appendChild(this.downloadButtonContainer, this.progressBarContainer, this.console);
- }
- async fetchMods() {
- const response = await fetch("https://next.nexusmods.com/api/graphql", {
- "headers": {
- "content-type": "application/json",
- },
- "referrer": `https://next.nexusmods.com/${this.gameId}/collections/${this.collectionId}?tab=mods`,
- "referrerPolicy": "strict-origin-when-cross-origin",
- "body": JSON.stringify({
- "query": "query CollectionRevisionMods ($revision: Int, $slug: String!, $viewAdultContent: Boolean) { collectionRevision (revision: $revision, slug: $slug, viewAdultContent: $viewAdultContent) { externalResources { id, name, resourceType, resourceUrl }, modFiles { fileId, optional, file { fileId, name, scanned, size, sizeInBytes, version, mod { adult, author, category, modId, name, pictureUrl, summary, version, game { domainName }, uploader { avatar, memberId, name } } } } } }",
- "variables": { "slug": this.collectionId, "viewAdultContent": true },
- "operationName": "CollectionRevisionMods"
- }),
- "method": "POST",
- "mode": "cors",
- "credentials": "include"
- });
- const data = await response.json();
- data.data.collectionRevision.modFiles = data.data.collectionRevision.modFiles.map(modFile => {
- modFile.file.url = `https://www.nexusmods.com/${this.gameId}/mods/${modFile.file.mod.modId}?tab=files&file_id=${modFile.file.fileId}`;
- return modFile;
- });
- return data.data.collectionRevision;
- }
- async fetchSlowDownloadLink(mod) {
- let downloadUrl = '';
- const url = mod.file.url + '&nmm=1';
- const response = await fetchViaGM(url, {
- "headers": {
- "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
- "cache-control": "max-age=0",
- },
- "referrer": url,
- "referrerPolicy": "strict-origin-when-cross-origin",
- "method": "GET",
- "mode": "cors",
- "credentials": "include"
- });
- const text = await response.text();
- const html = new DOMParser().parseFromString(text, "text/html");
- const slow = html.getElementById("slowDownloadButton");
- if (slow) {
- downloadUrl = slow.getAttribute("data-download-url");
- }
- return { downloadUrl, text, html }
- }
- async addModToVortex(mod) {
- // const {downloadUrl, text} = await new Promise(resolve => setTimeout(() => resolve({downloadUrl: 'debug', text: 'debug'}), 1000));
- // const {downloadUrl, text} = await new Promise(resolve => setTimeout(() => resolve({downloadUrl: '', text: 'debug'}), 1000));
- const { downloadUrl, text, html } = await this.fetchSlowDownloadLink(mod);
- if (downloadUrl === '') {
- // make link to copy in the clipboard the response
- const logRow = this.console.log('ERROR', `Failed to get download link for
- <a href="${mod.file.url}" target="_blank" class="text-primary-moderate">${mod.file.name}</a>
- <button class="text-primary-moderate" title="Copy response to clipboard"></button>`);
- const svg = new NDCElement('svg', {
- classes: 'w-4 h-4 fill-current',
- attributes: {
- viewBox: '0 0 24 24',
- xmlns: 'http://www.w3.org/2000/svg',
- role: 'presentation',
- style: 'width: 1rem; height: 1rem;'
- },
- html: '<path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z" style="fill: currentcolor;"></path>'
- });
- // add svg to the button
- const copyButton = logRow.element.querySelector('button');
- copyButton.innerHTML = svg.element.outerHTML;
- copyButton.addEventListener('click', () => {
- navigator.clipboard.writeText(text);
- alert('Response copied to clipboard');
- });
- // check if find .replaced-login-link in the html it is because the user is not connect on nexusmods
- if (html.querySelector('.replaced-login-link')) {
- this.console.log('ERROR', 'You are not connected on NexusMods. <a href="https://users.nexusmods.com/auth/continue?client_id=nexus&redirect_uri=https://www.nexusmods.com/oauth/callback&response_type=code&referrer=https%3A%2F%2Fwww.nexusmods.com%2F" target="_blank" class="text-primary-moderate">Login</a> and try again.');
- this.forceStop = true;
- }
- return false;
- }
- document.location.href = downloadUrl;
- return true;
- }
- // function to avoid repeating the same code this.downloadButtonContainer.hide(); this.progressBarContainer.setModsCount(this.downloadButtonContainer.modsCount); this.progressBarContainer.show();
- async downloadMods(mods, type = "all") {
- const history = await GM.getValue('history', {}); // {"gameId": {"collectionId": {"type": []}}}
- this.progressBarContainer.setModsCount(mods.length);
- this.progressBarContainer.setProgress(0);
- this.progressBarContainer.setStatus(NDCProgressBar.STATUS_DOWNLOADING);
- // get history for this collection (index is the collectionId)
- let historyForThisCollection = [];
- if (history[this.gameId] && history[this.gameId][this.collectionId] && history[this.gameId][this.collectionId][type]) {
- historyForThisCollection = [...new Set(history[this.gameId][this.collectionId][type])];
- } else {
- history[this.gameId] = history[this.gameId] || {};
- history[this.gameId][this.collectionId] = history[this.gameId][this.collectionId] || {};
- }
- if (historyForThisCollection && historyForThisCollection.length > 0) {
- const confirm = await new Promise(resolve => {
- const confirm = window.confirm(`You already downloaded ${historyForThisCollection.length} out of ${mods.length} mods from this collection.\nDo you want to resume the download?\nCancel will clear the history and download all mods again.`);
- resolve(confirm);
- });
- // if the user doesn't want to resume the download, clear the history
- if (!confirm) {
- historyForThisCollection = [];
- history[this.gameId][this.collectionId][type] = historyForThisCollection;
- await GM.setValue('history', history);
- } else {
- // update mods to download
- mods = mods.filter(mod => !historyForThisCollection.includes(mod.file.mod.modId));
- this.progressBarContainer.setProgress(historyForThisCollection.length);
- }
- }
- this.downloadButtonContainer.hide();
- this.progressBarContainer.show();
- this.console.log('INFO', 'Download started.');
- for (const [_, mod] of mods.entries()) {
- if (this.progressBarContainer.status == NDCProgressBar.STATUS_PAUSED) {
- this.console.log('INFO', `Download paused.`);
- while (this.progressBarContainer.status == NDCProgressBar.STATUS_PAUSED) {
- await new Promise(resolve => setTimeout(resolve, 100));
- }
- }
- if (this.progressBarContainer.status == NDCProgressBar.STATUS_STOPPED) {
- this.console.log('INFO', 'Download stopped.');
- break;
- }
- const status = await this.addModToVortex(mod);
- if (this.forceStop) {
- this.console.log('INFO', 'Download stopped.');
- break;
- }
- if (!status) {
- continue;
- }
- historyForThisCollection.push(mod.file.mod.modId); // update historyForThisCollection
- history[this.gameId][this.collectionId][type] = [...new Set(historyForThisCollection)]; // remove duplicate and update history
- await GM.setValue('history', history);
- this.console.log('INFO', `Sending download link to Vortex <a href="${mod.file.url}" target="_blank" class="text-primary-moderate">${mod.file.name}</a>`);
- this.progressBarContainer.incrementProgress();
- // based on download 1.5mb/s wait until the download is supposed to be finished + 5 seconds for the download to start on vortex
- const downloadTime = Math.round(mod.file.sizeInBytes / 1500000) + this.pauseBetweenDownload;
- const downloadEstimatifTimeLog = this.console.log('INFO', `Waiting approximately ${downloadTime} seconds for the download to finish on Vortex before starting the next one.`);
- const downloadProgressLog = this.console.log('INFO', `Downloading... ${downloadTime} seconds left (~0%)`);
- const downloadProgressLogCreatedAt = Date.now();
- await new Promise(resolve => {
- const downloadProgressLogInterval = setInterval(() => {
- // if Status is paused or stopped clear the interval
- if (this.progressBarContainer.status == NDCProgressBar.STATUS_PAUSED || this.progressBarContainer.status == NDCProgressBar.STATUS_STOPPED) {
- clearInterval(downloadProgressLogInterval);
- downloadEstimatifTimeLog.remove();
- downloadProgressLog.remove();
- resolve();
- return;
- }
- const timeLeft = downloadTime - Math.round((Date.now() - downloadProgressLogCreatedAt) / 1000);
- // 0 to 100% based on the time left
- const approximativePercent = Math.round((downloadTime - timeLeft) / downloadTime * 100);
- downloadProgressLog.element.innerHTML = `[${new Date().toLocaleTimeString()}][INFO] Downloading... ${timeLeft} seconds left (~${approximativePercent}%)`;
- if (timeLeft <= 0) {
- clearInterval(downloadProgressLogInterval);
- downloadEstimatifTimeLog.remove();
- downloadProgressLog.remove();
- resolve();
- }
- }, 1000);
- });
- // if all mods are downloaded clear the history
- if (this.progressBarContainer.progress === this.progressBarContainer.modsCount) {
- history[this.gameId][this.collectionId][type] = [];
- await GM.setValue('history', history);
- }
- }
- this.console.log('INFO', 'Download finished.');
- this.progressBarContainer.setStatus(NDCProgressBar.STATUS_FINISHED);
- this.progressBarContainer.hide();
- this.downloadButtonContainer.show();
- if (this.forceStop) {
- this.forceStop = false;
- return;
- }
- }
- }
- class NDCDownloadButton extends NDCElement {
- constructor(ndc, options = {}) {
- super('div', { classes: 'flex w-100' });
- this.ndc = ndc;
- this.mainBtn = new NDCElement('button', {
- classes: 'w-full font-montserrat font-semibold text-sm leading-none tracking-wider uppercase flex gap-x-2 justify-center items-center transition-colors relative min-h-9 focus:outline focus:outline-2 focus:outline-accent focus:outline-offset-2 px-2 py-1 cursor-pointer bg-primary-moderate fill-font-primary text-font-primary border-transparent focus:bg-primary-strong hover:bg-primary-subdued justify-between rounded-l',
- html: 'Add all mods to vortex'
- });
- this.mainModsCount = new NDCElement('span', {
- classes: 'p-2 bg-surface-low rounded-full text-xs text-white whitespace-nowrap',
- });
- this.mainBtn.appendChild(this.mainModsCount);
- this.menuBtn = new NDCElement('button', {
- classes: 'font-montserrat font-semibold text-sm leading-none tracking-wider uppercase flex gap-x-2 justify-center items-center transition-colors relative min-h-9 focus:outline focus:outline-2 focus:outline-accent focus:outline-offset-2 px-2 py-1 cursor-pointer bg-primary-moderate fill-font-primary text-font-primary border-transparent focus:bg-primary-strong hover:bg-primary-subdued justify-between rounded-r',
- html: '<svg class="w-4 h-4 fill-current" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="presentation" style="width: 1.5rem; height: 1.5rem;"><path d="M7.41,8.58L12,13.17L16.59,8.58L18,10L12,16L6,10L7.41,8.58Z" style="fill: currentcolor;"></path></svg>'
- });
- this.menu = new NDCElement('div', {
- classes: 'absolute z-10 min-w-48 py-1 px-0 mt-1 text-base text-gray-600 border-stroke-subdued bg-surface-low border border-gray-200 rounded-md shadow-lg outline-none hidden',
- attributes: {
- style: 'transform: translate(488.453px, 36px);'
- }
- });
- this.menuBtnMandatory = new NDCElement('button', {
- classes: 'font-montserrat text-sm font-semibold uppercase leading-none tracking-wider first:rounded-t last:rounded-b relative flex w-full items-center gap-x-2 p-2 text-left font-normal hover:bg-surface-mid hover:text-primary-moderate focus:shadow-accent focus:z-10 focus:outline-none text-start justify-between',
- html: 'Add all mandatory mods'
- });
- this.menuBtnMandatoryModsCount = new NDCElement('span', {
- classes: 'p-2 bg-primary-moderate rounded-full text-xs text-white whitespace-nowrap',
- });
- this.menuBtnMandatory.appendChild(this.menuBtnMandatoryModsCount);
- this.menuBtnOptional = new NDCElement('button', {
- classes: 'font-montserrat text-sm font-semibold uppercase leading-none tracking-wider first:rounded-t last:rounded-b relative flex w-full items-center gap-x-2 p-2 text-left font-normal hover:bg-surface-mid hover:text-primary-moderate focus:shadow-accent focus:z-10 focus:outline-none text-start justify-between',
- html: 'Add all optional mods'
- });
- this.menuBtnOptionalModsCount = new NDCElement('span', {
- classes: 'p-2 bg-primary-moderate rounded-full text-xs text-white whitespace-nowrap',
- });
- this.menuBtnOptional.appendChild(this.menuBtnOptionalModsCount);
- this.menu.appendChild(this.menuBtnMandatory, this.menuBtnOptional);
- // on menuButton click show menuButtonMenu
- this.menuBtn.element.addEventListener('click', () => {
- const btnGroupOffset = this.element.getBoundingClientRect();
- this.menu.element.classList.toggle('hidden');
- const dropdownMenuOffset = this.menu.element.getBoundingClientRect();
- this.menu.element.style.transform = `translate(${btnGroupOffset.width - dropdownMenuOffset.width}px, ${btnGroupOffset.height}px)`;
- });
- document.addEventListener('click', (event) => {
- const isClickInside = this.menu.element.contains(event.target) || this.menuBtn.element.contains(event.target);
- if (!isClickInside) {
- this.menu.element.classList.add('hidden');
- }
- });
- this.appendChild(this.mainBtn, this.menuBtn, this.menu);
- }
- updateModsCountSpan() {
- this.mainModsCount.element.innerHTML = `${this.ndc.mods.mandatory.length + this.ndc.mods.optional.length} mods`;
- }
- updateMenuButtonMenuDownloadAllMandatoryModsCount() {
- this.menuBtnMandatoryModsCount.element.innerHTML = `${this.ndc.mods.mandatory.length} mods`;
- }
- updateMenuButtonMenuDownloadAllOptionalModsCount() {
- this.menuBtnOptionalModsCount.element.innerHTML = `${this.ndc.mods.optional.length} mods`;
- }
- render() {
- this.updateModsCountSpan();
- this.updateMenuButtonMenuDownloadAllMandatoryModsCount();
- this.updateMenuButtonMenuDownloadAllOptionalModsCount();
- }
- }
- class NDCProgressBar extends NDCElement {
- static STATUS_DOWNLOADING = 0;
- static STATUS_PAUSED = 1;
- static STATUS_FINISHED = 2;
- static STATUS_STOPPED = 3;
- static STATUS_TEXT = {
- [NDCProgressBar.STATUS_DOWNLOADING]: 'Downloading...',
- [NDCProgressBar.STATUS_PAUSED]: 'Paused',
- [NDCProgressBar.STATUS_FINISHED]: 'Finished',
- [NDCProgressBar.STATUS_STOPPED]: 'Stopped'
- }
- constructor(ndc, options = {}) {
- super('div', { classes: 'flex flex-wrap w-100', attributes: { style: 'display: none;' } });
- this.ndc = ndc;
- this.modsCount = 0;
- this.progress = 0;
- this.status = NDCProgressBar.STATUS_DOWNLOADING;
- this.progressBar = new NDCElement('div', {
- classes: 'flex-1 relative w-100 min-h-9 bg-surface-low rounded-l overflow-hidden'
- });
- this.progressBarFill = new NDCElement('div', {
- classes: 'absolute top-0 left-0 w-0 h-full bg-primary-moderate',
- attributes: {
- style: 'transition: width 0.3s ease 0s; width: 0%;'
- }
- });
- this.progressBarText = new NDCElement('div', {
- classes: 'absolute top-0 left-0 w-full h-full cursor-pointer grid grid-cols-3 items-center text-white font-montserrat font-semibold text-sm leading-none tracking-wider uppercase'
- });
- this.progressBarProgress = new NDCElement('div', {
- classes: 'ml-2',
- html: `${this.progress}%`
- });
- this.progressBarTextCenter = new NDCElement('div', {
- html: 'Downloading...',
- classes: 'text-center'
- });
- this.progressBarTextRight = new NDCElement('div', {
- html: `${this.progress}/${this.modsCount}`,
- classes: 'text-right mr-2'
- });
- this.progressBarText.appendChild(this.progressBarProgress, this.progressBarTextCenter, this.progressBarTextRight);
- this.progressBar.appendChild(this.progressBarFill, this.progressBarText);
- this.actionBtnGroup = new NDCElement('div', {
- classes: 'flex'
- });
- this.playPauseBtn = new NDCElement('button', {
- classes: 'font-montserrat font-semibold text-sm leading-none tracking-wider uppercase flex gap-x-2 justify-center items-center transition-colors relative min-h-9 focus:outline focus:outline-2 focus:outline-accent focus:outline-offset-2 px-2 py-1 cursor-pointer bg-primary-moderate fill-font-primary text-font-primary border-transparent focus:bg-primary-strong hover:bg-primary-subdued justify-between',
- html: '<svg class="w-4 h-4 fill-current" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="presentation" style="width: 1.5rem; height: 1.5rem;"><path d="M14,19H18V5H14M6,19H10V5H6V19Z" style="fill: currentcolor;"></path></svg>'
- });
- this.stopBtn = new NDCElement('button', {
- classes: 'font-montserrat font-semibold text-sm leading-none tracking-wider uppercase flex gap-x-2 justify-center items-center transition-colors relative min-h-9 focus:outline focus:outline-2 focus:outline-accent focus:outline-offset-2 px-2 py-1 cursor-pointer bg-primary-moderate fill-font-primary text-font-primary border-transparent focus:bg-primary-strong hover:bg-primary-subdued justify-between rounded-r',
- html: '<svg class="w-4 h-4 fill-current" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="presentation" style="width: 1.5rem; height: 1.5rem;"><path d="M18,18H6V6H18V18Z" style="fill: currentcolor;"></path></svg>'
- });
- this.playPauseBtn.element.addEventListener('click', () => {
- const status = this.status == NDCProgressBar.STATUS_DOWNLOADING ? NDCProgressBar.STATUS_PAUSED : NDCProgressBar.STATUS_DOWNLOADING;
- this.setStatus(status);
- });
- this.stopBtn.element.addEventListener('click', () => {
- this.setStatus(NDCProgressBar.STATUS_STOPPED);
- });
- this.actionBtnGroup.appendChild(this.playPauseBtn, this.stopBtn);
- this.pauseBetweenDownloadInputContainer = new NDCElement('div', {
- classes: 'flex my-2 gap-2 items-center',
- attributes: {
- style: 'flex-basis: 100%;'
- }
- });
- this.pauseBetweenDownloadInput = new NDCElement('input', {
- classes: 'text-md text-neutral-subdued border-neutral-subdued bg-surface-mid rounded border indent-2 outline-none hover:border-white focus:border-white focus:text-white p-1 w-14',
- attributes: {
- type: 'number',
- min: 0,
- placeholder: 'Extra pause between download (in seconds)',
- value: this.ndc.pauseBetweenDownload
- }
- });
- this.pauseBetweenDownloadLabel = new NDCElement('label', {
- classes: 'text-white font-montserrat font-semibold text-sm leading-none tracking-wider uppercase',
- html: 'Extra pause between download (in seconds)'
- });
- this.pauseBetweenDownloadInput.element.addEventListener('change', async (event) => {
- this.ndc.pauseBetweenDownload = parseInt(event.target.value);
- await GM.setValue('pauseBetweenDownload', this.ndc.pauseBetweenDownload);
- });
- this.pauseBetweenDownloadInputContainer.appendChild(this.pauseBetweenDownloadInput, this.pauseBetweenDownloadLabel);
- this.appendChild(this.progressBar, this.actionBtnGroup, this.pauseBetweenDownloadInputContainer);
- }
- setModsCount(modsCount) {
- this.modsCount = modsCount;
- this.render();
- }
- getProgressPercent() {
- return this.modsCount ? Math.round((this.progress / this.modsCount) * 100) : 0;
- }
- setProgress(progress) {
- this.progress = progress;
- this.render();
- }
- incrementProgress() {
- this.progress++;
- this.render();
- }
- setStatus(status) {
- this.status = status;
- this.progressBarTextCenter.element.innerHTML = NDCProgressBar.STATUS_TEXT[status];
- this.render();
- }
- updateProgressBarFillWidth() {
- this.progressBarFill.element.style.width = `${this.getProgressPercent()}%`;
- }
- updateProgressBarTextProgress() {
- this.progressBarProgress.element.innerHTML = `${this.getProgressPercent()}%`;
- }
- updateProgressBarTextRight() {
- this.progressBarTextRight.element.innerHTML = `${this.progress}/${this.modsCount}`;
- }
- updatePlayPauseBtn() {
- this.playPauseBtn.element.innerHTML = this.status == NDCProgressBar.STATUS_PAUSED ? '<svg class="w-4 h-4 fill-current" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="presentation" style="width: 1.5rem; height: 1.5rem;"><path d="M8,5.14V19.14L19,12.14L8,5.14Z" style="fill: currentcolor;"></path></svg>' : '<svg class="w-4 h-4 fill-current" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" role="presentation" style="width: 1.5rem; height: 1.5rem;"><path d="M14,19H18V5H14M6,19H10V5H6V19Z" style="fill: currentcolor;"></path></svg>';
- }
- updatePauseBetweenDownloadInput() {
- this.pauseBetweenDownloadInput.element.value = this.ndc.pauseBetweenDownload;
- }
- render() {
- this.updateProgressBarFillWidth()
- this.updateProgressBarTextProgress()
- this.updateProgressBarTextRight()
- this.updatePlayPauseBtn()
- this.updatePauseBetweenDownloadInput()
- }
- }
- class NDCLogConsole extends NDCElement {
- constructor(ndc, options = {}) {
- super('div', { classes: 'flex flex-col w-100 gap-3 mt-3' });
- this.ndc = ndc;
- this.hidden = false;
- this.toggleLogsButton = new NDCElement('button', {
- classes: 'w-full font-montserrat font-semibold text-sm leading-none tracking-wider uppercase',
- html: 'Hide logs'
- });
- this.logContainer = new NDCElement('div', {
- classes: 'w-full bg-surface-low rounded overflow-y-auto text-white font-montserrat font-semibold text-sm border border-primary',
- attributes: {
- style: 'height: 10rem; resize: vertical;'
- }
- });
- this.toggleLogsButton.element.addEventListener('click', () => {
- this.hidden = !this.hidden;
- this.logContainer.element.style.display = this.hidden ? 'none' : '';
- this.toggleLogsButton.element.innerHTML = this.hidden ? 'Show logs' : 'Hide logs';
- });
- this.appendChild(this.toggleLogsButton, this.logContainer);
- }
- log(type, message) {
- const logMessage = new NDCElement('div', {
- classes: 'gap-x-2 px-2 py-1',
- html: `[${new Date().toLocaleTimeString()}][${type}] ${message}`
- });
- this.logContainer.appendChild(logMessage);
- this.logContainer.element.scrollTop = this.logContainer.element.scrollHeight;
- console.log(`[${type}] ${message}`);
- return logMessage;
- }
- clear() {
- this.logContainer.element.innerHTML = '';
- }
- }
- let previousRoute = null;
- let ndc = null;
- async function handleNextRouterChange() {
- if (next.router.state.route === "/[gameDomain]/collections/[collectionSlug]") {
- const { gameDomain, collectionSlug, tab } = next.router.query;
- if (previousRoute !== `${gameDomain}/${collectionSlug}`) {
- previousRoute = `${gameDomain}/${collectionSlug}`;
- ndc = new NDC(gameDomain, collectionSlug);
- }
- if (tab === "mods") {
- document.querySelector("#tabcontent-mods > div > div > div").prepend(ndc.element);
- } else {
- ndc.element.remove();
- }
- }
- }
- // Add an event listener for the hashchange event
- next.router.events.on('routeChangeComplete', handleNextRouterChange);
- handleNextRouterChange();
Add Comment
Please, Sign In to add comment