pervert-monkey 1.0.13 → 1.0.17

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 (58) hide show
  1. package/dist/core/pervertmonkey.core.es.d.ts +90 -36
  2. package/dist/core/pervertmonkey.core.es.js +258 -129
  3. package/dist/core/pervertmonkey.core.es.js.map +1 -1
  4. package/dist/core/pervertmonkey.core.umd.js +258 -129
  5. package/dist/core/pervertmonkey.core.umd.js.map +1 -1
  6. package/dist/userscripts/3hentai.user.js +4 -5
  7. package/dist/userscripts/camgirlfinder.user.js +2 -2
  8. package/dist/userscripts/camwhores.user.js +7 -16
  9. package/dist/userscripts/e-hentai.user.js +8 -8
  10. package/dist/userscripts/ebalka.user.js +18 -10
  11. package/dist/userscripts/eporner.user.js +24 -41
  12. package/dist/userscripts/erome.user.js +13 -16
  13. package/dist/userscripts/eroprofile.user.js +5 -14
  14. package/dist/userscripts/javhdporn.user.js +6 -5
  15. package/dist/userscripts/missav.user.js +10 -4
  16. package/dist/userscripts/motherless.user.js +13 -6
  17. package/dist/userscripts/namethatporn.user.js +10 -16
  18. package/dist/userscripts/nhentai.user.js +5 -13
  19. package/dist/userscripts/obmenvsem.user.js +11 -4
  20. package/dist/userscripts/pornhub.user.js +14 -6
  21. package/dist/userscripts/spankbang.user.js +28 -7
  22. package/dist/userscripts/thisvid.user.js +15 -33
  23. package/dist/userscripts/xhamster.user.js +13 -18
  24. package/dist/userscripts/xvideos.user.js +33 -5
  25. package/package.json +1 -1
  26. package/src/core/data-handler/data-filter-fn-defaults.ts +52 -0
  27. package/src/core/data-handler/data-filter-fn.ts +62 -0
  28. package/src/core/data-handler/data-filter.ts +31 -103
  29. package/src/core/data-handler/data-manager.ts +91 -28
  30. package/src/core/jabroni-config/default-scheme.ts +54 -5
  31. package/src/core/jabroni-config/index.ts +1 -0
  32. package/src/core/jabroni-config/jabroni-gui-controller.ts +1 -1
  33. package/src/core/jabroni-config/scheme-selectors-mapping.ts +12 -0
  34. package/src/core/parsers/thumb-data-parser.ts +15 -19
  35. package/src/core/rules/index.ts +15 -9
  36. package/src/userscripts/index.ts +1 -1
  37. package/src/userscripts/scripts/3hentai.ts +3 -4
  38. package/src/userscripts/scripts/camgirlfinder.ts +1 -1
  39. package/src/userscripts/scripts/camwhores.ts +5 -14
  40. package/src/userscripts/scripts/e-hentai.ts +12 -12
  41. package/src/userscripts/scripts/ebalka.ts +16 -8
  42. package/src/userscripts/scripts/eporner.ts +23 -39
  43. package/src/userscripts/scripts/erome.ts +13 -17
  44. package/src/userscripts/scripts/eroprofile.ts +4 -12
  45. package/src/userscripts/scripts/javhdporn.ts +7 -8
  46. package/src/userscripts/scripts/missav.ts +10 -4
  47. package/src/userscripts/scripts/motherless.ts +13 -7
  48. package/src/userscripts/scripts/namethatporn.ts +10 -17
  49. package/src/userscripts/scripts/nhentai.ts +6 -13
  50. package/src/userscripts/scripts/obmenvsem.ts +14 -4
  51. package/src/userscripts/scripts/pornhub.ts +13 -4
  52. package/src/userscripts/scripts/spankbang.ts +29 -5
  53. package/src/userscripts/scripts/thisvid.ts +16 -31
  54. package/src/userscripts/scripts/xhamster.ts +13 -18
  55. package/src/userscripts/scripts/xvideos.ts +32 -3
  56. package/src/utils/dom/dom-observers.ts +3 -3
  57. package/src/utils/dom/index.ts +1 -1
  58. package/src/utils/parsers/index.ts +5 -2
@@ -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.3',
25
+ version: '8.0.7',
25
26
  description:
26
- 'Infinite scroll [optional]. Preview for private videos. Filter: title, duration, public/private. Check access to private vids. Mass friend request button. Sorts messages. Download button 📼',
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
- containerSelectorLast: '.thumbs-items',
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
- 'Text Filter',
99
+ 'Title Filter',
112
100
  'Duration Filter',
113
101
  'Privacy Filter',
114
- {
115
- title: 'HD Filter',
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',
@@ -192,8 +175,8 @@ async function getMemberFriends(
192
175
  return g();
193
176
  }
194
177
 
195
- function getMembers(el: HTMLElement) {
196
- const friendsList = el.querySelector('#list_members_friends_items') || el;
178
+ function getMembers(e: HTMLElement) {
179
+ const friendsList = e.querySelector('#list_members_friends_items') || e;
197
180
  return Array.from(friendsList.querySelectorAll<HTMLAnchorElement>('.tumbpu') || [])
198
181
  .map((e) => e.href.match(/\d+/)?.[0] as string)
199
182
  .filter((_) => _);
@@ -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
- 'Text Filter',
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.4',
19
- description: 'Infinite scroll [optional], Filter by Title and Duration',
17
+ version: '5.0.8',
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
- type: 'boolean',
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,20 +102,16 @@ const rules = new Rules({
104
102
  remove: '[loading]',
105
103
  },
106
104
  gropeStrategy: 'all-in-all',
107
- customDataSelectorFns: [
108
- 'filterInclude',
109
- 'filterExclude',
110
- 'filterDuration',
105
+ customDataFilterFns: [
111
106
  {
112
- filterWatched: (el, state) => !!(state.filterWatched && el.watched),
107
+ filterWatched: (e, state) => !!(state.filterWatched && e.watched),
113
108
  },
114
109
  {
115
- filterUnwatched: (el, state) => !!(state.filterUnwatched && !el.watched),
110
+ filterUnwatched: (e, state) => !!(state.filterUnwatched && !e.watched),
116
111
  },
117
112
  ],
118
113
  schemeOptions: [
119
- 'Text Filter',
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,18 +152,17 @@ 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
  }
163
158
 
164
159
  function expandMoreVideoPage() {
165
160
  watchElementChildrenCount(rules.container, () => setTimeout(rules.gropeInit, 1800));
166
- waitForElementToAppear(document.body, 'button[data-role="show-more-next"]', (el) => {
161
+ waitForElementToAppear(document.body, 'button[data-role="show-more-next"]', (e) => {
167
162
  const observer = new Observer((target) => {
168
163
  (target as HTMLButtonElement).click();
169
164
  });
170
- observer.observe(el);
165
+ observer.observe(e);
171
166
  });
172
167
  }
173
168
 
@@ -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.4',
9
- description: 'Infinite scroll [optional], Filter by Title and Duration',
8
+ version: '4.0.8',
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
- schemeOptions: ['Text Filter', 'Duration Filter', 'Badge', 'Advanced'],
38
+ customDataFilterFns: [
39
+ { qualityLow: (e, state) => !!state.qualityLow && e.quality !== '' },
40
+ { quality360: (e, state) => !!state.quality360 && e.quality !== '360p' },
41
+ { quality720: (e, state) => !!state.quality720 && e.quality !== '720p' },
42
+ { quality1080: (e, state) => !!state.quality1080 && e.quality !== '1080p' },
43
+ { quality1440: (e, state) => !!state.quality1440 && e.quality !== '1440p' },
44
+ { quality4k: (e, state) => !!state.quality4k && e.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
 
@@ -4,10 +4,10 @@ export function waitForElementToAppear(
4
4
  callback: (el: Element) => void,
5
5
  ) {
6
6
  const observer = new MutationObserver((_mutations) => {
7
- const el = parent.querySelector(selector);
8
- if (el) {
7
+ const e = parent.querySelector(selector);
8
+ if (e) {
9
9
  observer.disconnect();
10
- callback(el);
10
+ callback(e);
11
11
  }
12
12
  });
13
13
 
@@ -80,7 +80,7 @@ export function removeClassesAndDataAttributes(
80
80
 
81
81
  export function getCommonParents(elements: HTMLCollection | HTMLElement[]): HTMLElement[] {
82
82
  const parents = Array.from(elements)
83
- .map((el) => el.parentElement)
83
+ .map((e) => e.parentElement)
84
84
  .filter((parent): parent is HTMLElement => parent !== null);
85
85
 
86
86
  return [...new Set(parents)];
@@ -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(/^([\d.]+)(\w)?$/);
14
+ const match = str.trim().match(/([\d., ]+)(\w)?/);
15
15
 
16
16
  if (!match) return 0;
17
17
 
18
- const num = parseFloat(match[1]);
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) {