progress.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. import base64
  2. import io
  3. import time
  4. import gradio as gr
  5. from pydantic import BaseModel, Field
  6. from modules.shared import opts
  7. import modules.shared as shared
  8. current_task = None
  9. pending_tasks = {}
  10. finished_tasks = []
  11. recorded_results = []
  12. recorded_results_limit = 2
  13. def start_task(id_task):
  14. global current_task
  15. current_task = id_task
  16. pending_tasks.pop(id_task, None)
  17. def finish_task(id_task):
  18. global current_task
  19. if current_task == id_task:
  20. current_task = None
  21. finished_tasks.append(id_task)
  22. if len(finished_tasks) > 16:
  23. finished_tasks.pop(0)
  24. def record_results(id_task, res):
  25. recorded_results.append((id_task, res))
  26. if len(recorded_results) > recorded_results_limit:
  27. recorded_results.pop(0)
  28. def add_task_to_queue(id_job):
  29. pending_tasks[id_job] = time.time()
  30. class ProgressRequest(BaseModel):
  31. id_task: str = Field(default=None, title="Task ID", description="id of the task to get progress for")
  32. id_live_preview: int = Field(default=-1, title="Live preview image ID", description="id of last received last preview image")
  33. class ProgressResponse(BaseModel):
  34. active: bool = Field(title="Whether the task is being worked on right now")
  35. queued: bool = Field(title="Whether the task is in queue")
  36. completed: bool = Field(title="Whether the task has already finished")
  37. progress: float = Field(default=None, title="Progress", description="The progress with a range of 0 to 1")
  38. eta: float = Field(default=None, title="ETA in secs")
  39. live_preview: str = Field(default=None, title="Live preview image", description="Current live preview; a data: uri")
  40. id_live_preview: int = Field(default=None, title="Live preview image ID", description="Send this together with next request to prevent receiving same image")
  41. textinfo: str = Field(default=None, title="Info text", description="Info text used by WebUI.")
  42. def setup_progress_api(app):
  43. return app.add_api_route("/internal/progress", progressapi, methods=["POST"], response_model=ProgressResponse)
  44. def progressapi(req: ProgressRequest):
  45. active = req.id_task == current_task
  46. queued = req.id_task in pending_tasks
  47. completed = req.id_task in finished_tasks
  48. if not active:
  49. return ProgressResponse(active=active, queued=queued, completed=completed, id_live_preview=-1, textinfo="In queue..." if queued else "Waiting...")
  50. progress = 0
  51. job_count, job_no = shared.state.job_count, shared.state.job_no
  52. sampling_steps, sampling_step = shared.state.sampling_steps, shared.state.sampling_step
  53. if job_count > 0:
  54. progress += job_no / job_count
  55. if sampling_steps > 0 and job_count > 0:
  56. progress += 1 / job_count * sampling_step / sampling_steps
  57. progress = min(progress, 1)
  58. elapsed_since_start = time.time() - shared.state.time_start
  59. predicted_duration = elapsed_since_start / progress if progress > 0 else None
  60. eta = predicted_duration - elapsed_since_start if predicted_duration is not None else None
  61. id_live_preview = req.id_live_preview
  62. shared.state.set_current_image()
  63. if opts.live_previews_enable and shared.state.id_live_preview != req.id_live_preview:
  64. image = shared.state.current_image
  65. if image is not None:
  66. buffered = io.BytesIO()
  67. if opts.live_previews_image_format == "png":
  68. # using optimize for large images takes an enormous amount of time
  69. if max(*image.size) <= 256:
  70. save_kwargs = {"optimize": True}
  71. else:
  72. save_kwargs = {"optimize": False, "compress_level": 1}
  73. else:
  74. save_kwargs = {}
  75. image.save(buffered, format=opts.live_previews_image_format, **save_kwargs)
  76. base64_image = base64.b64encode(buffered.getvalue()).decode('ascii')
  77. live_preview = f"data:image/{opts.live_previews_image_format};base64,{base64_image}"
  78. id_live_preview = shared.state.id_live_preview
  79. else:
  80. live_preview = None
  81. else:
  82. live_preview = None
  83. return ProgressResponse(active=active, queued=queued, completed=completed, progress=progress, eta=eta, live_preview=live_preview, id_live_preview=id_live_preview, textinfo=shared.state.textinfo)
  84. def restore_progress(id_task):
  85. while id_task == current_task or id_task in pending_tasks:
  86. time.sleep(0.1)
  87. res = next(iter([x[1] for x in recorded_results if id_task == x[0]]), None)
  88. if res is not None:
  89. return res
  90. return gr.update(), gr.update(), gr.update(), f"Couldn't restore progress for {id_task}: results either have been discarded or never were obtained"