Skip to content

Commit d0a6f15

Browse files
atotw4lldko
authored
Add inject_scripts option (#967)
* Add inject_scripts option This option enables injecting custom scripts from static/ into replayed pages in both the client-side and server-side replay modes. This is useful for emulating removed browser features (such as emulating Flash Player with Ruffle) or applying compatibility or behavior tweaks. * Supports injecting scripts in both server-side and client-side replay * Supports injecting scripts globally and per collection, eg: ``` inject_scripts: - all.js - ruffle.js collections: mycoll: inject_scripts: - all.js # static/all.js - _/mycoll/tweaks.js # collections/mycoll/static/tweaks.js ``` Co-authored-by: Tessa Walsh <[email protected]> Co-authored-by: Lauren Ko <[email protected]>
1 parent ac94081 commit d0a6f15

File tree

9 files changed

+57
-4
lines changed

9 files changed

+57
-4
lines changed

config.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,7 @@ redirect_to_exact: true
4040
# locales:
4141
# - en
4242
# - ru
43+
44+
# Uncomment to inject extra scripts into replayed pages (place scripts under static/ or your alternate static_dir location)
45+
# inject_scripts:
46+
# - ruffle/ruffle.js

docs/manual/configuring.rst

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,3 +655,30 @@ By default, SSL-Certificates of websites are not verified. To enable verificatio
655655

656656
``ca_cert_dir`` can optionally point to a directory containing the CA certificates that you trust. Most linux distributions provide CA certificates via a package called ``ca-certificates``.
657657
If omitted, the default system CA used by Python is used.
658+
659+
Injecting Scripts
660+
-----------------
661+
662+
Extra JavaScript files can be injected into replayed pages. This can be useful for emulating removed browser features
663+
or applying compatibility tweaks.
664+
665+
For example, to emulate Flash Player using `Ruffle <https://ruffle.rs/>`_, create a subdirectory named ``static/ruffle``
666+
and unzip the `Ruffle self-hosted package <https://ruffle.rs/downloads#website-package>`_ into it. Then add the following
667+
configuration::
668+
669+
inject_scripts:
670+
- ruffle/ruffle.js
671+
672+
Note: Paths listed under ``inject_scripts`` are relative to the ``static_dir`` directory (default ``static/``).
673+
674+
Injected scripts can also be configured per collection::
675+
676+
inject_scripts:
677+
- all.js
678+
- other.js
679+
680+
collections:
681+
mycoll:
682+
inject_scripts:
683+
- all.js # static/all.js
684+
- _/mycoll/tweaks.js # collections/mycoll/static/tweaks.js

pywb/apps/rewriterapp.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,8 @@ def render_content(self, wb_url, kwargs, environ):
536536
replay_mod=self.replay_mod,
537537
metadata=kwargs.get('metadata', {}),
538538
ui=kwargs.get('ui', {}),
539-
config=self.config))
539+
config=self.config,
540+
inject_scripts=self.get_inject_scripts(kwargs)))
540541

541542
cookie_rewriter = None
542543
if self.cookie_tracker and cookie_key:
@@ -926,6 +927,14 @@ def get_top_frame_params(self, wb_url, kwargs):
926927
'ui': kwargs.get('ui', {})
927928
}
928929

930+
def get_inject_scripts(self, kwargs):
931+
coll = kwargs.get('coll')
932+
coll_config = self.config.get('collections', {}).get(coll, {})
933+
# ignore special collections like live or all
934+
if isinstance(coll_config, str):
935+
coll_config = {}
936+
return coll_config.get('inject_scripts', self.config.get('inject_scripts', []))
937+
929938
def handle_custom_response(self, environ, wb_url, full_prefix, host_prefix, kwargs):
930939
if self.is_framed_replay(wb_url):
931940
extra_params = self.get_top_frame_params(wb_url, kwargs)
@@ -936,6 +945,7 @@ def handle_custom_response(self, environ, wb_url, full_prefix, host_prefix, kwar
936945
self.frame_mod,
937946
self.replay_mod,
938947
self.client_side_replay,
948+
self.get_inject_scripts(kwargs),
939949
coll=kwargs.get("coll"),
940950
extra_params=extra_params)
941951

pywb/rewrite/templateview.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ def get_top_frame(self, wb_url,
389389
frame_mod,
390390
replay_mod,
391391
client_side_replay,
392+
inject_scripts,
392393
coll='',
393394
extra_params=None):
394395
"""
@@ -429,7 +430,8 @@ def get_top_frame(self, wb_url,
429430
'timestamp': timestamp,
430431
'url': wb_url.get_url(),
431432

432-
'sw_prefix': env.get('pywb.app_prefix', '')
433+
'sw_prefix': env.get('pywb.app_prefix', ''),
434+
'inject_scripts': inject_scripts,
433435
}
434436

435437
if extra_params:

pywb/static/loadWabac.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
class WabacReplay
22
{
3-
constructor(prefix, url, ts, staticPrefix, coll, swScopePrefix) {
3+
constructor(prefix, url, ts, staticPrefix, coll, swScopePrefix, injectScripts) {
44
this.prefix = prefix;
55
this.url = url;
66
this.ts = ts;
77
this.staticPrefix = staticPrefix;
88
this.collName = coll;
99
this.isRoot = coll === "$root";
1010
this.swScope = swScopePrefix;
11+
this.injectScripts = injectScripts;
1112
this.adblockUrl = undefined;
1213

1314
this.queryParams = {"replayPrefix": ""};
@@ -54,6 +55,7 @@ class WabacReplay
5455
archiveMod: "ir_",
5556
adblockUrl: this.adblockUrl,
5657
noPostToGet: true,
58+
injectScripts: this.injectScripts.map(src => "../" + src),
5759
},
5860
};
5961

pywb/templates/frame_insert.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555

5656
if (navigator.serviceWorker) {
5757
{% if client_side_replay %}
58-
window.cframe = new WabacReplay("{{ wb_prefix }}", "{{ url }}", "{{ timestamp }}", "{{ static_prefix }}", "{{ coll }}", "{{ sw_prefix }}");
58+
window.cframe = new WabacReplay("{{ wb_prefix }}", "{{ url }}", "{{ timestamp }}", "{{ static_prefix }}", "{{ coll }}", "{{ sw_prefix }}", {{ inject_scripts | tojson }});
5959
window.cframe.init();
6060
{% else %}
6161
navigator.serviceWorker.getRegistration("{{ sw_prefix }}").then(reg => { if (reg) reg.unregister() });

pywb/templates/head_insert.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,10 @@
5454
</script>
5555
{% endif %}
5656

57+
{% for script in inject_scripts %}
58+
<script src='{{ static_prefix }}/{{ script }}'></script>
59+
{% endfor %}
60+
5761
{% if config.enable_flash_video_rewrite or config.transclusions_version == 1 %}
5862
<script src='{{ static_prefix }}/vidrw.js'> </script>
5963

tests/config_test.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,6 @@ enable_memento: true
4141

4242
# enable new transclusion system
4343
transclusions_version: 2
44+
45+
inject_scripts:
46+
- inject.js

tests/test_integration.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ def test_replay_content(self, fmod):
118118
assert '"20140127171238"' in resp.text, resp.text
119119
assert 'wombat.js' in resp.text
120120
assert 'transclusions.js' in resp.text
121+
assert 'inject.js' in resp.text
121122
assert '_WBWombatInit' in resp.text, resp.text
122123
assert 'wbinfo.enable_auto_fetch = false;' in resp.text
123124
assert '/pywb/20140127171238{0}/http://www.iana.org/time-zones"'.format(fmod) in resp.text

0 commit comments

Comments
 (0)