Vast.ai Balance Hours Remaining

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

当前为 2025-02-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @license MIT
  3. // @name Vast.ai Balance Hours Remaining
  4. // @namespace http://tampermonkey.net/
  5. // @version 0.1
  6. // @description Add hours remaining and total spend rate to balance display on Vast.ai
  7. // @author You
  8. // @match https://cloud.vast.ai/*
  9. // @grant GM_xmlhttpRequest
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. let lastCredit = 0;
  16. let totalSpendRate = 0;
  17.  
  18. function formatTimeRemaining(hours) {
  19. if (!isFinite(hours) || isNaN(hours)) return "-- remaining";
  20.  
  21. const totalHours = Math.floor(hours);
  22. const minutes = Math.round((hours - totalHours) * 60);
  23.  
  24. if (totalHours === 0) {
  25. return `${minutes}m remaining`;
  26. } else if (minutes === 0) {
  27. return `${totalHours}h remaining`;
  28. } else {
  29. return `${totalHours}h ${minutes}m remaining`;
  30. }
  31. }
  32.  
  33. function updateDisplay() {
  34. const creditDiv = document.evaluate(
  35. '/html/body/div[1]/div[1]/header/div[2]/span[1]',
  36. document,
  37. null,
  38. XPathResult.FIRST_ORDERED_NODE_TYPE,
  39. null
  40. ).singleNodeValue;
  41.  
  42. if (creditDiv) {
  43. // Add or update hours remaining
  44. let hoursDiv = document.getElementById('hours-remaining');
  45. if (!hoursDiv) {
  46. hoursDiv = document.createElement('span');
  47. hoursDiv.id = 'hours-remaining';
  48. hoursDiv.style.fontSize = '14px';
  49. hoursDiv.style.marginLeft = '10px';
  50. creditDiv.after(hoursDiv);
  51. }
  52.  
  53. const hoursRemaining = totalSpendRate > 0 ? lastCredit / totalSpendRate : 0;
  54. hoursDiv.textContent = ``;
  55. hoursDiv.innerHTML = `<span style="color: green; font-weight: bold">($${totalSpendRate.toFixed(3)}/hr)</span> <span style="color: #66cc66; font-weight: bold">${formatTimeRemaining(hoursRemaining)}</span>`;
  56.  
  57.  
  58. }
  59. }
  60.  
  61. function fetchData() {
  62. // Fetch current user data
  63. fetch('https://cloud.vast.ai/api/v0/users/current/')
  64. .then(response => response.json())
  65. .then(data => {
  66. lastCredit = data.credit;
  67. })
  68. .catch(error => console.error('Error fetching user data:', error));
  69.  
  70. // Fetch instances data
  71. fetch('https://cloud.vast.ai/api/v0/instances/')
  72. .then(response => response.json())
  73. .then(data => {
  74. totalSpendRate = 0;
  75. if (data.instances && Array.isArray(data.instances)) {
  76. data.instances.forEach(instance => {
  77. if (instance.search && instance.search.totalHour) {
  78. totalSpendRate += instance.search.totalHour;
  79. }
  80. });
  81. }
  82. updateDisplay();
  83. })
  84. .catch(error => console.error('Error fetching instances data:', error));
  85. }
  86.  
  87. // Initial fetch
  88. fetchData();
  89.  
  90. // Update every 30 seconds
  91. setInterval(fetchData, 30000);
  92.  
  93. // Monitor XHR responses
  94. const originalFetch = window.fetch;
  95. window.fetch = async function(...args) {
  96. const response = await originalFetch.apply(this, args);
  97. const url = args[0].toString();
  98.  
  99. if (url.includes('/api/v0/users/current/') || url.includes('/api/v0/instances/')) {
  100. fetchData();
  101. }
  102. return response;
  103. };
  104. })();