Vast.ai Balance Hours Remaining

Add hours remaining and total spend rate to balance display on Vast.ai

目前為 2025-02-08 提交的版本,檢視 最新版本

// ==UserScript==
// @license MIT 
// @name         Vast.ai Balance Hours Remaining
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Add hours remaining and total spend rate to balance display on Vast.ai
// @author       You
// @match        https://cloud.vast.ai/*
// @grant        GM_xmlhttpRequest
// ==/UserScript==

(function() {
    'use strict';

    let lastCredit = 0;
    let totalSpendRate = 0;

    function formatTimeRemaining(hours) {
        if (!isFinite(hours) || isNaN(hours)) return "-- remaining";

        const totalHours = Math.floor(hours);
        const minutes = Math.round((hours - totalHours) * 60);

        if (totalHours === 0) {
            return `${minutes}m remaining`;
        } else if (minutes === 0) {
            return `${totalHours}h remaining`;
        } else {
            return `${totalHours}h ${minutes}m remaining`;
        }
    }

    function updateDisplay() {
        const creditDiv = document.evaluate(
            '/html/body/div[1]/div[1]/header/div[2]/span[1]',
            document,
            null,
            XPathResult.FIRST_ORDERED_NODE_TYPE,
            null
        ).singleNodeValue;

        if (creditDiv) {
            // Add or update hours remaining
            let hoursDiv = document.getElementById('hours-remaining');
            if (!hoursDiv) {
                hoursDiv = document.createElement('span');
                hoursDiv.id = 'hours-remaining';
                hoursDiv.style.fontSize = '14px';
                hoursDiv.style.marginLeft = '10px';
                creditDiv.after(hoursDiv);
            }

            const hoursRemaining = totalSpendRate > 0 ? lastCredit / totalSpendRate : 0;
            hoursDiv.textContent = ``;
            hoursDiv.innerHTML = `<span style="color: green; font-weight: bold">($${totalSpendRate.toFixed(3)}/hr)</span> <span style="color: #66cc66; font-weight: bold">${formatTimeRemaining(hoursRemaining)}</span>`;


        }
    }

    function fetchData() {
        // Fetch current user data
        fetch('https://cloud.vast.ai/api/v0/users/current/')
            .then(response => response.json())
            .then(data => {
                lastCredit = data.credit;
            })
            .catch(error => console.error('Error fetching user data:', error));

        // Fetch instances data
        fetch('https://cloud.vast.ai/api/v0/instances/')
            .then(response => response.json())
            .then(data => {
                totalSpendRate = 0;
                if (data.instances && Array.isArray(data.instances)) {
                    data.instances.forEach(instance => {
                        if (instance.search && instance.search.totalHour) {
                            totalSpendRate += instance.search.totalHour;
                        }
                    });
                }
                updateDisplay();
            })
            .catch(error => console.error('Error fetching instances data:', error));
    }

    // Initial fetch
    fetchData();

    // Update every 30 seconds
    setInterval(fetchData, 30000);

    // Monitor XHR responses
    const originalFetch = window.fetch;
    window.fetch = async function(...args) {
        const response = await originalFetch.apply(this, args);
        const url = args[0].toString();

        if (url.includes('/api/v0/users/current/') || url.includes('/api/v0/instances/')) {
            fetchData();
        }
        return response;
    };
})();