pervert-monkey 1.0.18 → 1.0.19
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 +8 -11
- package/dist/core/pervertmonkey.core.es.js +31 -35
- package/dist/core/pervertmonkey.core.es.js.map +1 -1
- package/dist/core/pervertmonkey.core.umd.js +31 -35
- package/dist/core/pervertmonkey.core.umd.js.map +1 -1
- package/dist/test/test.user.js +10301 -0
- package/dist/userscripts/3hentai.user.js +2 -2
- package/dist/userscripts/camgirlfinder.user.js +2 -2
- package/dist/userscripts/camwhores.user.js +2 -2
- package/dist/userscripts/e-hentai.user.js +2 -2
- package/dist/userscripts/ebalka.user.js +2 -2
- package/dist/userscripts/eporner.user.js +2 -2
- package/dist/userscripts/erome.user.js +2 -2
- package/dist/userscripts/eroprofile.user.js +2 -2
- package/dist/userscripts/javhdporn.user.js +2 -2
- package/dist/userscripts/missav.user.js +2 -2
- package/dist/userscripts/motherless.user.js +2 -2
- package/dist/userscripts/namethatporn.user.js +2 -2
- package/dist/userscripts/nhentai.user.js +2 -2
- package/dist/userscripts/obmenvsem.user.js +3 -3
- package/dist/userscripts/pornhub.user.js +2 -2
- package/dist/userscripts/spankbang.user.js +2 -2
- package/dist/userscripts/thisvid.user.js +33 -27
- package/dist/userscripts/xhamster.user.js +3 -3
- package/dist/userscripts/xvideos.user.js +3 -3
- package/package.json +4 -4
- package/src/core/data-handler/data-filter.ts +4 -4
- package/src/core/infinite-scroll/index.ts +2 -9
- package/src/core/parsers/thumb-data-parser.ts +4 -3
- package/src/core/parsers/thumbs-parser.ts +9 -10
- package/src/core/rules/index.ts +6 -7
- package/src/userscripts/index.ts +1 -1
- package/src/userscripts/scripts/3hentai.ts +1 -1
- package/src/userscripts/scripts/camgirlfinder.ts +1 -1
- package/src/userscripts/scripts/camwhores.ts +1 -1
- package/src/userscripts/scripts/e-hentai.ts +1 -1
- package/src/userscripts/scripts/ebalka.ts +1 -1
- package/src/userscripts/scripts/eporner.ts +1 -1
- package/src/userscripts/scripts/erome.ts +1 -1
- package/src/userscripts/scripts/eroprofile.ts +1 -1
- package/src/userscripts/scripts/javhdporn.ts +1 -1
- package/src/userscripts/scripts/missav.ts +1 -1
- package/src/userscripts/scripts/motherless.ts +1 -1
- package/src/userscripts/scripts/namethatporn.ts +1 -1
- package/src/userscripts/scripts/nhentai.ts +1 -2
- package/src/userscripts/scripts/obmenvsem.ts +2 -2
- package/src/userscripts/scripts/pornhub.ts +1 -1
- package/src/userscripts/scripts/spankbang.ts +1 -1
- package/src/userscripts/scripts/thisvid.ts +43 -35
- package/src/userscripts/scripts/xhamster.ts +2 -2
- package/src/userscripts/scripts/xvideos.ts +2 -2
- package/src/utils/dom/index.ts +14 -10
- package/src/utils/parsers/index.ts +1 -1
package/src/userscripts/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import './scripts/
|
|
1
|
+
import './scripts/camwhores';
|
|
@@ -4,7 +4,7 @@ import { circularShift, OnHover, Tick } from '../../utils';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: '3Hentai PervertMonkey',
|
|
7
|
-
version: '1.0.
|
|
7
|
+
version: '1.0.12',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title, thumb preview',
|
|
9
9
|
match: 'https://*.3hentai.net/*',
|
|
10
10
|
};
|
|
@@ -2,7 +2,7 @@ import type { MonkeyUserScript } from 'vite-plugin-monkey';
|
|
|
2
2
|
|
|
3
3
|
export const meta: MonkeyUserScript = {
|
|
4
4
|
name: 'CamGirlFinder PervertMonkey',
|
|
5
|
-
version: '1.6.
|
|
5
|
+
version: '1.6.11',
|
|
6
6
|
description:
|
|
7
7
|
'Adds model links for CamWhores, webcamrecordings, recu.me, camvideos, privat-zapisi',
|
|
8
8
|
match: ['https://camgirlfinder.net/*'],
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
|
|
18
18
|
export const meta: MonkeyUserScript = {
|
|
19
19
|
name: 'CamWhores PervertMonkey',
|
|
20
|
-
version: '3.0.
|
|
20
|
+
version: '3.0.15',
|
|
21
21
|
description:
|
|
22
22
|
'Infinite scroll [optional]. Filter by Title, Duration and Private/Public. Sort by Duration and Views. Mass friend request button. Download button',
|
|
23
23
|
match: ['https://*.camwhores.tv', 'https://*.camwhores.*/*'],
|
|
@@ -4,7 +4,7 @@ import { downloader, exterminateVideo, OnHover, parseHtml } from '../../utils';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'Ebalka PervertMonkey',
|
|
7
|
-
version: '3.0.
|
|
7
|
+
version: '3.0.12',
|
|
8
8
|
description:
|
|
9
9
|
'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration, Download button.',
|
|
10
10
|
match: [
|
|
@@ -5,7 +5,7 @@ import { OnHover } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'Eporner PervertMonkey',
|
|
8
|
-
version: '2.0.
|
|
8
|
+
version: '2.0.14',
|
|
9
9
|
description:
|
|
10
10
|
'Infinite scroll [optional], Filter by Title, Uploader, Duration and HD, Sort by Views and Duration',
|
|
11
11
|
match: ['https://*.eporner.com/*', 'https://*.eporner.*/*'],
|
|
@@ -4,7 +4,7 @@ import { Rules } from '../../core';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'Erome PervertMonkey',
|
|
7
|
-
version: '5.0.
|
|
7
|
+
version: '5.0.13',
|
|
8
8
|
description:
|
|
9
9
|
'Infinite scroll [optional], Filter by Title, Uploader and Video/Photo albums, Sort by Views. Show/Hide Photos in album. Remove disclaimer.',
|
|
10
10
|
match: ['*://*.erome.com/*'],
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'Eroprofile PervertMonkey',
|
|
6
|
-
version: '2.0.
|
|
6
|
+
version: '2.0.12',
|
|
7
7
|
description: 'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration',
|
|
8
8
|
match: ['https://*.eroprofile.com/*'],
|
|
9
9
|
};
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'Javhdporn PervertMonkey',
|
|
6
|
-
version: '3.0.
|
|
6
|
+
version: '3.0.12',
|
|
7
7
|
description:
|
|
8
8
|
'Infinite scroll [optional], Filter by Title and Duration, Sort By Duration and Views',
|
|
9
9
|
match: ['https://*.javhdporn.net/*', 'https://*.javhdporn.*/*'],
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'Missav PervertMonkey',
|
|
6
|
-
version: '3.0.
|
|
6
|
+
version: '3.0.11',
|
|
7
7
|
description: 'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration',
|
|
8
8
|
match: [
|
|
9
9
|
'https://*.missav123.com/*',
|
|
@@ -5,7 +5,7 @@ import { fetchWith, OnHover, replaceElementTag, Tick } from '../../utils';
|
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
7
|
name: 'Motherless PervertMonkey',
|
|
8
|
-
version: '5.0.
|
|
8
|
+
version: '5.0.13',
|
|
9
9
|
description:
|
|
10
10
|
'Infinite scroll [optional], Filter by Title, Uploader and Duration, Sort by Duration and Views',
|
|
11
11
|
match: ['https://motherless.com/*'],
|
|
@@ -4,7 +4,7 @@ import { Rules } from '../../core';
|
|
|
4
4
|
|
|
5
5
|
export const meta: MonkeyUserScript = {
|
|
6
6
|
name: 'NameThatPorn PervertMonkey',
|
|
7
|
-
version: '3.0.
|
|
7
|
+
version: '3.0.12',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title, Uploader and Solved/Unsolved',
|
|
9
9
|
match: ['https://namethatporn.com/*'],
|
|
10
10
|
};
|
|
@@ -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.12',
|
|
8
8
|
description: 'Infinite scroll [optional], Filter by Title',
|
|
9
9
|
match: ['https://*.nhentai.net/*', 'https://*.nhentai.*/*'],
|
|
10
10
|
};
|
|
@@ -21,7 +21,6 @@ const nhentaiRules = new Rules({
|
|
|
21
21
|
gropeStrategy: 'all-in-all',
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
-
// 2026: now only one language works, problem in nhentai.net itself
|
|
25
24
|
const filterDescriptors = {
|
|
26
25
|
english: { query: 'english', name: '🇬🇧' },
|
|
27
26
|
japanese: { query: 'japanese', name: '🇯🇵' },
|
|
@@ -4,8 +4,8 @@ import { Rules } from '../../core';
|
|
|
4
4
|
import { fetchHtml, parseUrl } from '../../utils';
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
|
-
name: '
|
|
8
|
-
version: '1.0.
|
|
7
|
+
name: 'Obmenvsem PervertMonkey',
|
|
8
|
+
version: '1.0.14',
|
|
9
9
|
description: 'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration',
|
|
10
10
|
match: [
|
|
11
11
|
'https://*.obmenvsem.com/*',
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'PornHub PervertMonkey',
|
|
6
|
-
version: '4.0.
|
|
6
|
+
version: '4.0.12',
|
|
7
7
|
description:
|
|
8
8
|
'Infinite scroll [optional]. Filter by Title, Uploader and Duration. Sort by Duration and Views',
|
|
9
9
|
match: ['https://*.pornhub.com/*'],
|
|
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
|
|
|
3
3
|
|
|
4
4
|
export const meta: MonkeyUserScript = {
|
|
5
5
|
name: 'SpankBang.com PervertMonkey',
|
|
6
|
-
version: '4.0.
|
|
6
|
+
version: '4.0.12',
|
|
7
7
|
description:
|
|
8
8
|
'Infinite scroll [optional]. Filter by Title and Duration. Sort by Duration and Views',
|
|
9
9
|
match: ['https://*.spankbang.com/*', 'https://*.spankbang.*/*'],
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
objectToFormData,
|
|
14
14
|
parseCssUrl,
|
|
15
15
|
parseHtml,
|
|
16
|
-
querySelectorLast,
|
|
17
16
|
querySelectorLastNumber,
|
|
18
17
|
range,
|
|
19
18
|
replaceElementTag,
|
|
@@ -21,8 +20,8 @@ import {
|
|
|
21
20
|
} from '../../utils';
|
|
22
21
|
|
|
23
22
|
export const meta: MonkeyUserScript = {
|
|
24
|
-
name: 'ThisVid.com
|
|
25
|
-
version: '8.0.
|
|
23
|
+
name: 'ThisVid.com PervertMonkey',
|
|
24
|
+
version: '8.0.13',
|
|
26
25
|
description:
|
|
27
26
|
'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 📼',
|
|
28
27
|
match: ['https://*.thisvid.com/*'],
|
|
@@ -52,10 +51,46 @@ const IS_MEMBER_FRIEND =
|
|
|
52
51
|
'is in your friends',
|
|
53
52
|
);
|
|
54
53
|
|
|
54
|
+
function fixContainersDuplication() {
|
|
55
|
+
const containers = document.querySelectorAll<HTMLElement>(
|
|
56
|
+
'div:has(> .tumbpu[title]):not(.thumbs-photo), div:has(> .thumb-holder)',
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
if (containers.length < 2 || getCommonParents(containers).length > 1) return;
|
|
60
|
+
|
|
61
|
+
const mainContainer = containers.values().find((e) => e.id);
|
|
62
|
+
const badContainers = containers.values().filter((e) => !e.id);
|
|
63
|
+
|
|
64
|
+
badContainers.forEach((e) => {
|
|
65
|
+
mainContainer?.append(...e.children);
|
|
66
|
+
mainContainer && e.remove();
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
fixContainersDuplication();
|
|
71
|
+
|
|
55
72
|
function fixPlaylistThumbUrl(src: string) {
|
|
56
73
|
return src.replace(/playlist\/\d+\/video/, () => 'videos');
|
|
57
74
|
}
|
|
58
75
|
|
|
76
|
+
function getImgData(thumb: HTMLElement) {
|
|
77
|
+
const img = thumb.querySelector('img') as HTMLImageElement;
|
|
78
|
+
const privateThumb = thumb.querySelector('.private') as HTMLElement;
|
|
79
|
+
|
|
80
|
+
let imgSrc = img?.getAttribute('data-original') as string;
|
|
81
|
+
|
|
82
|
+
if (privateThumb) {
|
|
83
|
+
imgSrc = parseCssUrl(privateThumb.style.background);
|
|
84
|
+
privateThumb.removeAttribute('style');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
img.removeAttribute('data-original');
|
|
88
|
+
img.removeAttribute('data-cnt');
|
|
89
|
+
img.classList.remove('lazy-load');
|
|
90
|
+
|
|
91
|
+
return { img, imgSrc };
|
|
92
|
+
}
|
|
93
|
+
|
|
59
94
|
const defaultRulesConfig: RulesConfig = {
|
|
60
95
|
thumbs: {
|
|
61
96
|
selector:
|
|
@@ -70,30 +105,9 @@ const defaultRulesConfig: RulesConfig = {
|
|
|
70
105
|
views: { selector: '.view', type: 'number' },
|
|
71
106
|
},
|
|
72
107
|
},
|
|
73
|
-
thumbImg: {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const privateThumb = thumb.querySelector('.private') as HTMLElement;
|
|
77
|
-
|
|
78
|
-
let imgSrc = img?.getAttribute('data-original') as string;
|
|
79
|
-
|
|
80
|
-
if (privateThumb) {
|
|
81
|
-
imgSrc = parseCssUrl(privateThumb.style.background);
|
|
82
|
-
privateThumb.removeAttribute('style');
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
img.removeAttribute('data-original');
|
|
86
|
-
img.removeAttribute('data-cnt');
|
|
87
|
-
img.classList.remove('lazy-load');
|
|
88
|
-
|
|
89
|
-
return { img, imgSrc };
|
|
90
|
-
},
|
|
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'),
|
|
108
|
+
thumbImg: { getImgData },
|
|
109
|
+
containerSelectorLast:
|
|
110
|
+
'div:has(> .tumbpu[title]):not(.thumbs-photo), div:has(> .thumb-holder), .thumbs-items',
|
|
97
111
|
animatePreview,
|
|
98
112
|
schemeOptions: [
|
|
99
113
|
'Title Filter',
|
|
@@ -107,14 +121,7 @@ const defaultRulesConfig: RulesConfig = {
|
|
|
107
121
|
content: [{ autoRequestAccess: false, label: 'check access sends friend requests' }],
|
|
108
122
|
},
|
|
109
123
|
],
|
|
110
|
-
gropeStrategy:
|
|
111
|
-
getCommonParents([
|
|
112
|
-
...document.querySelectorAll<HTMLElement>(
|
|
113
|
-
'div:has(> .tumbpu[title]):not(.thumbs-photo) > .tumbpu[title], .thumb-holder',
|
|
114
|
-
),
|
|
115
|
-
]).length < 2
|
|
116
|
-
? 'all-in-one'
|
|
117
|
-
: 'all-in-all',
|
|
124
|
+
gropeStrategy: 'all-in-all',
|
|
118
125
|
};
|
|
119
126
|
|
|
120
127
|
const config: RulesConfig =
|
|
@@ -552,6 +559,7 @@ async function createPrivateFeed() {
|
|
|
552
559
|
const hideMemberVideos = (e: PointerEvent, ignore = true) => {
|
|
553
560
|
const container = (e.target as HTMLElement)?.closest('div') as HTMLElement;
|
|
554
561
|
let id = container.id;
|
|
562
|
+
feedGenerator.skip(1);
|
|
555
563
|
|
|
556
564
|
const videosCount = querySelectorLastNumber(`#${id}`);
|
|
557
565
|
document
|
|
@@ -13,8 +13,8 @@ import {
|
|
|
13
13
|
} from '../../utils';
|
|
14
14
|
|
|
15
15
|
export const meta: MonkeyUserScript = {
|
|
16
|
-
name: 'Xhamster
|
|
17
|
-
version: '5.0.
|
|
16
|
+
name: 'Xhamster PervertMonkey',
|
|
17
|
+
version: '5.0.14',
|
|
18
18
|
description:
|
|
19
19
|
'Infinite scroll [optional], Filter by Title, Duration and Watched/Unwatched. Sort by Duration and Views',
|
|
20
20
|
match: ['https://*.xhamster.com/*', 'https://*.xhamster.*/*'],
|
|
@@ -4,8 +4,8 @@ import { Rules } from '../../core';
|
|
|
4
4
|
import { exterminateVideo, OnHover, parseHtml } from '../../utils';
|
|
5
5
|
|
|
6
6
|
export const meta: MonkeyUserScript = {
|
|
7
|
-
name: 'XVideos
|
|
8
|
-
version: '4.0.
|
|
7
|
+
name: 'XVideos PervertMonkey',
|
|
8
|
+
version: '4.0.14',
|
|
9
9
|
description:
|
|
10
10
|
'Infinite scroll [optional], Filter by Title, Uploader and Duration. Sort by Duration and Views.',
|
|
11
11
|
match: 'https://*.xvideos.com/*',
|
package/src/utils/dom/index.ts
CHANGED
|
@@ -8,11 +8,11 @@ export {
|
|
|
8
8
|
watchElementChildrenCount,
|
|
9
9
|
} from './dom-observers';
|
|
10
10
|
|
|
11
|
-
export function
|
|
11
|
+
export function querySelectorOrSelf<T extends Element = HTMLElement>(
|
|
12
12
|
element: T,
|
|
13
13
|
selector: string,
|
|
14
14
|
): T | null {
|
|
15
|
-
if (element.matches(selector)) {
|
|
15
|
+
if (element.matches?.(selector)) {
|
|
16
16
|
return element as T;
|
|
17
17
|
}
|
|
18
18
|
return element.querySelector<T>(selector);
|
|
@@ -23,7 +23,10 @@ export function querySelectorLast<T extends Element = HTMLElement>(
|
|
|
23
23
|
selector: string,
|
|
24
24
|
): T | undefined {
|
|
25
25
|
const nodes = root.querySelectorAll<T>(selector);
|
|
26
|
-
|
|
26
|
+
if (nodes.length < 1) {
|
|
27
|
+
return querySelectorOrSelf<T>(root as T, selector) || undefined;
|
|
28
|
+
}
|
|
29
|
+
return nodes[nodes.length - 1];
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
export function querySelectorLastNumber(selector: string, e: ParentNode = document) {
|
|
@@ -33,7 +36,7 @@ export function querySelectorLastNumber(selector: string, e: ParentNode = docume
|
|
|
33
36
|
|
|
34
37
|
export function querySelectorText(e: ParentNode, selector?: string): string {
|
|
35
38
|
if (typeof selector !== 'string') return '';
|
|
36
|
-
const text = e
|
|
39
|
+
const text = querySelectorOrSelf(e as HTMLElement, selector)?.innerText || '';
|
|
37
40
|
return sanitizeStr(text);
|
|
38
41
|
}
|
|
39
42
|
|
|
@@ -78,12 +81,13 @@ export function removeClassesAndDataAttributes(
|
|
|
78
81
|
});
|
|
79
82
|
}
|
|
80
83
|
|
|
81
|
-
export function getCommonParents
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
export function getCommonParents<T extends HTMLElement>(
|
|
85
|
+
elements: Iterable<T>,
|
|
86
|
+
): HTMLElement[] {
|
|
87
|
+
return Map.groupBy(elements, (e) => e.parentElement)
|
|
88
|
+
.keys()
|
|
89
|
+
.filter((e) => e !== null)
|
|
90
|
+
.toArray();
|
|
87
91
|
}
|
|
88
92
|
|
|
89
93
|
export function findNextSibling<T extends Element = HTMLElement>(e: T) {
|
|
@@ -9,7 +9,7 @@ export function parseIntegerOr(n: string | number, or: number): number {
|
|
|
9
9
|
return Number.isSafeInteger(num) ? num : or;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function
|
|
12
|
+
export function parseNumericAbbreviation(str: string): number {
|
|
13
13
|
const multipliers = { k: 1e3, m: 1e6 } as const;
|
|
14
14
|
const match = str.trim().match(/([\d., ]+)(\w)?/);
|
|
15
15
|
|