Skip to content

Commit 8e28a83

Browse files
committed
Dual support for custom error schema
1 parent 6f91ff5 commit 8e28a83

File tree

4 files changed

+152
-7
lines changed

4 files changed

+152
-7
lines changed

special-pages/pages/duckplayer/app/settings.js

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,69 @@
11
const DEFAULT_SIGN_IN_REQURED_HREF = '[href*="//support.google.com/youtube/answer/3037019"]';
22

3+
/**
4+
* @typedef {object} CustomErrorSettings
5+
* @property {'enabled'|'disabled'} state
6+
* @property {object} [settings]
7+
* @property {string} [settings.signInRequiredSelector]
8+
*/
9+
310
export class Settings {
411
/**
512
* @param {object} params
613
* @param {{name: ImportMeta['platform']}} [params.platform]
714
* @param {{state: 'enabled' | 'disabled'}} [params.pip]
815
* @param {{state: 'enabled' | 'disabled'}} [params.autoplay]
916
* @param {{state: 'enabled' | 'disabled'}} [params.focusMode]
10-
* @param {import("../types/duckplayer.js").InitialSetupResponse['settings']['customError']} [params.customError]
17+
* @param {import("../types/duckplayer.js").DuckPlayerPageSettings['customError']} [params.customError]
1118
*/
1219
constructor({
1320
platform = { name: 'macos' },
1421
pip = { state: 'disabled' },
1522
autoplay = { state: 'enabled' },
1623
focusMode = { state: 'enabled' },
17-
customError = { state: 'disabled', signInRequiredSelector: '' },
24+
customError = { state: 'disabled', settings: {}, signInRequiredSelector: '' },
1825
}) {
1926
this.platform = platform;
2027
this.pip = pip;
2128
this.autoplay = autoplay;
2229
this.focusMode = focusMode;
23-
this.customError = customError;
30+
this.customError = this.parseLegacyCustomError(customError);
31+
}
32+
33+
/**
34+
* Parses custom error settings so that both old and new schemas are accepted.
35+
*
36+
* Old schema:
37+
* {
38+
* state: "enabled",
39+
* signInRequiredSelector: "div"
40+
* }
41+
*
42+
* New schema:
43+
* {
44+
* state: "disabled",
45+
* settings: {
46+
* signInRequiredSelector: "div"
47+
* }
48+
* }
49+
*
50+
* @param {import("../types/duckplayer.js").InitialSetupResponse['settings']['customError']} customErrorSettings
51+
* @return {CustomErrorSettings}
52+
*/
53+
parseLegacyCustomError(customErrorSettings) {
54+
if (!customErrorSettings || customErrorSettings.state !== 'enabled') {
55+
return { state: 'disabled'};
56+
}
57+
58+
const { settings, signInRequiredSelector } = customErrorSettings;
59+
60+
return {
61+
state: 'enabled',
62+
settings: {
63+
...settings,
64+
...(signInRequiredSelector && { signInRequiredSelector })
65+
}
66+
};
2467
}
2568

2669
/**

special-pages/pages/duckplayer/messages/initialSetup.response.json

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,15 +42,24 @@
4242
"customError": {
4343
"type": "object",
4444
"description": "Configures a custom error message for YouTube errors",
45-
"required": ["state", "signInRequiredSelector"],
45+
"required": ["state"],
4646
"properties": {
4747
"state": {
4848
"type": "string",
4949
"enum": ["enabled", "disabled"]
5050
},
51+
"settings": {
52+
"type": "object",
53+
"description": "Custom error settings",
54+
"properties": {
55+
"signInRequiredSelector": {
56+
"$ref": "#/definitions/signInRequiredSelector"
57+
}
58+
}
59+
},
5160
"signInRequiredSelector": {
52-
"description": "A selector that, when not empty, indicates a sign-in required error",
53-
"type": "string"
61+
"$ref": "#/definitions/signInRequiredSelector",
62+
"$comment": "This setting is duplicated at the top level until Apple and Windows are migrated to the settings object above"
5463
}
5564
}
5665
}
@@ -77,5 +86,11 @@
7786
"type": "string",
7887
"description": "Optional locale-specific strings"
7988
}
89+
},
90+
"definitions": {
91+
"signInRequiredSelector": {
92+
"description": "A selector that, when not empty, indicates a sign-in required error",
93+
"type": "string"
94+
}
8095
}
8196
}

special-pages/pages/duckplayer/types/duckplayer.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,10 +134,19 @@ export interface DuckPlayerPageSettings {
134134
*/
135135
customError?: {
136136
state: "enabled" | "disabled";
137+
/**
138+
* Custom error settings
139+
*/
140+
settings?: {
141+
/**
142+
* A selector that, when not empty, indicates a sign-in required error
143+
*/
144+
signInRequiredSelector?: string;
145+
};
137146
/**
138147
* A selector that, when not empty, indicates a sign-in required error
139148
*/
140-
signInRequiredSelector: string;
149+
signInRequiredSelector?: string;
141150
};
142151
}
143152
/**
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { describe, it } from 'node:test';
2+
import { deepEqual } from 'node:assert/strict';
3+
import { Settings } from '../app/settings.js';
4+
5+
describe('parses settings', () => {
6+
it('handles disabled custom error schema', () => {
7+
const settings = new Settings({
8+
customError: {
9+
state: 'disabled'
10+
}
11+
});
12+
const expected = {
13+
state: 'disabled'
14+
};
15+
16+
deepEqual(settings.customError, expected);
17+
});
18+
it('handles old custom error schema', () => {
19+
const settings = new Settings({
20+
customError: {
21+
state: 'enabled',
22+
signInRequiredSelector: 'div'
23+
}
24+
});
25+
const expected = {
26+
state: 'enabled',
27+
settings: {
28+
signInRequiredSelector: 'div'
29+
}
30+
};
31+
32+
deepEqual(settings.customError, expected);
33+
});
34+
it('handles new custom error schema', () => {
35+
const settings = new Settings({
36+
customError: {
37+
state: 'enabled',
38+
settings: {
39+
signInRequiredSelector: 'div'
40+
}
41+
}
42+
});
43+
const expected = {
44+
state: 'enabled',
45+
settings: {
46+
signInRequiredSelector: 'div'
47+
}
48+
};
49+
50+
deepEqual(settings.customError, expected);
51+
});
52+
it('handles custom error enabled without settings', () => {
53+
const settings = new Settings({
54+
customError: {
55+
state: 'enabled'
56+
}
57+
});
58+
const expected = {
59+
state: 'enabled',
60+
settings: {}
61+
};
62+
63+
deepEqual(settings.customError, expected);
64+
});
65+
it('handles malformed custom error schema', () => {
66+
const settings = new Settings({
67+
customError: {
68+
// @ts-expect-error - Malformed object on purpose
69+
status: 'enabled'
70+
}
71+
});
72+
const expected = {
73+
state: 'disabled'
74+
};
75+
76+
deepEqual(settings.customError, expected);
77+
});
78+
});

0 commit comments

Comments
 (0)