-
Notifications
You must be signed in to change notification settings - Fork 27
Audio Muting: Adding a Custom Site
Adding support for audio muting requires being able to identify the caption/subtitle text, as opposed to all other text on the page. Once text has been identified as a caption/subtitle, it can be checked for any words that should be filtered, and then ultimately muted. Because each site is a little different, there are different modes that can be used:
-
Element
- Text is contained in HTML elements on the page
- Best suited for sites with a predictable pattern of adding the next caption/subtitle text to the page
- Requires all captions/subtitles to be included in the same mutation
-
ElementChild
- Like Element mode, but also supports a
parentSelector
for identifying the caption/subtitle text
- Like Element mode, but also supports a
-
Text Only
- Caption/subtitle text is contained in a plain
#text
element on the page - Requires a consistent
parentSelector
that contains the text element
- Caption/subtitle text is contained in a plain
-
Video TextTrack Cues
- Caption/subtitle text is stored inside the HTML video element
- Because this mode may have access to actual timing data, it is possible to adjust the sync with
videoCueSync
-
Watcher:
- Watch a specific HTML element (parent) for captions/subtitles
- Useful when captions are not updated through a predictable way (different elements, getting replaced, etc)
- While a video is playing, all other filtering is disabled
- Less efficient, but sometimes the only way to filter some sites
To add support for a new site, the first step is identifying which mode is appropriate, and then include enough detail to precisely identify the subtitle text.
These instructions were tested with Google Chrome. You may need to alter these steps if you are using a different browser.
- Pause a video on the desired site with the subtitles visible
- Open the browser's Developer Tools (F12, CTRL+SHIFT+I, or Right-click on the page and choose "Inspect").
- On the "Elements" tab, search for a word (
CTRL+F
) that is in the visible subtitle text, the less common the better. - Look through results until you find the one that contains the rest of the subtitle text (Note: it may be split into multiple elements, especially if there are multiple lines)
- If you found the subtitle text element, proceed to Element/Text Mode
- If you didn't find the subtitle text in step 4, try the steps in the Video TextTrack Mode
- If you still haven't found a match, you can try these steps again on a different video, or a different set of words. If there are still no matches, you have likely found a site that uses a different mechanism for subtitles. Feel free to open an issue to request some help.
-
Click on the "Sources" tab in the Developer Tools. On the left panel click on "Content scripts" (You might have to click on the ">>" menu to see it), then expand "Advanced Profanity Filter" and then click on
webFilter.js
. -
Search (CTRL+F) for this line
this.mutePage ?
(inside theprocessAddedNode
function definition) -
Right click on the line (left side of the pane) and select
Add conditional breakpoint
- Note: If you left click it will instantly add the breakpoint. You can then right-click it and select edit to add the condition.
-
Use the following condition but replace
'word'
with a visible word in the subtitle (keep the single quotes!):-
e.textContent.toLowerCase().includes('word');
-
-
You will then need to put the video back a few seconds (before the subtitle in question is visible), and the let it play. When the breakpoint is hit, type
e
and press enter in the JavaScript console, and then right click on the element and select "Copy element" (or expand it and take a screenshot).Tip: If the JavaScript console isn't visible, press the
ESC
key once or twice. If it still doesn't appear you can use theConsole
tab at the top of the window. -
If the node element is just
#text
, then you will need to find its closest parent element that can be targeted, and then use the Text-Only mode, something like this:
{
"videosite.com": {
"textParentSelector": "div.subtitles-container"
}
}
- If node is not a plain text
#text
element, then use the available options in the Custom Audio Site Config Structure to help the filter know what text should be considered a subtitle. If you are unsure what to do, or need help, please create an issue that includes the site and the node (from step 5).
While on the desired site, follow the first 2 steps here Identify Mode and then paste the following code in the Developer Tools console, but replace words to find
on the first line with 2-3 words (make sure they are on the same line) in the currently visible subtitles.
textToFind = 'words to find';
videos = document.querySelectorAll('video');
if (videos.length) {
videos.forEach((video) => {
found = false;
for (i = 0; i < video.textTracks.length; i++) {
track = video.textTracks[i];
for (j = 0; j < track.activeCues.length; j++) {
activeCue = track.activeCues[j];
if (activeCue.text.toLowerCase().includes(textToFind.toLowerCase())) {
found = true;
data = {};
data[window.location.host] = {
mode: 'cue',
videoCueLanguage: track.language
};
console.log(`[APF] Found Video TextTrack match: "${activeCue.text}"`, video, track);
console.log(`[APF] Possible config:\n${JSON.stringify(data, null, 2)}`);
if (videos.length > 1) {
console.log(`['APF'] Warning, ${videos.length} video elements found. You'll likely need to add a videoSelector to the rule.`);
}
}
}
}
if (!found) {
console.log('[APF] No match found in the Video Text Track', video);
}
});
} else {
console.log('[APF] No videos found: Are you sure the there is a video on the page?');
}
You should see some output from the command, and if a match was found, you will also be presented with a possible configuration. You can take that config and put it in the Custom Audio Sites box found in the Audio tab of the extension's options page. If you see "No match found in the Video Text Track", that means that this mode probably won't work for the current video.
You can add your custom audio sites by putting them in the Custom Audio Site box found in the Option's Audio page. They must be formatted as JSON.
You can override the built-in Audio Sites by adding your own entry for the desired site. To see all the built-in Audio Sites and their config: Open the extension's options page, click on the "Audio" tab, and then select the "View Supported Sites" button.
Here are some examples from the supported sites for reference.
Name | Default | Description |
---|---|---|
filterSubtitles |
true |
Filter/update text (Note: can causes issues on some sites) |
iframe |
undefined |
true: only IFRAMES, false: no IFRAMES, undefined: all |
muteMethod |
0 /user |
Override global muteMthod (0: tab, 1: video) |
showSubtitles |
0 /user |
Show subtitles (0: All, 1: Filtered, 2: Unfiltered, 3: None) |
Name | Example | Description | Method |
---|---|---|---|
mode * |
"element" |
Enable the element mode | |
tagName * |
"DIV" |
The subtitle node element tag | node.tagName |
subtitleSelector ** |
"span > sub" |
Selector for the subtitle text | node.querySelector() |
className |
"myClass" |
Class name found on the subtitle node | node.className.includes() |
dataPropPresent |
"myData" |
Check for a data property to be there | node.dataset.hasOwnProperty() |
hasChildrenElements |
true |
Has children elements (Uncommon) | node.childElementCount > 0 |
containsSelector |
"subs" |
Has matching children (Uncommon) | node.querySelector() |
*
Required
**
Used for Filtering
Example:
{
"www.youtube.com": {
"mode": "element",
"className": "caption-window",
"subtitleSelector": "span.ytp-caption-segment",
"tagName": "DIV"
}
}
Name | Example | Description | Method |
---|---|---|---|
mode * |
"text" |
Enable the Text mode | |
parentSelector |
"div.subs" |
Check if node is a child of parent | parent.contains(node) |
Example:
{
"www.sonycrackle.com": {
"mode": "text",
"parentSelector": "div.clpp-subtitles-container"
}
}
Name | Example | Description | Method |
---|---|---|---|
mode * |
"cue" |
Enable video text track mode | |
videoCueLanguage |
"en" |
Text Track language | textTrack.language |
videoCueSync |
1.5 |
[Beta] Adjust Sync +/- (in seconds) | cue.startTime + videoCueSync |
videoSelector |
"video.class" |
Selector used to find desired video | document.querySelector('video') |
*
Required
Example:
{
"www.tntdrama.com": {
"mode": "cue",
"videoSelector": "video.top-media-element"
}
}
Name | Example | Description | Method |
---|---|---|---|
mode * |
"watcher" |
Enable the Watcher mode | |
checkInterval |
30 |
How often to check captions/subtitles | |
parentSelector |
"div.subs" |
Check if node is a child of parent | parent.contains(node) |
subtitleSelector |
"div.subs > p |
Selector for the subtitle text | node.querySelector() |
Example:
{
"www.amazon.com": [
{
"checkInterval": 10,
"mode": "watcher",
"iframe": false,
"parentSelector": "div.webPlayer div.persistentPanel",
"showSubtitles": 0,
"subtitleSelector": "div.webPlayer div.persistentPanel > div > div > div > p > span > span"
}
]
}
Although the Developer Tools in Firefox are different than Google Chrome, you should still be able to use it for this method. To find webFilter.js
, open the Developer Tools, navigate to the "Debugger" tab. In the "Sources" tab on the left-hand side, right-click in that pane and click on "Expand all". It will be under a moz-extension://
. Learn more about Debugging Content Scripts in Firefox.
This information may still be useful, but is old
When a change (mutation) happens on the page, the filter will:
- Check to see if the current element is a subtitle, and will proceed if all are true.
- Check if the tagName is
DIV
- Check if the node has the
caption-window
class - Check if the node contains a child matching
span.ytp-caption-segment
- Check if the tagName is
- If all above are true, then the filter will check the text inside the subtitleSelector if present, otherwise it will look at the text inside the node itself.
- If a blocked word is found, the subtitle will be filtered and the audio will be muted. If its already muted and a blocked word wasn't found, then it will be unmuted.
Once you have you have a Custom Site Config, you can put it to use by visiting the Audio tab in Advanced Profanity Filter's options page. If it doesn't work quite right, feel free to adjust it as necessary, or open an issue.
Example: Adding Hulu Support
This object is constructed of supported domains in the key (example: www.youtube.com), and a value of the child element that holds the actual subtitle text to filter.
static readonly subtitleSelectors = {
'www.youtube.com': 'span.ytp-caption-segment'
}
See webAudio.ts for more examples.
Check to see if the current node is a supported subtitle/caption node. This should be specific enough to not cause false-positives, but not check everything under the sun for performance reasons. In this example, we check the following:
- The tag is a
div
- Includes a class named
caption-window
- Has at least 1 child element that is a
span
with a class namedcaptions-text
, which in turn has aspan
child with the classytp-caption-segment
.
static supportedNode(hostname: string, node: any): boolean {
switch(hostname) {
case 'www.youtube.com':
return !!(node.tagName == 'DIV' && node.className.includes('caption-window') && node.querySelectorAll('span.captions-text span.ytp-caption-segment').length > 0);
}
See webAudio.ts for more examples.