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.
Files changed (53) hide show
  1. package/dist/core/pervertmonkey.core.es.d.ts +8 -11
  2. package/dist/core/pervertmonkey.core.es.js +31 -35
  3. package/dist/core/pervertmonkey.core.es.js.map +1 -1
  4. package/dist/core/pervertmonkey.core.umd.js +31 -35
  5. package/dist/core/pervertmonkey.core.umd.js.map +1 -1
  6. package/dist/test/test.user.js +10301 -0
  7. package/dist/userscripts/3hentai.user.js +2 -2
  8. package/dist/userscripts/camgirlfinder.user.js +2 -2
  9. package/dist/userscripts/camwhores.user.js +2 -2
  10. package/dist/userscripts/e-hentai.user.js +2 -2
  11. package/dist/userscripts/ebalka.user.js +2 -2
  12. package/dist/userscripts/eporner.user.js +2 -2
  13. package/dist/userscripts/erome.user.js +2 -2
  14. package/dist/userscripts/eroprofile.user.js +2 -2
  15. package/dist/userscripts/javhdporn.user.js +2 -2
  16. package/dist/userscripts/missav.user.js +2 -2
  17. package/dist/userscripts/motherless.user.js +2 -2
  18. package/dist/userscripts/namethatporn.user.js +2 -2
  19. package/dist/userscripts/nhentai.user.js +2 -2
  20. package/dist/userscripts/obmenvsem.user.js +3 -3
  21. package/dist/userscripts/pornhub.user.js +2 -2
  22. package/dist/userscripts/spankbang.user.js +2 -2
  23. package/dist/userscripts/thisvid.user.js +33 -27
  24. package/dist/userscripts/xhamster.user.js +3 -3
  25. package/dist/userscripts/xvideos.user.js +3 -3
  26. package/package.json +4 -4
  27. package/src/core/data-handler/data-filter.ts +4 -4
  28. package/src/core/infinite-scroll/index.ts +2 -9
  29. package/src/core/parsers/thumb-data-parser.ts +4 -3
  30. package/src/core/parsers/thumbs-parser.ts +9 -10
  31. package/src/core/rules/index.ts +6 -7
  32. package/src/userscripts/index.ts +1 -1
  33. package/src/userscripts/scripts/3hentai.ts +1 -1
  34. package/src/userscripts/scripts/camgirlfinder.ts +1 -1
  35. package/src/userscripts/scripts/camwhores.ts +1 -1
  36. package/src/userscripts/scripts/e-hentai.ts +1 -1
  37. package/src/userscripts/scripts/ebalka.ts +1 -1
  38. package/src/userscripts/scripts/eporner.ts +1 -1
  39. package/src/userscripts/scripts/erome.ts +1 -1
  40. package/src/userscripts/scripts/eroprofile.ts +1 -1
  41. package/src/userscripts/scripts/javhdporn.ts +1 -1
  42. package/src/userscripts/scripts/missav.ts +1 -1
  43. package/src/userscripts/scripts/motherless.ts +1 -1
  44. package/src/userscripts/scripts/namethatporn.ts +1 -1
  45. package/src/userscripts/scripts/nhentai.ts +1 -2
  46. package/src/userscripts/scripts/obmenvsem.ts +2 -2
  47. package/src/userscripts/scripts/pornhub.ts +1 -1
  48. package/src/userscripts/scripts/spankbang.ts +1 -1
  49. package/src/userscripts/scripts/thisvid.ts +43 -35
  50. package/src/userscripts/scripts/xhamster.ts +2 -2
  51. package/src/userscripts/scripts/xvideos.ts +2 -2
  52. package/src/utils/dom/index.ts +14 -10
  53. package/src/utils/parsers/index.ts +1 -1
@@ -1 +1 @@
1
- import './scripts/spankbang';
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.8',
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.7',
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.11',
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 { fetchHtml } from '../../utils';
4
4
 
5
5
  export const meta: MonkeyUserScript = {
6
6
  name: 'E-Hentai PervertMonkey',
7
- version: '1.0.8',
7
+ version: '1.0.12',
8
8
  description: 'Infinite scroll [optional], Filter by Title',
9
9
  match: ['https://*.e-hentai.org/*'],
10
10
  };
@@ -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.8',
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.10',
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.9',
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.8',
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.8',
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.7',
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.9',
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.8',
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.8',
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: 'Obmensvem PervertMonkey',
8
- version: '1.0.9',
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.8',
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.8',
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 Improved',
25
- version: '8.0.8',
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
- getImgData(thumb: HTMLElement) {
75
- const img = thumb.querySelector('img') as HTMLImageElement;
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 Improved',
17
- version: '5.0.9',
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 Improved',
8
- version: '4.0.9',
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/*',
@@ -8,11 +8,11 @@ export {
8
8
  watchElementChildrenCount,
9
9
  } from './dom-observers';
10
10
 
11
- export function findSelfOrChild<T extends HTMLElement>(
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
- return nodes.length > 0 ? nodes[nodes.length - 1] : undefined;
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.querySelector<HTMLElement>(selector)?.innerText || '';
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(elements: HTMLCollection | HTMLElement[]): HTMLElement[] {
82
- const parents = Array.from(elements)
83
- .map((e) => e.parentElement)
84
- .filter((parent): parent is HTMLElement => parent !== null);
85
-
86
- return [...new Set(parents)];
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 parseNumberWithLetter(str: string): number {
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