single-file-core 1.5.111 → 1.5.112
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/core/helper.js +22 -3
- package/core/index.js +21 -1
- package/core/util.js +2 -1
- package/package.json +1 -1
package/core/helper.js
CHANGED
|
@@ -75,6 +75,8 @@ const COMMENT_HEADER_LEGACY = "Archive processed by SingleFile";
|
|
|
75
75
|
const SINGLE_FILE_UI_ELEMENT_CLASS = "single-file-ui-element";
|
|
76
76
|
const INFOBAR_TAGNAME = infobar.INFOBAR_TAGNAME;
|
|
77
77
|
const EMPTY_RESOURCE = "data:,";
|
|
78
|
+
const POSTER_CONTENT_TYPES = ["image/webp", "image/jpeg"];
|
|
79
|
+
const POSTER_QUALITY = 0.8;
|
|
78
80
|
const DEFAULT_REPLACED_CHARACTERS = ["~", "+", "?", "%", "*", ":", "|", "\"", "<", ">", "\\\\", "\x00-\x1f", "\x7F"];
|
|
79
81
|
const DEFAULT_REPLACEMENT_CHARACTER = "_";
|
|
80
82
|
const DEFAULT_REPLACEMENT_CHARACTERS = ["~", "+", "?", "%", "*", ":", "|", """, "<", ">", "\"];
|
|
@@ -143,9 +145,24 @@ export {
|
|
|
143
145
|
WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
144
146
|
MESSAGE_PREFIX,
|
|
145
147
|
NO_SCRIPT_PROPERTY_NAME,
|
|
146
|
-
NESTING_TRACK_ID_ATTRIBUTE_NAME
|
|
148
|
+
NESTING_TRACK_ID_ATTRIBUTE_NAME,
|
|
149
|
+
getPosterDataURI
|
|
147
150
|
};
|
|
148
151
|
|
|
152
|
+
// a poster is a still of a video the reader cannot play, so it is stored in a
|
|
153
|
+
// lossy format: the same frame costs an order of magnitude less than as a PNG.
|
|
154
|
+
// toDataURL falls back to PNG without reporting it when it does not support the
|
|
155
|
+
// format, so the type it returned is tested instead of assumed
|
|
156
|
+
function getPosterDataURI(canvasElement) {
|
|
157
|
+
for (const contentType of POSTER_CONTENT_TYPES) {
|
|
158
|
+
const dataURI = canvasElement.toDataURL(contentType, POSTER_QUALITY);
|
|
159
|
+
if (dataURI.startsWith("data:" + contentType)) {
|
|
160
|
+
return dataURI;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return canvasElement.toDataURL("image/png");
|
|
164
|
+
}
|
|
165
|
+
|
|
149
166
|
let userScriptHandlerObserver;
|
|
150
167
|
|
|
151
168
|
function initUserScriptHandler() {
|
|
@@ -534,14 +551,16 @@ function getResourcesInfo(win, doc, element, options, data, elementHidden, compu
|
|
|
534
551
|
});
|
|
535
552
|
element.setAttribute(VIDEO_ATTRIBUTE_NAME, data.videos.length - 1);
|
|
536
553
|
}
|
|
537
|
-
|
|
554
|
+
// the posters are only inserted when the videos are blocked, capturing them
|
|
555
|
+
// otherwise encodes a frame per video for nothing
|
|
556
|
+
if (options.blockVideos && !element.getAttribute("poster")) {
|
|
538
557
|
const canvasElement = doc.createElement("canvas");
|
|
539
558
|
const context = canvasElement.getContext("2d");
|
|
540
559
|
canvasElement.width = element.videoWidth;
|
|
541
560
|
canvasElement.height = element.videoHeight;
|
|
542
561
|
try {
|
|
543
562
|
context.drawImage(element, 0, 0, canvasElement.width, canvasElement.height);
|
|
544
|
-
data.posters.push(canvasElement
|
|
563
|
+
data.posters.push(getPosterDataURI(canvasElement));
|
|
545
564
|
element.setAttribute(POSTER_ATTRIBUTE_NAME, data.posters.length - 1);
|
|
546
565
|
data.markedElements.push(element);
|
|
547
566
|
// eslint-disable-next-line no-unused-vars
|
package/core/index.js
CHANGED
|
@@ -155,6 +155,7 @@ const STAGES = [{
|
|
|
155
155
|
{ action: "replaceStylesheets" },
|
|
156
156
|
{ action: "replaceStyleAttributes" },
|
|
157
157
|
{ action: "insertVariables" },
|
|
158
|
+
{ action: "removeEmptyMediaAutoplay" },
|
|
158
159
|
{ option: "compressHTML", action: "compressHTML" },
|
|
159
160
|
{ action: "cleanupPage" }
|
|
160
161
|
],
|
|
@@ -824,6 +825,25 @@ class Processor {
|
|
|
824
825
|
});
|
|
825
826
|
}
|
|
826
827
|
|
|
828
|
+
// a media element left without any source can never start, and the attribute
|
|
829
|
+
// would keep it announcing a playback that never happens. The sources are
|
|
830
|
+
// dropped whenever they could not be stored, so this is not specific to
|
|
831
|
+
// blocked videos, and it runs on the final state to leave alone an element
|
|
832
|
+
// that kept one of several sources
|
|
833
|
+
removeEmptyMediaAutoplay() {
|
|
834
|
+
this.doc.querySelectorAll("video[autoplay], audio[autoplay]").forEach(element => {
|
|
835
|
+
const sourceElements = Array.from(element.querySelectorAll("source"));
|
|
836
|
+
if (!hasSource(element) && !sourceElements.some(hasSource)) {
|
|
837
|
+
element.removeAttribute("autoplay");
|
|
838
|
+
}
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
function hasSource(element) {
|
|
842
|
+
const src = element.getAttribute("src");
|
|
843
|
+
return Boolean(src) && src != util.EMPTY_RESOURCE;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
|
|
827
847
|
removeFrames() {
|
|
828
848
|
const frameElements = this.doc.querySelectorAll("iframe, frame, object[type=\"text/html\"][data]");
|
|
829
849
|
this.stats.set("discarded", "frames", frameElements.length);
|
|
@@ -1195,7 +1215,7 @@ class Processor {
|
|
|
1195
1215
|
canvasElement.height = videoData.size.videoHeight;
|
|
1196
1216
|
context.drawImage(temporaryVideoElement, 0, 0, canvasElement.width, canvasElement.height);
|
|
1197
1217
|
try {
|
|
1198
|
-
videoElement.poster =
|
|
1218
|
+
videoElement.poster = util.getPosterDataURI(canvasElement);
|
|
1199
1219
|
// eslint-disable-next-line no-unused-vars
|
|
1200
1220
|
} catch (error) {
|
|
1201
1221
|
videoElement.poster = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='" + videoData.size.videoWidth + "' height='" + videoData.size.videoHeight + "'%3E%3C/svg%3E";
|
package/core/util.js
CHANGED
|
@@ -225,7 +225,8 @@ function getInstance(utilOptions) {
|
|
|
225
225
|
INFOBAR_TAGNAME: helper.INFOBAR_TAGNAME,
|
|
226
226
|
WAIT_FOR_USERSCRIPT_PROPERTY_NAME: helper.WAIT_FOR_USERSCRIPT_PROPERTY_NAME,
|
|
227
227
|
NO_SCRIPT_PROPERTY_NAME: helper.NO_SCRIPT_PROPERTY_NAME,
|
|
228
|
-
NESTING_TRACK_ID_ATTRIBUTE_NAME: helper.NESTING_TRACK_ID_ATTRIBUTE_NAME
|
|
228
|
+
NESTING_TRACK_ID_ATTRIBUTE_NAME: helper.NESTING_TRACK_ID_ATTRIBUTE_NAME,
|
|
229
|
+
getPosterDataURI: helper.getPosterDataURI
|
|
229
230
|
};
|
|
230
231
|
|
|
231
232
|
async function getContent(resourceURL, options) {
|