RunPod Balance Hours Remaining

Add hours remaining to balance display on RunPod

  1. // ==UserScript==
  2. // @license MIT
  3. // @name RunPod Balance Hours Remaining
  4. // @namespace http://tampermonkey.net/
  5. // @version 0.3
  6. // @description Add hours remaining to balance display on RunPod
  7. // @author You
  8. // @match https://www.runpod.io/*
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. 'use strict';
  14.  
  15. function formatTimeRemaining(hours) {
  16. if (!isFinite(hours) || isNaN(hours)) return "-- remaining";
  17. const totalHours = Math.floor(hours);
  18. const minutes = Math.round((hours - totalHours) * 60);
  19.  
  20. if (totalHours === 0) {
  21. return `${minutes}m remaining`;
  22. } else if (minutes === 0) {
  23. return `${totalHours}h remaining`;
  24. } else {
  25. return `${totalHours}h ${minutes}m remaining`;
  26. }
  27. }
  28.  
  29. function updateHoursRemaining(balance, spendPerHr) {
  30. const balanceDiv = document.evaluate(
  31. '/html/body/div[1]/div[1]/div[2]/div[2]/div/div[2]/div/div/a/button/div/div[1]',
  32. document,
  33. null,
  34. XPathResult.FIRST_ORDERED_NODE_TYPE,
  35. null
  36. ).singleNodeValue;
  37.  
  38. if (balanceDiv) {
  39. // Check if values are valid numbers and spendPerHr is not zero
  40. if (!isFinite(balance) || !isFinite(spendPerHr) || spendPerHr === 0) {
  41. console.log('RunPod Hours Script - Invalid values:', {balance, spendPerHr});
  42. return;
  43. }
  44.  
  45. const hoursRemaining = balance / spendPerHr;
  46. let hoursDiv = document.getElementById('hours-remaining');
  47.  
  48. if (!hoursDiv) {
  49. hoursDiv = document.createElement('div');
  50. hoursDiv.id = 'hours-remaining';
  51. hoursDiv.style.fontSize = '10px';
  52. hoursDiv.style.color = '#94a3b8';
  53. hoursDiv.style.fontWeight = '600';
  54. balanceDiv.after(hoursDiv);
  55. }
  56.  
  57. hoursDiv.textContent = formatTimeRemaining(hoursRemaining);
  58. }
  59. }
  60.  
  61. // Monitor XHR responses for GraphQL data
  62. const originalFetch = window.fetch;
  63. window.fetch = async function(...args) {
  64. const response = await originalFetch.apply(this, args);
  65. if (response.url.includes('api.runpod.io/graphql')) {
  66. response.clone().json().then(data => {
  67. if (data.data?.myself) {
  68. const balance = parseFloat(data.data.myself.clientBalance);
  69. const spendPerHr = parseFloat(data.data.myself.currentSpendPerHr);
  70. console.log('RunPod Hours Script - Received values:', {balance, spendPerHr});
  71. updateHoursRemaining(balance, spendPerHr);
  72. }
  73. }).catch(error => {
  74. console.error('RunPod Hours Script - Error processing response:', error);
  75. });
  76. }
  77. return response;
  78. };
  79. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址