pervert-monkey 1.0.25 → 1.0.27

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 (62) hide show
  1. package/README.md +5 -2
  2. package/dist/core/pervertmonkey.core.es.d.ts +2 -541
  3. package/dist/core/pervertmonkey.core.es.js +8005 -9281
  4. package/dist/core/pervertmonkey.core.es.js.map +1 -1
  5. package/dist/core/pervertmonkey.core.umd.js +8833 -10051
  6. package/dist/core/pervertmonkey.core.umd.js.map +1 -1
  7. package/dist/userscripts/3hentai.user.js +40 -46
  8. package/dist/userscripts/camgirlfinder.user.js +56 -49
  9. package/dist/userscripts/camwhores.user.js +286 -343
  10. package/dist/userscripts/e-hentai.user.js +54 -57
  11. package/dist/userscripts/ebalka.user.js +52 -55
  12. package/dist/userscripts/eporner.user.js +76 -68
  13. package/dist/userscripts/erome.user.js +79 -84
  14. package/dist/userscripts/eroprofile.user.js +28 -29
  15. package/dist/userscripts/javhdporn.user.js +28 -22
  16. package/dist/userscripts/missav.user.js +22 -23
  17. package/dist/userscripts/motherless.user.js +141 -152
  18. package/dist/userscripts/namethatporn.user.js +49 -58
  19. package/dist/userscripts/nhentai.user.js +104 -99
  20. package/dist/userscripts/obmenvsem.user.js +61 -69
  21. package/dist/userscripts/pornhub.user.js +50 -47
  22. package/dist/userscripts/simpcity.user.js +82 -24
  23. package/dist/userscripts/socialmediagirls.user.js +26 -22
  24. package/dist/userscripts/spankbang.user.js +62 -44
  25. package/dist/userscripts/thisvid.user.js +1269 -1366
  26. package/dist/userscripts/xhamster.user.js +119 -132
  27. package/dist/userscripts/xmegadrive.user.js +82 -0
  28. package/dist/userscripts/xvideos.user.js +115 -91
  29. package/package.json +10 -6
  30. package/src/core/data-handler/data-filter.ts +3 -3
  31. package/src/core/data-handler/data-manager.ts +11 -3
  32. package/src/core/parsers/pagination-parser/index.ts +0 -4
  33. package/src/core/parsers/pagination-parser/pagination-strategies/PaginationStrategyPathnameParams.ts +36 -1
  34. package/src/core/rules/index.ts +3 -3
  35. package/src/userscripts/index.ts +1 -1
  36. package/src/userscripts/meta.json +1 -1
  37. package/src/userscripts/scripts/3hentai.ts +1 -1
  38. package/src/userscripts/scripts/camgirlfinder.ts +1 -1
  39. package/src/userscripts/scripts/camwhores.ts +2 -2
  40. package/src/userscripts/scripts/e-hentai.ts +1 -1
  41. package/src/userscripts/scripts/ebalka.ts +2 -2
  42. package/src/userscripts/scripts/eporner.ts +41 -15
  43. package/src/userscripts/scripts/erome.ts +1 -1
  44. package/src/userscripts/scripts/eroprofile.ts +6 -6
  45. package/src/userscripts/scripts/javhdporn.ts +1 -1
  46. package/src/userscripts/scripts/missav.ts +1 -1
  47. package/src/userscripts/scripts/motherless.ts +28 -19
  48. package/src/userscripts/scripts/namethatporn.ts +1 -1
  49. package/src/userscripts/scripts/nhentai.ts +1 -1
  50. package/src/userscripts/scripts/obmenvsem.ts +1 -1
  51. package/src/userscripts/scripts/pornhub.ts +4 -2
  52. package/src/userscripts/scripts/simpcity.ts +72 -5
  53. package/src/userscripts/scripts/{socialmediagirls copy.ts → socialmediagirls.ts} +1 -1
  54. package/src/userscripts/scripts/spankbang.ts +20 -4
  55. package/src/userscripts/scripts/thisvid.ts +8 -21
  56. package/src/userscripts/scripts/xhamster.ts +12 -4
  57. package/src/userscripts/scripts/xmegadrive.ts +78 -0
  58. package/src/userscripts/scripts/xvideos.ts +26 -7
  59. package/src/utils/fetch/index.ts +2 -0
  60. package/src/utils/objects/memoize.ts +1 -9
  61. package/dist/test/test.user.js +0 -10301
  62. package/dist/userscripts/socialmediagirls copy.user.js +0 -41
@@ -15,9 +15,17 @@ export class DataManager {
15
15
  );
16
16
  public dataFilter: DataFilter;
17
17
 
18
+ public static create(
19
+ rules: Rules,
20
+ o: Partial<Pick<DataManager, 'removeDuplicates' | 'containerHomogenity'>> = {},
21
+ ) {
22
+ return new DataManager(rules, o.removeDuplicates, o.containerHomogenity);
23
+ }
24
+
18
25
  constructor(
19
- private rules: Rules,
20
- private containerHomogenity?: Parameters<typeof areElementsAlike>[2],
26
+ public rules: Rules,
27
+ public removeDuplicates = false,
28
+ public containerHomogenity?: Parameters<typeof areElementsAlike>[2],
21
29
  ) {
22
30
  this.dataFilter = new DataFilter(this.rules);
23
31
  }
@@ -65,7 +73,7 @@ export class DataManager {
65
73
  public async parseData(
66
74
  html: HTMLElement,
67
75
  container?: HTMLElement,
68
- removeDuplicates = false,
76
+ removeDuplicates = this.removeDuplicates,
69
77
  shouldLazify = true,
70
78
  ) {
71
79
  const thumbs = this.rules.thumbsParser.getThumbs(html);
@@ -19,19 +19,15 @@ export function getPaginationStrategy(
19
19
  const { url, searchParamSelector } = options;
20
20
 
21
21
  if (!pagination) {
22
- // console.error('Found No Pagination');
23
22
  return _paginationStrategy;
24
23
  }
25
24
 
26
25
  if (typeof options.getPaginationUrlGenerator === 'function') {
27
- // console.log(PaginationStrategy.name);
28
26
  return new PaginationStrategy(options);
29
27
  }
30
28
 
31
29
  const pageLinks = getPaginationLinks(pagination, url).map((l) => new URL(l));
32
30
 
33
- // console.log({ pageLinks: pageLinks.map((l) => l.href) });
34
-
35
31
  const selectStrategy = (): typeof PaginationStrategy => {
36
32
  if (PaginationStrategyDataParams.testLinks(pagination)) {
37
33
  return PaginationStrategyDataParams;
@@ -7,7 +7,8 @@ export class PaginationStrategyPathnameParams extends PaginationStrategy {
7
7
  const href = typeof a === 'string' ? a : a.href;
8
8
  const { pathname } = new URL(href, this.doc.baseURI || this.url.origin);
9
9
  return parseInt(
10
- pathname.match(this.pathnameSelector)?.pop() || this.offsetMin.toString(),
10
+ pathname.match(this.pathnameSelector)?.filter(Boolean)?.pop() ||
11
+ this.offsetMin.toString(),
11
12
  );
12
13
  };
13
14
 
@@ -55,19 +56,53 @@ export class PaginationStrategyPathnameParams extends PaginationStrategy {
55
56
  getPaginationUrlGenerator(url_: URL = this.url) {
56
57
  const url = new URL(url_.href);
57
58
 
59
+ // console.log('this.pathnameSelector', this.pathnameSelector);
60
+
58
61
  const pathnameSelectorPlaceholder = this.pathnameSelector
59
62
  .toString()
60
63
  .replace(/\\{1,}/g, '')
61
64
  .replace(/[$?()]+/g, '')
62
65
  .replace(/\/{1,}/g, '/');
63
66
 
67
+ // console.log({ pathnameSelectorPlaceholder, 'url.pathname': url.pathname });
68
+
64
69
  if (!this.pathnameSelector.test(url.pathname)) {
65
70
  url.pathname = url.pathname
66
71
  .concat(pathnameSelectorPlaceholder.replace(/d\+/, this.offsetMin.toString()))
67
72
  .replace(/\/{1,}/g, '/');
68
73
  }
69
74
 
75
+ // console.log({ pathnameSelectorPlaceholder, 'url.pathname': url.pathname });
76
+
70
77
  const paginationUrlGenerator = (offset: number) => {
78
+ // console.log({
79
+ // pathname: url.pathname,
80
+ // replace: '',
81
+ // pathnameSelector: this.pathnameSelector,
82
+ // pathnameSelectorPlaceholder: pathnameSelectorPlaceholder.replace(
83
+ // /d\+/,
84
+ // offset.toString(),
85
+ // ),
86
+ // });
87
+
88
+ // pathnameSelectorPlaceholder.replace()
89
+ // need to create tests
90
+
91
+ // url.pathname = url.pathname.replace(
92
+ // this.pathnameSelector,
93
+ // (a, b, c, d) => {
94
+ // console.log({ a, b, c, d, pathnameSelectorPlaceholder });
95
+ // if (a.length + 1 < pathnameSelectorPlaceholder.length) {
96
+ // return a.replace(/\d+/, offset.toString());
97
+ // }
98
+ // // return a;
99
+ // return pathnameSelectorPlaceholder.replace(/d\+/, offset.toString());
100
+ // },
101
+ // // pathnameSelectorPlaceholder.replace(/d\+/, offset.toString()),
102
+ // );
103
+ // return url.href;
104
+ // };
105
+
71
106
  url.pathname = url.pathname.replace(
72
107
  this.pathnameSelector,
73
108
  pathnameSelectorPlaceholder.replace(/d\+/, offset.toString()),
@@ -62,7 +62,7 @@ export class Rules {
62
62
  public paginationStrategy: PaginationStrategy;
63
63
 
64
64
  public dataManager: DataManager;
65
- public containerHomogenity: ConstructorParameters<typeof DataManager>[1];
65
+ public dataManagerOptions: Parameters<typeof DataManager.create>[1] = {};
66
66
 
67
67
  public customDataFilterFns: (Record<string, DataFilterFnFrom<any>> | string)[] = [];
68
68
  private hookDataFilterFns() {
@@ -157,7 +157,7 @@ export class Rules {
157
157
 
158
158
  this.paginationStrategy = getPaginationStrategy(this.paginationStrategyOptions);
159
159
 
160
- this.dataManager = new DataManager(this, this.containerHomogenity);
160
+ this.dataManager = DataManager.create(this, this.dataManagerOptions);
161
161
 
162
162
  this.inputController.dispose();
163
163
  this.inputController = new JabronioGuiController(this.store, this.dataManager);
@@ -188,7 +188,7 @@ export class Rules {
188
188
  this.gui = this.createGui();
189
189
 
190
190
  this.hookDataFilterFns();
191
- this.dataManager = new DataManager(this, this.containerHomogenity);
191
+ this.dataManager = DataManager.create(this, this.dataManagerOptions);
192
192
  this.inputController = new JabronioGuiController(this.store, this.dataManager);
193
193
 
194
194
  this.reset();
@@ -1 +1 @@
1
- import './scripts/motherless';
1
+ import './scripts/xmegadrive';
@@ -6,5 +6,5 @@
6
6
  "grant": ["unsafeWindow", "GM_addStyle"],
7
7
  "homepageURL": "https://sleazyfork.org/en/users/1253342-smartacephale",
8
8
  "supportURL": "https://github.com/smartacephale/sleazy-fork/issues",
9
- "source": "https://github.com/smartacephale"
9
+ "source": "https://github.com/smartacephale/sleazy-fork"
10
10
  }
@@ -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.19',
7
+ version: '1.0.20',
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.18',
5
+ version: '1.6.19',
6
6
  description:
7
7
  'Adds model links for CamWhores, webcamrecordings, recu.me, camvideos, privat-zapisi',
8
8
  match: ['https://camgirlfinder.net/*'],
@@ -17,10 +17,10 @@ import {
17
17
 
18
18
  export const meta: MonkeyUserScript = {
19
19
  name: 'CamWhores PervertMonkey',
20
- version: '3.0.22',
20
+ version: '3.0.24',
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
- match: ['https://*.camwhores.tv', 'https://*.camwhores.*/*'],
23
+ match: ['https://*.camwhores.tv/*', 'https://*.camwhores.*/*'],
24
24
  exclude: 'https://*.camwhores.tv/*mode=async*',
25
25
  };
26
26
 
@@ -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.19',
7
+ version: '1.0.20',
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.20',
7
+ version: '3.0.21',
8
8
  description:
9
9
  'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration, Download button.',
10
10
  match: [
@@ -49,7 +49,7 @@ function animatePreview(container: HTMLElement) {
49
49
  const src = e.querySelector('.card__image')?.getAttribute('data-preview') as string;
50
50
 
51
51
  const videoElem =
52
- parseHtml(`<video style="position: absolute; left: 0px; top: 0px; visibility: visible; margin-top: -1px;"
52
+ parseHtml(`<video style="position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%) scale(1.1); visibility: visible; margin-top: -1px;"
53
53
  autoplay="" loop="" playsinline="true" webkit-playsinline="true" src="${src}"></video>`) as HTMLVideoElement;
54
54
  e.appendChild(videoElem);
55
55
 
@@ -5,22 +5,24 @@ import { OnHover } from '../../utils';
5
5
 
6
6
  export const meta: MonkeyUserScript = {
7
7
  name: 'Eporner PervertMonkey',
8
- version: '2.0.21',
8
+ version: '2.1.4',
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.*/*'],
12
+ supportURL: 'https://sleazyfork.org/en/scripts/552559-eporner-pervertmonkey/feedback',
12
13
  };
13
14
 
14
- const show_video_prev = (unsafeWindow as any).show_video_prev;
15
-
16
15
  const rules = new Rules({
17
16
  paginationStrategyOptions: {
18
17
  paginationSelector: '.numlist2',
18
+ // pathnameSelector: /\/(\d+)\/?$/,
19
+ // pathnameSelector: /\/(\d+)\/([\w-]+\/)?$/
20
+ overwritePaginationLast: (n, _) => (n === 9 ? 9999 : n),
19
21
  },
20
22
  thumbs: { selector: 'div[id^=vf][data-id]' },
21
23
  thumb: {
22
24
  selectors: {
23
- title: 'a',
25
+ title: 'a[href*=video-]',
24
26
  uploader: '[title="Uploader"]',
25
27
  duration: '[title="Duration"]',
26
28
  views: { selector: '[title="Views"]', type: 'float' },
@@ -31,14 +33,40 @@ const rules = new Rules({
31
33
  strategy: 'auto',
32
34
  remove: 'auto',
33
35
  },
34
- containerSelectorLast: '#vidresults',
36
+ containerSelectorLast: 'div:has(div[id^=vf][data-id])',
35
37
  customDataFilterFns: [
36
- { quality360: (el, state) => !!state.quality360 && el.quality !== 360 },
37
- { quality480: (el, state) => !!state.quality480 && el.quality !== 480 },
38
- { quality720: (el, state) => !!state.quality720 && el.quality !== 720 },
39
- { quality1080: (el, state) => !!state.quality1080 && el.quality !== 1080 },
40
- { quality2k: (el, state) => !!state.quality2k && el.quality !== 2 },
41
- { quality4k: (el, state) => !!state.quality4k && el.quality !== 4 },
38
+ {
39
+ qualityFilter: {
40
+ handle(el, state) {
41
+ const hasAnyQualitySelected =
42
+ state.quality360 ||
43
+ state.quality480 ||
44
+ state.quality720 ||
45
+ state.quality1080 ||
46
+ state.quality2k ||
47
+ state.quality4k;
48
+
49
+ if (!hasAnyQualitySelected) return false;
50
+
51
+ return !(
52
+ (state.quality360 && el.quality === 360) ||
53
+ (state.quality480 && el.quality === 480) ||
54
+ (state.quality720 && el.quality === 720) ||
55
+ (state.quality1080 && el.quality === 1080) ||
56
+ (state.quality2k && el.quality === 2) ||
57
+ (state.quality4k && el.quality === 4)
58
+ );
59
+ },
60
+ deps: [
61
+ 'quality360',
62
+ 'quality480',
63
+ 'quality720',
64
+ 'quality1080',
65
+ 'quality2k',
66
+ 'quality4k',
67
+ ],
68
+ },
69
+ },
42
70
  ],
43
71
  schemeOptions: [
44
72
  'Title Filter',
@@ -65,9 +93,7 @@ const rules = new Rules({
65
93
 
66
94
  function animatePreview(doc: HTMLElement) {
67
95
  OnHover.create(doc, 'div[id^=vf][data-id]', (e) => {
68
- const target = e as HTMLImageElement;
69
- const thumb = target.closest('[data-id]');
70
- const id = thumb?.getAttribute('data-id');
71
- show_video_prev(id);
96
+ const thumb = e.closest('[data-id]');
97
+ (unsafeWindow as any).EP.thumbs.preview.start(thumb);
72
98
  });
73
99
  }
@@ -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.20',
7
+ version: '5.0.21',
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. Restore Search Bar',
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.19',
6
+ version: '2.0.21',
7
7
  description: 'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration',
8
8
  match: ['https://*.eroprofile.com/*'],
9
9
  };
@@ -12,17 +12,17 @@ document.querySelector('.videoGrid')?.after(document.querySelector('.clB') as HT
12
12
 
13
13
  const rules = new Rules({
14
14
  paginationStrategyOptions: {
15
- paginationSelector: '.boxNav2',
15
+ paginationSelector: '.page-nav',
16
16
  searchParamSelector: 'pnum',
17
17
  },
18
- thumbs: { selector: '.video' },
18
+ thumbs: { selector: '.grid-tile-video' },
19
19
  thumb: {
20
20
  selectors: {
21
- title: '[title]',
22
- duration: '.videoDur',
21
+ title: '.title',
22
+ duration: '.duration',
23
23
  },
24
24
  },
25
- containerSelector: '.videoGrid',
25
+ containerSelector: 'div:has(>.grid-tile-video)',
26
26
  schemeOptions: [
27
27
  'Title Filter',
28
28
  'Duration Filter',
@@ -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.19',
6
+ version: '3.0.20',
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.18',
6
+ version: '3.0.19',
7
7
  description: 'Infinite scroll [optional], Filter by Title and Duration, Sort by Duration',
8
8
  match: [
9
9
  'https://*.missav123.com/*',
@@ -1,14 +1,20 @@
1
1
  import type { MonkeyUserScript } from 'vite-plugin-monkey';
2
2
  import { GM_addElement, GM_addStyle, unsafeWindow } from '$';
3
3
  import { Rules } from '../../core';
4
- import { fetchWith, OnHover, replaceElementTag, Tick } from '../../utils';
4
+ import {
5
+ fetchWith,
6
+ OnHover,
7
+ replaceElementTag,
8
+ Tick,
9
+ waitForElementToAppear,
10
+ } from '../../utils';
5
11
 
6
12
  export const meta: MonkeyUserScript = {
7
13
  name: 'Motherless PervertMonkey',
8
- version: '5.0.20',
14
+ version: '5.0.25',
9
15
  description:
10
16
  'Infinite scroll [optional], Filter by Title, Uploader and Duration, Sort by Duration and Views',
11
- match: ['https://motherless.com/*'],
17
+ match: ['https://motherless.xxx/*'],
12
18
  grant: ['GM_addElement', 'GM_addStyle', 'unsafeWindow'],
13
19
  };
14
20
 
@@ -16,7 +22,7 @@ export const meta: MonkeyUserScript = {
16
22
  const $ = (unsafeWindow as any).$;
17
23
 
18
24
  const rules = new Rules({
19
- containerSelectorLast: '.content-inner',
25
+ containerSelector: '.content-inner',
20
26
  thumbs: { selector: '.thumb-container, .mobile-thumb' },
21
27
  thumb: {
22
28
  selectors: {
@@ -142,25 +148,28 @@ async function desktopAddMobGalleries() {
142
148
  const galleries = document.querySelector('.media-related-galleries');
143
149
  if (!galleries) return;
144
150
 
151
+ await new Promise((resolve) => {
152
+ waitForElementToAppear(document, '.thumb-container.gallery-container', resolve);
153
+ });
154
+
155
+ const mobDom = await fetchWith<HTMLElement>(window.location.href, {
156
+ type: 'html',
157
+ mobile: true,
158
+ });
159
+ const mobGalleries = mobDom.querySelectorAll<HTMLElement>('.ml-gallery-thumb');
160
+
145
161
  const galleriesContainer = galleries.querySelector('.content-inner') as HTMLElement;
146
- const galleriesCount = galleries.querySelectorAll('.gallery-container').length;
147
- const mobDom = await fetchWith(window.location.href, { type: 'html', mobile: true });
148
- const mobGalleries = (mobDom as HTMLElement).querySelectorAll<HTMLElement>(
149
- '.ml-gallery-thumb',
150
- );
151
-
152
- for (const [i, x] of mobGalleries.entries()) {
153
- if (i > galleriesCount - 1) {
154
- galleriesContainer.append(mobileGalleryToDesktop(x));
155
- }
162
+
163
+ for (const x of mobGalleries.values()) {
164
+ const mobGallery = mobileGalleryToDesktop(x);
165
+ const mobGalleryId = (mobGallery.querySelector('a') as HTMLAnchorElement).href
166
+ .split('/')
167
+ .at(-1);
168
+ if (galleriesContainer.querySelector(`a[href*=${mobGalleryId}]`)) continue;
169
+ galleriesContainer.append(mobGallery);
156
170
  }
157
171
  }
158
172
 
159
- const overwrite1 = (x: string) => `@media only screen and (max-width: 1280px) {
160
- #categories-page.inner ${x} }`;
161
-
162
- rules.dataManager.dataFilter.createCssFilters(overwrite1);
163
-
164
173
  GM_addStyle(`
165
174
  .img-container, .desktop-thumb { min-height: 150px; max-height: 150px; }
166
175
 
@@ -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.19',
7
+ version: '3.0.20',
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.19',
7
+ version: '4.0.20',
8
8
  description: 'Infinite scroll [optional], Filter by Title',
9
9
  match: ['https://*.nhentai.net/*', 'https://*.nhentai.*/*'],
10
10
  };
@@ -5,7 +5,7 @@ import { fetchHtml, parseUrl } from '../../utils';
5
5
 
6
6
  export const meta: MonkeyUserScript = {
7
7
  name: 'Obmenvsem PervertMonkey',
8
- version: '1.0.21',
8
+ version: '1.0.22',
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.19',
6
+ version: '4.0.20',
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/*'],
@@ -20,7 +20,9 @@ const rules = new Rules({
20
20
  .filter((e) => e.children.length > 0 && e.checkVisibility())
21
21
  .sort((a, b) => b.children.length - a.children.length)?.[0] as HTMLElement,
22
22
 
23
- containerHomogenity: { id: true, className: true },
23
+ dataManagerOptions: {
24
+ containerHomogenity: { id: true, className: true },
25
+ },
24
26
  thumbs: { selector: 'li[data-video-vkey]' },
25
27
  thumb: {
26
28
  selectors: {
@@ -3,13 +3,72 @@ import { Rules } from '../../core';
3
3
 
4
4
  export const meta: MonkeyUserScript = {
5
5
  name: 'Simpcity PervertMonkey',
6
- version: '1.0.1',
7
- description: 'Infinite scroll [optional], Filter by Title',
8
- match: 'https://simpcity.cr/threads/*',
6
+ version: '1.0.3',
7
+ description: 'Infinite scroll [optional], Filter by Title and Uploader, Sort by Views',
8
+ match: [
9
+ 'https://simpcity.cr/threads/*',
10
+ 'https://simpcity.cr/watched/threads*',
11
+ 'https://simpcity.cr/forums/*',
12
+ 'https://simpcity.cr/account/bookmarks*',
13
+ 'https://simpcity.cr/tags/*',
14
+ ],
9
15
  'run-at': 'document-end',
10
16
  };
11
17
 
12
- const rules = new Rules({
18
+ const IS_WATCHED_THREADS = /^\/watched\/threads/.test(location.pathname);
19
+ const IS_FORUM_PAGE = /^\/forums\//.test(location.pathname);
20
+ const IS_BOOKMARKS_PAGE = /^\/account\/bookmarks/.test(location.pathname);
21
+ const IS_TAGS_PAGE = /^\/tags\//.test(location.pathname);
22
+
23
+ type RulesConfig = ConstructorParameters<typeof Rules>[0];
24
+
25
+ const structItemConfig: RulesConfig = {
26
+ containerSelectorLast: '.structItemContainer-group, .structItemContainer',
27
+ paginationStrategyOptions: {
28
+ paginationSelector: '.block-outer--after .pageNav',
29
+ pathnameSelector: /\/page-(\d+)\/?$/,
30
+ },
31
+ thumbs: {
32
+ selector: '.structItem.structItem--thread',
33
+ },
34
+ thumb: {
35
+ selectors: {
36
+ title: '.structItem-title a',
37
+ uploader: '.structItem-parts .username',
38
+ views: {
39
+ selector: '.structItem-cell--meta dl:nth-of-type(2) dd',
40
+ type: 'float',
41
+ },
42
+ replies: {
43
+ selector: '.structItem-cell--meta dl:nth-of-type(1) dd',
44
+ type: 'float',
45
+ },
46
+ },
47
+ },
48
+ gropeStrategy: 'all-in-all',
49
+ schemeOptions: ['Title Filter', 'Uploader Filter', 'Sort By Views', 'Badge', 'Advanced'],
50
+ };
51
+
52
+ const contentRowConfig: RulesConfig = {
53
+ containerSelector: 'ol.block-body, ol.listPlain',
54
+ paginationStrategyOptions: {
55
+ paginationSelector: '.block-outer--after .pageNav',
56
+ pathnameSelector: /\/page-(\d+)\/?$/,
57
+ },
58
+ thumbs: {
59
+ selector: 'li.block-row',
60
+ },
61
+ thumb: {
62
+ selectors: {
63
+ title: '.contentRow-title a',
64
+ uploader: '.contentRow-minor .username',
65
+ },
66
+ getUrlSelector: '.contentRow-title a',
67
+ },
68
+ schemeOptions: ['Title Filter', 'Uploader Filter', 'Badge', 'Advanced'],
69
+ };
70
+
71
+ const threadConfig: RulesConfig = {
13
72
  containerSelector: '.js-replyNewMessageContainer',
14
73
  paginationStrategyOptions: {
15
74
  paginationSelector: '.block-container + * .pageNav',
@@ -24,4 +83,12 @@ const rules = new Rules({
24
83
  },
25
84
  gropeStrategy: 'all-in-all',
26
85
  schemeOptions: ['Title Filter', 'Badge', 'Advanced'],
27
- });
86
+ };
87
+
88
+ const rules = new Rules(
89
+ IS_BOOKMARKS_PAGE || IS_TAGS_PAGE
90
+ ? contentRowConfig
91
+ : IS_WATCHED_THREADS || IS_FORUM_PAGE
92
+ ? structItemConfig
93
+ : threadConfig,
94
+ );
@@ -3,7 +3,7 @@ import { Rules } from '../../core';
3
3
 
4
4
  export const meta: MonkeyUserScript = {
5
5
  name: 'Socialmediagirls PervertMonkey',
6
- version: '1.0.4',
6
+ version: '1.0.5',
7
7
  description: 'Infinite scroll [optional], Filter by Title',
8
8
  match: 'https://forums.socialmediagirls.com/threads/*',
9
9
  'run-at': 'document-end',
@@ -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.19',
6
+ version: '4.1.0',
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.*/*'],
@@ -27,9 +27,25 @@ const rules = new Rules({
27
27
  thumbImg: { strategy: 'auto' },
28
28
  gropeStrategy: 'all-in-all',
29
29
  customDataFilterFns: [
30
- { qualityLow: (e, state) => !!state.qualityLow && e.quality !== '' },
31
- { qualityHD: (e, state) => !!state.qualityHD && e.quality !== 'HD' },
32
- { quality4k: (e, state) => !!state.quality4k && e.quality !== '4K' },
30
+ {
31
+ qualityFilter: {
32
+ handle(el, state) {
33
+ const hasAnyQualitySelected =
34
+ state.qualityLow ||
35
+ state.qualityHD ||
36
+ state.quality4k;
37
+
38
+ if (!hasAnyQualitySelected) return false;
39
+
40
+ return !(
41
+ (state.qualityLow && el.quality === '') ||
42
+ (state.qualityHD && el.quality === 'HD') ||
43
+ (state.quality4k && el.quality === '4K')
44
+ );
45
+ },
46
+ deps: ['qualityLow', 'qualityHD', 'quality4k'],
47
+ },
48
+ },
33
49
  ],
34
50
  schemeOptions: [
35
51
  'Title Filter',