File size: 4,867 Bytes
c19ca42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
let lastState = {};

function request(url, data, handler, errorHandler) {
  const xhr = new XMLHttpRequest();
  xhr.open('POST', url, true);
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.onreadystatechange = () => {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        try {
          const js = JSON.parse(xhr.responseText);
          handler(js);
        } catch (error) {
          console.error(error);
          errorHandler();
        }
      } else {
        errorHandler();
      }
    }
  };
  const js = JSON.stringify(data);
  xhr.send(js);
}

function pad2(x) {
  return x < 10 ? `0${x}` : x;
}

function formatTime(secs) {
  if (secs > 3600) return `${pad2(Math.floor(secs / 60 / 60))}:${pad2(Math.floor(secs / 60) % 60)}:${pad2(Math.floor(secs) % 60)}`;
  if (secs > 60) return `${pad2(Math.floor(secs / 60))}:${pad2(Math.floor(secs) % 60)}`;
  return `${Math.floor(secs)}s`;
}

function checkPaused(state) {
  lastState.paused = state ? !state : !lastState.paused;
  document.getElementById('txt2img_pause').innerText = lastState.paused ? 'Resume' : 'Pause';
  document.getElementById('img2img_pause').innerText = lastState.paused ? 'Resume' : 'Pause';
}

function setProgress(res) {
  const elements = ['txt2img_generate', 'img2img_generate', 'extras_generate', 'control_generate'];
  const progress = (res?.progress || 0);
  const job = res?.job || '';
  const perc = res && (progress > 0) ? `${Math.round(100.0 * progress)}%` : '';
  let sec = res?.eta || 0;
  let eta = '';
  if (res?.paused) eta = 'Paused';
  else if (res?.completed || (progress > 0.99)) eta = 'Finishing';
  else if (sec === 0) eta = 'Starting';
  else {
    const min = Math.floor(sec / 60);
    sec %= 60;
    eta = min > 0 ? `${Math.round(min)}m ${Math.round(sec)}s` : `${Math.round(sec)}s`;
  }
  document.title = `SD.Next ${perc}`;
  for (const elId of elements) {
    const el = document.getElementById(elId);
    if (el) {
      el.innerText = (res ? `${job} ${perc} ${eta}` : 'Generate');
      el.style.background = res && (progress > 0)
        ? `linear-gradient(to right, var(--primary-500) 0%, var(--primary-800) ${perc}, var(--neutral-700) ${perc})`
        : 'var(--button-primary-background-fill)';
    }
  }
}

function requestInterrupt() {
  setProgress();
}

function randomId() {
  return `task(${Math.random().toString(36).slice(2, 7)}${Math.random().toString(36).slice(2, 7)}${Math.random().toString(36).slice(2, 7)})`;
}

// starts sending progress requests to "/internal/progress" uri, creating progressbar above progressbarContainer element and preview inside gallery element
// Cleans up all created stuff when the task is over and calls atEnd. calls onProgress every time there is a progress update
function requestProgress(id_task, progressEl, galleryEl, atEnd = null, onProgress = null, once = false) {
  localStorage.setItem('task', id_task);
  let hasStarted = false;
  const dateStart = new Date();
  const prevProgress = null;
  const parentGallery = galleryEl ? galleryEl.parentNode : null;
  let livePreview;
  let img;

  const initLivePreview = () => {
    img = new Image();
    if (parentGallery) {
      livePreview = document.createElement('div');
      livePreview.className = 'livePreview';
      parentGallery.insertBefore(livePreview, galleryEl);
      const rect = galleryEl.getBoundingClientRect();
      if (rect.width) {
        livePreview.style.width = `${rect.width}px`;
        livePreview.style.height = `${rect.height}px`;
      }
      img.onload = () => {
        livePreview.appendChild(img);
        if (livePreview.childElementCount > 2) livePreview.removeChild(livePreview.firstElementChild);
      };
    }
  };

  const done = () => {
    debug('taskEnd:', id_task);
    localStorage.removeItem('task');
    setProgress();
    if (parentGallery && livePreview) parentGallery.removeChild(livePreview);
    checkPaused(true);
    if (atEnd) atEnd();
  };

  const start = (id_task, id_live_preview) => { // eslint-disable-line no-shadow
    if (!opts.live_previews_enable || opts.live_preview_refresh_period === 0 || opts.show_progress_every_n_steps === 0) return;
    request('./internal/progress', { id_task, id_live_preview }, (res) => {
      lastState = res;
      const elapsedFromStart = (new Date() - dateStart) / 1000;
      hasStarted |= res.active;
      if (res.completed || (!res.active && (hasStarted || once)) || (elapsedFromStart > 30 && !res.queued && res.progress === prevProgress)) {
        done();
        return;
      }
      setProgress(res);
      if (res.live_preview && !livePreview) initLivePreview();
      if (res.live_preview && galleryEl) img.src = res.live_preview;
      if (onProgress) onProgress(res);
      setTimeout(() => start(id_task, id_live_preview), opts.live_preview_refresh_period || 500);
    }, done);
  };
  start(id_task, 0);
}