sns-auto-builder 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.html +16 -3
- package/package.json +1 -1
- package/scripts/chatgpt-image-gen.js +16 -4
- package/server.mjs +4 -1
package/index.html
CHANGED
|
@@ -722,7 +722,7 @@ function renderIg({ dir, slides }) {
|
|
|
722
722
|
sync();
|
|
723
723
|
}
|
|
724
724
|
|
|
725
|
-
function renderSlides({ dir, slides, canRegen }) {
|
|
725
|
+
function renderSlides({ dir, slides, canRegen, expected }) {
|
|
726
726
|
const host = $('slides');
|
|
727
727
|
renderIg({ dir, slides });
|
|
728
728
|
host.textContent = '';
|
|
@@ -744,8 +744,21 @@ function renderSlides({ dir, slides, canRegen }) {
|
|
|
744
744
|
if (canRegen) cell.append(regenButton({ dir, f, img, cell }));
|
|
745
745
|
host.append(cell);
|
|
746
746
|
});
|
|
747
|
-
|
|
748
|
-
|
|
747
|
+
// 장수가 모자란 채로 "완료"만 뜨면, 몇 번이 빠졌는지 모르고 그대로 쓰게 된다.
|
|
748
|
+
// 실제로 ChatGPT 가 3장만 만들었는데 4장인 것처럼 끝난 적이 있다.
|
|
749
|
+
const want = Number(expected) || 0;
|
|
750
|
+
const short = want && slides.length < want;
|
|
751
|
+
if (short) {
|
|
752
|
+
const got = new Set(slides.map((f) => Number(f.match(/\d+/)[0])));
|
|
753
|
+
const miss = [];
|
|
754
|
+
for (let i = 1; i <= want; i++) if (!got.has(i)) miss.push(i);
|
|
755
|
+
$('outPath').innerHTML = `<b style="color:#e0603a">${want}장 중 ${slides.length}장만 나왔습니다`
|
|
756
|
+
+ ` — 빠진 장: ${miss.join(', ')}번</b><br>ChatGPT 가 그 장의 이미지를 안 준 것입니다.`
|
|
757
|
+
+ ` 아래에서 다시 만들거나, 같은 주제로 한 번 더 실행하세요.`;
|
|
758
|
+
} else {
|
|
759
|
+
$('outPath').textContent = `${slides.length}장 · ${dir}/ — 슬라이드를 누르면 내려받습니다`
|
|
760
|
+
+ (canRegen ? ' · 오타가 있으면 “다시 만들기”' : '');
|
|
761
|
+
}
|
|
749
762
|
$('outPath').hidden = false;
|
|
750
763
|
// 한 장씩 누르면 브라우저가 연속 다운로드를 막는다. zip 하나로 묶어 받는다.
|
|
751
764
|
const zip = $('zipAll');
|
package/package.json
CHANGED
|
@@ -270,11 +270,17 @@ const readPageError = (page) => page.evaluate((needles) => {
|
|
|
270
270
|
return (line || hit).trim().slice(0, 200);
|
|
271
271
|
}, CHATGPT_ERRORS);
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
// used = 이 실행에서 **이미 저장에 쓴** 이미지들. before(이번 턴 직전 스냅샷)만으로는
|
|
274
|
+
// 부족하다: ChatGPT 가 위쪽 메시지를 DOM 에서 걷어냈다가 다시 붙이면 그 이미지가
|
|
275
|
+
// before 에 없어서 "새 것" 으로 잡힌다. 실제로 그렇게 2번 이미지가 3번 자리에도
|
|
276
|
+
// 저장돼, 3장만 만들어졌는데 4장이 나온 것처럼 끝난 적이 있다.
|
|
277
|
+
async function waitForNewImage(page, before, timeoutMs, used = new Set()) {
|
|
274
278
|
const deadline = Date.now() + timeoutMs;
|
|
275
|
-
let fresh = [], lastChange = 0;
|
|
279
|
+
let fresh = [], lastChange = 0, sawOnlyUsed = false;
|
|
276
280
|
while (Date.now() < deadline) {
|
|
277
|
-
const
|
|
281
|
+
const all = (await fileIds(page)).filter((id) => !before.includes(id));
|
|
282
|
+
const now = all.filter((id) => !used.has(id));
|
|
283
|
+
if (all.length && !now.length) sawOnlyUsed = true;
|
|
278
284
|
if (!now.length) {
|
|
279
285
|
// 이미지가 아직 없을 때만 본다. 이미 나왔으면 옛 대화의 문구를 오탐할 수 있다.
|
|
280
286
|
const err = await readPageError(page);
|
|
@@ -290,6 +296,10 @@ async function waitForNewImage(page, before, timeoutMs) {
|
|
|
290
296
|
await new Promise((r) => setTimeout(r, 3000));
|
|
291
297
|
}
|
|
292
298
|
if (fresh.length) return fresh[0]; // 타임아웃이어도 받은 게 있으면 쓴다
|
|
299
|
+
if (sawOnlyUsed) {
|
|
300
|
+
throw new Error('이 프롬프트로는 새 이미지가 안 나왔습니다 (화면에 있는 건 앞 장에 이미 쓴 것). '
|
|
301
|
+
+ '앞 장을 복사해 넣으면 안 되므로 이 장은 실패로 둡니다');
|
|
302
|
+
}
|
|
293
303
|
throw new Error(`이미지가 ${Math.round(timeoutMs / 1000)}초 안에 안 나옴`);
|
|
294
304
|
}
|
|
295
305
|
|
|
@@ -444,6 +454,7 @@ async function main() {
|
|
|
444
454
|
|
|
445
455
|
const summary = [];
|
|
446
456
|
let primed = false; // 이 실행에서 스타일/레퍼런스를 실은 첫 장을 이미 보냈는가
|
|
457
|
+
const usedIds = new Set(); // 이미 저장에 쓴 이미지 — 같은 것을 두 장에 넣지 않기 위해
|
|
447
458
|
const page = await browser.newPage();
|
|
448
459
|
try {
|
|
449
460
|
await page.goto(CHATGPT, { waitUntil: 'domcontentloaded', timeout: 120_000 });
|
|
@@ -479,7 +490,8 @@ async function main() {
|
|
|
479
490
|
).catch(() => console.warn('\n (경고) 레퍼런스 썸네일을 확인하지 못했습니다.'));
|
|
480
491
|
before = await fileIds(page);
|
|
481
492
|
}
|
|
482
|
-
const fileId = await waitForNewImage(page, before, timeoutMs);
|
|
493
|
+
const fileId = await waitForNewImage(page, before, timeoutMs, usedIds);
|
|
494
|
+
usedIds.add(fileId);
|
|
483
495
|
let buf = await downloadImage(page, fileId);
|
|
484
496
|
const src = checkRatio(buf);
|
|
485
497
|
if (mode === 'slide') buf = await cropTo45(buf, cropPage, logo);
|
package/server.mjs
CHANGED
|
@@ -887,7 +887,10 @@ ${context || '(없음)'}
|
|
|
887
887
|
// 이미지 트랙만 장 단위 재생성이 된다. HTML 트랙은 글자를 HTML 이 그리므로 오타가 안 난다.
|
|
888
888
|
const meta = { engine, refs: paths, slides: engine === 'image' ? newestPromptFile() : null, count: slideCount, topic };
|
|
889
889
|
writeGenMeta(abs, meta);
|
|
890
|
-
|
|
890
|
+
// expected 를 같이 보낸다. 장수가 모자라도 화면은 "완료"만 띄웠고,
|
|
891
|
+
// 몇 번이 빠졌는지는 아무 데도 안 나왔다.
|
|
892
|
+
if (slides.length < slideCount) log('장수 부족', { dir, got: slides.length, want: slideCount });
|
|
893
|
+
send({ result: { dir, slides, caption, canRegen: !!meta.slides, expected: slideCount } });
|
|
891
894
|
});
|
|
892
895
|
// 모델 출력을 기다리지 않고 바로 감시를 켠다. 완성된 슬라이드가 생기는 즉시 화면에 뜬다.
|
|
893
896
|
stopWatch = watchProgress(dir, engine, sendOut, slideCount);
|