mirror of
https://github.com/webrecorder/pywb.git
synced 2025-03-15 00:03:28 +01:00
WIP: Add client-side replay option
This commit is contained in:
parent
db56fb2df0
commit
8707918269
@ -29,6 +29,10 @@ framed_replay: true
|
|||||||
|
|
||||||
redirect_to_exact: true
|
redirect_to_exact: true
|
||||||
|
|
||||||
|
# Use wabac.js-style client-side replay system
|
||||||
|
client_side_replay: false
|
||||||
|
|
||||||
|
|
||||||
# Uncomment and change to set default locale
|
# Uncomment and change to set default locale
|
||||||
# default_locale: en
|
# default_locale: en
|
||||||
|
|
||||||
|
@ -81,6 +81,8 @@ class FrontEndApp(object):
|
|||||||
|
|
||||||
self.debug = config.get('debug', False)
|
self.debug = config.get('debug', False)
|
||||||
|
|
||||||
|
self.client_side_replay = config.get('client_side_replay', False)
|
||||||
|
|
||||||
self.warcserver_server = GeventServer(self.warcserver, port=0)
|
self.warcserver_server = GeventServer(self.warcserver, port=0)
|
||||||
|
|
||||||
self.proxy_prefix = None # the URL prefix to be used for the collection with proxy mode (e.g. /coll/id_/)
|
self.proxy_prefix = None # the URL prefix to be used for the collection with proxy mode (e.g. /coll/id_/)
|
||||||
@ -130,6 +132,9 @@ class FrontEndApp(object):
|
|||||||
coll_prefix = '/<coll>'
|
coll_prefix = '/<coll>'
|
||||||
self.url_map.add(Rule('/', endpoint=self.serve_home))
|
self.url_map.add(Rule('/', endpoint=self.serve_home))
|
||||||
|
|
||||||
|
if self.client_side_replay:
|
||||||
|
self.url_map.add(Rule('/static/sw.js', endpoint=self.serve_wabac_service_worker))
|
||||||
|
|
||||||
self._init_coll_routes(coll_prefix)
|
self._init_coll_routes(coll_prefix)
|
||||||
|
|
||||||
if self.proxy_prefix is not None:
|
if self.proxy_prefix is not None:
|
||||||
@ -818,6 +823,17 @@ class FrontEndApp(object):
|
|||||||
response.add_access_control_headers(env=env)
|
response.add_access_control_headers(env=env)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
def serve_wabac_service_worker(self, env):
|
||||||
|
"""Serve wabac.js service worker.
|
||||||
|
|
||||||
|
:param dict env: The WSGI environment dictionary
|
||||||
|
:return: WbResponse with service worker
|
||||||
|
:rtype: WbResponse
|
||||||
|
"""
|
||||||
|
response = self.serve_static(env, coll='', filepath='wabacWorker.js')
|
||||||
|
response.status_headers['Service-Worker-Allowed'] = '/'
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
class MetadataCache(object):
|
class MetadataCache(object):
|
||||||
|
@ -84,6 +84,8 @@ class RewriterApp(object):
|
|||||||
self._html_templ('head_insert_html'),
|
self._html_templ('head_insert_html'),
|
||||||
self.custom_banner_view)
|
self.custom_banner_view)
|
||||||
|
|
||||||
|
self.client_side_replay = self.config.get('client_side_replay', False)
|
||||||
|
|
||||||
self.frame_insert_view = TopFrameView(self.jinja_env,
|
self.frame_insert_view = TopFrameView(self.jinja_env,
|
||||||
self._html_templ('frame_insert_html'),
|
self._html_templ('frame_insert_html'),
|
||||||
self.banner_view)
|
self.banner_view)
|
||||||
@ -933,6 +935,7 @@ class RewriterApp(object):
|
|||||||
environ,
|
environ,
|
||||||
self.frame_mod,
|
self.frame_mod,
|
||||||
self.replay_mod,
|
self.replay_mod,
|
||||||
|
self.client_side_replay,
|
||||||
coll='',
|
coll='',
|
||||||
extra_params=extra_params)
|
extra_params=extra_params)
|
||||||
|
|
||||||
|
@ -388,6 +388,7 @@ class TopFrameView(BaseInsertView):
|
|||||||
env,
|
env,
|
||||||
frame_mod,
|
frame_mod,
|
||||||
replay_mod,
|
replay_mod,
|
||||||
|
client_side_replay,
|
||||||
coll='',
|
coll='',
|
||||||
extra_params=None):
|
extra_params=None):
|
||||||
"""
|
"""
|
||||||
@ -397,6 +398,7 @@ class TopFrameView(BaseInsertView):
|
|||||||
:param dict env: The WSGI environment dictionary for the request this template is being rendered for
|
:param dict env: The WSGI environment dictionary for the request this template is being rendered for
|
||||||
:param str frame_mod: The modifier to be used for framing (e.g. if_)
|
:param str frame_mod: The modifier to be used for framing (e.g. if_)
|
||||||
:param str replay_mod: The modifier to be used in the URL of the page being replayed (e.g. mp_)
|
:param str replay_mod: The modifier to be used in the URL of the page being replayed (e.g. mp_)
|
||||||
|
:param bool client_side_replay: Boolean indicating whether to use wabac.js-based client side replay
|
||||||
:param str coll: The name of the collection this template is being rendered for
|
:param str coll: The name of the collection this template is being rendered for
|
||||||
:param dict extra_params: Additional parameters to be supplied to the Jninja template render method
|
:param dict extra_params: Additional parameters to be supplied to the Jninja template render method
|
||||||
:return: The frame insert string
|
:return: The frame insert string
|
||||||
@ -423,6 +425,7 @@ class TopFrameView(BaseInsertView):
|
|||||||
|
|
||||||
'embed_url': embed_url,
|
'embed_url': embed_url,
|
||||||
'is_proxy': is_proxy,
|
'is_proxy': is_proxy,
|
||||||
|
'client_side_replay': client_side_replay,
|
||||||
'timestamp': timestamp,
|
'timestamp': timestamp,
|
||||||
'url': wb_url.get_url()
|
'url': wb_url.get_url()
|
||||||
}
|
}
|
||||||
|
80
pywb/static/loadWabac.js
Normal file
80
pywb/static/loadWabac.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
class WabacReplay
|
||||||
|
{
|
||||||
|
constructor(prefix, url, ts) {
|
||||||
|
this.prefix = prefix;
|
||||||
|
this.url = url;
|
||||||
|
this.ts = ts;
|
||||||
|
this.collName = new URL(prefix, "http://dummy").pathname.split('/')[1];
|
||||||
|
this.adblockUrl = undefined;
|
||||||
|
|
||||||
|
this.queryParams = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
async init() {
|
||||||
|
const scope = '/';
|
||||||
|
|
||||||
|
await navigator.serviceWorker.register("/static/sw.js?" + new URLSearchParams(this.queryParams).toString(), {scope});
|
||||||
|
|
||||||
|
let initedResolve = null;
|
||||||
|
|
||||||
|
const inited = new Promise((resolve) => initedResolve = resolve);
|
||||||
|
|
||||||
|
navigator.serviceWorker.addEventListener("message", (event) => {
|
||||||
|
if (event.data.msg_type === "collAdded") {
|
||||||
|
// the replay is ready to be loaded when this message is received
|
||||||
|
initedResolve();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const baseUrl = new URL(window.location);
|
||||||
|
baseUrl.hash = "";
|
||||||
|
|
||||||
|
const proxyPrefix = "";
|
||||||
|
|
||||||
|
const msg = {
|
||||||
|
msg_type: "addColl",
|
||||||
|
name: this.collName,
|
||||||
|
type: "live",
|
||||||
|
file: {"sourceUrl": `proxy:${proxyPrefix}`},
|
||||||
|
skipExisting: false,
|
||||||
|
extraConfig: {
|
||||||
|
prefix: proxyPrefix,
|
||||||
|
isLive: false,
|
||||||
|
baseUrl: baseUrl.href,
|
||||||
|
baseUrlHashReplay: true,
|
||||||
|
noPostToGet: false,
|
||||||
|
archivePrefix: `/${this.collName}/`,
|
||||||
|
adblockUrl: this.adblockUrl
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!navigator.serviceWorker.controller) {
|
||||||
|
navigator.serviceWorker.addEventListener("controllerchange", () => {
|
||||||
|
navigator.serviceWorker.controller.postMessage(msg);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
navigator.serviceWorker.controller.postMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('message', event => {
|
||||||
|
let data = event.data;
|
||||||
|
if (data.wb_type !== 'load') return;
|
||||||
|
history.replaceState({}, data.title, this.prefix + data.ts + '/' + data.url);
|
||||||
|
window.WBBanner.onMessage(event);
|
||||||
|
});
|
||||||
|
|
||||||
|
window.cframe = this;
|
||||||
|
|
||||||
|
if (inited) {
|
||||||
|
await inited;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.load_url(this.url, this.ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by the Vue banner when the timeline is clicked
|
||||||
|
load_url(url, ts) {
|
||||||
|
const iframe = document.querySelector('#replay_iframe');
|
||||||
|
iframe.src = `/w/${this.collName}/${ts}mp_/${url}`;
|
||||||
|
}
|
||||||
|
}
|
@ -12,7 +12,15 @@ html, body
|
|||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
{% if client_side_replay %}
|
||||||
|
<script src='{{ static_prefix }}/loadWabac.js'></script>
|
||||||
|
<script>
|
||||||
|
new WabacReplay("{{ wb_prefix }}", "{{ url }}", "{{ timestamp }}").init();
|
||||||
|
</script>
|
||||||
|
{% else %}
|
||||||
<script src='{{ static_prefix }}/wb_frame.js'> </script>
|
<script src='{{ static_prefix }}/wb_frame.js'> </script>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% autoescape false %}
|
{% autoescape false %}
|
||||||
|
|
||||||
@ -45,6 +53,8 @@ html, body
|
|||||||
<div id="wb_iframe_div">
|
<div id="wb_iframe_div">
|
||||||
<iframe id="replay_iframe" frameborder="0" seamless="seamless" scrolling="yes" class="wb_iframe" allow="autoplay; fullscreen"></iframe>
|
<iframe id="replay_iframe" frameborder="0" seamless="seamless" scrolling="yes" class="wb_iframe" allow="autoplay; fullscreen"></iframe>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if not client_side_replay %}
|
||||||
<script>
|
<script>
|
||||||
var cframe = new ContentFrame({"url": "{{ url }}" + window.location.hash,
|
var cframe = new ContentFrame({"url": "{{ url }}" + window.location.hash,
|
||||||
"prefix": "{{ wb_prefix }}",
|
"prefix": "{{ wb_prefix }}",
|
||||||
@ -52,6 +62,7 @@ html, body
|
|||||||
"iframe": "#replay_iframe"});
|
"iframe": "#replay_iframe"});
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
{% endif %}
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
{% endautoescape %}
|
{% endautoescape %}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user