pervert-monkey 1.0.13 → 1.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/pervertmonkey.core.es.d.ts +82 -29
- package/dist/core/pervertmonkey.core.es.js +233 -115
- package/dist/core/pervertmonkey.core.es.js.map +1 -1
- package/dist/core/pervertmonkey.core.umd.js +233 -115
- package/dist/core/pervertmonkey.core.umd.js.map +1 -1
- package/dist/userscripts/3hentai.user.js +4 -11
- package/dist/userscripts/camgirlfinder.user.js +2 -2
- package/dist/userscripts/camwhores.user.js +7 -16
- package/dist/userscripts/e-hentai.user.js +3 -4
- package/dist/userscripts/ebalka.user.js +13 -5
- package/dist/userscripts/eporner.user.js +21 -41
- package/dist/userscripts/erome.user.js +9 -8
- package/dist/userscripts/eroprofile.user.js +5 -14
- package/dist/userscripts/javhdporn.user.js +6 -5
- package/dist/userscripts/missav.user.js +10 -4
- package/dist/userscripts/motherless.user.js +12 -5
- package/dist/userscripts/namethatporn.user.js +8 -14
- package/dist/userscripts/nhentai.user.js +5 -13
- package/dist/userscripts/obmenvsem.user.js +10 -4
- package/dist/userscripts/pornhub.user.js +14 -6
- package/dist/userscripts/spankbang.user.js +28 -7
- package/dist/userscripts/thisvid.user.js +12 -30
- package/dist/userscripts/xhamster.user.js +9 -14
- package/dist/userscripts/xvideos.user.js +33 -5
- package/package.json +1 -1
- package/src/core/data-handler/data-filter-fn-defaults.ts +52 -0
- package/src/core/data-handler/data-filter-fn.ts +60 -0
- package/src/core/data-handler/data-filter.ts +22 -96
- package/src/core/data-handler/data-manager.ts +75 -26
- package/src/core/jabroni-config/default-scheme.ts +54 -5
- package/src/core/jabroni-config/index.ts +1 -0
- package/src/core/jabroni-config/jabroni-gui-controller.ts +1 -1
- package/src/core/jabroni-config/scheme-selectors-mapping.ts +12 -0
- package/src/core/parsers/thumb-data-parser.ts +4 -15
- package/src/core/rules/index.ts +15 -9
- package/src/userscripts/index.ts +1 -1
- package/src/userscripts/scripts/3hentai.ts +3 -6
- package/src/userscripts/scripts/camgirlfinder.ts +1 -1
- package/src/userscripts/scripts/camwhores.ts +5 -14
- package/src/userscripts/scripts/e-hentai.ts +4 -6
- package/src/userscripts/scripts/ebalka.ts +11 -3
- package/src/userscripts/scripts/eporner.ts +20 -39
- package/src/userscripts/scripts/erome.ts +9 -7
- package/src/userscripts/scripts/eroprofile.ts +4 -12
- package/src/userscripts/scripts/javhdporn.ts +7 -8
- package/src/userscripts/scripts/missav.ts +10 -4
- package/src/userscripts/scripts/motherless.ts +11 -6
- package/src/userscripts/scripts/namethatporn.ts +8 -15
- package/src/userscripts/scripts/nhentai.ts +6 -13
- package/src/userscripts/scripts/obmenvsem.ts +9 -3
- package/src/userscripts/scripts/pornhub.ts +13 -4
- package/src/userscripts/scripts/spankbang.ts +29 -5
- package/src/userscripts/scripts/thisvid.ts +14 -29
- package/src/userscripts/scripts/xhamster.ts +9 -14
- package/src/userscripts/scripts/xvideos.ts +32 -3
- package/src/utils/parsers/index.ts +5 -2
|
@@ -4,8 +4,9 @@ import { Rules } from '../../core';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'Erome PervertMonkey',
|
|
7
|
-
version: '5.0.
|
|
8
|
-
description:
|
|
7
|
+
version: '5.0.6',
|
|
8
|
+
description:
|
|
9
|
+
'Infinite scroll [optional], Filter by Title, Uploader and Video/Photo albums, Sort by Views',
|
|
9
10
|
match: ['*://*.erome.com/*'],
|
|
10
11
|
};
|
|
11
12
|
|
|
@@ -22,13 +23,12 @@ const rules = new Rules({
|
|
|
22
23
|
selectors: {
|
|
23
24
|
title: '.album-title',
|
|
24
25
|
uploader: '.album-user',
|
|
25
|
-
videoAlbum: {
|
|
26
|
+
videoAlbum: { selector: '.album-videos', type: 'boolean' },
|
|
27
|
+
views: { selector: '.album-bottom-views', type: 'float' },
|
|
26
28
|
},
|
|
27
29
|
},
|
|
28
30
|
storeOptions: { showPhotos: true },
|
|
29
|
-
|
|
30
|
-
'filterInclude',
|
|
31
|
-
'filterExclude',
|
|
31
|
+
customDataFilterFns: [
|
|
32
32
|
{
|
|
33
33
|
filterPhotoAlbums: (el, state) =>
|
|
34
34
|
(state.filterPhotoAlbums && !el.videoAlbum) as boolean,
|
|
@@ -39,7 +39,8 @@ const rules = new Rules({
|
|
|
39
39
|
},
|
|
40
40
|
],
|
|
41
41
|
schemeOptions: [
|
|
42
|
-
'
|
|
42
|
+
'Title Filter',
|
|
43
|
+
'Uploader Filter',
|
|
43
44
|
{
|
|
44
45
|
title: 'Filter Albums',
|
|
45
46
|
content: [
|
|
@@ -53,6 +54,7 @@ const rules = new Rules({
|
|
|
53
54
|
},
|
|
54
55
|
],
|
|
55
56
|
},
|
|
57
|
+
'Sort By Views',
|
|
56
58
|
'Badge',
|
|
57
59
|
'Advanced',
|
|
58
60
|
],
|
|
@@ -3,8 +3,8 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'Eroprofile PervertMonkey',
|
|
6
|
-
version: '2.0.
|
|
7
|
-
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
6
|
+
version: '2.0.6',
|
|
7
|
+
description: 'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration',
|
|
8
8
|
match: ['https://*.eroprofile.com/*'],
|
|
9
9
|
};
|
|
10
10
|
|
|
@@ -23,18 +23,10 @@ const rules = new Rules({
|
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
containerSelector: '.videoGrid',
|
|
26
|
-
customDataSelectorFns: ['filterInclude', 'filterExclude', 'filterDuration'],
|
|
27
26
|
schemeOptions: [
|
|
28
|
-
'
|
|
27
|
+
'Title Filter',
|
|
29
28
|
'Duration Filter',
|
|
30
|
-
|
|
31
|
-
title: 'Sort By ',
|
|
32
|
-
content: [
|
|
33
|
-
{
|
|
34
|
-
'sort by duration': () => {},
|
|
35
|
-
},
|
|
36
|
-
],
|
|
37
|
-
},
|
|
29
|
+
'Sort By Duration',
|
|
38
30
|
'Badge',
|
|
39
31
|
'Advanced',
|
|
40
32
|
],
|
|
@@ -3,12 +3,10 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'Javhdporn PervertMonkey',
|
|
6
|
-
version: '3.0.
|
|
7
|
-
description:
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
"https://*.javhdporn.*/*"
|
|
11
|
-
],
|
|
6
|
+
version: '3.0.6',
|
|
7
|
+
description:
|
|
8
|
+
'Infinite scroll [optional], Filter by Title and Duration, Sort By Duration and Views',
|
|
9
|
+
match: ['https://*.javhdporn.net/*', 'https://*.javhdporn.*/*'],
|
|
12
10
|
};
|
|
13
11
|
|
|
14
12
|
const rules = new Rules({
|
|
@@ -18,10 +16,11 @@ const rules = new Rules({
|
|
|
18
16
|
selectors: {
|
|
19
17
|
title: 'header.entry-header',
|
|
20
18
|
duration: '.duration',
|
|
21
|
-
|
|
19
|
+
views: { selector: '.views', type: 'float' },
|
|
20
|
+
},
|
|
22
21
|
},
|
|
23
22
|
paginationStrategyOptions: {
|
|
24
23
|
pathnameSelector: /\/page\/(\d+)\/?$/,
|
|
25
24
|
},
|
|
26
|
-
schemeOptions: ['
|
|
25
|
+
schemeOptions: ['Title Filter', 'Duration Filter', 'Sort By', 'Badge', 'Advanced'],
|
|
27
26
|
});
|
|
@@ -3,8 +3,8 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'Missav PervertMonkey',
|
|
6
|
-
version: '3.0.
|
|
7
|
-
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
6
|
+
version: '3.0.6',
|
|
7
|
+
description: 'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration',
|
|
8
8
|
match: [
|
|
9
9
|
'https://*.missav123.com/*',
|
|
10
10
|
'https://*.missav.*/*',
|
|
@@ -24,8 +24,14 @@ const rules = new Rules({
|
|
|
24
24
|
selectors: {
|
|
25
25
|
title: 'div > div > a.text-secondary',
|
|
26
26
|
duration: 'div > a > span.text-xs',
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
28
|
},
|
|
29
29
|
thumbImg: { strategy: 'auto' },
|
|
30
|
-
schemeOptions: [
|
|
30
|
+
schemeOptions: [
|
|
31
|
+
'Title Filter',
|
|
32
|
+
'Duration Filter',
|
|
33
|
+
'Sort By Duration',
|
|
34
|
+
'Badge',
|
|
35
|
+
'Advanced',
|
|
36
|
+
],
|
|
31
37
|
});
|
|
@@ -5,8 +5,8 @@ import { fetchWith, OnHover, replaceElementTag, Tick } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'Motherless PervertMonkey',
|
|
8
|
-
version: '5.0.
|
|
9
|
-
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
8
|
+
version: '5.0.7',
|
|
9
|
+
description: 'Infinite scroll [optional], Filter by Title, Uploader and Duration, Sort by Duration and Views',
|
|
10
10
|
match: ['https://motherless.com/*'],
|
|
11
11
|
grant: ['GM_addElement', 'GM_addStyle', 'unsafeWindow'],
|
|
12
12
|
};
|
|
@@ -31,7 +31,14 @@ const rules = new Rules({
|
|
|
31
31
|
},
|
|
32
32
|
animatePreview,
|
|
33
33
|
gropeStrategy: 'all-in-all',
|
|
34
|
-
schemeOptions: [
|
|
34
|
+
schemeOptions: [
|
|
35
|
+
'Title Filter',
|
|
36
|
+
'Uploader Filter',
|
|
37
|
+
'Duration Filter',
|
|
38
|
+
'Sort By',
|
|
39
|
+
'Badge',
|
|
40
|
+
'Advanced',
|
|
41
|
+
],
|
|
35
42
|
});
|
|
36
43
|
|
|
37
44
|
function animatePreview(_: HTMLElement) {
|
|
@@ -174,9 +181,7 @@ function applySearchFilters() {
|
|
|
174
181
|
let pathname = window.location.pathname;
|
|
175
182
|
|
|
176
183
|
const wordsToFilter =
|
|
177
|
-
(rules.store.state.filterExcludeWords as string)
|
|
178
|
-
.replace(/f:/g, '')
|
|
179
|
-
.match(/(?<!user:)\b\w+\b(?!\s*:)/g) || [];
|
|
184
|
+
(rules.store.state.filterExcludeWords as string).match(/\w+/g) || [];
|
|
180
185
|
|
|
181
186
|
wordsToFilter
|
|
182
187
|
.filter((w) => !pathname.includes(w))
|
|
@@ -4,8 +4,8 @@ import { Rules } from '../../core';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'NameThatPorn PervertMonkey',
|
|
7
|
-
version: '3.0.
|
|
8
|
-
description: 'Infinite scroll [optional], Filter by Title and
|
|
7
|
+
version: '3.0.6',
|
|
8
|
+
description: 'Infinite scroll [optional], Filter by Title, Uploader and Solved/Unsolved',
|
|
9
9
|
match: ['https://namethatporn.com/*'],
|
|
10
10
|
};
|
|
11
11
|
|
|
@@ -16,26 +16,18 @@ const rules = new Rules({
|
|
|
16
16
|
selectors: {
|
|
17
17
|
title: '.item_title, .nsw_r_tit',
|
|
18
18
|
uploader: '.item_answer b, .nsw_r_desc',
|
|
19
|
-
solved: {
|
|
20
|
-
type: 'boolean',
|
|
21
|
-
selector: '.item_solved, .nsw_r_slvd',
|
|
22
|
-
},
|
|
19
|
+
solved: { selector: '.item_solved, .nsw_r_slvd', type: 'boolean' },
|
|
23
20
|
},
|
|
24
21
|
},
|
|
25
22
|
thumbImg: {
|
|
26
|
-
selector: (img: HTMLImageElement) =>
|
|
27
|
-
|
|
28
|
-
img.getAttribute('data-dyn')?.concat('.webp') || (img.getAttribute('src') as string)
|
|
29
|
-
);
|
|
30
|
-
},
|
|
23
|
+
selector: (img: HTMLImageElement) =>
|
|
24
|
+
img.getAttribute('data-dyn')?.concat('.webp') || (img.getAttribute('src') as string),
|
|
31
25
|
},
|
|
32
26
|
paginationStrategyOptions: {
|
|
33
27
|
paginationSelector: '#smi_wrp, #nsw_p',
|
|
34
28
|
},
|
|
35
29
|
gropeStrategy: 'all-in-all',
|
|
36
|
-
|
|
37
|
-
'filterInclude',
|
|
38
|
-
'filterExclude',
|
|
30
|
+
customDataFilterFns: [
|
|
39
31
|
{
|
|
40
32
|
filterSolved: (el, state) => (state.filterSolved && el.solved) as boolean,
|
|
41
33
|
},
|
|
@@ -44,7 +36,8 @@ const rules = new Rules({
|
|
|
44
36
|
},
|
|
45
37
|
],
|
|
46
38
|
schemeOptions: [
|
|
47
|
-
'
|
|
39
|
+
'Title Filter',
|
|
40
|
+
'Uploader Filter',
|
|
48
41
|
{
|
|
49
42
|
title: 'Filter Status',
|
|
50
43
|
content: [
|
|
@@ -4,7 +4,7 @@ import { parseHtml } from '../../utils';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'NHentai PervertMonkey',
|
|
7
|
-
version: '4.0.
|
|
7
|
+
version: '4.0.6',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title',
|
|
9
9
|
match: ['https://*.nhentai.net/*', 'https://*.nhentai.*/*'],
|
|
10
10
|
};
|
|
@@ -14,18 +14,10 @@ const IS_SEARCH_PAGE = /^\/search\//.test(location.pathname);
|
|
|
14
14
|
|
|
15
15
|
const nhentaiRules = new Rules({
|
|
16
16
|
thumbs: { selector: '.gallery' },
|
|
17
|
-
thumb: {
|
|
18
|
-
|
|
19
|
-
title: '.caption'
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
thumbImg: {
|
|
23
|
-
strategy: 'auto',
|
|
24
|
-
remove: 'auto'
|
|
25
|
-
},
|
|
17
|
+
thumb: { selectors: { title: '.caption' } },
|
|
18
|
+
thumbImg: { strategy: 'auto', remove: 'auto' },
|
|
26
19
|
containerSelectorLast: '.index-container, .container',
|
|
27
|
-
|
|
28
|
-
schemeOptions: ['Text Filter', 'Badge', 'Advanced'],
|
|
20
|
+
schemeOptions: ['Title Filter', 'Badge', 'Advanced'],
|
|
29
21
|
gropeStrategy: 'all-in-all',
|
|
30
22
|
});
|
|
31
23
|
|
|
@@ -57,7 +49,8 @@ function filtersUI() {
|
|
|
57
49
|
const btns = parseHtml(`<div class="sort-type"></div>`);
|
|
58
50
|
groupOfButtons.forEach((k) => {
|
|
59
51
|
const btn = parseHtml(
|
|
60
|
-
`<a href="#" ${
|
|
52
|
+
`<a href="#" ${
|
|
53
|
+
state.custom[k] ? 'style="background: rgba(59, 49, 70, 1)"' : ''
|
|
61
54
|
}>${filterDescriptors[k as keyof typeof filterDescriptors].name}</a>`,
|
|
62
55
|
);
|
|
63
56
|
btn.addEventListener('click', (e) => {
|
|
@@ -5,8 +5,8 @@ import { fetchHtml, parseUrl } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'Obmensvem PervertMonkey',
|
|
8
|
-
version: '1.0.
|
|
9
|
-
description: 'Infinite scroll [optional], Filter by Title and Duration',
|
|
8
|
+
version: '1.0.6',
|
|
9
|
+
description: 'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration',
|
|
10
10
|
match: ['https://*.obmenvsem.com/*', 'https://*.obmenvsem.*/*'],
|
|
11
11
|
grant: ['GM_addStyle', 'GM_addElement', 'unsafeWindow'],
|
|
12
12
|
};
|
|
@@ -67,5 +67,11 @@ const rules = new Rules({
|
|
|
67
67
|
return img.src;
|
|
68
68
|
},
|
|
69
69
|
},
|
|
70
|
-
schemeOptions: [
|
|
70
|
+
schemeOptions: [
|
|
71
|
+
'Title Filter',
|
|
72
|
+
'Duration Filter',
|
|
73
|
+
'Sort By Duration',
|
|
74
|
+
'Badge',
|
|
75
|
+
'Advanced',
|
|
76
|
+
],
|
|
71
77
|
});
|
|
@@ -3,8 +3,9 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'PornHub PervertMonkey',
|
|
6
|
-
version: '4.0.
|
|
7
|
-
description:
|
|
6
|
+
version: '4.0.6',
|
|
7
|
+
description:
|
|
8
|
+
'Infinite scroll [optional]. Filter by Title, Uploader and Duration. Sort by Duration and Views',
|
|
8
9
|
match: ['https://*.pornhub.com/*'],
|
|
9
10
|
exclude: 'https://*.pornhub.com/embed/*',
|
|
10
11
|
};
|
|
@@ -19,20 +20,28 @@ const rules = new Rules({
|
|
|
19
20
|
.filter((e) => e.children.length > 0 && e.checkVisibility())
|
|
20
21
|
.pop() as HTMLElement,
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
containerHomogenity: { id: true, className: true },
|
|
23
24
|
thumbs: { selector: 'li[data-video-vkey]' },
|
|
24
25
|
thumb: {
|
|
25
26
|
selectors: {
|
|
26
27
|
title: 'span.title',
|
|
27
28
|
uploader: '.usernameWrap',
|
|
28
29
|
duration: '.duration',
|
|
30
|
+
views: { selector: '.views', type: 'float' },
|
|
29
31
|
},
|
|
30
32
|
},
|
|
31
33
|
thumbImg: {
|
|
32
34
|
selector: ['data-mediumthumb', 'data-image'],
|
|
33
35
|
},
|
|
34
36
|
gropeStrategy: 'all-in-all',
|
|
35
|
-
schemeOptions: [
|
|
37
|
+
schemeOptions: [
|
|
38
|
+
'Title Filter',
|
|
39
|
+
'Uploader Filter',
|
|
40
|
+
'Duration Filter',
|
|
41
|
+
'Sort By',
|
|
42
|
+
'Badge',
|
|
43
|
+
'Advanced',
|
|
44
|
+
],
|
|
36
45
|
});
|
|
37
46
|
|
|
38
47
|
function bypassAgeVerification() {
|
|
@@ -3,8 +3,9 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'SpankBang.com PervertMonkey',
|
|
6
|
-
version: '4.0.
|
|
7
|
-
description:
|
|
6
|
+
version: '4.0.6',
|
|
7
|
+
description:
|
|
8
|
+
'Infinite scroll [optional]. Filter by Title and Duration. Sort by Duration and Views',
|
|
8
9
|
match: ['https://*.spankbang.com/*', 'https://*.spankbang.*/*'],
|
|
9
10
|
};
|
|
10
11
|
|
|
@@ -17,11 +18,34 @@ const rules = new Rules({
|
|
|
17
18
|
thumb: {
|
|
18
19
|
selectors: {
|
|
19
20
|
title: '[title]',
|
|
20
|
-
tags: { selector: '[data-testid="title"]', type: 'string' },
|
|
21
21
|
duration: '[data-testid="video-item-length"]',
|
|
22
|
-
|
|
22
|
+
// tags: { selector: '[data-testid="title"]', type: 'string' },
|
|
23
|
+
views: { selector: '[data-testid="views"]', type: 'float' },
|
|
24
|
+
quality: { selector: '[data-testid="video-item-resolution"]', type: 'string' },
|
|
25
|
+
},
|
|
23
26
|
},
|
|
24
27
|
thumbImg: { strategy: 'auto' },
|
|
25
28
|
gropeStrategy: 'all-in-all',
|
|
26
|
-
|
|
29
|
+
customDataFilterFns: [
|
|
30
|
+
{ qualityLow: (el, state) => !!state.qualityLow && el.quality !== '' },
|
|
31
|
+
{ qualityHD: (el, state) => !!state.qualityHD && el.quality !== 'HD' },
|
|
32
|
+
{ quality4k: (el, state) => !!state.quality4k && el.quality !== '4K' },
|
|
33
|
+
],
|
|
34
|
+
schemeOptions: [
|
|
35
|
+
'Title Filter',
|
|
36
|
+
'Duration Filter',
|
|
37
|
+
{
|
|
38
|
+
title: 'Quality Filter',
|
|
39
|
+
content: [
|
|
40
|
+
{ qualityLow: false, label: 'Low' },
|
|
41
|
+
{ qualityHD: false, label: 'HD' },
|
|
42
|
+
{ quality4k: false, label: '4K' },
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
'Sort By',
|
|
46
|
+
'Badge',
|
|
47
|
+
'Advanced',
|
|
48
|
+
],
|
|
27
49
|
});
|
|
50
|
+
|
|
51
|
+
console.log(rules.dataManager.data.values().toArray());
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
objectToFormData,
|
|
14
14
|
parseCssUrl,
|
|
15
15
|
parseHtml,
|
|
16
|
+
querySelectorLast,
|
|
16
17
|
querySelectorLastNumber,
|
|
17
18
|
range,
|
|
18
19
|
replaceElementTag,
|
|
@@ -21,9 +22,9 @@ import {
|
|
|
21
22
|
|
|
22
23
|
export const meta: MonkeyUserScript = {
|
|
23
24
|
name: 'ThisVid.com Improved',
|
|
24
|
-
version: '8.0.
|
|
25
|
+
version: '8.0.6',
|
|
25
26
|
description:
|
|
26
|
-
'Infinite scroll [optional]. Preview for private videos. Filter
|
|
27
|
+
'Infinite scroll [optional]. Preview for private videos. Filter by Title, Duration, Quality and Public/Private. Sort by Duration and Views. Private/Public feed of friends uploads. Check access to private vids. Mass friend request button. Sorts messages. Download button 📼',
|
|
27
28
|
match: ['https://*.thisvid.com/*'],
|
|
28
29
|
};
|
|
29
30
|
|
|
@@ -88,36 +89,18 @@ const defaultRulesConfig: RulesConfig = {
|
|
|
88
89
|
return { img, imgSrc };
|
|
89
90
|
},
|
|
90
91
|
},
|
|
91
|
-
|
|
92
|
+
containerSelector: () =>
|
|
93
|
+
(querySelectorLast(
|
|
94
|
+
document,
|
|
95
|
+
'div:has(> .tumbpu[title]):not(.thumbs-photo), div:has(> .thumb-holder)',
|
|
96
|
+
) as HTMLElement) || document.querySelector<HTMLElement>('.thumbs-items'),
|
|
92
97
|
animatePreview,
|
|
93
|
-
customDataSelectorFns: [
|
|
94
|
-
'filterInclude',
|
|
95
|
-
'filterExclude',
|
|
96
|
-
'filterDuration',
|
|
97
|
-
{
|
|
98
|
-
filterPrivate: (el, state) => (state.filterPrivate && el.private) as boolean,
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
filterPublic: (el, state) => (state.filterPublic && !el.private) as boolean,
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
filterHD: (el, state) => (state.filterHD && !el.hd) as boolean,
|
|
105
|
-
},
|
|
106
|
-
{
|
|
107
|
-
filterNonHD: (el, state) => (state.filterNonHD && el.hd) as boolean,
|
|
108
|
-
},
|
|
109
|
-
],
|
|
110
98
|
schemeOptions: [
|
|
111
|
-
'
|
|
99
|
+
'Title Filter',
|
|
112
100
|
'Duration Filter',
|
|
113
101
|
'Privacy Filter',
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
content: [
|
|
117
|
-
{ filterHD: false, label: 'hd' },
|
|
118
|
-
{ filterNonHD: false, label: 'non-hd' },
|
|
119
|
-
],
|
|
120
|
-
},
|
|
102
|
+
'HD Filter',
|
|
103
|
+
'Sort By',
|
|
121
104
|
'Badge',
|
|
122
105
|
{
|
|
123
106
|
title: 'Advanced',
|
|
@@ -398,6 +381,8 @@ function animatePreview(_: HTMLElement) {
|
|
|
398
381
|
(target) => {
|
|
399
382
|
const img = target.querySelector('img') as HTMLImageElement;
|
|
400
383
|
const orig = img.getAttribute('src') as string;
|
|
384
|
+
if (!orig) return;
|
|
385
|
+
|
|
401
386
|
tick.start(
|
|
402
387
|
() => iteratePreviewFrames(img),
|
|
403
388
|
() => {
|
|
@@ -610,7 +595,7 @@ async function createPrivateFeed() {
|
|
|
610
595
|
paginationSelector: '.footer',
|
|
611
596
|
},
|
|
612
597
|
schemeOptions: [
|
|
613
|
-
'
|
|
598
|
+
'Title Filter',
|
|
614
599
|
'Duration Filter',
|
|
615
600
|
'Privacy Filter',
|
|
616
601
|
'Badge',
|
|
@@ -11,12 +11,12 @@ import {
|
|
|
11
11
|
waitForElementToAppear,
|
|
12
12
|
watchElementChildrenCount,
|
|
13
13
|
} from '../../utils';
|
|
14
|
-
import { findSelfOrChild } from '../../utils/dom';
|
|
15
14
|
|
|
16
15
|
export const meta: MonkeyUserScript = {
|
|
17
16
|
name: 'Xhamster Improved',
|
|
18
|
-
version: '5.0.
|
|
19
|
-
description:
|
|
17
|
+
version: '5.0.7',
|
|
18
|
+
description:
|
|
19
|
+
'Infinite scroll [optional], Filter by Title, Duration and Watched/Unwatched. Sort by Duration and Views',
|
|
20
20
|
match: ['https://*.xhamster.com/*', 'https://*.xhamster.*/*'],
|
|
21
21
|
exclude: 'https://*.xhamster.com/embed*',
|
|
22
22
|
grant: ['GM_addElement', 'GM_addStyle', 'unsafeWindow'],
|
|
@@ -93,10 +93,8 @@ const rules = new Rules({
|
|
|
93
93
|
selectors: {
|
|
94
94
|
title: '.video-thumb-info__name,.video-thumb-info>a',
|
|
95
95
|
duration: '.thumb-image-container__duration',
|
|
96
|
-
watched: {
|
|
97
|
-
|
|
98
|
-
selector: '[data-role="video-watched',
|
|
99
|
-
},
|
|
96
|
+
watched: { selector: '[data-role="video-watched', type: 'boolean' },
|
|
97
|
+
views: { selector: '.video-thumb-views', type: 'float' },
|
|
100
98
|
},
|
|
101
99
|
},
|
|
102
100
|
thumbImg: {
|
|
@@ -104,10 +102,7 @@ const rules = new Rules({
|
|
|
104
102
|
remove: '[loading]',
|
|
105
103
|
},
|
|
106
104
|
gropeStrategy: 'all-in-all',
|
|
107
|
-
|
|
108
|
-
'filterInclude',
|
|
109
|
-
'filterExclude',
|
|
110
|
-
'filterDuration',
|
|
105
|
+
customDataFilterFns: [
|
|
111
106
|
{
|
|
112
107
|
filterWatched: (el, state) => !!(state.filterWatched && el.watched),
|
|
113
108
|
},
|
|
@@ -116,8 +111,7 @@ const rules = new Rules({
|
|
|
116
111
|
},
|
|
117
112
|
],
|
|
118
113
|
schemeOptions: [
|
|
119
|
-
'
|
|
120
|
-
'Badge',
|
|
114
|
+
'Title Filter',
|
|
121
115
|
{
|
|
122
116
|
title: 'Filter Watched',
|
|
123
117
|
content: [
|
|
@@ -125,7 +119,9 @@ const rules = new Rules({
|
|
|
125
119
|
{ filterUnwatched: false, label: 'unwatched' },
|
|
126
120
|
],
|
|
127
121
|
},
|
|
122
|
+
'Sort By',
|
|
128
123
|
'Duration Filter',
|
|
124
|
+
'Badge',
|
|
129
125
|
'Advanced',
|
|
130
126
|
],
|
|
131
127
|
animatePreview,
|
|
@@ -156,7 +152,6 @@ function animatePreview() {
|
|
|
156
152
|
const videoSrc = e
|
|
157
153
|
.querySelector('[data-previewvideo]')
|
|
158
154
|
?.getAttribute('data-previewvideo') as string;
|
|
159
|
-
console.log(container, videoSrc);
|
|
160
155
|
return createPreviewVideoElement(videoSrc, container);
|
|
161
156
|
});
|
|
162
157
|
}
|
|
@@ -5,8 +5,9 @@ import { exterminateVideo, OnHover, parseHtml } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'XVideos Improved',
|
|
8
|
-
version: '4.0.
|
|
9
|
-
description:
|
|
8
|
+
version: '4.0.7',
|
|
9
|
+
description:
|
|
10
|
+
'Infinite scroll [optional], Filter by Title, Uploader and Duration. Sort by Duration and Views.',
|
|
10
11
|
match: 'https://*.xvideos.com/*',
|
|
11
12
|
};
|
|
12
13
|
|
|
@@ -24,6 +25,8 @@ const rules = new Rules({
|
|
|
24
25
|
title: '[class*=title]',
|
|
25
26
|
uploader: '[class*=name]',
|
|
26
27
|
duration: '[class*=duration]',
|
|
28
|
+
views: { selector: '.metadata a ~ span', type: 'float' },
|
|
29
|
+
quality: { selector: '.video-hd-mark', type: 'string' },
|
|
27
30
|
},
|
|
28
31
|
callback: (thumb) => {
|
|
29
32
|
setTimeout(() => {
|
|
@@ -32,7 +35,33 @@ const rules = new Rules({
|
|
|
32
35
|
}, 200);
|
|
33
36
|
},
|
|
34
37
|
},
|
|
35
|
-
|
|
38
|
+
customDataFilterFns: [
|
|
39
|
+
{ qualityLow: (el, state) => !!state.qualityLow && el.quality !== '' },
|
|
40
|
+
{ quality360: (el, state) => !!state.quality360 && el.quality !== '360p' },
|
|
41
|
+
{ quality720: (el, state) => !!state.quality720 && el.quality !== '720p' },
|
|
42
|
+
{ quality1080: (el, state) => !!state.quality1080 && el.quality !== '1080p' },
|
|
43
|
+
{ quality1440: (el, state) => !!state.quality1440 && el.quality !== '1440p' },
|
|
44
|
+
{ quality4k: (el, state) => !!state.quality4k && el.quality !== '4k' },
|
|
45
|
+
],
|
|
46
|
+
schemeOptions: [
|
|
47
|
+
'Title Filter',
|
|
48
|
+
'Uploader Filter',
|
|
49
|
+
'Duration Filter',
|
|
50
|
+
{
|
|
51
|
+
title: 'Quality Filter',
|
|
52
|
+
content: [
|
|
53
|
+
{ qualityLow: false, label: 'Low' },
|
|
54
|
+
{ quality360: false, label: '360p' },
|
|
55
|
+
{ quality720: false, label: '720p' },
|
|
56
|
+
{ quality1080: false, label: '1080p' },
|
|
57
|
+
{ quality1440: false, label: '1440p' },
|
|
58
|
+
{ quality4k: false, label: '4k' },
|
|
59
|
+
],
|
|
60
|
+
},
|
|
61
|
+
'Sort By',
|
|
62
|
+
'Badge',
|
|
63
|
+
'Advanced',
|
|
64
|
+
],
|
|
36
65
|
animatePreview,
|
|
37
66
|
});
|
|
38
67
|
|
|
@@ -11,11 +11,14 @@ export function parseIntegerOr(n: string | number, or: number): number {
|
|
|
11
11
|
|
|
12
12
|
export function parseNumberWithLetter(str: string): number {
|
|
13
13
|
const multipliers = { k: 1e3, m: 1e6 } as const;
|
|
14
|
-
const match = str.trim().match(
|
|
14
|
+
const match = str.trim().match(/([\d., ]+)(\w)?/);
|
|
15
15
|
|
|
16
16
|
if (!match) return 0;
|
|
17
17
|
|
|
18
|
-
const
|
|
18
|
+
const s1 = match[1].replace(/,/g, '.').replace(/[ ]/g, '');
|
|
19
|
+
const s2 = s1.split('.').filter(Boolean).length < 3 ? s1 : s1.replace('.', '');
|
|
20
|
+
|
|
21
|
+
const num = parseFloat(s2);
|
|
19
22
|
const suffix = match[2]?.toLowerCase() as keyof typeof multipliers;
|
|
20
23
|
|
|
21
24
|
if (suffix && suffix in multipliers) {
|