Repository navigation
Expand file tree
/
Copy pathconsole_script.js
More file actions
159 lines (130 loc) · 4.25 KB
/
Copy pathconsole_script.js
File metadata and controls
159 lines (130 loc) · 4.25 KB
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// Add to console, and start the script by calling funcs start() or start(pageNumber, totalPages) to begin, stop() to abort
// Config:
// startPage - starting page number for filenames (default: 1)
// totalPages - how many pages to download before stopping (default: Infinity)
// waitBuffer - ms to wait after last XHR activity before saving (default: 5000)
const config = {
startPage: 1,
totalPages: Infinity,
// How long after last XHR activity to start checking canvas stability
xhrIdleDelay: 2000,
// How often to compare canvas snapshots when polling for stability
stabilityPollInterval: 600,
// How many consecutive identical snapshots = canvas is done rendering
stabilityRequiredMatches: 3,
}
const nextPageElSelector = ".app-view-controls > mat-icon:last-of-type"
let currentPage = config.startPage
let timeoutHandle = null
let running = false
function log(msg) {
console.log(`[DK Scraper | page ${currentPage}] ${msg}`)
}
function zeroPad(n, width = 4) {
return String(n).padStart(width, '0')
}
function nextPage() {
const btn = document.querySelector(nextPageElSelector)
if (!btn) {
log("next page button not found — stopping.")
running = false
return
}
btn.click()
}
function getSharpCanvases() {
const all = document.querySelectorAll("canvas")
if (all.length < 2) return null
// DK uses 4 canvases in double-page mode (indices 1 and 3 are the sharp ones), rest are probably some previews
const isDouble = all.length > 3
const canvases = isDouble ? [all[1], all[3]] : [all[1]]
return { canvases: canvases.filter(Boolean), isDouble, all }
}
function waitForCanvasStable(onStable) {
let matchCount = 0
let lastSnapshot = null
function poll() {
if (!running) return
const result = getSharpCanvases()
if (!result) {
log("canvases not found yet, waiting...")
matchCount = 0
lastSnapshot = null
setTimeout(poll, config.stabilityPollInterval)
return
}
const snapshot = result.canvases.map(c => c.toDataURL("image/png")).join("|")
if (snapshot === lastSnapshot) {
matchCount++
log(`⏳ Stable check ${matchCount}/${config.stabilityRequiredMatches}`)
} else {
matchCount = 0
log("⏳ Canvas still rendering...")
}
lastSnapshot = snapshot
if (matchCount >= config.stabilityRequiredMatches) {
onStable(result)
} else {
setTimeout(poll, config.stabilityPollInterval)
}
}
poll()
}
function saveCanvasLocally() {
if (!running) return
log("waiting for canvas to stabilize")
waitForCanvasStable(({ canvases, isDouble, all }) => {
function downloadCanvas(canvas, suffix = '') {
if (!canvas) return
const image = canvas.toDataURL("image/png")
const link = document.createElement('a')
link.download = `page_${zeroPad(currentPage)}${suffix}.png`
link.href = image
link.click()
}
if (isDouble) {
downloadCanvas(canvases[0], '_a')
downloadCanvas(canvases[1], '_b')
} else {
downloadCanvas(canvases[0])
}
// Remove canvases to prevent stale images on next page
all.forEach(c => c.remove())
currentPage++
if (currentPage > config.startPage + config.totalPages - 1) {
log("reached page limit")
running = false
return
}
nextPage()
})
}
function scheduleCapture() {
clearTimeout(timeoutHandle)
timeoutHandle = setTimeout(saveCanvasLocally, config.xhrIdleDelay)
}
// Intercept XHR to detect when page has finished loading
XMLHttpRequest.prototype._realSend = XMLHttpRequest.prototype._realSend || XMLHttpRequest.prototype.send
XMLHttpRequest.prototype.send = function (value) {
this.addEventListener("progress", () => {
if (running) {
log("loading")
scheduleCapture()
}
}, false)
this._realSend(value)
}
function start(fromPage = config.startPage, pages = config.totalPages) {
config.startPage = fromPage
config.totalPages = pages
currentPage = fromPage
running = true
log(`starting from page ${fromPage}${pages !== Infinity ? `, downloading ${pages} pages` : ''}`)
scheduleCapture()
}
function stop() {
running = false
clearTimeout(timeoutHandle)
log("⏹️ Stopped.")
}
console.log("DK Scraper loaded. Call start() or start(pageNumber, totalPages) to begin, stop() to abort")