browsertime 17.0.0 → 17.1.1
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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Browsertime changelog (we do [semantic versioning](https://semver.org))
|
|
2
2
|
|
|
3
|
+
|
|
4
|
+
## 17.1.1 - 2022-03-04
|
|
5
|
+
### Fixed
|
|
6
|
+
* Updated jimp, yargs, webdriver, execa and chrome-remote-interface dependencies [#1909](https://github.com/sitespeedio/browsertime/pull/1909).
|
|
7
|
+
* Added extra log message when using the package.json hack.
|
|
8
|
+
|
|
9
|
+
## 17.1.0 - 2022-02-24
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
* Expose chrome-remote-interface client to scripting. This makes it so much easier to run whatever CDP command. Get the raw version `commands.cdp.getRawClient();`. See PR [#1905](https://github.com/sitespeedio/browsertime/pull/1905) for a concrete example until we update the documentation.
|
|
13
|
+
### Fixed
|
|
14
|
+
* Fix visual-complete progress calculations to not use fuzz/slop, thank you [Gregory Mierzwinski](https://github.com/gmierz) for PR [#1902](https://github.com/sitespeedio/browsertime/pull/1902).
|
|
15
|
+
* Update to new Chrome HAR [#1906](https://github.com/sitespeedio/browsertime/pull/1906).
|
|
16
|
+
* Fix flushing DNS on Ubuntu 22.04 [#1907](https://github.com/sitespeedio/browsertime/pull/1907).
|
|
3
17
|
## 17.0.0 - 2022-02-10
|
|
4
18
|
|
|
5
19
|
### Breaking changes
|
|
@@ -372,7 +372,7 @@ def video_to_frames(
|
|
|
372
372
|
viewport_retries,
|
|
373
373
|
viewport_min_height,
|
|
374
374
|
viewport_min_width,
|
|
375
|
-
full_resolution
|
|
375
|
+
full_resolution,
|
|
376
376
|
):
|
|
377
377
|
"""Extract the video frames"""
|
|
378
378
|
global client_viewport
|
|
@@ -413,9 +413,7 @@ def video_to_frames(
|
|
|
413
413
|
if orange_file is not None:
|
|
414
414
|
remove_frames_before_orange(dir, orange_file)
|
|
415
415
|
remove_orange_frames(dir, orange_file)
|
|
416
|
-
find_render_start(
|
|
417
|
-
dir, orange_file, cropped, is_mobile
|
|
418
|
-
)
|
|
416
|
+
find_render_start(dir, orange_file, cropped, is_mobile)
|
|
419
417
|
adjust_frame_times(dir)
|
|
420
418
|
eliminate_duplicate_frames(dir, cropped, is_mobile)
|
|
421
419
|
crop_viewport(dir)
|
|
@@ -508,6 +506,7 @@ def find_recording_platform(video):
|
|
|
508
506
|
|
|
509
507
|
return is_mobile
|
|
510
508
|
|
|
509
|
+
|
|
511
510
|
def remove_frames_before_orange(directory, orange_file):
|
|
512
511
|
"""Remove stray frames from the start of the video"""
|
|
513
512
|
frames = sorted(glob.glob(os.path.join(directory, "video-*.png")))
|
|
@@ -533,6 +532,7 @@ def remove_frames_before_orange(directory, orange_file):
|
|
|
533
532
|
logging.debug("Removing pre-orange frame %s", frame)
|
|
534
533
|
os.remove(frame)
|
|
535
534
|
|
|
535
|
+
|
|
536
536
|
def remove_orange_frames(directory, orange_file):
|
|
537
537
|
"""Remove orange frames from the beginning of the video"""
|
|
538
538
|
frames = sorted(glob.glob(os.path.join(directory, "video-*.png")))
|
|
@@ -708,6 +708,7 @@ def find_video_viewport(
|
|
|
708
708
|
|
|
709
709
|
return viewport, cropped
|
|
710
710
|
|
|
711
|
+
|
|
711
712
|
def adjust_frame_times(directory):
|
|
712
713
|
offset = None
|
|
713
714
|
frames = sorted(glob.glob(os.path.join(directory, "video-*.png")))
|
|
@@ -728,6 +729,7 @@ def adjust_frame_times(directory):
|
|
|
728
729
|
dest = os.path.join(directory, "ms_{0:06d}.png".format(new_time))
|
|
729
730
|
os.rename(frame, dest)
|
|
730
731
|
|
|
732
|
+
|
|
731
733
|
def find_render_start(directory, orange_file, cropped, is_mobile):
|
|
732
734
|
logging.debug("Finding Render Start...")
|
|
733
735
|
try:
|
|
@@ -902,6 +904,7 @@ def eliminate_duplicate_frames(directory, cropped, is_mobile):
|
|
|
902
904
|
except BaseException:
|
|
903
905
|
logging.exception("Error processing frames for duplicates")
|
|
904
906
|
|
|
907
|
+
|
|
905
908
|
def crop_viewport(directory):
|
|
906
909
|
if client_viewport is not None:
|
|
907
910
|
try:
|
|
@@ -928,7 +931,9 @@ def crop_viewport(directory):
|
|
|
928
931
|
def get_decimate_filter():
|
|
929
932
|
decimate = None
|
|
930
933
|
try:
|
|
931
|
-
filters = subprocess.check_output(
|
|
934
|
+
filters = subprocess.check_output(
|
|
935
|
+
["ffmpeg", "-filters"], stderr=subprocess.STDOUT, encoding="UTF-8"
|
|
936
|
+
)
|
|
932
937
|
lines = filters.split("\n")
|
|
933
938
|
match = re.compile(
|
|
934
939
|
r"(?P<filter>[\w]*decimate).*V->V.*Remove near-duplicate frames"
|
|
@@ -1005,6 +1010,7 @@ def is_color_frame(file, color_file):
|
|
|
1005
1010
|
frame_cache[file][color_file] = bool(match)
|
|
1006
1011
|
return match
|
|
1007
1012
|
|
|
1013
|
+
|
|
1008
1014
|
def colors_are_similar(a, b, threshold=15):
|
|
1009
1015
|
similar = True
|
|
1010
1016
|
sum = 0
|
|
@@ -1073,6 +1079,7 @@ def generate_orange_png(orange_file):
|
|
|
1073
1079
|
except BaseException:
|
|
1074
1080
|
logging.exception("Error generating orange png " + orange_file)
|
|
1075
1081
|
|
|
1082
|
+
|
|
1076
1083
|
##########################################################################
|
|
1077
1084
|
# Histogram calculations
|
|
1078
1085
|
##########################################################################
|
|
@@ -1190,6 +1197,7 @@ def convert_to_jpeg(directory, quality):
|
|
|
1190
1197
|
|
|
1191
1198
|
logging.debug("Done converting video frames to JPEG")
|
|
1192
1199
|
|
|
1200
|
+
|
|
1193
1201
|
##########################################################################
|
|
1194
1202
|
# Visual Metrics
|
|
1195
1203
|
##########################################################################
|
|
@@ -1266,9 +1274,7 @@ def calculate_visual_metrics(
|
|
|
1266
1274
|
viewport = hero_data["viewport"]
|
|
1267
1275
|
hero_timings = []
|
|
1268
1276
|
for hero in hero_data["heroes"]:
|
|
1269
|
-
hero_time = calculate_hero_time(
|
|
1270
|
-
progress, dirs, hero, viewport
|
|
1271
|
-
)
|
|
1277
|
+
hero_time = calculate_hero_time(progress, dirs, hero, viewport)
|
|
1272
1278
|
if hero_time is not None:
|
|
1273
1279
|
hero_timings.append(
|
|
1274
1280
|
{
|
|
@@ -1352,30 +1358,34 @@ def calculate_visual_progress(histograms):
|
|
|
1352
1358
|
|
|
1353
1359
|
|
|
1354
1360
|
def calculate_frame_progress(histogram, start, final):
|
|
1361
|
+
"""Calculate the progress percentage of a given frame histogram.
|
|
1362
|
+
|
|
1363
|
+
This method finds the visually-complete progress by taking a sum of
|
|
1364
|
+
all the differences in the histogram between the current frame, and the
|
|
1365
|
+
final frame. The initial/first frame is used to remove values that are
|
|
1366
|
+
consitent between the first, and final frame (i.e. it's a baseline).
|
|
1367
|
+
|
|
1368
|
+
Note that this method should not be using a slop/fuzz because we aren't
|
|
1369
|
+
looking at individual pixel intensities which is where the fuzz can be
|
|
1370
|
+
found. Within individual channels, we hit an issue where we cannot tell
|
|
1371
|
+
if a value in the red channel is purely from a red colour in the image, or
|
|
1372
|
+
if it's mixed with other colours such as grey (e.g. red with rbg(240,0,0),
|
|
1373
|
+
and light grey with rgb(245,245,245)).
|
|
1374
|
+
"""
|
|
1355
1375
|
total = 0
|
|
1356
1376
|
matched = 0
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
channel_total += target
|
|
1370
|
-
low = max(0, i - slop)
|
|
1371
|
-
high = min(buckets, i + slop)
|
|
1372
|
-
for j in range(low, high):
|
|
1373
|
-
this_match = min(target, available[j])
|
|
1374
|
-
available[j] -= this_match
|
|
1375
|
-
channel_matched += this_match
|
|
1376
|
-
target -= this_match
|
|
1377
|
-
total += channel_total
|
|
1378
|
-
matched += channel_matched
|
|
1377
|
+
for channel in ("r", "g", "b"):
|
|
1378
|
+
for pixel_ind in range(256):
|
|
1379
|
+
curr = histogram[channel][pixel_ind]
|
|
1380
|
+
init = start[channel][pixel_ind]
|
|
1381
|
+
target = final[channel][pixel_ind]
|
|
1382
|
+
|
|
1383
|
+
curr_diff = abs(curr - init)
|
|
1384
|
+
target_diff = abs(target - init)
|
|
1385
|
+
|
|
1386
|
+
matched += min(curr_diff, target_diff)
|
|
1387
|
+
total += target_diff
|
|
1388
|
+
|
|
1379
1389
|
progress = (float(matched) / float(total)) if total else 1
|
|
1380
1390
|
return math.floor(progress * 100)
|
|
1381
1391
|
|
|
@@ -1609,16 +1619,14 @@ def calculate_hero_time(progress, directory, hero, viewport):
|
|
|
1609
1619
|
def check_config():
|
|
1610
1620
|
ok = True
|
|
1611
1621
|
|
|
1612
|
-
|
|
1613
1622
|
if get_decimate_filter() is not None:
|
|
1614
|
-
logging.debug(
|
|
1623
|
+
logging.debug("FFMPEG found")
|
|
1615
1624
|
else:
|
|
1616
1625
|
print("ffmpeg: FAIL")
|
|
1617
1626
|
ok = False
|
|
1618
1627
|
|
|
1619
|
-
|
|
1620
1628
|
if sys.version_info >= (3, 6):
|
|
1621
|
-
logging.debug(
|
|
1629
|
+
logging.debug("Python 3.6+ found")
|
|
1622
1630
|
else:
|
|
1623
1631
|
print("Python 3.6+: FAIL")
|
|
1624
1632
|
ok = False
|
|
@@ -1626,33 +1634,31 @@ def check_config():
|
|
|
1626
1634
|
try:
|
|
1627
1635
|
import numpy as np
|
|
1628
1636
|
|
|
1629
|
-
logging.debug(
|
|
1637
|
+
logging.debug("Numpy found")
|
|
1630
1638
|
except BaseException:
|
|
1631
1639
|
print("Numpy: FAIL")
|
|
1632
1640
|
ok = False
|
|
1633
1641
|
|
|
1634
|
-
|
|
1635
1642
|
try:
|
|
1636
1643
|
import cv2
|
|
1637
|
-
|
|
1638
|
-
logging.debug(
|
|
1644
|
+
|
|
1645
|
+
logging.debug("OpenCV-Python found")
|
|
1639
1646
|
except BaseException:
|
|
1640
1647
|
print("OpenCV-Python: FAIL")
|
|
1641
1648
|
ok = False
|
|
1642
1649
|
|
|
1643
|
-
|
|
1644
1650
|
try:
|
|
1645
1651
|
from PIL import Image, ImageCms, ImageDraw, ImageOps # noqa
|
|
1646
1652
|
|
|
1647
|
-
logging.debug(
|
|
1653
|
+
logging.debug("Pillow found")
|
|
1648
1654
|
except BaseException:
|
|
1649
1655
|
print("Pillow: FAIL")
|
|
1650
1656
|
ok = False
|
|
1651
1657
|
|
|
1652
|
-
|
|
1653
1658
|
try:
|
|
1654
1659
|
from ssim import compute_ssim # noqa
|
|
1655
|
-
|
|
1660
|
+
|
|
1661
|
+
logging.debug("SSIM found")
|
|
1656
1662
|
except BaseException:
|
|
1657
1663
|
print("SSIM: FAIL")
|
|
1658
1664
|
ok = False
|
|
@@ -1662,8 +1668,10 @@ def check_config():
|
|
|
1662
1668
|
|
|
1663
1669
|
def check_process(command, output):
|
|
1664
1670
|
ok = False
|
|
1665
|
-
try:
|
|
1666
|
-
out = subprocess.check_output(
|
|
1671
|
+
try:
|
|
1672
|
+
out = subprocess.check_output(
|
|
1673
|
+
command, stderr=subprocess.STDOUT, shell=True, encoding="UTF-8"
|
|
1674
|
+
)
|
|
1667
1675
|
if out.find(output) > -1:
|
|
1668
1676
|
ok = True
|
|
1669
1677
|
except BaseException:
|
|
@@ -1834,11 +1842,7 @@ def main():
|
|
|
1834
1842
|
|
|
1835
1843
|
options = parser.parse_args()
|
|
1836
1844
|
|
|
1837
|
-
if
|
|
1838
|
-
not options.check
|
|
1839
|
-
and not options.dir
|
|
1840
|
-
and not options.video
|
|
1841
|
-
):
|
|
1845
|
+
if not options.check and not options.dir and not options.video:
|
|
1842
1846
|
parser.error(
|
|
1843
1847
|
"A video, Directory of images or histograms file needs to be provided.\n\n"
|
|
1844
1848
|
"Use -h to see available options"
|
|
@@ -1910,7 +1914,7 @@ def main():
|
|
|
1910
1914
|
options.viewportminwidth,
|
|
1911
1915
|
options.full,
|
|
1912
1916
|
)
|
|
1913
|
-
|
|
1917
|
+
|
|
1914
1918
|
# Calculate the histograms and visual metrics
|
|
1915
1919
|
calculate_histograms(directory, histogram_file, options.force)
|
|
1916
1920
|
metrics = calculate_visual_metrics(
|
|
@@ -51,6 +51,14 @@ export class ChromeDevelopmentToolsProtocol {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
getRawClient() {
|
|
55
|
+
if (this.browserName === 'chrome' || this.browserName === 'edge') {
|
|
56
|
+
return this.engineDelegate.getCDPClient().getRawClient();
|
|
57
|
+
} else {
|
|
58
|
+
throw new Error('DevToolsProtocol only supported in Chrome and Edge');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
54
62
|
async send(command, arguments_) {
|
|
55
63
|
if (this.browserName === 'chrome' || this.browserName === 'edge') {
|
|
56
64
|
try {
|
package/lib/support/dns.js
CHANGED
|
@@ -25,6 +25,11 @@ export async function flushDNS(options) {
|
|
|
25
25
|
});
|
|
26
26
|
} else if (process.platform === 'linux') {
|
|
27
27
|
log.info('Flush DNS cache on Linux');
|
|
28
|
+
|
|
29
|
+
// Unbuntu 22.04
|
|
30
|
+
await execa('sudo', ['resolvectl', 'flush-caches'], {
|
|
31
|
+
reject: false
|
|
32
|
+
});
|
|
28
33
|
await execa('sudo', ['systemd-resolve', '--flush-caches'], {
|
|
29
34
|
reject: false
|
|
30
35
|
});
|
|
@@ -33,7 +33,9 @@ async function loadFile(script, options, throwError) {
|
|
|
33
33
|
// Hack a way! Try to add a package.json file in the same folder as the
|
|
34
34
|
// script file. That way, the .js file will be treated as commonjs =
|
|
35
35
|
// working as before we moved Browsertime to module
|
|
36
|
-
|
|
36
|
+
log.info(
|
|
37
|
+
'Will try to load load JS as a commonjs file by adding an emtpy package.json'
|
|
38
|
+
);
|
|
37
39
|
let createdPackageJson = false;
|
|
38
40
|
try {
|
|
39
41
|
const packageJson = join(dirname(resolve(script)), 'package.json');
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"
|
|
3
|
-
"
|
|
2
|
+
"name": "browsertime",
|
|
3
|
+
"description": "Get performance metrics from your web page using Browsertime.",
|
|
4
|
+
"version": "17.1.1",
|
|
4
5
|
"bin": "./bin/browsertime.js",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"dependencies": {
|
|
@@ -12,10 +13,10 @@
|
|
|
12
13
|
"@sitespeed.io/throttle": "5.0.0",
|
|
13
14
|
"@sitespeed.io/tracium": "0.3.3",
|
|
14
15
|
"btoa": "1.2.1",
|
|
15
|
-
"chrome-har": "0.13.
|
|
16
|
-
"chrome-remote-interface": "0.32.
|
|
16
|
+
"chrome-har": "0.13.1",
|
|
17
|
+
"chrome-remote-interface": "0.32.1",
|
|
17
18
|
"dayjs": "1.11.7",
|
|
18
|
-
"execa": "
|
|
19
|
+
"execa": "7.0.0",
|
|
19
20
|
"fast-stats": "0.0.6",
|
|
20
21
|
"find-up": "6.3.0",
|
|
21
22
|
"get-port": "6.1.2",
|
|
@@ -27,11 +28,11 @@
|
|
|
27
28
|
"lodash.merge": "4.6.2",
|
|
28
29
|
"lodash.pick": "4.4.0",
|
|
29
30
|
"lodash.set": "4.3.2",
|
|
30
|
-
"selenium-webdriver": "4.8.
|
|
31
|
-
"yargs": "17.
|
|
31
|
+
"selenium-webdriver": "4.8.1",
|
|
32
|
+
"yargs": "17.7.1"
|
|
32
33
|
},
|
|
33
34
|
"optionalDependencies": {
|
|
34
|
-
"jimp": "0.22.
|
|
35
|
+
"jimp": "0.22.7"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"ava": "5.1.0",
|
|
@@ -70,9 +71,11 @@
|
|
|
70
71
|
"lint": "eslint .",
|
|
71
72
|
"lint:fix": "eslint . --fix"
|
|
72
73
|
},
|
|
73
|
-
"name": "browsertime",
|
|
74
74
|
"author": "Peter Hedenskog",
|
|
75
75
|
"contributors": [
|
|
76
|
+
{
|
|
77
|
+
"name": "Gregory Mierzwinski"
|
|
78
|
+
},
|
|
76
79
|
{
|
|
77
80
|
"name": "Tobias Lidskog"
|
|
78
81
|
},
|