Skip to content

Commit 8707918

Browse files
committed
WIP: Add client-side replay option
1 parent db56fb2 commit 8707918

File tree

6 files changed

+117
-0
lines changed

6 files changed

+117
-0
lines changed

config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ framed_replay: true
2929

3030
redirect_to_exact: true
3131

32+
# Use wabac.js-style client-side replay system
33+
client_side_replay: false
34+
35+
3236
# Uncomment and change to set default locale
3337
# default_locale: en
3438

pywb/apps/frontendapp.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,8 @@ def __init__(self, config_file=None, custom_config=None):
8181

8282
self.debug = config.get('debug', False)
8383

84+
self.client_side_replay = config.get('client_side_replay', False)
85+
8486
self.warcserver_server = GeventServer(self.warcserver, port=0)
8587

8688
self.proxy_prefix = None # the URL prefix to be used for the collection with proxy mode (e.g. /coll/id_/)
@@ -130,6 +132,9 @@ def _init_routes(self):
130132
coll_prefix = '/<coll>'
131133
self.url_map.add(Rule('/', endpoint=self.serve_home))
132134

135+
if self.client_side_replay:
136+
self.url_map.add(Rule('/static/sw.js', endpoint=self.serve_wabac_service_worker))
137+
133138
self._init_coll_routes(coll_prefix)
134139

135140
if self.proxy_prefix is not None:
@@ -818,6 +823,17 @@ def proxy_fetch(self, env, url):
818823
response.add_access_control_headers(env=env)
819824
return response
820825

826+
def serve_wabac_service_worker(self, env):
827+
"""Serve wabac.js service worker.
828+
829+
:param dict env: The WSGI environment dictionary
830+
:return: WbResponse with service worker
831+
:rtype: WbResponse
832+
"""
833+
response = self.serve_static(env, coll='', filepath='wabacWorker.js')
834+
response.status_headers['Service-Worker-Allowed'] = '/'
835+
return response
836+
821837

822838
# ============================================================================
823839
class MetadataCache(object):

pywb/apps/rewriterapp.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ def __init__(self, framed_replay=False, jinja_env=None, config=None, paths=None)
8484
self._html_templ('head_insert_html'),
8585
self.custom_banner_view)
8686

87+
self.client_side_replay = self.config.get('client_side_replay', False)
88+
8789
self.frame_insert_view = TopFrameView(self.jinja_env,
8890
self._html_templ('frame_insert_html'),
8991
self.banner_view)
@@ -933,6 +935,7 @@ def handle_custom_response(self, environ, wb_url, full_prefix, host_prefix, kwar
933935
environ,
934936
self.frame_mod,
935937
self.replay_mod,
938+
self.client_side_replay,
936939
coll='',
937940
extra_params=extra_params)
938941

pywb/rewrite/templateview.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ def get_top_frame(self, wb_url,
388388
env,
389389
frame_mod,
390390
replay_mod,
391+
client_side_replay,
391392
coll='',
392393
extra_params=None):
393394
"""
@@ -397,6 +398,7 @@ def get_top_frame(self, wb_url,
397398
:param dict env: The WSGI environment dictionary for the request this template is being rendered for
398399
:param str frame_mod: The modifier to be used for framing (e.g. if_)
399400
:param str replay_mod: The modifier to be used in the URL of the page being replayed (e.g. mp_)
401+
:param bool client_side_replay: Boolean indicating whether to use wabac.js-based client side replay
400402
:param str coll: The name of the collection this template is being rendered for
401403
:param dict extra_params: Additional parameters to be supplied to the Jninja template render method
402404
:return: The frame insert string
@@ -423,6 +425,7 @@ def get_top_frame(self, wb_url,
423425

424426
'embed_url': embed_url,
425427
'is_proxy': is_proxy,
428+
'client_side_replay': client_side_replay,
426429
'timestamp': timestamp,
427430
'url': wb_url.get_url()
428431
}

pywb/static/loadWabac.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
class WabacReplay
2+
{
3+
constructor(prefix, url, ts) {
4+
this.prefix = prefix;
5+
this.url = url;
6+
this.ts = ts;
7+
this.collName = new URL(prefix, "http://dummy").pathname.split('/')[1];
8+
this.adblockUrl = undefined;
9+
10+
this.queryParams = {};
11+
}
12+
13+
async init() {
14+
const scope = '/';
15+
16+
await navigator.serviceWorker.register("/static/sw.js?" + new URLSearchParams(this.queryParams).toString(), {scope});
17+
18+
let initedResolve = null;
19+
20+
const inited = new Promise((resolve) => initedResolve = resolve);
21+
22+
navigator.serviceWorker.addEventListener("message", (event) => {
23+
if (event.data.msg_type === "collAdded") {
24+
// the replay is ready to be loaded when this message is received
25+
initedResolve();
26+
}
27+
});
28+
29+
const baseUrl = new URL(window.location);
30+
baseUrl.hash = "";
31+
32+
const proxyPrefix = "";
33+
34+
const msg = {
35+
msg_type: "addColl",
36+
name: this.collName,
37+
type: "live",
38+
file: {"sourceUrl": `proxy:${proxyPrefix}`},
39+
skipExisting: false,
40+
extraConfig: {
41+
prefix: proxyPrefix,
42+
isLive: false,
43+
baseUrl: baseUrl.href,
44+
baseUrlHashReplay: true,
45+
noPostToGet: false,
46+
archivePrefix: `/${this.collName}/`,
47+
adblockUrl: this.adblockUrl
48+
},
49+
};
50+
51+
if (!navigator.serviceWorker.controller) {
52+
navigator.serviceWorker.addEventListener("controllerchange", () => {
53+
navigator.serviceWorker.controller.postMessage(msg);
54+
});
55+
} else {
56+
navigator.serviceWorker.controller.postMessage(msg);
57+
}
58+
59+
window.addEventListener('message', event => {
60+
let data = event.data;
61+
if (data.wb_type !== 'load') return;
62+
history.replaceState({}, data.title, this.prefix + data.ts + '/' + data.url);
63+
window.WBBanner.onMessage(event);
64+
});
65+
66+
window.cframe = this;
67+
68+
if (inited) {
69+
await inited;
70+
}
71+
72+
this.load_url(this.url, this.ts);
73+
}
74+
75+
// called by the Vue banner when the timeline is clicked
76+
load_url(url, ts) {
77+
const iframe = document.querySelector('#replay_iframe');
78+
iframe.src = `/w/${this.collName}/${ts}mp_/${url}`;
79+
}
80+
}

pywb/templates/frame_insert.html

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,15 @@
1212
}
1313

1414
</style>
15+
16+
{% if client_side_replay %}
17+
<script src='{{ static_prefix }}/loadWabac.js'></script>
18+
<script>
19+
new WabacReplay("{{ wb_prefix }}", "{{ url }}", "{{ timestamp }}").init();
20+
</script>
21+
{% else %}
1522
<script src='{{ static_prefix }}/wb_frame.js'> </script>
23+
{% endif %}
1624

1725
{% autoescape false %}
1826

@@ -45,13 +53,16 @@
4553
<div id="wb_iframe_div">
4654
<iframe id="replay_iframe" frameborder="0" seamless="seamless" scrolling="yes" class="wb_iframe" allow="autoplay; fullscreen"></iframe>
4755
</div>
56+
57+
{% if not client_side_replay %}
4858
<script>
4959
var cframe = new ContentFrame({"url": "{{ url }}" + window.location.hash,
5060
"prefix": "{{ wb_prefix }}",
5161
"request_ts": "{{ wb_url.timestamp }}",
5262
"iframe": "#replay_iframe"});
5363

5464
</script>
65+
{% endif %}
5566
</body>
5667
</html>
5768
{% endautoescape %}

0 commit comments

Comments
 (0)