Add hours remaining and total spend rate to balance display on Vast.ai
当前为
// ==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;
};
})();